From d600c1e6c88df7204280f5a4470057b5e139cfe9 Mon Sep 17 00:00:00 2001 From: JoshuaVSherman Date: Wed, 3 Jun 2026 12:18:29 -0400 Subject: [PATCH] feat(gig): rename tour->gig + add duration & promoImageUrl (Phase 1, #225) Full, consistent rename of the tour domain to gig, plus the two new gig fields, on the SocketCluster side (closes #225): - src/model/tour/ -> src/model/gig/: `Gig` model bound to collection `gigs`, `gig-controller`/`gig-facade`/`reset-gig` (export key tour->gig). New schema fields `duration` (Number, default 0) and `promoImageUrl` (String). Dropped dead commented `tour-router.ts`. - AgController + utils renamed to gig (`sendGigs`, `newGig`, `removeGig`, `updateGig`, `assertCanCreateGig`, `handleGig`). - Migration safety (dropped in the Phase-4 cleanup once all clients are on gig): - read: `sendGigs` transmits BOTH `allGigs` and legacy `allTours`. - write: handlers registered for BOTH new and legacy names (`newGig`+`newTour`, `deleteGig`+`deleteTour`, `editGig`+`editTour`), and normalize both `{gig}`/`{tour}` and `gigId`/`tourId` payload shapes. - `assertCanCreateGig` accepts `gig:create` OR legacy `tour:create`. - One-time DB migration script `src/scripts/migrate-tours-to-gigs.ts` (idempotent: renames collection `tours`->`gigs`, no-ops if already done). - Tests renamed/updated. typecheck + 68 tests + eslint all green. Co-Authored-By: Claude Opus 4.8 --- package-lock.json | 4 +- package.json | 2 +- src/AgController/index.ts | 163 +++++++++--------- src/AgController/utils.ts | 52 +++--- src/model/gig/gig-controller.ts | 7 + src/model/gig/gig-facade.ts | 8 + .../tour-schema.ts => gig/gig-schema.ts} | 7 +- .../{tour/reset-tour.ts => gig/reset-gig.ts} | 2 +- src/model/tour/tour-controller.ts | 14 -- src/model/tour/tour-facade.ts | 8 - src/model/tour/tour-router.ts | 20 --- src/scripts/migrate-tours-to-gigs.ts | 37 ++++ test/AgController/index.spec.ts | 96 +++++------ test/AgController/utils.spec.ts | 18 +- .../gig-controller.test.ts} | 4 +- 15 files changed, 232 insertions(+), 210 deletions(-) create mode 100644 src/model/gig/gig-controller.ts create mode 100644 src/model/gig/gig-facade.ts rename src/model/{tour/tour-schema.ts => gig/gig-schema.ts} (65%) rename src/model/{tour/reset-tour.ts => gig/reset-gig.ts} (99%) delete mode 100644 src/model/tour/tour-controller.ts delete mode 100644 src/model/tour/tour-facade.ts delete mode 100644 src/model/tour/tour-router.ts create mode 100644 src/scripts/migrate-tours-to-gigs.ts rename test/{tour/tour-controller.test.ts => gig/gig-controller.test.ts} (95%) diff --git a/package-lock.json b/package-lock.json index dfea157..da0c9db 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "webjamsocketserver", - "version": "3.0.2", + "version": "3.0.3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "webjamsocketserver", - "version": "3.0.2", + "version": "3.0.3", "hasInstallScript": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index 4e19d5b..1da9f22 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "webjamsocketserver", "description": "Uses latest version of socketcluster-server", - "version": "3.0.2", + "version": "3.0.3", "license": "MIT", "type": "module", "main": "build/src/index.js", diff --git a/src/AgController/index.ts b/src/AgController/index.ts index d68a0f4..bc6ac4c 100644 --- a/src/AgController/index.ts +++ b/src/AgController/index.ts @@ -1,16 +1,16 @@ import Debug from 'debug'; import JWT from 'jsonwebtoken'; import type socketClusterServer from 'socketcluster-server'; -import TourController from '../model/tour/tour-controller.js'; -import tourData from '../model/tour/reset-tour.js'; +import GigController from '../model/gig/gig-controller.js'; +import gigData from '../model/gig/reset-gig.js'; import BookController from '../model/book/book-controller.js'; import bookData from '../model/book/reset-book.js'; import mongoose from '../model/db.js'; import utils from './utils.js'; export interface IClient { - socket:any, - listener: (arg0: string) => { (): any; new(): any; createConsumer: { (): any; new(): any; }; }; id: any; + socket:any, + listener: (arg0: string) => { (): any; new(): any; createConsumer: { (): any; new(): any; }; }; id: any; } const debug = Debug('WebJamSocketServer:AgController'); @@ -21,7 +21,7 @@ class AgController { clients: any[]; - tourController = TourController; + gigController = GigController; bookController = BookController; @@ -31,17 +31,17 @@ class AgController { } async resetData():Promise { - const { tour } = tourData; + const { gig } = gigData; const { book } = bookData; - await utils.resetData(tour, book, this.tourController, this.bookController); + await utils.resetData(gig, book, this.gigController, this.bookController); } handleDisconnect(client: IClient, interval: NodeJS.Timeout):void { (async () => { let disconnect: { value: undefined; done: any; }; const dConsumer = client.listener('disconnect').createConsumer(); - while (true) { - disconnect = await dConsumer.next(); + while (true) { + disconnect = await dConsumer.next(); clearInterval(interval); if (disconnect.value !== undefined) { const index = this.clients.indexOf(client.id); @@ -66,23 +66,25 @@ class AgController { return this.handleDisconnect(client.socket, interval); } - async sendTours(client:IClient):Promise { - let allTours: any; - try { allTours = await this.tourController.getAllSort({ datetime: -1 }); } catch (e) { + async sendGigs(client:IClient):Promise { + let allGigs: any; + try { allGigs = await this.gigController.getAllSort({ datetime: -1 }); } catch (e) { const eMessage = (e as Error).message; - debug(eMessage); - return eMessage; + debug(eMessage); + return eMessage; } - client.socket.transmit('allTours', allTours); - return 'sent tours'; + client.socket.transmit('allGigs', allGigs); + // legacy alias so any still-deployed old frontend keeps showing gigs during the rename migration + client.socket.transmit('allTours', allGigs); + return 'sent gigs'; } async sendBooks(client: IClient):Promise { let allBooks: any; try { allBooks = await this.bookController.getAll(); } catch (e) { const eMessage = (e as Error).message; - debug(eMessage); - return eMessage; + debug(eMessage); + return eMessage; } client.socket.transmit('allBooks', allBooks); return 'sent books'; @@ -92,11 +94,11 @@ class AgController { (async () => { let receiver: { value: number; done: any; }; const rConsumer = client.socket.receiver('initial message').createConsumer(); - while (true) { - receiver = await rConsumer.next(); + while (true) { + receiver = await rConsumer.next(); debug(`received initial message: ${receiver.value}`); if (receiver.value === 123) { - await this.sendTours(client); + await this.sendGigs(client); await this.sendBooks(client); } else { break; @@ -112,8 +114,8 @@ class AgController { // eslint-disable-next-line security/detect-object-injection try { r = await (this.bookController as any)[func](data); } catch (e) { const eMessage = (e as Error).message; - debug(eMessage); - return eMessage; + debug(eMessage); + return eMessage; } this.server.exchange.transmitPublish(message, r); return message; @@ -123,14 +125,14 @@ class AgController { (async () => { let receiver: { value: { token: string; image: any }, done: any; }; const rConsumer = client.socket.receiver('newImage').createConsumer(); - while (true) { - receiver = await rConsumer.next(); + while (true) { + receiver = await rConsumer.next(); debug(`received newImage message: ${JSON.stringify(receiver.value)}`); if (!receiver.value) break; if (typeof receiver.value.token === 'string' && typeof receiver.value.image.title === 'string' && typeof receiver.value.image.url === 'string' ) { - await this.handleImage('createDocs', receiver.value.image, 'imageCreated'); + await this.handleImage('createDocs', receiver.value.image, 'imageCreated'); } /* istanbul ignore else */if (receiver.done) break; } @@ -138,22 +140,22 @@ class AgController { } async updateImage( - data: { token:string; - editPic: { _id: mongoose.Types.ObjectId, title:string, url:string, comments:string }; }, + data: { token:string; + editPic: { _id: mongoose.Types.ObjectId, title:string, url:string, comments:string }; }, client:IClient, ):Promise { const { editPic, token } = data; const { - - _id, title, url, comments, + + _id, title, url, comments, } = editPic; - let r: any; + let r: any; if (typeof token !== 'string') throw new Error('invalid token'); try { r = await this.bookController.findByIdAndUpdate(_id, { title, url, comments }); } catch (e) { const eMessage = (e as Error).message; client.socket.transmit('socketError', { updateImage: eMessage });// send error back to client - debug(eMessage); - return eMessage; + debug(eMessage); + return eMessage; } this.server.exchange.transmitPublish('imageUpdated', r); return 'image updated'; @@ -163,28 +165,30 @@ class AgController { (async () => { let receiver: { value: { data: string; token: string; }; done: any; }; const rConsumer = client.socket.receiver('deleteImage').createConsumer(); - while (true) { - receiver = await rConsumer.next(); + while (true) { + receiver = await rConsumer.next(); debug(`received deleteImage message: ${JSON.stringify(receiver.value)}`); if (!receiver.value) break; if (typeof receiver.value.token === 'string' && typeof receiver.value.data === 'string') { - await this.handleImage('deleteById', receiver.value.data, 'imageDeleted'); + await this.handleImage('deleteById', receiver.value.data, 'imageDeleted'); } /* istanbul ignore else */if (receiver.done) break; } })(); } - newTour(client: IClient):void { + // Listens on `messageName` ('newGig' and, during migration, legacy 'newTour'). + // Tolerates both the new { gig } and legacy { tour } payload shapes. + newGig(client: IClient, messageName: string):void { (async () => { - let receiver: { value: { token: string; - tour: { datetime: Date; venue: string; city:string, usState: string }; }; done: any; }; - const rConsumer = client.socket.receiver('newTour').createConsumer(); - while (true) { - receiver = await rConsumer.next(); + let receiver: { value: any; done: any; }; + const rConsumer = client.socket.receiver(messageName).createConsumer(); + while (true) { + receiver = await rConsumer.next(); let decoded, user, goodRoles; if (!receiver.value) break; try { + const gig = receiver.value.gig ?? receiver.value.tour; decoded = this.jwt.verify(receiver.value.token, process.env.HashString || /* istanbul ignore next */''); const userRes = await fetch(`${process.env.BackendUrl}/user/${decoded.sub}`, { headers: { Accept: 'application/json', Authorization: `Bearer ${receiver.value.token}` }, @@ -192,56 +196,52 @@ class AgController { if (!userRes.ok) throw new Error(`${userRes.status} ${userRes.statusText}`); user = await userRes.json(); goodRoles = JSON.parse(process.env.userRoles || /* istanbul ignore next */'{}').roles; - utils.assertCanCreateTour(user, goodRoles); - if (receiver.value.tour.datetime && receiver.value.tour.city - && receiver.value.tour.usState && receiver.value.tour.venue) { - await utils.handleTour( - 'createDocs', - receiver.value.tour, - 'tourCreated', - this.tourController, - this.server, - ); + utils.assertCanCreateGig(user, goodRoles); + if (gig && gig.datetime && gig.city && gig.usState && gig.venue) { + await utils.handleGig('createDocs', gig, 'gigCreated', this.gigController, this.server); } else throw new Error('Invalid create gig data'); if (receiver.done) break; } catch (e) { const eMessage = (e as Error).message; - debug(eMessage); - client.socket.transmit('socketError', { newTour: eMessage });// send error back to client - break; - } + debug(eMessage); + client.socket.transmit('socketError', { newGig: eMessage });// send error back to client + break; + } } })(); } - removeTour(client:IClient):void { + // Listens on `messageName` ('deleteGig' and, during migration, legacy 'deleteTour'). + removeGig(client:IClient, messageName: string):void { (async () => { - let receiver: { value: { tour:any; token: any; }; done: any; }; - const rConsumer = client.socket.receiver('deleteTour').createConsumer(); - while (true) { - receiver = await rConsumer.next(); - debug(`received deleteTour message: ${JSON.stringify(receiver.value)}`); + let receiver: { value: { gig?:any; tour?:any; token: any; }; done: any; }; + const rConsumer = client.socket.receiver(messageName).createConsumer(); + while (true) { + receiver = await rConsumer.next(); + debug(`received ${messageName} message: ${JSON.stringify(receiver.value)}`); if (!receiver.value) break; - await utils.removeTour(receiver, client, this.tourController, this.server); + await utils.removeGig(receiver, client, this.gigController, this.server); /* istanbul ignore else */if (receiver.done) break; } })(); } - async updateTour( - data: { tourId: mongoose.Types.ObjectId; tour: Record; }, + async updateGig( + data: { gigId?: any; tourId?: any; + gig?: Record; tour?: Record; }, ):Promise { - let r: any; - try { - const { tourId, tour } = data; - if (!tour.venue || !tour.datetime || !tour.city || !tour.usState) throw new Error('Invalid gig data'); - r = await this.tourController.findByIdAndUpdate(tourId, tour); + let r: any; + try { + const id = data.gigId ?? data.tourId; + const gig = data.gig ?? data.tour ?? {}; + if (!gig.venue || !gig.datetime || !gig.city || !gig.usState) throw new Error('Invalid gig data'); + r = await this.gigController.findByIdAndUpdate(id, gig); } catch (e) { const eMessage = (e as Error).message; - debug(eMessage); + debug(eMessage); return eMessage; } - this.server.exchange.transmitPublish('tourUpdated', r); + this.server.exchange.transmitPublish('gigUpdated', r); return 'Gig updated'; } @@ -249,15 +249,15 @@ class AgController { (async () => { let receiver: { value:any; done: any; }; const rConsumer = client.socket.receiver(action).createConsumer(); - while (true) { - receiver = await rConsumer.next(); + while (true) { + receiver = await rConsumer.next(); const obj = JSON.stringify(receiver.value); debug(`received ${action} message: ${obj}`); if (!receiver.value) break; if (typeof receiver.value.token === 'string') { - - if (action === 'editTour') await this.updateTour(receiver.value); - + + if (action === 'editGig' || action === 'editTour') await this.updateGig(receiver.value); + else await this.updateImage(receiver.value, client); } /* istanbul ignore else */if (receiver.done) break; @@ -271,9 +271,12 @@ class AgController { debug(this.clients); this.handleReceiver(client); this.sendPulse(client); - this.newTour(client); - this.removeTour(client); - this.editDoc(client, 'editTour'); + this.newGig(client, 'newGig'); + this.newGig(client, 'newTour'); // legacy alias during migration + this.removeGig(client, 'deleteGig'); + this.removeGig(client, 'deleteTour'); // legacy alias during migration + this.editDoc(client, 'editGig'); + this.editDoc(client, 'editTour'); // legacy alias during migration this.newImage(client); this.editDoc(client, 'editImage'); this.removeImage(client); diff --git a/src/AgController/utils.ts b/src/AgController/utils.ts index 7ca3b4a..17f56ee 100644 --- a/src/AgController/utils.ts +++ b/src/AgController/utils.ts @@ -1,68 +1,74 @@ import Debug from 'debug'; -import tourData from '../model/tour/reset-tour.js'; +import gigData from '../model/gig/reset-gig.js'; import bookData from '../model/book/reset-book.js'; -import TourController from '../model/tour/tour-controller.js'; +import GigController from '../model/gig/gig-controller.js'; import BookController from '../model/book/book-controller.js'; const debug = Debug('WebJamSocketServer:AgController/utils'); async function resetData( - tour: typeof tourData['tour'], + gig: typeof gigData['gig'], book: typeof bookData['book'], - tourController: typeof TourController, + gigController: typeof GigController, bookController: typeof BookController, ) { try { - await tourController.deleteAllDocs(); - await tourController.createDocs(tour); + await gigController.deleteAllDocs(); + await gigController.createDocs(gig); await bookController.deleteAllDocs(); await bookController.createDocs(book); return true; } catch (e) { const eMessage = (e as Error).message; - debug(eMessage); + debug(eMessage); return false; } } -async function handleTour( - func: string, - data: { datetime: Date; venue: string; city:string, usState:string }, +async function handleGig( + func: string, + data: any, message: string, - tourController:any, + gigController:any, server:any, ):Promise { // eslint-disable-next-line security/detect-object-injection - const r = await (tourController)[func](data); + const r = await (gigController)[func](data); server.exchange.transmitPublish(message, r); } -async function removeTour(receiver:any, client:any, tourController:any, server:any) { +async function removeGig(receiver:any, client:any, gigController:any, server:any) { try { - if (typeof receiver.value.tour.tourId === 'string' && typeof receiver.value.token === 'string') { - await handleTour('deleteById', receiver.value.tour.tourId, 'tourDeleted', tourController, server); + // Tolerate both the new { gig: { gigId } } and the legacy { tour: { tourId } } payloads. + const payload = receiver.value.gig ?? receiver.value.tour; + const id = payload?.gigId ?? payload?.tourId; + if (typeof id === 'string' && typeof receiver.value.token === 'string') { + await handleGig('deleteById', id, 'gigDeleted', gigController, server); } - } catch (e) { + } catch (e) { const eMessage = (e as Error).message; - client.socket.transmit('socketError', { deleteTour: eMessage });// send error back to client - debug(eMessage); + client.socket.transmit('socketError', { deleteGig: eMessage });// send error back to client + debug(eMessage); } } -function assertCanCreateTour( +function assertCanCreateGig( user: { userType?: string; privileges?: string[] } | null | undefined, goodRoles: string[] | undefined, ): void { - if (!user) throw new Error('Not allowed to create new tour'); + if (!user) throw new Error('Not allowed to create new gig'); if (Array.isArray(user.privileges) && user.privileges.length > 0) { - if (!user.privileges.includes('tour:create')) throw new Error('missing capability tour:create'); + // Accept the new gig:create and the legacy tour:create during the rename migration. + if (!user.privileges.includes('gig:create') && !user.privileges.includes('tour:create')) { + throw new Error('missing capability gig:create'); + } return; } if (!goodRoles || !user.userType || goodRoles.indexOf(user.userType) === -1) { - throw new Error('Not allowed to create new tour'); + throw new Error('Not allowed to create new gig'); } } export default { - resetData, removeTour, handleTour, assertCanCreateTour, + resetData, removeGig, handleGig, assertCanCreateGig, }; diff --git a/src/model/gig/gig-controller.ts b/src/model/gig/gig-controller.ts new file mode 100644 index 0000000..858fec6 --- /dev/null +++ b/src/model/gig/gig-controller.ts @@ -0,0 +1,7 @@ +import Controller from '../../lib/controller.js'; +import gigModel from './gig-facade.js'; + +class GigController extends Controller { +} + +export default new GigController(gigModel); diff --git a/src/model/gig/gig-facade.ts b/src/model/gig/gig-facade.ts new file mode 100644 index 0000000..6799728 --- /dev/null +++ b/src/model/gig/gig-facade.ts @@ -0,0 +1,8 @@ +import Model from '../../lib/facade.js'; +import gigSchema from './gig-schema.js'; + +class GigModel extends Model { + +} + +export default new GigModel(gigSchema); diff --git a/src/model/tour/tour-schema.ts b/src/model/gig/gig-schema.ts similarity index 65% rename from src/model/tour/tour-schema.ts rename to src/model/gig/gig-schema.ts index f0d9383..85e3b65 100644 --- a/src/model/tour/tour-schema.ts +++ b/src/model/gig/gig-schema.ts @@ -6,7 +6,7 @@ const options = { const { Schema } = mongoose; -const tourSchema = new Schema({ +const gigSchema = new Schema({ date: { type: String, required: false }, time: { type: String, required: false }, datetime: { type: Date, require: true }, @@ -15,7 +15,10 @@ const tourSchema = new Schema({ usState: { type: String, required: false }, venue: { type: String, required: true }, tickets: { type: String, required: false }, + duration: { type: Number, required: false, default: 0 }, + promoImageUrl: { type: String, required: false }, more: { type: String, required: false }, }, options); -export default mongoose.models.Tour || mongoose.model('Tour', tourSchema); +// Explicit collection name 'gigs' (a tours -> gigs migration moves the data). +export default mongoose.models.Gig || mongoose.model('Gig', gigSchema, 'gigs'); diff --git a/src/model/tour/reset-tour.ts b/src/model/gig/reset-gig.ts similarity index 99% rename from src/model/tour/reset-tour.ts rename to src/model/gig/reset-gig.ts index de1bf82..e5055c4 100644 --- a/src/model/tour/reset-tour.ts +++ b/src/model/gig/reset-gig.ts @@ -5,7 +5,7 @@ const makefuture = (day:number) => { return future; }; export default { - tour: [{ + gig: [{ date: 'Nov 17, 2019', datetime: new Date('November 17, 2019'), time: '1:00 pm', diff --git a/src/model/tour/tour-controller.ts b/src/model/tour/tour-controller.ts deleted file mode 100644 index bf16f89..0000000 --- a/src/model/tour/tour-controller.ts +++ /dev/null @@ -1,14 +0,0 @@ -import Controller from '../../lib/controller.js'; -import tourModel from './tour-facade.js'; - -class TourController extends Controller { - // async createTour(body) { - // let result; - // try { result = await this.model.create(body); } catch (e) { - // debug(e.message); throw e; - // } debug(result); - // return Promise.resolve(true); - // } -} - -export default new TourController(tourModel); diff --git a/src/model/tour/tour-facade.ts b/src/model/tour/tour-facade.ts deleted file mode 100644 index 76a3a11..0000000 --- a/src/model/tour/tour-facade.ts +++ /dev/null @@ -1,8 +0,0 @@ -import Model from '../../lib/facade.js'; -import tourSchema from './tour-schema.js'; - -class TourModel extends Model { - -} - -export default new TourModel(tourSchema); diff --git a/src/model/tour/tour-router.ts b/src/model/tour/tour-router.ts deleted file mode 100644 index 93aa807..0000000 --- a/src/model/tour/tour-router.ts +++ /dev/null @@ -1,20 +0,0 @@ -// const router = require('express').Router(); -// const controller = require('./book-controller'); -// const authUtils = require('../../auth/authUtils'); -// const routeUtils = require('../../lib/routeUtils'); -// -// routeUtils.setRoot(router, controller, authUtils); -// -// router.route('/one') -// .get((...args) => controller.findOne(...args)) -// .put(authUtils.ensureAuthenticated, (...args) => controller.findOneAndUpdate(...args)); -// -// router.route('/:id', authUtils.ensureAuthenticated) -// .put((...args) => controller.findByIdAndUpdate(...args)) -// .get((...args) => controller.findById(...args)) -// .delete((...args) => controller.findByIdAndRemove(...args)); -// -// router.route('/findcheckedout/:id', authUtils.ensureAuthenticated) -// .get((...args) => controller.findCheckedOut(...args)); -// -// module.exports = router; diff --git a/src/scripts/migrate-tours-to-gigs.ts b/src/scripts/migrate-tours-to-gigs.ts new file mode 100644 index 0000000..5f72af9 --- /dev/null +++ b/src/scripts/migrate-tours-to-gigs.ts @@ -0,0 +1,37 @@ +// One-time migration for the tour -> gig rename: rename the MongoDB collection +// `tours` -> `gigs` so the new `Gig` model (bound to the `gigs` collection) +// reads the existing data. Idempotent and safe to run more than once. +// +// Run against the target DB (set MONGO_DB_URI), e.g. after a build: +// MONGO_DB_URI=... node build/src/scripts/migrate-tours-to-gigs.js +import dotenv from 'dotenv'; +import mongoose from 'mongoose'; + +dotenv.config(); + +async function migrate(): Promise { + const uri = process.env.MONGO_DB_URI || ''; + if (!uri) throw new Error('MONGO_DB_URI is not set'); + await mongoose.connect(uri, { autoIndex: false }); + const { db } = mongoose.connection; + if (!db) throw new Error('no database connection'); + const names = (await db.listCollections().toArray()).map((c) => c.name); + const hasTours = names.includes('tours'); + const hasGigs = names.includes('gigs'); + if (hasGigs && !hasTours) { + console.log('Already migrated: `gigs` exists and there is no `tours` collection. Nothing to do.'); + } else if (!hasTours) { + console.log('No `tours` collection found; nothing to migrate.'); + } else if (hasTours && hasGigs) { + throw new Error('Both `tours` and `gigs` collections exist — refusing to auto-merge. Resolve manually.'); + } else { + await db.renameCollection('tours', 'gigs'); + console.log('Renamed collection `tours` -> `gigs`.'); + } + await mongoose.disconnect(); +} + +migrate().catch((e) => { + console.error(e); + process.exitCode = 1; +}); diff --git a/test/AgController/index.spec.ts b/test/AgController/index.spec.ts index 592d181..9099afc 100644 --- a/test/AgController/index.spec.ts +++ b/test/AgController/index.spec.ts @@ -42,8 +42,8 @@ describe('AgControler', () => { const agController = new AgController(aStub); agController.handleReceiver = vi.fn(); agController.sendPulse = vi.fn(); - agController.newTour = vi.fn(); - agController.removeTour = vi.fn(); + agController.newGig = vi.fn(); + agController.removeGig = vi.fn(); agController.editDoc = vi.fn(); agController.newImage = vi.fn(); agController.removeImage = vi.fn(); @@ -145,9 +145,9 @@ describe('AgControler', () => { receiver: () => ({ createConsumer: () => ({ next: () => Promise.resolve({ value: 123, done: true }) }) }), }, }; - agController.tourController.getAllSort = vi.fn(() => Promise.resolve([])); - r = await agController.sendTours(cStub); - expect(r).toBe('sent tours'); + agController.gigController.getAllSort = vi.fn(() => Promise.resolve([])); + r = await agController.sendGigs(cStub); + expect(r).toBe('sent gigs'); }); it('handles error when gets all tours', async () => { const agController = new AgController(aStub); @@ -159,8 +159,8 @@ describe('AgControler', () => { receiver: () => ({ createConsumer: () => ({ next: () => Promise.resolve({ value: 123, done: true }) }) }), }, }; - agController.tourController.getAllSort = vi.fn(() => Promise.reject(new Error('bad'))); - r = await agController.sendTours(sStub); + agController.gigController.getAllSort = vi.fn(() => Promise.reject(new Error('bad'))); + r = await agController.sendGigs(sStub); expect(r).toBe('bad'); }); it('handles error when gets all books', async () => { @@ -179,8 +179,8 @@ describe('AgControler', () => { }); it('updates a tours', async () => { const agController = new AgController(aStub); - agController.tourController.findByIdAndUpdate = vi.fn(() => Promise.resolve(true)); - r = await agController.updateTour({ + agController.gigController.findByIdAndUpdate = vi.fn(() => Promise.resolve(true)); + r = await agController.updateGig({ tourId: testId, tour: { venue: 'venue', datetime: new Date(), city: 'city', usState: 'state', @@ -190,8 +190,8 @@ describe('AgControler', () => { }); it('handles error from updates a tours', async () => { const agController = new AgController(aStub); - agController.tourController.findByIdAndUpdate = vi.fn(() => Promise.reject(new Error('bad'))); - r = await agController.updateTour({ + agController.gigController.findByIdAndUpdate = vi.fn(() => Promise.reject(new Error('bad'))); + r = await agController.updateGig({ tourId: testId, tour: { venue: 'venue', datetime: new Date(), city: 'city', usState: 'state', @@ -202,7 +202,7 @@ describe('AgControler', () => { it('does not process the newTour message from client when token is not valid', async () => { const agController = new AgController(aStub); agController.clients = ['123']; - agController.tourController.createDocs = vi.fn(() => Promise.resolve([])); + agController.gigController.createDocs = vi.fn(() => Promise.resolve([])); const cStub:any = { socket: { id: '123', @@ -225,14 +225,14 @@ describe('AgControler', () => { }; const setIntervalMock:any = vi.fn((cb:any) => cb()); global.setInterval = setIntervalMock; - agController.newTour(cStub); + agController.newGig(cStub, 'newGig'); await delay(1000); - expect(agController.tourController.createDocs).not.toHaveBeenCalled(); + expect(agController.gigController.createDocs).not.toHaveBeenCalled(); }); it('processes the newTour message from client', async () => { const agController = new AgController(aStub); agController.clients = ['123']; - agController.tourController.createDocs = vi.fn(() => Promise.resolve([])); + agController.gigController.createDocs = vi.fn(() => Promise.resolve([])); const cStub:any = { socket: { id: '123', @@ -261,14 +261,14 @@ describe('AgControler', () => { ok: true, json: () => Promise.resolve({ userType: JSON.parse(process.env.userRoles || '{}').roles[0] }), })); - agController.newTour(cStub); + agController.newGig(cStub, 'newGig'); await delay(1000); - expect(agController.tourController.createDocs).toHaveBeenCalled(); + expect(agController.gigController.createDocs).toHaveBeenCalled(); }); it('return the not allowed socketError when processes the newTour message from client', async () => { const agController = new AgController(aStub); agController.clients = ['123']; - agController.tourController.createDocs = vi.fn(() => Promise.resolve([])); + agController.gigController.createDocs = vi.fn(() => Promise.resolve([])); clientStub = { socket: { id: '123', @@ -297,15 +297,15 @@ describe('AgControler', () => { ok: true, json: () => Promise.resolve({ userType: 'cool' }), })); - agController.newTour(clientStub); + agController.newGig(clientStub, 'newGig'); await delay(1000); - expect(clientStub.socket.transmit).toHaveBeenCalledWith('socketError', { newTour: 'Not allowed to create new tour' }); + expect(clientStub.socket.transmit).toHaveBeenCalledWith('socketError', { newGig: 'Not allowed to create new gig' }); }); it('allows newTour when user has tour:create privilege (capability path)', async () => { const agController = new AgController(aStub); agController.clients = ['123']; - agController.tourController.createDocs = vi.fn(() => Promise.resolve([])); + agController.gigController.createDocs = vi.fn(() => Promise.resolve([])); const cStub:any = { socket: { id: '123', @@ -333,15 +333,15 @@ describe('AgControler', () => { ok: true, json: () => Promise.resolve({ userType: 'unrecognized-role', privileges: ['tour:create'] }), })); - agController.newTour(cStub); + agController.newGig(cStub, 'newGig'); await delay(1000); - expect(agController.tourController.createDocs).toHaveBeenCalled(); + expect(agController.gigController.createDocs).toHaveBeenCalled(); }); it('rejects newTour with missing capability error when privileges lack tour:create', async () => { const agController = new AgController(aStub); agController.clients = ['123']; - agController.tourController.createDocs = vi.fn(() => Promise.resolve([])); + agController.gigController.createDocs = vi.fn(() => Promise.resolve([])); const cStub:any = { socket: { id: '123', @@ -369,15 +369,15 @@ describe('AgControler', () => { ok: true, json: () => Promise.resolve({ userType: 'unrecognized-role', privileges: ['song:read'] }), })); - agController.newTour(cStub); + agController.newGig(cStub, 'newGig'); await delay(1000); - expect(cStub.socket.transmit).toHaveBeenCalledWith('socketError', { newTour: 'missing capability tour:create' }); + expect(cStub.socket.transmit).toHaveBeenCalledWith('socketError', { newGig: 'missing capability gig:create' }); }); it('return the invalid request socketError when processes the newTour message from client', async () => { const agController = new AgController(aStub); agController.clients = ['123']; - agController.tourController.createDocs = vi.fn(() => Promise.resolve([])); + agController.gigController.createDocs = vi.fn(() => Promise.resolve([])); clientStub = { socket: { id: '123', @@ -406,17 +406,17 @@ describe('AgControler', () => { ok: true, json: () => Promise.resolve({ userType: JSON.parse(process.env.userRoles || '{}').roles[0] }), })); - agController.newTour(clientStub); + agController.newGig(clientStub, 'newGig'); await delay(1000); expect(clientStub.socket.transmit).toHaveBeenCalledWith( 'socketError', - { newTour: 'Invalid create gig data' }, + { newGig: 'Invalid create gig data' }, ); }); it('handles missing receiver value when process the newTour message from client', () => { const agController = new AgController(aStub); agController.clients = ['123']; - utils.handleTour = vi.fn(); + utils.handleGig = vi.fn(); const cStub:any = { socket: { id: '123', @@ -433,8 +433,8 @@ describe('AgControler', () => { }; const setIntervalMock:any = vi.fn((cb:any) => cb()); global.setInterval = setIntervalMock; - agController.newTour(cStub); - expect(utils.handleTour).not.toHaveBeenCalled(); + agController.newGig(cStub, 'newGig'); + expect(utils.handleGig).not.toHaveBeenCalled(); }); it('process the newImage message from client', async () => { const agController = new AgController(aStub); @@ -541,9 +541,9 @@ describe('AgControler', () => { agController.removeImage(cStub); expect(agController.bookController.deleteById).not.toHaveBeenCalled(); }); - it('process the removeTour message from client', () => { + it('process the removeGig message from client', () => { const agController = new AgController(aStub); - utils.handleTour = vi.fn(); + utils.handleGig = vi.fn(); agController.clients = ['123']; const cStub:any = { socket: { @@ -567,12 +567,12 @@ describe('AgControler', () => { }; const setIntervalMock:any = vi.fn((cb:any) => cb()); global.setInterval = setIntervalMock; - utils.removeTour = vi.fn(() => Promise.resolve()); - expect(agController.removeTour(cStub)).toBeUndefined(); + utils.removeGig = vi.fn(() => Promise.resolve()); + expect(agController.removeGig(cStub, 'deleteGig')).toBeUndefined(); }); - it('does not process the removeTour message from client', () => { + it('does not process the removeGig message from client', () => { const agController = new AgController(aStub); - utils.handleTour = vi.fn(); + utils.handleGig = vi.fn(); agController.clients = ['123']; const cStub:any = { socket: { @@ -589,8 +589,8 @@ describe('AgControler', () => { }; const setIntervalMock:any = vi.fn((cb:any) => cb()); global.setInterval = setIntervalMock; - agController.removeTour(cStub); - expect(utils.handleTour).not.toHaveBeenCalled(); + agController.removeGig(cStub, 'deleteGig'); + expect(utils.handleGig).not.toHaveBeenCalled(); }); it('processes the updateImage message from client', async () => { const agController = new AgController(aStub); @@ -671,10 +671,10 @@ describe('AgControler', () => { }; const setIntervalMock: any = vi.fn((cb:any) => cb()); global.setInterval = setIntervalMock; - agController.updateTour = vi.fn(); + agController.updateGig = vi.fn(); agController.editDoc(sStub, 'editTour'); await delay(1000); - expect(agController.updateTour).toHaveBeenCalled(); + expect(agController.updateGig).toHaveBeenCalled(); }); it('does not process the editTour message from client when token is missing', async () => { const agController = new AgController(aStub); @@ -700,10 +700,10 @@ describe('AgControler', () => { }; const setIntervalMock:any = vi.fn((cb:any) => cb()); global.setInterval = setIntervalMock; - agController.updateTour = vi.fn(); + agController.updateGig = vi.fn(); agController.editDoc(sStub, 'editTour'); await delay(1000); - expect(agController.updateTour).not.toHaveBeenCalled(); + expect(agController.updateGig).not.toHaveBeenCalled(); }); it('handles error when process the editTour message from client', async () => { const agController = new AgController(aStub); @@ -730,7 +730,7 @@ describe('AgControler', () => { }; const setIntervalMock:any = vi.fn((cb:any) => cb()); global.setInterval = setIntervalMock; - agController.tourController.findByIdAndUpdate = vi.fn(() => Promise.reject(new Error('bad'))); + agController.gigController.findByIdAndUpdate = vi.fn(() => Promise.reject(new Error('bad'))); agController.server.exchange.transmitPublish = vi.fn(); agController.editDoc(sStub, 'editTour'); await delay(1000); @@ -761,10 +761,10 @@ describe('AgControler', () => { }; const setIntervalMock:any = vi.fn((cb:any) => cb()); global.setInterval = setIntervalMock; - utils.handleTour = vi.fn(); - agController.removeTour(sStub); + utils.handleGig = vi.fn(); + agController.removeGig(sStub, 'deleteGig'); await delay(1000); - expect(utils.handleTour).not.toHaveBeenCalled(); + expect(utils.handleGig).not.toHaveBeenCalled(); }); it('creates a book (image)', async () => { const agController = new AgController(aStub); diff --git a/test/AgController/utils.spec.ts b/test/AgController/utils.spec.ts index 282f3d4..6c01f62 100644 --- a/test/AgController/utils.spec.ts +++ b/test/AgController/utils.spec.ts @@ -7,29 +7,29 @@ describe('AgController/utils', () => { const result = await utils.resetData({} as any, {} as any, { deleteAllDocs } as any, {} as any); expect(result).toBe(false); }); - it('handleTour is successful', async () => { + it('handleGig is successful', async () => { const transmitPublish = vi.fn(); const server = { exchange: { transmitPublish } }; - const tourController = { create: vi.fn(() => Promise.resolve()) }; - await utils.handleTour('create', {} as any, 'created', tourController, server as any); + const gigController = { create: vi.fn(() => Promise.resolve()) }; + await utils.handleGig('create', {} as any, 'created', gigController, server as any); expect(transmitPublish).toHaveBeenCalled(); }); - it('removeTour', async () => { + it('removeGig', async () => { const transmitPublish = vi.fn(); const server = { exchange: { transmitPublish } }; const client = { socket: { transmit: vi.fn() } }; const receiver = { value: { token: 'token', tour: { tourId: 'asdf' } } }; - const tourController = { deleteById: vi.fn(() => Promise.resolve()) }; - await utils.removeTour(receiver, client, tourController, server); + const gigController = { deleteById: vi.fn(() => Promise.resolve()) }; + await utils.removeGig(receiver, client, gigController, server); expect(transmitPublish).toHaveBeenCalled(); }); - it('removeTour catches error', async () => { + it('removeGig catches error', async () => { const transmitPublish = vi.fn(); const server = { exchange: { transmitPublish } }; const client = { socket: { transmit: vi.fn() } }; const receiver = { value: { token: 'token', tour: { tourId: 'asdf' } } }; - const tourController = { deleteById: vi.fn(() => Promise.reject(new Error('bad'))) }; - await utils.removeTour(receiver, client, tourController, server); + const gigController = { deleteById: vi.fn(() => Promise.reject(new Error('bad'))) }; + await utils.removeGig(receiver, client, gigController, server); expect(transmitPublish).not.toHaveBeenCalled(); expect(client.socket.transmit).toHaveBeenCalled(); }); diff --git a/test/tour/tour-controller.test.ts b/test/gig/gig-controller.test.ts similarity index 95% rename from test/tour/tour-controller.test.ts rename to test/gig/gig-controller.test.ts index 4127f3e..89faf6c 100644 --- a/test/tour/tour-controller.test.ts +++ b/test/gig/gig-controller.test.ts @@ -1,7 +1,7 @@ import mongoose from 'mongoose'; -import controller from '../../src/model/tour/tour-controller.js'; +import controller from '../../src/model/gig/gig-controller.js'; -describe('TourController', () => { +describe('GigController', () => { const testId = new mongoose.Types.ObjectId(); it('deletes all tours', async () => { controller.model.deleteMany = vi.fn(() => Promise.resolve(true));