Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 45 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
---
name: Unit Tests

on:
push:
branches: [main, master]
pull_request:
branches: [main, master]
workflow_dispatch: true

jobs:
test:
name: Test on Node.js ${{ matrix.node-version }}
runs-on: ubuntu-latest

permissions:
contents: read

strategy:
matrix:
node-version: [18, 20, 22, latest]

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Setup Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}

- name: Install dependencies
run: npm ci

- name: Run tests
run: npm test

- name: Upload test output as artifact
if: always()
uses: actions/upload-artifact@v4
with:
name: test-output-node-${{ matrix.node-version }}
path: test-output.json
if-no-files-found: ignore
retention-days: 7
40 changes: 40 additions & 0 deletions test-benchmark.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
{
"metadata": {
"generated": "2024-01-01T00:00:00.000Z",
"source": "https://www.itu.int/pub/T-SP-E.212B-202401-I/en",
"etag": "\"benchmark\""
},
"areas": {
"united states": [
{
"name": "Verizon Wireless",
"mcc": "310",
"mnc": "004"
},
{
"name": "AT&T Mobility",
"mcc": "310",
"mnc": "410"
}
],
"canada": [
{
"name": "Rogers Wireless",
"mcc": "302",
"mnc": "720"
}
],
"united kingdom": [
{
"name": "Vodafone",
"mcc": "234",
"mnc": "015"
}
]
},
"areaNames": [
"United States",
"Canada",
"United Kingdom"
]
}
64 changes: 62 additions & 2 deletions test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,19 @@ import assert from 'node:assert/strict';
import fs from 'fs/promises';
import path from 'path';
import { fileURLToPath } from 'url';
import { spawnSync } from 'child_process';

const __dirname = path.dirname(fileURLToPath(import.meta.url));
const testOutputPath = path.join(__dirname, 'test-output.json');
const benchmarkPath = path.join(__dirname, 'test-benchmark.json');

test('MCC/MNC tool', async (t) => {
test('MCC/MNC tool - structure validation', async (t) => {
// Cleanup from previous runs
try {
await fs.unlink(testOutputPath);
} catch {}

// Run script
const { spawnSync } = await import('child_process');
const result = spawnSync('node', ['index.mjs', '--output', testOutputPath], {
stdio: 'inherit'
});
Expand All @@ -32,6 +33,65 @@ test('MCC/MNC tool', async (t) => {
assert.ok(Array.isArray(data.areaNames), 'Should have areaNames array');
assert.ok(Object.keys(data.areas).length > 0, 'Should have at least one area');

// Cleanup
await fs.unlink(testOutputPath);
});

test('MCC/MNC tool - content validation', async (t) => {
// Cleanup from previous runs
try {
await fs.unlink(testOutputPath);
} catch {}

// Run script
const result = spawnSync('node', ['index.mjs', '--output', testOutputPath], {
stdio: 'inherit'
});

assert.strictEqual(result.status, 0, 'Script should exit with code 0');

// Load actual and benchmark data
const actualContent = await fs.readFile(testOutputPath, 'utf-8');
const actual = JSON.parse(actualContent);

const benchmarkContent = await fs.readFile(benchmarkPath, 'utf-8');
const benchmark = JSON.parse(benchmarkContent);

// Validate structure matches benchmark
assert.ok(actual.metadata, 'Should have metadata object');
assert.ok(actual.areas, 'Should have areas object');
assert.ok(actual.areaNames, 'Should have areaNames array');

// Validate all area names are strings
assert.ok(actual.areaNames.every(name => typeof name === 'string'), 'All area names should be strings');

// Validate areas structure
for (const [areaKey, entries] of Object.entries(actual.areas)) {
assert.ok(Array.isArray(entries), `Area "${areaKey}" should be an array`);
for (const entry of entries) {
assert.strictEqual(typeof entry.name, 'string', `Entry name should be string in area "${areaKey}"`);
assert.strictEqual(typeof entry.mcc, 'string', `Entry mcc should be string in area "${areaKey}"`);
assert.strictEqual(typeof entry.mnc, 'string', `Entry mnc should be string in area "${areaKey}"`);
assert.ok(entry.name.length > 0, `Entry name should not be empty in area "${areaKey}"`);
assert.ok(entry.mcc.length > 0, `Entry mcc should not be empty in area "${areaKey}"`);
assert.ok(entry.mnc.length > 0, `Entry mnc should not be empty in area "${areaKey}"`);
}
}

// Validate that we have a reasonable number of areas (at least as many as benchmark)
assert.ok(
Object.keys(actual.areas).length >= Object.keys(benchmark.areas).length,
`Should have at least ${Object.keys(benchmark.areas).length} areas`
);

// Validate that we have a reasonable number of entries
const actualEntriesCount = Object.values(actual.areas).flat().length;
const benchmarkEntriesCount = Object.values(benchmark.areas).flat().length;
assert.ok(
actualEntriesCount >= benchmarkEntriesCount,
`Should have at least ${benchmarkEntriesCount} entries, got ${actualEntriesCount}`
);

// Cleanup
await fs.unlink(testOutputPath);
});