From 6b615093822f89d0a9bf0a83851c10104d02545f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 15 Dec 2025 07:28:50 +0000 Subject: [PATCH 1/5] Initial plan From e77c94a766f947b11143d62d6ff51ae27dfc8e0b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 15 Dec 2025 07:31:46 +0000 Subject: [PATCH 2/5] Add CI unit test workflow and benchmark validation Co-authored-by: meros <450310+meros@users.noreply.github.com> --- .github/workflows/test.yml | 41 +++++++++++++++++++++++++ test-benchmark.json | 40 ++++++++++++++++++++++++ test.mjs | 63 +++++++++++++++++++++++++++++++++++++- 3 files changed, 143 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/test.yml create mode 100644 test-benchmark.json diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..8c980ec --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,41 @@ +name: Unit Tests + +on: + push: + branches: [ main, master ] + pull_request: + branches: [ main, master ] + workflow_dispatch: + +jobs: + test: + name: Test on Node.js ${{ matrix.node-version }} + runs-on: ubuntu-latest + + strategy: + matrix: + node-version: [18, 20, 22] + + 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 diff --git a/test-benchmark.json b/test-benchmark.json new file mode 100644 index 0000000..8cb41db --- /dev/null +++ b/test-benchmark.json @@ -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" + ] +} diff --git a/test.mjs b/test.mjs index eafc5b3..52f99ae 100644 --- a/test.mjs +++ b/test.mjs @@ -6,8 +6,9 @@ import { fileURLToPath } from 'url'; 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); @@ -32,6 +33,66 @@ 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 { spawnSync } = await import('child_process'); + 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.ok(entry.name, `Entry should have name in area "${areaKey}"`); + assert.ok(entry.mcc, `Entry should have mcc in area "${areaKey}"`); + assert.ok(entry.mnc, `Entry should have mnc in area "${areaKey}"`); + assert.strictEqual(typeof entry.name, 'string', 'Entry name should be string'); + assert.strictEqual(typeof entry.mcc, 'string', 'Entry mcc should be string'); + assert.strictEqual(typeof entry.mnc, 'string', 'Entry mnc should be string'); + } + } + + // 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); }); \ No newline at end of file From f250dfb5a49c605a60e1b4455563d82e2296c002 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 15 Dec 2025 07:33:17 +0000 Subject: [PATCH 3/5] Fix YAML linting issues and add latest Node.js version Co-authored-by: meros <450310+meros@users.noreply.github.com> --- .github/workflows/test.yml | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 8c980ec..185e485 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -1,36 +1,37 @@ +--- name: Unit Tests on: push: - branches: [ main, master ] + branches: [main, master] pull_request: - branches: [ main, master ] - workflow_dispatch: + branches: [main, master] + workflow_dispatch: true jobs: test: name: Test on Node.js ${{ matrix.node-version }} runs-on: ubuntu-latest - + strategy: matrix: - node-version: [18, 20, 22] - + 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 From 4c1a6e928480f14970f5a409d9fe5fee484f967d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 15 Dec 2025 07:34:40 +0000 Subject: [PATCH 4/5] Address code review feedback - improve test assertions Co-authored-by: meros <450310+meros@users.noreply.github.com> --- test.mjs | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/test.mjs b/test.mjs index 52f99ae..d30add4 100644 --- a/test.mjs +++ b/test.mjs @@ -3,6 +3,7 @@ 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'); @@ -15,7 +16,6 @@ test('MCC/MNC tool - structure validation', async (t) => { } catch {} // Run script - const { spawnSync } = await import('child_process'); const result = spawnSync('node', ['index.mjs', '--output', testOutputPath], { stdio: 'inherit' }); @@ -44,7 +44,6 @@ test('MCC/MNC tool - content validation', async (t) => { } catch {} // Run script - const { spawnSync } = await import('child_process'); const result = spawnSync('node', ['index.mjs', '--output', testOutputPath], { stdio: 'inherit' }); @@ -70,12 +69,12 @@ test('MCC/MNC tool - content validation', async (t) => { 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.ok(entry.name, `Entry should have name in area "${areaKey}"`); - assert.ok(entry.mcc, `Entry should have mcc in area "${areaKey}"`); - assert.ok(entry.mnc, `Entry should have mnc in area "${areaKey}"`); - assert.strictEqual(typeof entry.name, 'string', 'Entry name should be string'); - assert.strictEqual(typeof entry.mcc, 'string', 'Entry mcc should be string'); - assert.strictEqual(typeof entry.mnc, 'string', 'Entry mnc should be string'); + 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}"`); } } From d07421cd8d1d31afedf575318cb0e5ed8fb6e598 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 15 Dec 2025 07:35:40 +0000 Subject: [PATCH 5/5] Add explicit permissions to workflow for security Co-authored-by: meros <450310+meros@users.noreply.github.com> --- .github/workflows/test.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 185e485..60542f0 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -13,6 +13,9 @@ jobs: name: Test on Node.js ${{ matrix.node-version }} runs-on: ubuntu-latest + permissions: + contents: read + strategy: matrix: node-version: [18, 20, 22, latest]