Add deletions.

editsong
Sander Vocke 5 years ago
parent 299342824c
commit cd26f79e4b
  1. 2
      .vscode/launch.json
  2. 6
      client/src/api/endpoints/data.ts
  3. 22
      server/db/Album.ts
  4. 2
      server/endpoints/Tag.ts
  5. 1
      server/migrations/20200828124218_init_db.ts
  6. 62
      server/test/integration/flows/ResourceFlow.ts
  7. 42
      server/test/integration/helpers.ts
  8. 129
      server/test/reference_model/DBReferenceModel.ts
  9. 179
      server/test/reference_model/randomGen.ts

@ -10,7 +10,7 @@
"name": "Jasmine Tests with SQLite", "name": "Jasmine Tests with SQLite",
"env": { "env": {
"MUDBASE_DB_CONFIG": "{\"client\": \"sqlite3\", \"connection\": \":memory:\"}", "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", "program": "${workspaceFolder}/server/node_modules/jasmine-ts/lib/index",
"args": [ "args": [

@ -5,10 +5,14 @@
// referencing between objects. They do not correspond to IDs in the actual // referencing between objects. They do not correspond to IDs in the actual
// database. // database.
// Upon import, they might be replaced, and upon export, they might be randomly // 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"; 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 { export interface DBDataFormat {
tracks: TrackWithRefsWithId[], tracks: TrackWithRefsWithId[],
albums: AlbumWithRefsWithId[], albums: AlbumWithRefsWithId[],

@ -181,10 +181,10 @@ export async function modifyAlbum(userId: number, albumId: number, album: AlbumB
// Start retrieving tracks if we are modifying those. // Start retrieving tracks if we are modifying those.
const trackIdsPromise: Promise<number[] | undefined> = const trackIdsPromise: Promise<number[] | undefined> =
album.trackIds ? album.trackIds ?
trx.select('artistId') trx.select('id')
.from('tracks_albums') .from('tracks')
.whereIn('albumId', album.trackIds) .whereIn('albumId', album.trackIds)
.then((as: any) => as.map((a: any) => a['trackId'])) .then((as: any) => as.map((a: any) => a['id']))
: (async () => undefined)(); : (async () => undefined)();
// Start retrieving tags if we are modifying those. // 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 || []) .whereNotIn('tagId', album.tagIds || [])
.delete() : undefined; .delete() : undefined;
// Remove unlinked tracks. // Remove unlinked tracks by setting their references to null.
const removeUnlinkedTracks = tracks ? trx('tracks_albums') const removeUnlinkedTracks = tracks ? trx('tracks')
.where({ 'albumId': albumId }) .where({ 'albumId': albumId })
.whereNotIn('trackId', album.trackIds || []) .whereNotIn('id', album.trackIds || [])
.delete() : undefined; .update({ 'albumId': null }) : undefined;
// Link new artists. // Link new artists.
const addArtists = artists ? trx('artists_albums') const addArtists = artists ? trx('artists_albums')
@ -356,11 +356,11 @@ export async function deleteAlbum(userId: number, albumId: number, knex: Knex):
.from('albums_tags') .from('albums_tags')
.where({ 'albumId': albumId }); .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<any> = const deleteTracksPromise: Promise<any> =
trx.delete() trx.update({ 'album': null })
.from('tracks_albums') .from('tracks')
.where({ 'albumId': albumId }); .where({ 'album': albumId });
// Start deleting the album. // Start deleting the album.
const deleteAlbumPromise: Promise<any> = const deleteAlbumPromise: Promise<any> =

@ -37,7 +37,7 @@ export const DeleteTag: EndpointHandler = async (req: any, res: any, knex: Knex)
try { try {
deleteTag(userId, req.params.id, knex); await deleteTag(userId, req.params.id, knex);
res.status(200).send(); res.status(200).send();
} catch (e) { } catch (e) {

@ -204,7 +204,6 @@ export async function down(knex: Knex): Promise<void> {
await knex.schema.dropTable('albums'); await knex.schema.dropTable('albums');
await knex.schema.dropTable('tags'); await knex.schema.dropTable('tags');
await knex.schema.dropTable('tracks_artists'); await knex.schema.dropTable('tracks_artists');
await knex.schema.dropTable('tracks_albums');
await knex.schema.dropTable('tracks_tags'); await knex.schema.dropTable('tracks_tags');
await knex.schema.dropTable('artists_tags'); await knex.schema.dropTable('artists_tags');
await knex.schema.dropTable('albums_tags'); await knex.schema.dropTable('albums_tags');

@ -144,8 +144,18 @@ function transformActionIDs(action: DBAction, mappings: IDMappings, rng: any) {
tag.parentId = tag.parentId ? doMap(tag.parentId, mappings.tags) : null; tag.parentId = tag.parentId ? doMap(tag.parentId, mappings.tags) : null;
break; break;
} }
case DBActionType.DeleteTrack: { case DBActionType.DeleteTrack:
r.payload = mappings.tracks[r.payload]; 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; break;
} }
} }
@ -208,11 +218,14 @@ describe('Randomized model-based DB back-end tests', () => {
// be generated. // be generated.
let dist: RandomDBActionDistribution = { let dist: RandomDBActionDistribution = {
type: new Map([ type: new Map([
[DBActionType.CreateTrack, 0.2], [DBActionType.CreateTrack, 0.125],
[DBActionType.CreateArtist, 0.2], [DBActionType.CreateArtist, 0.125],
[DBActionType.CreateAlbum, 0.2], [DBActionType.CreateAlbum, 0.125],
[DBActionType.CreateTag, 0.2], [DBActionType.CreateTag, 0.125],
[DBActionType.DeleteTrack, 0.2] [DBActionType.DeleteTrack, 0.125],
[DBActionType.DeleteArtist, 0.125],
[DBActionType.DeleteAlbum, 0.125],
[DBActionType.DeleteTag, 0.125],
]), ]),
userId: new Map([[1, 1.0]]), userId: new Map([[1, 1.0]]),
createTrackParams: { createTrackParams: {
@ -259,8 +272,17 @@ describe('Randomized model-based DB back-end tests', () => {
}, },
deleteTrackParams: { 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. // 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 this was an object creation action, we need to update the mappings.
if (refStatus === 200 && realStatus === 200) { if (refStatus === 200 && realStatus === 200) {
if (refAction.type === DBActionType.CreateTrack) { switch(refAction.type) {
idMappingsRefToReal.tracks[refResponse.id] = realResponse.id; case DBActionType.CreateTrack: { idMappingsRefToReal.tracks[refResponse.id] = realResponse.id; break; }
} else if (refAction.type === DBActionType.CreateAlbum) { case DBActionType.CreateArtist: { idMappingsRefToReal.artists[refResponse.id] = realResponse.id; break; }
idMappingsRefToReal.albums[refResponse.id] = realResponse.id; case DBActionType.CreateAlbum: { idMappingsRefToReal.albums[refResponse.id] = realResponse.id; break; }
} else if (refAction.type === DBActionType.CreateArtist) { case DBActionType.CreateTag: { idMappingsRefToReal.tags[refResponse.id] = realResponse.id; break; }
idMappingsRefToReal.artists[refResponse.id] = realResponse.id; default: { break; }
} else if (refAction.type === DBActionType.CreateTag) {
idMappingsRefToReal.tags[refResponse.id] = realResponse.id;
} }
} }
@ -300,7 +320,7 @@ describe('Randomized model-based DB back-end tests', () => {
expect(normalizeResponse(realResponse)).to.deep.equal(normalizeResponse(refResponse)); expect(normalizeResponse(realResponse)).to.deep.equal(normalizeResponse(refResponse));
// Compare the database state after the action. // Compare the database state after the action.
let newRefState = refDB; let newRefState = _.cloneDeep(refDB);
let newRealState = { let newRealState = {
[1]: (await helpers.getExport(req)).body, [1]: (await helpers.getExport(req)).body,
}; };
@ -318,11 +338,15 @@ describe('Randomized model-based DB back-end tests', () => {
e.idMappingsRefToReal = idMappingsRefToReal; e.idMappingsRefToReal = idMappingsRefToReal;
if (e.actual && e.expected) { if (e.actual && e.expected) {
let basename = tmp.tmpNameSync(); let basename = tmp.tmpNameSync();
e.actualDump = basename + "_actual";
e.expectedDump = basename + "_expected";
e.actionTraceDump = basename + "_actiontrace"; e.actionTraceDump = basename + "_actiontrace";
e.startingDBDump = basename + "_startingDB"; e.startingDBDump = basename + "_startingDB";
e.lastRefDBDump = basename + "_lastRefDB"; e.lastRefDBDump = basename + "_lastRefDB";
e.lastRealDBDump = basename + "_lastRealDB"; e.lastRealDBDump = basename + "_lastRealDB";
e.idMappingsRefToRealDump = basename + "_idMappings"; 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.actionTraceDump, stringify(e.actionTrace, { space: ' ' }));
fs.writeFileSync(e.startingDBDump, stringify(e.startingDB, { space: ' ' })); fs.writeFileSync(e.startingDBDump, stringify(e.startingDB, { space: ' ' }));
fs.writeFileSync(e.lastRefDBDump, stringify(e.lastRefDB, { space: ' ' })); fs.writeFileSync(e.lastRefDBDump, stringify(e.lastRefDB, { space: ' ' }));
@ -331,6 +355,8 @@ describe('Randomized model-based DB back-end tests', () => {
console.log( console.log(
"A comparison error occurred. Wrote compared values to temporary files for debugging:\n" "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` + ` DB action trace: ${e.actionTraceDump}\n`
+ ` Starting DB: ${e.startingDBDump}\n` + ` Starting DB: ${e.startingDBDump}\n`
+ ` Reference DB before last action: ${e.lastRefDBDump}\n` + ` Reference DB before last action: ${e.lastRefDBDump}\n`

@ -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( export async function createTag(
req: any, req: any,
props = { name: "Tag" }, 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( export async function createAlbum(
req: any, req: any,
props = { name: "Album" }, 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( export async function createUser(
req: any, req: any,
email: string, email: string,

@ -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 { makeNotFoundError } from "../../db/common";
import filterInPlace from "../../lib/filterInPlace"; import filterInPlace from "../../lib/filterInPlace";
// The mock reference database is in the same format as // The mock reference database is in the same format as
// the JSON import/export format, for multiple users. // the JSON import/export format, for multiple users.
export type ReferenceDatabase = Record<number, DBDataFormat> export type ReferenceDatabase = Record<number, DBDataFormat>
type ObjectsType = "tracks" | "artists" | "tags" | "albums"; type ObjectsType = "tracks" | "artists" | "tags" | "albums";
@ -42,23 +43,27 @@ function ensureLinked(fromObjects: number[], fromObjectsType: ObjectsType,
} }
// Create a new object. // 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( export function createObject(
userId: number, userId: number,
object: any, object: any,
objectType: ObjectsType, objectType: ObjectsType,
singularLinkFields: LinkField[], singularReverseRefs: ReferencingField[],
pluralLinkFields: LinkField[], pluralReverseRefs: ReferencingField[],
db: ReferenceDatabase db: ReferenceDatabase
): { id: number } { ): { id: number } {
// Existence checks // Existence checks
if (!(userId in db)) { throw makeNotFoundError() } 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]] : [])) { if (!checkExists(db[userId][f.otherObjectType], object[f.field] ? [object[f.field]] : [])) {
throw makeNotFoundError(); throw makeNotFoundError();
} }
}); });
pluralLinkFields.forEach((f: LinkField) => { pluralReverseRefs.forEach((f: ReferencingField) => {
if (!checkExists(db[userId][f.otherObjectType], object[f.field] || [])) { if (!checkExists(db[userId][f.otherObjectType], object[f.field] || [])) {
throw makeNotFoundError(); throw makeNotFoundError();
} }
@ -72,16 +77,51 @@ export function createObject(
}) })
// reverse links // reverse links
singularLinkFields.forEach((f: LinkField) => { singularReverseRefs.forEach((f: ReferencingField) => {
ensureLinked(object[f.field] ? [object[f.field]] : [], f.otherObjectType, id, objectType, db[userId]); 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]); ensureLinked(object[f.field] || [], f.otherObjectType, id, objectType, db[userId]);
}); });
return { id: id }; 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. // Create a new track.
export function createTrack(userId: number, track: PostTrackRequest, db: ReferenceDatabase): { id: number } { export function createTrack(userId: number, track: PostTrackRequest, db: ReferenceDatabase): { id: number } {
return createObject( return createObject(
@ -143,20 +183,69 @@ export function createTag(userId: number, tag: PostTagRequest, db: ReferenceData
// Delete a track. // Delete a track.
export function deleteTrack(userId: number, id: number, db: ReferenceDatabase): void { export function deleteTrack(userId: number, id: number, db: ReferenceDatabase): void {
// Existence checks return deleteObject(
if (!(userId in db)) { throw makeNotFoundError() } userId,
id,
'tracks',
[],
[
{ field: 'trackIds', otherObjectType: 'albums' },
{ field: 'trackIds', otherObjectType: 'artists' },
],
db
);
}
// Find the object to delete. // Delete an artist.
let idx = db[userId].tracks.findIndex((track: TrackWithRefsWithId) => track.id === id); export function deleteArtist(userId: number, id: number, db: ReferenceDatabase): void {
if (idx < 0) { return deleteObject(
// Not found 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(); throw makeNotFoundError();
} }
let children = db[userId].tags.filter((t: TagWithRefsWithId) => t.parentId === id);
children.forEach((child: TagWithRefsWithId) => { deleteTag(userId, child.id, db) })
// Remove references // Do the actual deletion of this tag.
db[userId].albums.forEach((x: AlbumWithRefsWithId) => { filterInPlace(x.trackIds, (tid: number) => tid !== id); }) return deleteObject(
db[userId].artists.forEach((x: ArtistWithRefsWithId) => { filterInPlace(x.trackIds, (tid: number) => tid !== id); }) userId,
id,
// Delete the object 'tags',
db[userId].tracks.splice(idx, 1); [],
[
{ field: 'tagIds', otherObjectType: 'albums' },
{ field: 'tagIds', otherObjectType: 'artists' },
{ field: 'tagIds', otherObjectType: 'tracks' },
],
db
);
} }

@ -1,6 +1,6 @@
import { AlbumWithRefs, AlbumWithRefsWithId, ArtistWithRefs, ArtistWithRefsWithId, TagWithRefs, TagWithRefsWithId, TrackWithRefs, TrackWithRefsWithId } from "../../../client/src/api/api"; import { AlbumWithRefs, AlbumWithRefsWithId, ArtistWithRefs, ArtistWithRefsWithId, TagWithRefs, TagWithRefsWithId, TrackWithRefs, TrackWithRefsWithId } from "../../../client/src/api/api";
import { userEndpoints } from "../../endpoints/User"; 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 * as helpers from '../integration/helpers';
import { DBErrorKind, isDBError } from "../../endpoints/types"; import { DBErrorKind, isDBError } from "../../endpoints/types";
@ -10,6 +10,9 @@ export enum DBActionType {
CreateTag = "CreateTag", CreateTag = "CreateTag",
CreateArtist = "CreateArtist", CreateArtist = "CreateArtist",
DeleteTrack = "DeleteTrack", DeleteTrack = "DeleteTrack",
DeleteAlbum = "DeleteAlbum",
DeleteArtist = "DeleteArtist",
DeleteTag = "DeleteTag",
} }
export interface DBAction { export interface DBAction {
@ -27,7 +30,10 @@ export interface RandomDBActionDistribution {
createAlbumParams: RandomCreateAlbumDistribution, createAlbumParams: RandomCreateAlbumDistribution,
createArtistParams: RandomCreateArtistDistribution, createArtistParams: RandomCreateArtistDistribution,
createTagParams: RandomCreateTagDistribution, createTagParams: RandomCreateTagDistribution,
deleteTrackParams: RandomDeleteTrackDistribution, deleteTrackParams: RandomDeleteObjectDistribution,
deleteArtistParams: RandomDeleteObjectDistribution,
deleteAlbumParams: RandomDeleteObjectDistribution,
deleteTagParams: RandomDeleteObjectDistribution,
} }
export interface RandomCreateTrackDistribution { export interface RandomCreateTrackDistribution {
@ -76,8 +82,8 @@ export interface RandomCreateTagDistribution {
linkParent: Distribution<boolean | 'nonexistent'>, linkParent: Distribution<boolean | 'nonexistent'>,
} }
export interface RandomDeleteTrackDistribution { export interface RandomDeleteObjectDistribution {
validTrack: Distribution<boolean>, validId: Distribution<boolean>,
} }
export function applyDistribution<T>( export function applyDistribution<T>(
@ -132,28 +138,33 @@ export function applyReferenceDBAction(
try { try {
switch (action.type) { switch (action.type) {
case DBActionType.CreateTrack: { case DBActionType.CreateTrack:
response = createTrack(action.userId, action.payload, db); 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; status = 200;
break; break;
} }
case DBActionType.CreateAlbum: { case DBActionType.DeleteTrack:
response = createAlbum(action.userId, action.payload, db); case DBActionType.DeleteAlbum:
status = 200; case DBActionType.DeleteArtist:
break; case DBActionType.DeleteTag:
} {
case DBActionType.CreateArtist: { let funcs = {
response = createArtist(action.userId, action.payload, db); [DBActionType.DeleteTrack]: deleteTrack,
status = 200; [DBActionType.DeleteAlbum]: deleteAlbum,
break; [DBActionType.DeleteArtist]: deleteArtist,
} [DBActionType.DeleteTag]: deleteTag,
case DBActionType.CreateTag: { }
response = createTag(action.userId, action.payload, db); funcs[action.type](action.userId, action.payload, db);
status = 200;
break;
}
case DBActionType.DeleteTrack: {
deleteTrack(action.userId, action.payload, db);
response = {}; response = {};
status = 200; status = 200;
break; break;
@ -184,32 +195,35 @@ export async function applyRealDBAction(
let status: number = 0; let status: number = 0;
switch (action.type) { switch (action.type) {
case DBActionType.CreateTrack: { case DBActionType.CreateTrack:
let res = await helpers.createTrack(req, action.payload); 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; status = res.status;
response = res.body; response = res.body;
break; break;
} }
case DBActionType.CreateAlbum: {
let res = await helpers.createAlbum(req, action.payload); case DBActionType.DeleteTrack:
status = res.status; case DBActionType.DeleteAlbum:
response = res.body; case DBActionType.DeleteArtist:
break; case DBActionType.DeleteTag:
} {
case DBActionType.CreateArtist: { let funcs = {
let res = await helpers.createArtist(req, action.payload); [DBActionType.DeleteTrack]: helpers.deleteTrack,
status = res.status; [DBActionType.DeleteAlbum]: helpers.deleteAlbum,
response = res.body; [DBActionType.DeleteArtist]: helpers.deleteArtist,
break; [DBActionType.DeleteTag]: helpers.deleteTag,
}
case DBActionType.CreateTag: {
let res = await helpers.createTag(req, action.payload);
status = res.status;
response = res.body;
break;
} }
case DBActionType.DeleteTrack: { let res = await funcs[action.type](req, action.payload);
let res = await helpers.deleteTrack(req, action.payload);
status = res.status; status = res.status;
response = res.body; response = res.body;
break; break;
@ -234,60 +248,55 @@ export function randomDBAction(
); );
switch (type) { switch (type) {
case DBActionType.CreateTrack: { case DBActionType.CreateTrack:
return { case DBActionType.CreateArtist:
type: type, case DBActionType.CreateAlbum:
payload: createRandomTrack( case DBActionType.CreateTag:
db, {
userId, let funcs = {
distribution.createTrackParams, [DBActionType.CreateTrack]: createRandomTrack,
randomNumGen [DBActionType.CreateArtist]: createRandomArtist,
), [DBActionType.CreateAlbum]: createRandomAlbum,
userId: userId, [DBActionType.CreateTag]: createRandomTag,
}; }
let params = {
[DBActionType.CreateTrack]: distribution.createTrackParams,
[DBActionType.CreateArtist]: distribution.createArtistParams,
[DBActionType.CreateAlbum]: distribution.createAlbumParams,
[DBActionType.CreateTag]: distribution.createTagParams,
} }
case DBActionType.CreateArtist: {
return { return {
type: type, type: type,
payload: createRandomArtist( payload: funcs[type](
db, db,
userId, userId,
distribution.createArtistParams, params[type] as any,
randomNumGen randomNumGen
), ),
userId: userId, userId: userId,
}; };
} }
case DBActionType.CreateAlbum: { case DBActionType.DeleteTrack:
return { case DBActionType.DeleteArtist:
type: type, case DBActionType.DeleteAlbum:
payload: createRandomAlbum( case DBActionType.DeleteTag: {
db, let params = {
userId, [DBActionType.DeleteTrack]: distribution.deleteTrackParams,
distribution.createAlbumParams, [DBActionType.DeleteArtist]: distribution.deleteArtistParams,
randomNumGen [DBActionType.DeleteAlbum]: distribution.deleteAlbumParams,
), [DBActionType.DeleteTag]: distribution.deleteTagParams,
userId: userId, }
}; let objectArrays = {
} [DBActionType.DeleteTrack]: db[userId].tracks,
case DBActionType.CreateTag: { [DBActionType.DeleteArtist]: db[userId].artists,
return { [DBActionType.DeleteAlbum]: db[userId].albums,
type: type, [DBActionType.DeleteTag]: db[userId].tags,
payload: createRandomTag(
db,
userId,
distribution.createTagParams,
randomNumGen
),
userId: userId,
};
} }
case DBActionType.DeleteTrack: {
return { return {
type: type, type: type,
payload: applyDistribution(distribution.deleteTrackParams.validTrack, randomNumGen) ? payload: applyDistribution(params[type].validId, randomNumGen) ?
Math.floor(randomNumGen() * db[userId].tracks.length) + 1 : Math.floor(randomNumGen() * objectArrays[type].length) + 1 :
Math.floor(randomNumGen() * db[userId].tracks.length) + 1 + db[userId].tracks.length, Math.floor(randomNumGen() * objectArrays[type].length) + 1 + objectArrays[type].length,
userId: userId, userId: userId,
} }
} }

Loading…
Cancel
Save