From cd26f79e4bd717b7cacac5c50789061abb6a8fcf Mon Sep 17 00:00:00 2001 From: Sander Vocke Date: Wed, 16 Dec 2020 20:08:55 +0100 Subject: [PATCH] Add deletions. --- .vscode/launch.json | 2 +- client/src/api/endpoints/data.ts | 6 +- server/db/Album.ts | 24 +- server/endpoints/Tag.ts | 2 +- server/migrations/20200828124218_init_db.ts | 1 - server/test/integration/flows/ResourceFlow.ts | 66 +++-- server/test/integration/helpers.ts | 42 ++++ .../test/reference_model/DBReferenceModel.ts | 129 ++++++++-- server/test/reference_model/randomGen.ts | 233 +++++++++--------- 9 files changed, 337 insertions(+), 168 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index 2b2ffa4..05c4452 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.61658" + "TEST_RANDOM_SEED": "0.77715" }, "program": "${workspaceFolder}/server/node_modules/jasmine-ts/lib/index", "args": [ diff --git a/client/src/api/endpoints/data.ts b/client/src/api/endpoints/data.ts index ad79b3b..e94f97b 100644 --- a/client/src/api/endpoints/data.ts +++ b/client/src/api/endpoints/data.ts @@ -5,10 +5,14 @@ // referencing between objects. They do not correspond to IDs in the actual // database. // Upon import, they might be replaced, and upon export, they might be randomly +// generated. import { AlbumWithRefsWithId, ArtistWithRefsWithId, isAlbumWithRefs, isArtistWithRefs, isTagWithRefs, isTrackBaseWithRefs, isTrackWithRefs, TagWithRefsWithId, TrackWithRefsWithId } from "../types/resources"; -// generated. +// The import/export DB format is just a set of lists of objects. +// Each object has an ID and references others by ID. +// Any object referenced by ID also has a reverse reference. +// In other words, if A references B, B must also reference A. export interface DBDataFormat { tracks: TrackWithRefsWithId[], albums: AlbumWithRefsWithId[], diff --git a/server/db/Album.ts b/server/db/Album.ts index fa075ba..012d1ba 100644 --- a/server/db/Album.ts +++ b/server/db/Album.ts @@ -73,7 +73,7 @@ export async function getAlbum(id: number, userId: number, knex: Knex): // Returns the id of the created album. export async function createAlbum(userId: number, album: AlbumWithRefs, knex: Knex): Promise { - return await knex.transaction(async (trx) => { + return await knex.transaction(async (trx) => { // Start retrieving artists. const artistIdsPromise: Promise = trx.select('id') @@ -181,10 +181,10 @@ export async function modifyAlbum(userId: number, albumId: number, album: AlbumB // Start retrieving tracks if we are modifying those. const trackIdsPromise: Promise = album.trackIds ? - trx.select('artistId') - .from('tracks_albums') + trx.select('id') + .from('tracks') .whereIn('albumId', album.trackIds) - .then((as: any) => as.map((a: any) => a['trackId'])) + .then((as: any) => as.map((a: any) => a['id'])) : (async () => undefined)(); // Start retrieving tags if we are modifying those. @@ -229,11 +229,11 @@ export async function modifyAlbum(userId: number, albumId: number, album: AlbumB .whereNotIn('tagId', album.tagIds || []) .delete() : undefined; - // Remove unlinked tracks. - const removeUnlinkedTracks = tracks ? trx('tracks_albums') + // Remove unlinked tracks by setting their references to null. + const removeUnlinkedTracks = tracks ? trx('tracks') .where({ 'albumId': albumId }) - .whereNotIn('trackId', album.trackIds || []) - .delete() : undefined; + .whereNotIn('id', album.trackIds || []) + .update({ 'albumId': null }) : undefined; // Link new artists. const addArtists = artists ? trx('artists_albums') @@ -356,11 +356,11 @@ export async function deleteAlbum(userId: number, albumId: number, knex: Knex): .from('albums_tags') .where({ 'albumId': albumId }); - // Start deleting track associations with the album. + // Start deleting track associations with the album by setting their references to null. const deleteTracksPromise: Promise = - trx.delete() - .from('tracks_albums') - .where({ 'albumId': albumId }); + trx.update({ 'album': null }) + .from('tracks') + .where({ 'album': albumId }); // Start deleting the album. const deleteAlbumPromise: Promise = diff --git a/server/endpoints/Tag.ts b/server/endpoints/Tag.ts index 8a1e06a..a44ab6c 100644 --- a/server/endpoints/Tag.ts +++ b/server/endpoints/Tag.ts @@ -37,7 +37,7 @@ export const DeleteTag: EndpointHandler = async (req: any, res: any, knex: Knex) try { - deleteTag(userId, req.params.id, knex); + await deleteTag(userId, req.params.id, knex); res.status(200).send(); } catch (e) { diff --git a/server/migrations/20200828124218_init_db.ts b/server/migrations/20200828124218_init_db.ts index 5080a7f..eddbe8b 100644 --- a/server/migrations/20200828124218_init_db.ts +++ b/server/migrations/20200828124218_init_db.ts @@ -204,7 +204,6 @@ export async function down(knex: Knex): Promise { await knex.schema.dropTable('albums'); await knex.schema.dropTable('tags'); await knex.schema.dropTable('tracks_artists'); - await knex.schema.dropTable('tracks_albums'); await knex.schema.dropTable('tracks_tags'); await knex.schema.dropTable('artists_tags'); await knex.schema.dropTable('albums_tags'); diff --git a/server/test/integration/flows/ResourceFlow.ts b/server/test/integration/flows/ResourceFlow.ts index 0036e07..367544e 100644 --- a/server/test/integration/flows/ResourceFlow.ts +++ b/server/test/integration/flows/ResourceFlow.ts @@ -144,10 +144,20 @@ function transformActionIDs(action: DBAction, mappings: IDMappings, rng: any) { tag.parentId = tag.parentId ? doMap(tag.parentId, mappings.tags) : null; break; } - case DBActionType.DeleteTrack: { - r.payload = mappings.tracks[r.payload]; - break; - } + case DBActionType.DeleteTrack: + case DBActionType.DeleteArtist: + case DBActionType.DeleteAlbum: + case DBActionType.DeleteTag: + { + let keys = { + [DBActionType.DeleteTrack]: 'tracks', + [DBActionType.DeleteArtist]: 'artists', + [DBActionType.DeleteAlbum]: 'albums', + [DBActionType.DeleteTag]: 'tags', + } + r.payload = (mappings as any)[keys[r.type]][r.payload]; + break; + } } return r; } @@ -208,11 +218,14 @@ describe('Randomized model-based DB back-end tests', () => { // be generated. let dist: RandomDBActionDistribution = { type: new Map([ - [DBActionType.CreateTrack, 0.2], - [DBActionType.CreateArtist, 0.2], - [DBActionType.CreateAlbum, 0.2], - [DBActionType.CreateTag, 0.2], - [DBActionType.DeleteTrack, 0.2] + [DBActionType.CreateTrack, 0.125], + [DBActionType.CreateArtist, 0.125], + [DBActionType.CreateAlbum, 0.125], + [DBActionType.CreateTag, 0.125], + [DBActionType.DeleteTrack, 0.125], + [DBActionType.DeleteArtist, 0.125], + [DBActionType.DeleteAlbum, 0.125], + [DBActionType.DeleteTag, 0.125], ]), userId: new Map([[1, 1.0]]), createTrackParams: { @@ -259,8 +272,17 @@ describe('Randomized model-based DB back-end tests', () => { }, deleteTrackParams: { - validTrack: new Map([[false, 0.2], [true, 0.8]]) - } + validId: new Map([[false, 0.2], [true, 0.8]]) + }, + deleteArtistParams: { + validId: new Map([[false, 0.2], [true, 0.8]]) + }, + deleteAlbumParams: { + validId: new Map([[false, 0.2], [true, 0.8]]) + }, + deleteTagParams: { + validId: new Map([[false, 0.2], [true, 0.8]]) + }, } // Loop to generate and execute a bunch of random actions. @@ -284,14 +306,12 @@ describe('Randomized model-based DB back-end tests', () => { // If this was an object creation action, we need to update the mappings. 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; + switch(refAction.type) { + case DBActionType.CreateTrack: { idMappingsRefToReal.tracks[refResponse.id] = realResponse.id; break; } + case DBActionType.CreateArtist: { idMappingsRefToReal.artists[refResponse.id] = realResponse.id; break; } + case DBActionType.CreateAlbum: { idMappingsRefToReal.albums[refResponse.id] = realResponse.id; break; } + case DBActionType.CreateTag: { idMappingsRefToReal.tags[refResponse.id] = realResponse.id; break; } + default: { break; } } } @@ -300,7 +320,7 @@ describe('Randomized model-based DB back-end tests', () => { expect(normalizeResponse(realResponse)).to.deep.equal(normalizeResponse(refResponse)); // Compare the database state after the action. - let newRefState = refDB; + let newRefState = _.cloneDeep(refDB); let newRealState = { [1]: (await helpers.getExport(req)).body, }; @@ -318,11 +338,15 @@ describe('Randomized model-based DB back-end tests', () => { e.idMappingsRefToReal = idMappingsRefToReal; if (e.actual && e.expected) { let basename = tmp.tmpNameSync(); + e.actualDump = basename + "_actual"; + e.expectedDump = basename + "_expected"; e.actionTraceDump = basename + "_actiontrace"; e.startingDBDump = basename + "_startingDB"; e.lastRefDBDump = basename + "_lastRefDB"; e.lastRealDBDump = basename + "_lastRealDB"; e.idMappingsRefToRealDump = basename + "_idMappings"; + fs.writeFileSync(e.actualDump, stringify(e.actual, { space: ' ' })); + fs.writeFileSync(e.expectedDump, stringify(e.expected, { space: ' ' })); fs.writeFileSync(e.actionTraceDump, stringify(e.actionTrace, { space: ' ' })); fs.writeFileSync(e.startingDBDump, stringify(e.startingDB, { space: ' ' })); fs.writeFileSync(e.lastRefDBDump, stringify(e.lastRefDB, { space: ' ' })); @@ -331,6 +355,8 @@ describe('Randomized model-based DB back-end tests', () => { console.log( "A comparison error occurred. Wrote compared values to temporary files for debugging:\n" + + ` Actual value: ${e.actualDump}\n` + + ` Expected value: ${e.actualDump}\n` + ` DB action trace: ${e.actionTraceDump}\n` + ` Starting DB: ${e.startingDBDump}\n` + ` Reference DB before last action: ${e.lastRefDBDump}\n` diff --git a/server/test/integration/helpers.ts b/server/test/integration/helpers.ts index 0ced839..8813dbc 100644 --- a/server/test/integration/helpers.ts +++ b/server/test/integration/helpers.ts @@ -128,6 +128,20 @@ export async function checkArtist( }) } +export async function deleteArtist( + req: any, + id = 1, + expectStatus: number | undefined = undefined, +) { + return await req + .delete('/artist/' + id) + .send() + .then((res: any) => { + expectStatus && expect(res).to.have.status(expectStatus); + return res; + }); +} + export async function createTag( req: any, props = { name: "Tag" }, @@ -174,6 +188,20 @@ export async function checkTag( }) } +export async function deleteTag( + req: any, + id = 1, + expectStatus: number | undefined = undefined, +) { + return await req + .delete('/tag/' + id) + .send() + .then((res: any) => { + expectStatus && expect(res).to.have.status(expectStatus); + return res; + }); +} + export async function createAlbum( req: any, props = { name: "Album" }, @@ -220,6 +248,20 @@ export async function checkAlbum( }) } +export async function deleteAlbum( + req: any, + id = 1, + expectStatus: number | undefined = undefined, +) { + return await req + .delete('/album/' + id) + .send() + .then((res: any) => { + expectStatus && expect(res).to.have.status(expectStatus); + return res; + }); +} + export async function createUser( req: any, email: string, diff --git a/server/test/reference_model/DBReferenceModel.ts b/server/test/reference_model/DBReferenceModel.ts index 3a3f107..8786f31 100644 --- a/server/test/reference_model/DBReferenceModel.ts +++ b/server/test/reference_model/DBReferenceModel.ts @@ -1,9 +1,10 @@ -import { AlbumWithRefsWithId, ArtistWithRefsWithId, DBDataFormat, PostAlbumRequest, PostArtistRequest, PostTagRequest, PostTrackRequest, TrackWithDetails, TrackWithRefsWithId } from "../../../client/src/api/api"; +import { AlbumWithRefsWithId, ArtistWithRefsWithId, DBDataFormat, PostAlbumRequest, PostArtistRequest, PostTagRequest, PostTrackRequest, TagWithRefsWithId, TrackWithDetails, TrackWithRefsWithId } from "../../../client/src/api/api"; import { makeNotFoundError } from "../../db/common"; import filterInPlace from "../../lib/filterInPlace"; // The mock reference database is in the same format as // the JSON import/export format, for multiple users. + export type ReferenceDatabase = Record type ObjectsType = "tracks" | "artists" | "tags" | "albums"; @@ -42,23 +43,27 @@ function ensureLinked(fromObjects: number[], fromObjectsType: ObjectsType, } // Create a new object. -export interface LinkField { field: string, otherObjectType: ObjectsType }; +// The general procedure for this is: +// - check that any existing objects referenced in the new object actually exist +// - generate a new ID and insert the object +// - add reverse references into any existing object referenced by the new object. +export interface ReferencingField { field: string, otherObjectType: ObjectsType }; export function createObject( userId: number, object: any, objectType: ObjectsType, - singularLinkFields: LinkField[], - pluralLinkFields: LinkField[], + singularReverseRefs: ReferencingField[], + pluralReverseRefs: ReferencingField[], db: ReferenceDatabase ): { id: number } { // Existence checks if (!(userId in db)) { throw makeNotFoundError() } - singularLinkFields.forEach((f: LinkField) => { + singularReverseRefs.forEach((f: ReferencingField) => { if (!checkExists(db[userId][f.otherObjectType], object[f.field] ? [object[f.field]] : [])) { throw makeNotFoundError(); } }); - pluralLinkFields.forEach((f: LinkField) => { + pluralReverseRefs.forEach((f: ReferencingField) => { if (!checkExists(db[userId][f.otherObjectType], object[f.field] || [])) { throw makeNotFoundError(); } @@ -72,16 +77,51 @@ export function createObject( }) // reverse links - singularLinkFields.forEach((f: LinkField) => { + singularReverseRefs.forEach((f: ReferencingField) => { ensureLinked(object[f.field] ? [object[f.field]] : [], f.otherObjectType, id, objectType, db[userId]); }); - pluralLinkFields.forEach((f: LinkField) => { + pluralReverseRefs.forEach((f: ReferencingField) => { ensureLinked(object[f.field] || [], f.otherObjectType, id, objectType, db[userId]); }); return { id: id }; } +// Delete an object. +// The general procedure for this is: +// - check that the to-be-deleted object exists +// - remove any references that exist to the object in other objects +// - delete the object +export function deleteObject( + userId: number, + objectId: number, + objectType: ObjectsType, + singularRefsToThisObject: ReferencingField[], + pluralRefsToThisObject: ReferencingField[], + db: ReferenceDatabase +): void { + // Existence checks + if (!(userId in db)) { throw makeNotFoundError() } + + // Find the object to delete. + let idx = db[userId][objectType].findIndex((o: any) => 'id' in o && o.id === objectId); + if (idx < 0) { + // Not found + throw makeNotFoundError(); + } + + // Remove references to this object + pluralRefsToThisObject.forEach((f: ReferencingField) => { + db[userId][f.otherObjectType].forEach((other: any) => { filterInPlace(other[f.field], (oid: number) => oid !== objectId) }) + }); + singularRefsToThisObject.forEach((f: ReferencingField) => { + db[userId][f.otherObjectType].forEach((other: any) => { if (other[f.field] === objectId) { other[f.field] = null; } }) + }); + + // Delete the object + db[userId][objectType].splice(idx, 1); +} + // Create a new track. export function createTrack(userId: number, track: PostTrackRequest, db: ReferenceDatabase): { id: number } { return createObject( @@ -143,20 +183,69 @@ export function createTag(userId: number, tag: PostTagRequest, db: ReferenceData // Delete a track. export function deleteTrack(userId: number, id: number, db: ReferenceDatabase): void { - // Existence checks - if (!(userId in db)) { throw makeNotFoundError() } + return deleteObject( + userId, + id, + 'tracks', + [], + [ + { field: 'trackIds', otherObjectType: 'albums' }, + { field: 'trackIds', otherObjectType: 'artists' }, + ], + db + ); +} - // Find the object to delete. - let idx = db[userId].tracks.findIndex((track: TrackWithRefsWithId) => track.id === id); - if (idx < 0) { - // Not found +// Delete an artist. +export function deleteArtist(userId: number, id: number, db: ReferenceDatabase): void { + return deleteObject( + userId, + id, + 'artists', + [], + [ + { field: 'artistIds', otherObjectType: 'tracks' }, + { field: 'artistIds', otherObjectType: 'albums' }, + ], + db + ); +} + +// Delete a track. +export function deleteAlbum(userId: number, id: number, db: ReferenceDatabase): void { + return deleteObject( + userId, + id, + 'albums', + [{ field: 'albumId', otherObjectType: 'tracks' }], + [{ field: 'albumIds', otherObjectType: 'artists' },], + db + ); +} + +// Delete a tag. +export function deleteTag(userId: number, id: number, db: ReferenceDatabase): void { + // Tags are special in that deleting them also deletes their children. Implement that here + // with recursive calls. + if (!(userId in db)) { throw makeNotFoundError() } + let tag = db[userId].tags.find((o: any) => 'id' in o && o.id === id); + if (!tag) { throw makeNotFoundError(); } + let children = db[userId].tags.filter((t: TagWithRefsWithId) => t.parentId === id); + children.forEach((child: TagWithRefsWithId) => { deleteTag(userId, child.id, db) }) - // Remove references - db[userId].albums.forEach((x: AlbumWithRefsWithId) => { filterInPlace(x.trackIds, (tid: number) => tid !== id); }) - db[userId].artists.forEach((x: ArtistWithRefsWithId) => { filterInPlace(x.trackIds, (tid: number) => tid !== id); }) - - // Delete the object - db[userId].tracks.splice(idx, 1); + // Do the actual deletion of this tag. + return deleteObject( + userId, + id, + 'tags', + [], + [ + { field: 'tagIds', otherObjectType: 'albums' }, + { field: 'tagIds', otherObjectType: 'artists' }, + { field: 'tagIds', otherObjectType: 'tracks' }, + ], + db + ); } \ No newline at end of file diff --git a/server/test/reference_model/randomGen.ts b/server/test/reference_model/randomGen.ts index b4f187d..57705a4 100644 --- a/server/test/reference_model/randomGen.ts +++ b/server/test/reference_model/randomGen.ts @@ -1,6 +1,6 @@ import { AlbumWithRefs, AlbumWithRefsWithId, ArtistWithRefs, ArtistWithRefsWithId, TagWithRefs, TagWithRefsWithId, TrackWithRefs, TrackWithRefsWithId } from "../../../client/src/api/api"; import { userEndpoints } from "../../endpoints/User"; -import { createAlbum, createArtist, createTag, createTrack, deleteTrack, ReferenceDatabase } from "./DBReferenceModel"; +import { createAlbum, createArtist, createTag, createTrack, deleteAlbum, deleteArtist, deleteTag, deleteTrack, ReferenceDatabase } from "./DBReferenceModel"; import * as helpers from '../integration/helpers'; import { DBErrorKind, isDBError } from "../../endpoints/types"; @@ -10,6 +10,9 @@ export enum DBActionType { CreateTag = "CreateTag", CreateArtist = "CreateArtist", DeleteTrack = "DeleteTrack", + DeleteAlbum = "DeleteAlbum", + DeleteArtist = "DeleteArtist", + DeleteTag = "DeleteTag", } export interface DBAction { @@ -27,7 +30,10 @@ export interface RandomDBActionDistribution { createAlbumParams: RandomCreateAlbumDistribution, createArtistParams: RandomCreateArtistDistribution, createTagParams: RandomCreateTagDistribution, - deleteTrackParams: RandomDeleteTrackDistribution, + deleteTrackParams: RandomDeleteObjectDistribution, + deleteArtistParams: RandomDeleteObjectDistribution, + deleteAlbumParams: RandomDeleteObjectDistribution, + deleteTagParams: RandomDeleteObjectDistribution, } export interface RandomCreateTrackDistribution { @@ -76,8 +82,8 @@ export interface RandomCreateTagDistribution { linkParent: Distribution, } -export interface RandomDeleteTrackDistribution { - validTrack: Distribution, +export interface RandomDeleteObjectDistribution { + validId: Distribution, } export function applyDistribution( @@ -132,32 +138,37 @@ export function applyReferenceDBAction( try { switch (action.type) { - case DBActionType.CreateTrack: { - response = createTrack(action.userId, action.payload, db); - status = 200; - break; - } - case DBActionType.CreateAlbum: { - response = createAlbum(action.userId, action.payload, db); - status = 200; - break; - } - case DBActionType.CreateArtist: { - response = createArtist(action.userId, action.payload, db); - status = 200; - break; - } - case DBActionType.CreateTag: { - response = createTag(action.userId, action.payload, db); - status = 200; - break; - } - case DBActionType.DeleteTrack: { - deleteTrack(action.userId, action.payload, db); - response = {}; - status = 200; - break; - } + case DBActionType.CreateTrack: + case DBActionType.CreateAlbum: + case DBActionType.CreateArtist: + case DBActionType.CreateTag: + { + let funcs = { + [DBActionType.CreateTrack]: createTrack, + [DBActionType.CreateAlbum]: createAlbum, + [DBActionType.CreateArtist]: createArtist, + [DBActionType.CreateTag]: createTag, + } + response = funcs[action.type](action.userId, action.payload, db); + status = 200; + break; + } + case DBActionType.DeleteTrack: + case DBActionType.DeleteAlbum: + case DBActionType.DeleteArtist: + case DBActionType.DeleteTag: + { + let funcs = { + [DBActionType.DeleteTrack]: deleteTrack, + [DBActionType.DeleteAlbum]: deleteAlbum, + [DBActionType.DeleteArtist]: deleteArtist, + [DBActionType.DeleteTag]: deleteTag, + } + funcs[action.type](action.userId, action.payload, db); + response = {}; + status = 200; + break; + } } } catch (e) { if (isDBError(e)) { @@ -184,36 +195,39 @@ export async function applyRealDBAction( let status: number = 0; switch (action.type) { - case DBActionType.CreateTrack: { - let res = await helpers.createTrack(req, action.payload); - status = res.status; - response = res.body; - break; - } - case DBActionType.CreateAlbum: { - let res = await helpers.createAlbum(req, action.payload); - status = res.status; - response = res.body; - break; - } - case DBActionType.CreateArtist: { - let res = await helpers.createArtist(req, action.payload); - status = res.status; - response = res.body; - break; - } - case DBActionType.CreateTag: { - let res = await helpers.createTag(req, action.payload); - status = res.status; - response = res.body; - break; - } - case DBActionType.DeleteTrack: { - let res = await helpers.deleteTrack(req, action.payload); - status = res.status; - response = res.body; - break; - } + case DBActionType.CreateTrack: + case DBActionType.CreateAlbum: + case DBActionType.CreateArtist: + case DBActionType.CreateTag: + { + let funcs = { + [DBActionType.CreateTrack]: helpers.createTrack, + [DBActionType.CreateAlbum]: helpers.createAlbum, + [DBActionType.CreateArtist]: helpers.createArtist, + [DBActionType.CreateTag]: helpers.createTag, + } + let res = await funcs[action.type](req, action.payload); + status = res.status; + response = res.body; + break; + } + + case DBActionType.DeleteTrack: + case DBActionType.DeleteAlbum: + case DBActionType.DeleteArtist: + case DBActionType.DeleteTag: + { + let funcs = { + [DBActionType.DeleteTrack]: helpers.deleteTrack, + [DBActionType.DeleteAlbum]: helpers.deleteAlbum, + [DBActionType.DeleteArtist]: helpers.deleteArtist, + [DBActionType.DeleteTag]: helpers.deleteTag, + } + let res = await funcs[action.type](req, action.payload); + status = res.status; + response = res.body; + break; + } } return { response: response, status: status }; @@ -234,60 +248,55 @@ export function randomDBAction( ); switch (type) { - case DBActionType.CreateTrack: { - return { - type: type, - payload: createRandomTrack( - db, - userId, - distribution.createTrackParams, - randomNumGen - ), - userId: userId, - }; - } - case DBActionType.CreateArtist: { - return { - type: type, - payload: createRandomArtist( - db, - userId, - distribution.createArtistParams, - randomNumGen - ), - userId: userId, - }; - } - case DBActionType.CreateAlbum: { - return { - type: type, - payload: createRandomAlbum( - db, - userId, - distribution.createAlbumParams, - randomNumGen - ), - userId: userId, - }; - } - case DBActionType.CreateTag: { - return { - type: type, - payload: createRandomTag( - db, - userId, - distribution.createTagParams, - randomNumGen - ), - userId: userId, - }; - } - case DBActionType.DeleteTrack: { + case DBActionType.CreateTrack: + case DBActionType.CreateArtist: + case DBActionType.CreateAlbum: + case DBActionType.CreateTag: + { + let funcs = { + [DBActionType.CreateTrack]: createRandomTrack, + [DBActionType.CreateArtist]: createRandomArtist, + [DBActionType.CreateAlbum]: createRandomAlbum, + [DBActionType.CreateTag]: createRandomTag, + } + let params = { + [DBActionType.CreateTrack]: distribution.createTrackParams, + [DBActionType.CreateArtist]: distribution.createArtistParams, + [DBActionType.CreateAlbum]: distribution.createAlbumParams, + [DBActionType.CreateTag]: distribution.createTagParams, + } + return { + type: type, + payload: funcs[type]( + db, + userId, + params[type] as any, + randomNumGen + ), + userId: userId, + }; + } + case DBActionType.DeleteTrack: + case DBActionType.DeleteArtist: + case DBActionType.DeleteAlbum: + case DBActionType.DeleteTag: { + let params = { + [DBActionType.DeleteTrack]: distribution.deleteTrackParams, + [DBActionType.DeleteArtist]: distribution.deleteArtistParams, + [DBActionType.DeleteAlbum]: distribution.deleteAlbumParams, + [DBActionType.DeleteTag]: distribution.deleteTagParams, + } + let objectArrays = { + [DBActionType.DeleteTrack]: db[userId].tracks, + [DBActionType.DeleteArtist]: db[userId].artists, + [DBActionType.DeleteAlbum]: db[userId].albums, + [DBActionType.DeleteTag]: db[userId].tags, + } return { type: type, - payload: applyDistribution(distribution.deleteTrackParams.validTrack, randomNumGen) ? - Math.floor(randomNumGen() * db[userId].tracks.length) + 1 : - Math.floor(randomNumGen() * db[userId].tracks.length) + 1 + db[userId].tracks.length, + payload: applyDistribution(params[type].validId, randomNumGen) ? + Math.floor(randomNumGen() * objectArrays[type].length) + 1 : + Math.floor(randomNumGen() * objectArrays[type].length) + 1 + objectArrays[type].length, userId: userId, } }