|
|
|
@ -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<boolean | 'nonexistent', number>([[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}` |
|
|
|
|
); |
|
|
|
|
} |
|
|
|
|