Skip to content
Merged
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
2 changes: 1 addition & 1 deletion oxfmt.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export default defineConfig({
endOfLine: 'lf',
insertFinalNewline: true,
proseWrap: 'preserve',
ignorePatterns: ['pnpm-lock.yaml', 'tests/**/snapshots/**'],
ignorePatterns: ['pnpm-lock.yaml'],
sortImports: true,
overrides: [
{
Expand Down
6 changes: 3 additions & 3 deletions tests/discogs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ A **fixture** is the JSON returned by the Discogs API for a release (e.g. `fixtu

A **snapshot** is the JSON produced by running `parseDiscogsRelease` on that fixture (e.g. `snapshots/1996829.json`). It is the expected MusicBrainz import data: artist credits, labels, discs, tracks, durations, and so on.

Snapshot files are excluded from oxfmt formatting so their format is controlled exclusively by the test runner.
Snapshot files are regenerated by the tests with stable key ordering when `pnpm test:discogs -u` is used.

When you change the parser, tests re-parse the fixtures and compare the result to the snapshots.

Expand All @@ -29,7 +29,7 @@ When you change the parser, tests re-parse the fixtures and compare the result t
1. Read each entry listed in `config.ts`.
2. Load the matching fixture from `fixtures/<id>.json`.
3. Run `parseDiscogsRelease` on it (via `load-parser.ts`, which loads the userscript in a Node VM with the required mocks).
4. Compare the output to `snapshots/<id>.json` using `toMatchFileSnapshot`.
4. Parse `snapshots/<id>.json` and compare it to the parser output with deep equality. Object key order is ignored, but array order and values still matter.

CI runs `pnpm test:discogs` on every pull request. No Discogs API calls are made during normal test runs.

Expand Down Expand Up @@ -67,7 +67,7 @@ Fetch fixtures from the Discogs API (needed after adding a release):
pnpm test:discogs:update-fixtures
```

Approve updated snapshots after an intentional parser change (no network request):
Regenerate snapshots after an intentional parser change (no network request):

```bash
pnpm test:discogs -u
Expand Down
38 changes: 36 additions & 2 deletions tests/discogs/discogs.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,47 @@ const SNAPSHOTS_DIR = path.join(DIR, 'snapshots');

const parseDiscogsRelease = loadParseDiscogsRelease();

function sortJsonValue(value: unknown): unknown {
if (Array.isArray(value)) {
return value.map(sortJsonValue);
}

if (value !== null && typeof value === 'object') {
return Object.fromEntries(
Object.entries(value)
.sort(([left], [right]) => left.localeCompare(right))
.map(([key, child]) => [key, sortJsonValue(child)]),
);
}

return value;
}

function stableStringify(value: unknown): string {
return `${JSON.stringify(sortJsonValue(JSON.parse(JSON.stringify(value))), null, 4)}\n`;
}

// These tests compare parsed JSON structurally, so mirror Vitest's snapshot update flags explicitly.
function shouldUpdateSnapshots(): boolean {
return process.argv.some(arg => arg === '-u' || arg === '--update' || arg === '--updateSnapshot');
}

const releases = RELEASES.map(({ url, description }) => ({
id: path.basename(new URL(url).pathname),
description,
}));

test.each(releases)('$id: $description', async ({ id }) => {
test.each(releases)('$id: $description', ({ id }) => {
const fixture = JSON.parse(fs.readFileSync(path.join(FIXTURES_DIR, `${id}.json`), 'utf8')) as Record<string, unknown>;
const snapshotPath = path.join(SNAPSHOTS_DIR, `${id}.json`);
const result = parseDiscogsRelease(fixture);
await expect(JSON.stringify(result, null, 4)).toMatchFileSnapshot(path.join(SNAPSHOTS_DIR, `${id}.json`));
const serializedResult = stableStringify(result);

if (shouldUpdateSnapshots() || !fs.existsSync(snapshotPath)) {
fs.writeFileSync(snapshotPath, serializedResult, 'utf8');
return;
}

const snapshot = JSON.parse(fs.readFileSync(snapshotPath, 'utf8')) as Record<string, unknown>;
expect(JSON.parse(serializedResult)).toEqual(snapshot);
});
5 changes: 4 additions & 1 deletion tests/discogs/fetch-fixtures.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ const DIR = path.dirname(fileURLToPath(import.meta.url));
const FIXTURES_DIR = path.join(DIR, 'fixtures');

function stableStringify(value: unknown): string {
return JSON.stringify(value, null, 4);
return `${JSON.stringify(value, null, 4)}\n`;
}

function fixturePath(id: number): string {
Expand Down Expand Up @@ -42,6 +42,9 @@ async function updateRelease(url: string): Promise<void> {
const fixture = await fetchFixture(url);
const id = releaseIdFromFixture(fixture, url);

// Delete unused fields that change too often and are not needed by the script.
delete fixture['community'];

fs.mkdirSync(FIXTURES_DIR, { recursive: true });
fs.writeFileSync(fixturePath(id), stableStringify(fixture));

Expand Down
147 changes: 2 additions & 145 deletions tests/discogs/fixtures/1156598.json
Original file line number Diff line number Diff line change
Expand Up @@ -110,147 +110,11 @@
}
],
"data_quality": "Correct",
"community": {
"have": 3158,
"want": 1359,
"rating": {
"count": 389,
"average": 4.36
},
"submitter": {
"username": "kinoeyevert",
"resource_url": "https://api.discogs.com/users/kinoeyevert"
},
"contributors": [
{
"username": "kinoeyevert",
"resource_url": "https://api.discogs.com/users/kinoeyevert"
},
{
"username": "kwulf",
"resource_url": "https://api.discogs.com/users/kwulf"
},
{
"username": "marcelrecords",
"resource_url": "https://api.discogs.com/users/marcelrecords"
},
{
"username": "blach",
"resource_url": "https://api.discogs.com/users/blach"
},
{
"username": "mcdustsucker",
"resource_url": "https://api.discogs.com/users/mcdustsucker"
},
{
"username": "jos_jaspers",
"resource_url": "https://api.discogs.com/users/jos_jaspers"
},
{
"username": "Lovinda",
"resource_url": "https://api.discogs.com/users/Lovinda"
},
{
"username": "3tone",
"resource_url": "https://api.discogs.com/users/3tone"
},
{
"username": "Rossmichael",
"resource_url": "https://api.discogs.com/users/Rossmichael"
},
{
"username": "nik",
"resource_url": "https://api.discogs.com/users/nik"
},
{
"username": "MannerHeikki",
"resource_url": "https://api.discogs.com/users/MannerHeikki"
},
{
"username": "kosats",
"resource_url": "https://api.discogs.com/users/kosats"
},
{
"username": "home-of-the-record",
"resource_url": "https://api.discogs.com/users/home-of-the-record"
},
{
"username": "Stathisan",
"resource_url": "https://api.discogs.com/users/Stathisan"
},
{
"username": "pshico87",
"resource_url": "https://api.discogs.com/users/pshico87"
},
{
"username": "karlglogauer",
"resource_url": "https://api.discogs.com/users/karlglogauer"
},
{
"username": "yuhann",
"resource_url": "https://api.discogs.com/users/yuhann"
},
{
"username": "solmaz.malek",
"resource_url": "https://api.discogs.com/users/solmaz.malek"
},
{
"username": "allvinyl2",
"resource_url": "https://api.discogs.com/users/allvinyl2"
},
{
"username": "Vinylfan4",
"resource_url": "https://api.discogs.com/users/Vinylfan4"
},
{
"username": "DasBosun",
"resource_url": "https://api.discogs.com/users/DasBosun"
},
{
"username": "mashanovmv",
"resource_url": "https://api.discogs.com/users/mashanovmv"
},
{
"username": "bdierkes",
"resource_url": "https://api.discogs.com/users/bdierkes"
},
{
"username": "Dreammare",
"resource_url": "https://api.discogs.com/users/Dreammare"
},
{
"username": "romel55",
"resource_url": "https://api.discogs.com/users/romel55"
},
{
"username": "dr._phibes_02",
"resource_url": "https://api.discogs.com/users/dr._phibes_02"
},
{
"username": "floodzoog",
"resource_url": "https://api.discogs.com/users/floodzoog"
},
{
"username": "anjo29",
"resource_url": "https://api.discogs.com/users/anjo29"
},
{
"username": "ernstlx",
"resource_url": "https://api.discogs.com/users/ernstlx"
},
{
"username": "templar53",
"resource_url": "https://api.discogs.com/users/templar53"
}
],
"data_quality": "Correct",
"status": "Accepted"
},
"format_quantity": 1,
"date_added": "2008-01-15T05:20:44-08:00",
"date_changed": "2025-02-16T06:54:30-08:00",
"num_for_sale": 95,
"lowest_price": 2.42,
"num_for_sale": 98,
"lowest_price": 2.38,
"master_id": 498,
"master_url": "https://api.discogs.com/masters/498",
"title": "Lizard",
Expand Down Expand Up @@ -390,13 +254,6 @@
"description": "Full Album Playlist: \nhttps://www.youtube.com/playlist?list=PLXhfRoiJBIivF7WWLowP0hkPDypPtqInQ\n\nLIZARD\n(Fripp / Sinfield)\n\nPRINCE RUPERT AWAKES\n\nFarewell the temple master’s bells \nHis kiosk and his black worm seed \nCourtship solely of his word \nWith Eden",
"duration": 0,
"embed": true
},
{
"uri": "https://www.youtube.com/watch?v=Zx4FOsyetMs",
"title": "King Crimson - Lizard (Album Visualiser)",
"description": "00:00:00 Cirkus (Including 'Entry Of The Cameleons')\n00:06:28 Indoor Games\n00:12:03 Happy Family\n00:16:26 Lady Of The Dancing Water\n00:19:14 Lizard ('Prince Rupert Awakes'/ 'Bolero'/ 'The Battle Of Glass Tears'/ 'Big Top')\n00:42:35 Studio Sessions: 'Cirku",
"duration": 0,
"embed": true
}
],
"genres": ["Rock"],
Expand Down
72 changes: 2 additions & 70 deletions tests/discogs/fixtures/1450895.json
Original file line number Diff line number Diff line change
Expand Up @@ -233,79 +233,11 @@
}
],
"data_quality": "Correct",
"community": {
"have": 323,
"want": 16,
"rating": {
"count": 16,
"average": 3.44
},
"submitter": {
"username": "aclbernie",
"resource_url": "https://api.discogs.com/users/aclbernie"
},
"contributors": [
{
"username": "aclbernie",
"resource_url": "https://api.discogs.com/users/aclbernie"
},
{
"username": "love-vinyl-records",
"resource_url": "https://api.discogs.com/users/love-vinyl-records"
},
{
"username": "Kergillian",
"resource_url": "https://api.discogs.com/users/Kergillian"
},
{
"username": "MaximusMCX",
"resource_url": "https://api.discogs.com/users/MaximusMCX"
},
{
"username": "harbour",
"resource_url": "https://api.discogs.com/users/harbour"
},
{
"username": "Bladerunner1858",
"resource_url": "https://api.discogs.com/users/Bladerunner1858"
},
{
"username": "gone4sure",
"resource_url": "https://api.discogs.com/users/gone4sure"
},
{
"username": "themusicgoesaround",
"resource_url": "https://api.discogs.com/users/themusicgoesaround"
},
{
"username": "vokuhila",
"resource_url": "https://api.discogs.com/users/vokuhila"
},
{
"username": "johntugby",
"resource_url": "https://api.discogs.com/users/johntugby"
},
{
"username": "uzumaki",
"resource_url": "https://api.discogs.com/users/uzumaki"
},
{
"username": "capitolfive",
"resource_url": "https://api.discogs.com/users/capitolfive"
},
{
"username": "andygrayrecords",
"resource_url": "https://api.discogs.com/users/andygrayrecords"
}
],
"data_quality": "Correct",
"status": "Accepted"
},
"format_quantity": 2,
"date_added": "2008-09-07T01:35:40-07:00",
"date_changed": "2020-01-12T09:14:01-08:00",
"num_for_sale": 70,
"lowest_price": 0.17,
"num_for_sale": 72,
"lowest_price": 0.18,
"master_id": 77692,
"master_url": "https://api.discogs.com/masters/77692",
"title": "Live",
Expand Down
28 changes: 0 additions & 28 deletions tests/discogs/fixtures/15313328.json
Original file line number Diff line number Diff line change
Expand Up @@ -87,34 +87,6 @@
}
],
"data_quality": "Needs Vote",
"community": {
"have": 17,
"want": 1,
"rating": {
"count": 1,
"average": 4
},
"submitter": {
"username": "steverogers",
"resource_url": "https://api.discogs.com/users/steverogers"
},
"contributors": [
{
"username": "steverogers",
"resource_url": "https://api.discogs.com/users/steverogers"
},
{
"username": "demier777",
"resource_url": "https://api.discogs.com/users/demier777"
},
{
"username": "M3acull",
"resource_url": "https://api.discogs.com/users/M3acull"
}
],
"data_quality": "Needs Vote",
"status": "Accepted"
},
"format_quantity": 41,
"date_added": "2020-05-15T15:52:30-07:00",
"date_changed": "2024-01-17T00:55:56-08:00",
Expand Down
Loading
Loading