diff --git a/.vscode/launch.json b/.vscode/launch.json index fac01f4..2b2ffa4 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -10,7 +10,7 @@ "name": "Jasmine Tests with SQLite", "env": { "MUDBASE_DB_CONFIG": "{\"client\": \"sqlite3\", \"connection\": \":memory:\"}", - "TEST_RANDOM_SEED": "0.15844" + "TEST_RANDOM_SEED": "0.61658" }, "program": "${workspaceFolder}/server/node_modules/jasmine-ts/lib/index", "args": [ diff --git a/server/test/integration/flows/ResourceFlow.ts b/server/test/integration/flows/ResourceFlow.ts index fe2aa91..0036e07 100644 --- a/server/test/integration/flows/ResourceFlow.ts +++ b/server/test/integration/flows/ResourceFlow.ts @@ -113,7 +113,8 @@ function transformActionIDs(action: DBAction, mappings: IDMappings, rng: any) { for (const [_, value] of Object.entries(mapping)) { highest = Math.max(value, highest); } - return highest + 1 + Math.floor(rng() * 100); + let r = highest + 1 + Math.floor(rng() * 100); + return r; } switch (r.type) { @@ -124,6 +125,25 @@ function transformActionIDs(action: DBAction, mappings: IDMappings, rng: any) { track.albumId = track.albumId ? doMap(track.albumId, mappings.albums) : null; break; } + case DBActionType.CreateArtist: { + let artist = r.payload as ArtistWithRefsWithId; + artist.tagIds.forEach((id: number) => doMap(id, mappings.tags)); + artist.albumIds.forEach((id: number) => doMap(id, mappings.albums)); + artist.trackIds.forEach((id: number) => doMap(id, mappings.tracks)); + break; + } + case DBActionType.CreateAlbum: { + let album = r.payload as AlbumWithRefsWithId; + album.tagIds.forEach((id: number) => doMap(id, mappings.tags)); + album.artistIds.forEach((id: number) => doMap(id, mappings.artists)); + album.trackIds.forEach((id: number) => doMap(id, mappings.tracks)); + break; + } + case DBActionType.CreateTag: { + let tag = r.payload as TagWithRefsWithId; + tag.parentId = tag.parentId ? doMap(tag.parentId, mappings.tags) : null; + break; + } case DBActionType.DeleteTrack: { r.payload = mappings.tracks[r.payload]; break; @@ -156,6 +176,12 @@ describe('Randomized model-based DB back-end tests', () => { let refState: ReferenceDatabase | undefined = undefined; let realState: ReferenceDatabase | undefined = undefined; + // As we perform operations, the real DB and reference model may assign + // different new IDs to new objects. We need to maintain mappings in order + // to keep the operations consistent (e.g. when we make references to) + // existing objects along the way. + let idMappingsRefToReal: IDMappings | undefined = undefined; + try { // Create a reference DB. let refDB: ReferenceDatabase = _.cloneDeep(sampleDB); @@ -166,19 +192,14 @@ describe('Randomized model-based DB back-end tests', () => { await helpers.login(req, "someone@email.com", "password1A!", 200); // Import the starting DB. let importResponse: DBImportResponse = (await helpers.importDB(req, refDB[1])).body; - - // As we perform operations, the real DB and reference model may assign - // different new IDs to new objects. We need to maintain mappings in order - // to keep the operations consistent (e.g. when we make references to) - // existing objects along the way. - let idMappingsRefToReal: IDMappings = importResponse; + idMappingsRefToReal = importResponse; // Check that we are starting from an equal situation - refState = normalizeDB(refDB); - realState = normalizeDB({ + refState = refDB; + realState = { [1]: (await helpers.getExport(req)).body, - }); - expect(realState).to.deep.equal(refState); + }; + expect(normalizeDB(realState)).to.deep.equal(normalizeDB(refState)); // Create a random number generator to use throughout the test. let rng = seedrandom(seed); @@ -235,7 +256,7 @@ describe('Randomized model-based DB back-end tests', () => { }, createTagParams: { linkParent: new Map([[false, 0.45], [true, 0.45], ['nonexistent', 0.1]]), - + }, deleteTrackParams: { validTrack: new Map([[false, 0.2], [true, 0.8]]) @@ -262,10 +283,16 @@ describe('Randomized model-based DB back-end tests', () => { let { response: realResponse, status: realStatus } = await applyRealDBAction(realAction, req); // If this was an object creation action, we need to update the mappings. - if (refAction.type === DBActionType.CreateTrack) { - let refId = refResponse.id; - let realId = realResponse.id; - idMappingsRefToReal.tracks[refId] = realId; + if (refStatus === 200 && realStatus === 200) { + if (refAction.type === DBActionType.CreateTrack) { + idMappingsRefToReal.tracks[refResponse.id] = realResponse.id; + } else if (refAction.type === DBActionType.CreateAlbum) { + idMappingsRefToReal.albums[refResponse.id] = realResponse.id; + } else if (refAction.type === DBActionType.CreateArtist) { + idMappingsRefToReal.artists[refResponse.id] = realResponse.id; + } else if (refAction.type === DBActionType.CreateTag) { + idMappingsRefToReal.tags[refResponse.id] = realResponse.id; + } } // Compare the response and status. @@ -273,33 +300,42 @@ describe('Randomized model-based DB back-end tests', () => { expect(normalizeResponse(realResponse)).to.deep.equal(normalizeResponse(refResponse)); // Compare the database state after the action. - refState = normalizeDB(refDB); - realState = normalizeDB({ + let newRefState = refDB; + let newRealState = { [1]: (await helpers.getExport(req)).body, - }); - expect(realState).to.deep.equal(refState); + }; + expect(normalizeDB(newRealState)).to.deep.equal(normalizeDB(newRefState)); + realState = newRealState; + refState = newRefState; } } catch (e) { // When catching a comparison error, add and dump various states to files for debugging. e.actionTrace = actionTrace; e.startingDB = normalizeDB(sampleDB); + e.lastRefDB = refState; + e.lastRealDB = realState; e.testSeed = seed; + e.idMappingsRefToReal = idMappingsRefToReal; if (e.actual && e.expected) { - e.realDBDump = tmp.tmpNameSync(); - e.refDBDump = tmp.tmpNameSync(); - e.actionTraceDump = tmp.tmpNameSync(); - e.startingDBDump = tmp.tmpNameSync(); - fs.writeFileSync(e.realDBDump, stringify(realState, { space: ' ' })); - fs.writeFileSync(e.refDBDump, stringify(refState, { space: ' ' })); + let basename = tmp.tmpNameSync(); + e.actionTraceDump = basename + "_actiontrace"; + e.startingDBDump = basename + "_startingDB"; + e.lastRefDBDump = basename + "_lastRefDB"; + e.lastRealDBDump = basename + "_lastRealDB"; + e.idMappingsRefToRealDump = basename + "_idMappings"; fs.writeFileSync(e.actionTraceDump, stringify(e.actionTrace, { space: ' ' })); fs.writeFileSync(e.startingDBDump, stringify(e.startingDB, { space: ' ' })); + fs.writeFileSync(e.lastRefDBDump, stringify(e.lastRefDB, { space: ' ' })); + fs.writeFileSync(e.lastRealDBDump, stringify(e.lastRealDB, { space: ' ' })); + fs.writeFileSync(e.idMappingsRefToRealDump, stringify(e.idMappingsRefToReal, { space: ' ' })); console.log( "A comparison error occurred. Wrote compared values to temporary files for debugging:\n" - + ` actual: ${e.realDBDump}\n` - + ` expected: ${e.refDBDump}\n` + ` DB action trace: ${e.actionTraceDump}\n` + ` Starting DB: ${e.startingDBDump}\n` + + ` Reference DB before last action: ${e.lastRefDBDump}\n` + + ` Real DB before last action: ${e.lastRealDBDump}\n` + + ` ID Mappings from ref to real DB: ${e.idMappingsRefToRealDump}\n` + ` TEST_RANDOM_SEED: ${seed}` ); } diff --git a/server/test/reference_model/DBReferenceModel.ts b/server/test/reference_model/DBReferenceModel.ts index 0174835..3a3f107 100644 --- a/server/test/reference_model/DBReferenceModel.ts +++ b/server/test/reference_model/DBReferenceModel.ts @@ -135,7 +135,7 @@ export function createTag(userId: number, tag: PostTagRequest, db: ReferenceData userId, tag, 'tags', - [], + [{ field: 'parentId', otherObjectType: 'tags' }], [], db );