diff --git a/.github/workflows/tests-ranges.yml b/.github/workflows/tests-ranges.yml index e20d33d..7c95868 100644 --- a/.github/workflows/tests-ranges.yml +++ b/.github/workflows/tests-ranges.yml @@ -3,7 +3,9 @@ name: "Test Suite: ranges" on: pull_request: paths: - - 'ranges/*' + - 'ranges/**' + - 'core/**' + - 'aliases/**' branches: - main workflow_dispatch: diff --git a/ranges/package.json b/ranges/package.json index abaf345..719edab 100644 --- a/ranges/package.json +++ b/ranges/package.json @@ -7,8 +7,8 @@ "scripts": { "lint": "biome check ./", "lint:write": "biome check ./ --write", - "test": "node --test", - "coverage": "c8 node --test", + "test": "node --test test/*.test.js", + "coverage": "c8 node --test test/*.test.js", "updates:check": "npx npm-check-updates", "updates:update": "npx npm-check-updates -u" }, diff --git a/ranges/test/alpha.test.js b/ranges/test/alpha.test.js index e2a14a1..633cdae 100644 --- a/ranges/test/alpha.test.js +++ b/ranges/test/alpha.test.js @@ -1,5 +1,6 @@ const { deepStrictEqual } = require('node:assert'); const ranges = require('../index'); +const fixtures = require('./fixtures'); const { describe, it } = require('node:test'); // these tests use made-up data that follows the release schedule starting with @@ -41,18 +42,8 @@ const schedule = { }, }; -// only serves the releases that would be out by `now`, like the real index.json -function at(now) { - return { - now, - fetch: async (url) => - Response.json( - url.endsWith('schedule.json') - ? schedule - : index.filter((entry) => entry.date <= now), - ), - }; -} +// serves the made-up data above instead of core's snapshots +const at = (now) => fixtures.at(now, { index, schedule }); describe('check to make sure that Alpha releases work as expected', async () => { it('should leave Alpha releases out of "all"', async () => { diff --git a/ranges/test/default.test.js b/ranges/test/default.test.js index 1e43e30..cdef3f4 100644 --- a/ranges/test/default.test.js +++ b/ranges/test/default.test.js @@ -1,10 +1,11 @@ const assert = require('node:assert'); const ranges = require('../index'); +const { at } = require('./fixtures'); const { describe, it } = require('node:test'); describe('data should exist as it is defined', async () => { it('versions should have correct types on every property', async () => { - const data = await ranges(); + const data = await ranges(undefined, at()); for (const key in data) { assert.deepStrictEqual(Array.isArray(data[key].versions), true); assert.deepStrictEqual(data[key].versions.length > 0, true); @@ -16,7 +17,7 @@ describe('data should exist as it is defined', async () => { }); it('both newest and oldest on every property should be the correct type', async () => { - const data = await ranges(); + const data = await ranges(undefined, at()); for (const key in data) { assert.deepStrictEqual(typeof data[key].newest, 'string'); assert.deepStrictEqual(typeof data[key].oldest, 'string'); diff --git a/ranges/test/fixtures.js b/ranges/test/fixtures.js new file mode 100644 index 0000000..b6be339 --- /dev/null +++ b/ranges/test/fixtures.js @@ -0,0 +1,31 @@ +const index = require('../../core/test/data/static/index.json'); +const schedule = require('../../core/test/data/static/schedule.json'); + +// the default date for tests. on 2025-09-01, v24 is Current, v22 is Active +// LTS, v20 is Maintenance LTS, and v23 has reached End of Life, so every +// range has versions. every one of those lines also has a security release +// by then, so no security field is empty either. +const now = '2025-09-01'; + +// returns options for `ranges` that serve `data` (core's index.json and +// schedule.json snapshots by default) instead of fetching them, only including +// releases that would be out by `now`, like the real index.json. any other URL +// throws, so tests never touch the network. +function at(date = now, data = { index, schedule }) { + return { + now: date, + fetch: async (url) => { + if (url.endsWith('/schedule.json')) { + return Response.json(data.schedule); + } + + if (url.endsWith('/index.json')) { + return Response.json(data.index.filter((entry) => entry.date <= date)); + } + + throw new Error(`No fixture data for ${url}`); + }, + }; +} + +module.exports = { at }; diff --git a/ranges/test/known.test.js b/ranges/test/known.test.js index 3b6d380..eecd8b3 100644 --- a/ranges/test/known.test.js +++ b/ranges/test/known.test.js @@ -1,18 +1,74 @@ const assert = require('node:assert'); const ranges = require('../index'); +const { at } = require('./fixtures'); const { describe, it } = require('node:test'); describe('check some values we know should exist', async () => { it('should include some known, static values in "all"', async () => { - const data = await ranges(); + const data = await ranges(undefined, at()); assert.deepStrictEqual(data.all.versions.includes('v19.0.0'), true); // a semver major we know exists assert.deepStrictEqual(data.all.versions.includes('v14.21.0'), true); // an LTS release we know happened assert.deepStrictEqual(data.all.versions.includes('v0.1.14'), true); // oldest included data in the array - this is the correct oldest data since it's technically the first entry on nodejs.org/dist/index.json }); it('should include some known, static values in "eol"', async () => { - const data = await ranges(); + const data = await ranges(undefined, at()); assert.deepStrictEqual(data.all.versions.includes('v14.21.0'), true); // an LTS release we know happened assert.deepStrictEqual(data.eol.versions.includes('v0.8.0'), true); // oldest included data in the array - this should be fixed since it's clearly not the oldest in the dataset. }); + + // on 2025-09-01, v24 is Current, v22 is Active LTS, v20 is Maintenance LTS, + // and v23 is the newest End of Life line + it('should put each release line in the right range', async () => { + const data = await ranges(undefined, at()); + const summary = Object.fromEntries( + Object.entries(data).map(([alias, { versions, ...range }]) => [ + alias, + range, + ]), + ); + + assert.deepStrictEqual(summary, { + all: { + newestLts: 'v22.19.0', + newestSecurity: 'v24.4.1', + newest: 'v24.7.0', + oldest: 'v0.1.14', + }, + current: { + oldestSecurity: '24.0.2', + newestSecurity: '24.4.1', + newest: 'v24.7.0', + oldest: 'v24.0.0', + }, + 'lts/latest': { + oldestLts: '22.11.0', + oldestSecurity: '22.4.1', + newestLts: '22.19.0', + newestSecurity: '22.17.1', + newest: 'v22.19.0', + oldest: 'v22.11.0', + }, + 'lts/active': { + oldestLts: '20.9.0', + oldestSecurity: '20.3.1', + newestLts: '22.19.0', + newestSecurity: '22.17.1', + newest: 'v22.19.0', + oldest: 'v20.9.0', + }, + 'lts/maintenance': { + oldestLts: '20.9.0', + oldestSecurity: '20.3.1', + newestLts: '20.19.4', + newestSecurity: '20.19.4', + newest: 'v20.19.4', + oldest: 'v20.9.0', + }, + eol: { + newest: 'v23.11.1', + oldest: 'v0.8.0', + }, + }); + }); }); diff --git a/ranges/test/matrixAlias.test.js b/ranges/test/matrixAlias.test.js index 91c9f9f..920e311 100644 --- a/ranges/test/matrixAlias.test.js +++ b/ranges/test/matrixAlias.test.js @@ -1,5 +1,6 @@ const assert = require('node:assert'); const ranges = require('../index'); +const { at } = require('./fixtures'); const { describe, it } = require('node:test'); // this is a non-exhaustive list of sets of aliases. @@ -7,7 +8,7 @@ const { describe, it } = require('node:test'); describe('data should exist as it is defined when calling multiple aliases', async () => { // begin by testing some of the more risky logic in the data construction it('versions should have correct types on every property if an array when "lts/latest" and "lts/maintenance" are passed', async () => { - const data = await ranges(['lts/latest', 'lts/maintenance']); + const data = await ranges(['lts/latest', 'lts/maintenance'], at()); // check lts/latest values assert.deepStrictEqual(Array.isArray(data['lts/latest'].versions), true); @@ -37,7 +38,7 @@ describe('data should exist as it is defined when calling multiple aliases', asy }); it('versions should have correct types on every property if an array when "lts/latest" and "lts/active" are passed', async () => { - const data = await ranges(['lts/latest', 'lts/active']); + const data = await ranges(['lts/latest', 'lts/active'], at()); // check lts/latest values assert.deepStrictEqual(Array.isArray(data['lts/latest'].versions), true); @@ -64,7 +65,7 @@ describe('data should exist as it is defined when calling multiple aliases', asy }); it('versions should have correct types on every property if an array when "lts/maintenance" and "lts/active" are passed', async () => { - const data = await ranges(['lts/maintenance', 'lts/active']); + const data = await ranges(['lts/maintenance', 'lts/active'], at()); // check lts/maintenance values assert.deepStrictEqual( @@ -94,7 +95,10 @@ describe('data should exist as it is defined when calling multiple aliases', asy }); it('versions should have correct types on every property if an array when "lts/maintenance", "lts/active", and "lts/latest" are passed', async () => { - const data = await ranges(['lts/maintenance', 'lts/active', 'lts/latest']); + const data = await ranges( + ['lts/maintenance', 'lts/active', 'lts/latest'], + at(), + ); // check lts/maintenance values assert.deepStrictEqual( @@ -138,7 +142,7 @@ describe('data should exist as it is defined when calling multiple aliases', asy // we want to make sure that we're specifically testing the translations // as a set to ensure that it is direct and clear when they're breaking. it('should pass the "lts" translation ("lts/active" and "lts/maintenance")', async () => { - const data = await ranges(['lts/active', 'lts/maintenance']); + const data = await ranges(['lts/active', 'lts/maintenance'], at()); // check lts/active values assert.deepStrictEqual(Array.isArray(data['lts/active'].versions), true); @@ -171,7 +175,10 @@ describe('data should exist as it is defined when calling multiple aliases', asy }); it('should pass the "supported" translation ("current", "lts/active", and "lts/maintenance")', async () => { - const data = await ranges(['current', 'lts/active', 'lts/maintenance']); + const data = await ranges( + ['current', 'lts/active', 'lts/maintenance'], + at(), + ); // check current values assert.deepStrictEqual(Array.isArray(data.current.versions), true); @@ -211,7 +218,10 @@ describe('data should exist as it is defined when calling multiple aliases', asy }); it('should pass the "supported" translation ("current", "lts/active", and "lts/maintenance")', async () => { - const data = await ranges(['current', 'lts/active', 'lts/maintenance']); + const data = await ranges( + ['current', 'lts/active', 'lts/maintenance'], + at(), + ); // check current values assert.deepStrictEqual(Array.isArray(data.current.versions), true); @@ -251,12 +261,10 @@ describe('data should exist as it is defined when calling multiple aliases', asy }); it('should pass the "all" translation ("current", "lts/active", "lts/maintenance", and "eol")', async () => { - const data = await ranges([ - 'current', - 'lts/active', - 'lts/maintenance', - 'eol', - ]); + const data = await ranges( + ['current', 'lts/active', 'lts/maintenance', 'eol'], + at(), + ); // check current values assert.deepStrictEqual(Array.isArray(data.current.versions), true); diff --git a/ranges/test/singleAlias.test.js b/ranges/test/singleAlias.test.js index 47996cc..980310b 100644 --- a/ranges/test/singleAlias.test.js +++ b/ranges/test/singleAlias.test.js @@ -1,10 +1,11 @@ const assert = require('node:assert'); const ranges = require('../index'); +const { at } = require('./fixtures'); const { describe, it } = require('node:test'); describe('data should exist as it is defined when calling a single alias', async () => { it('versions should have correct types on every property if an array with "all" is passed', async () => { - const data = await ranges(['all']); + const data = await ranges(['all'], at()); assert.deepStrictEqual(Array.isArray(data.all.versions), true); assert.deepStrictEqual(data.all.versions.length > 0, true); assert.deepStrictEqual( @@ -14,7 +15,7 @@ describe('data should exist as it is defined when calling a single alias', async }); it('versions should have correct types on every property if an array with "current" is passed', async () => { - const data = await ranges(['current']); + const data = await ranges(['current'], at()); assert.deepStrictEqual(Array.isArray(data.current.versions), true); assert.deepStrictEqual(data.current.versions.length > 0, true); assert.deepStrictEqual( @@ -24,7 +25,7 @@ describe('data should exist as it is defined when calling a single alias', async }); it('versions should have correct types on every property if an array with "lts/latest" is passed', async () => { - const data = await ranges(['lts/latest']); + const data = await ranges(['lts/latest'], at()); assert.deepStrictEqual(Array.isArray(data['lts/latest'].versions), true); assert.deepStrictEqual(data['lts/latest'].versions.length > 0, true); assert.deepStrictEqual( @@ -40,7 +41,7 @@ describe('data should exist as it is defined when calling a single alias', async }); it('versions should have correct types on every property if an array with "lts/active" is passed', async () => { - const data = await ranges(['lts/active']); + const data = await ranges(['lts/active'], at()); assert.deepStrictEqual(Array.isArray(data['lts/active'].versions), true); assert.deepStrictEqual(data['lts/active'].versions.length > 0, true); assert.deepStrictEqual( @@ -56,7 +57,7 @@ describe('data should exist as it is defined when calling a single alias', async }); it('versions should have correct types on every property if an array with "lts/maintenance" is passed', async () => { - const data = await ranges(['lts/maintenance']); + const data = await ranges(['lts/maintenance'], at()); assert.deepStrictEqual( Array.isArray(data['lts/maintenance'].versions), true, @@ -75,7 +76,7 @@ describe('data should exist as it is defined when calling a single alias', async }); it('versions should have correct types on every property if an array with "eol" is passed', async () => { - const data = await ranges(['eol']); + const data = await ranges(['eol'], at()); assert.deepStrictEqual(Array.isArray(data.eol.versions), true); assert.deepStrictEqual(data.eol.versions.length > 0, true); assert.deepStrictEqual( diff --git a/ranges/test/unknownFilter.test.js b/ranges/test/unknownFilter.test.js index cd47ab8..bd4b73b 100644 --- a/ranges/test/unknownFilter.test.js +++ b/ranges/test/unknownFilter.test.js @@ -1,11 +1,12 @@ const assert = require('node:assert'); const ranges = require('../index'); +const { at } = require('./fixtures'); const { describe, it } = require('node:test'); describe('failure states for arguments', async () => { it('should throw an error on a string with an unknown value', async () => { try { - const data = await ranges('poopin'); + const data = await ranges('poopin', at()); if (data) throw new Error('Should not have gotten here'); } catch (error) { assert.deepStrictEqual( @@ -17,7 +18,7 @@ describe('failure states for arguments', async () => { it('should throw an error on a string with an unknown value', async () => { try { - const data = await ranges('poopin'); + const data = await ranges('poopin', at()); if (data) throw new Error('Should not have gotten here'); } catch (error) { assert.deepStrictEqual( @@ -29,7 +30,7 @@ describe('failure states for arguments', async () => { it('should throw an error on a single unknown array entry', async () => { try { - const data = await ranges(['poopin']); + const data = await ranges(['poopin'], at()); if (data) throw new Error('Should not have gotten here'); } catch (error) { assert.deepStrictEqual( @@ -41,7 +42,7 @@ describe('failure states for arguments', async () => { it('should throw an error on an unknown array entry where multiple entries exist and it is the first entry', async () => { try { - const data = await ranges(['poopin', 'lts/active']); + const data = await ranges(['poopin', 'lts/active'], at()); if (data) throw new Error('Should not have gotten here'); } catch (error) { assert.deepStrictEqual( @@ -53,7 +54,7 @@ describe('failure states for arguments', async () => { it('should throw an error on an unknown array entry where multiple entries exist and it is the first entry', async () => { try { - const data = await ranges(['lts/active', 'poopin']); + const data = await ranges(['lts/active', 'poopin'], at()); if (data) throw new Error('Should not have gotten here'); } catch (error) { assert.deepStrictEqual( @@ -65,7 +66,7 @@ describe('failure states for arguments', async () => { it('should throw an error on passing an Object', async () => { try { - const data = await ranges({}); + const data = await ranges({}, at()); if (data) throw new Error('Should not have gotten here'); } catch (error) { assert.deepStrictEqual( diff --git a/ranges/test/v.test.js b/ranges/test/v.test.js index 38bea7b..534926a 100644 --- a/ranges/test/v.test.js +++ b/ranges/test/v.test.js @@ -1,11 +1,12 @@ const assert = require('node:assert'); const ranges = require('../index'); +const { at } = require('./fixtures'); const { describe, it } = require('node:test'); // tests various values to ensure that the start with the correct character describe('check that "v"s are where they should (and are not where they should not be)', async () => { it('should include the letter v before the strings that should have it', async () => { - const data = await ranges(); + const data = await ranges(undefined, at()); // loop over all versions arrays in all sets (all, // current, lts/latest, lts/active, lts/maintenance, @@ -29,7 +30,7 @@ describe('check that "v"s are where they should (and are not where they should n // this probably needs to be fixed. It's a bug in core and it trickles down to ranges. it('is missing the letter v where it should not be... for now', async () => { - const data = await ranges(); + const data = await ranges(undefined, at()); assert.deepStrictEqual(data.current.oldestSecurity.startsWith('v'), false); assert.deepStrictEqual(data.current.newestSecurity.startsWith('v'), false);