diff --git a/plice/src/lib/server/db/index.ts b/plice/src/lib/server/db/index.ts index d7eeb7a..4413eb6 100644 --- a/plice/src/lib/server/db/index.ts +++ b/plice/src/lib/server/db/index.ts @@ -41,6 +41,9 @@ export interface Database { }[]; }[]; } | null>; + + getLatestTrackVersion: (trackId: number) => Promise<{ id: string } | null>; + createTrack: (producerId: string, title: string) => Promise; createTrackVersion: (trackId: number) => Promise; createComment: (versionId: string, userId: string, content: string) => Promise; diff --git a/plice/src/lib/server/db/mock.ts b/plice/src/lib/server/db/mock.ts index 644ff08..1a87277 100644 --- a/plice/src/lib/server/db/mock.ts +++ b/plice/src/lib/server/db/mock.ts @@ -1,5 +1,7 @@ import type { Database } from '$lib/server/db'; +import { v4 as uuidv4 } from 'uuid'; + // TODO Flesh this out export class MockGenericDatabase implements Database { fetchHomepageData = async (_producerId: string) => { @@ -31,4 +33,12 @@ export class MockGenericDatabase implements Database { return Promise.resolve(data); }; + + getLatestTrackVersion = async (_trackId: number) => { + return Promise.resolve({ id: '66dbd96c-2c84-4fe6-8a74-eeb3876f6f34' }); + }; + + createComment = async (_versionId: string, _userId: string, _content: string) => { + return Promise.resolve(null); + }; } diff --git a/plice/src/lib/server/db/prisma.ts b/plice/src/lib/server/db/prisma.ts index 7d76082..1001baa 100644 --- a/plice/src/lib/server/db/prisma.ts +++ b/plice/src/lib/server/db/prisma.ts @@ -88,6 +88,20 @@ export class DatabasePrisma implements Database { }); }; + getLatestTrackVersion = async (trackId: number) => { + return await this.client.trackVersion.findFirst({ + select: { + id: true + }, + where: { + trackId + }, + orderBy: { + createdAt: 'desc' + } + }); + }; + createTrack = async (producerId: string, title: string) => { return await this.client.track.create({ data: { diff --git a/plice/src/routes/track/[slug]/+page.server.ts b/plice/src/routes/track/[slug]/+page.server.ts index 7862d61..217a912 100644 --- a/plice/src/routes/track/[slug]/+page.server.ts +++ b/plice/src/routes/track/[slug]/+page.server.ts @@ -1,6 +1,8 @@ import { error, redirect } from '@sveltejs/kit'; import type { Actions, PageServerLoad } from './$types'; +import { validate as validateUuid } from 'uuid'; + export const load: PageServerLoad = async ({ params: { slug }, locals: { database } }) => { const trackId = parseInt(slug); if (isNaN(trackId)) { @@ -22,11 +24,28 @@ export const actions: Actions = { const session = await authReq.validate(); if (!session) redirect(302, '/login'); - const versionId = slug; + let versionId; + + const re = /[1-9]\d*/; + + if (validateUuid(slug)) { + versionId = slug; + } else if (slug.match(re)?.[0] === slug) { + const trackId = parseInt(slug); + const result = await database.getLatestTrackVersion(trackId); + + if (result === null) { + error(404, 'Track not found'); + } + + versionId = result.id; + } else { + error(404, 'Track or track version not found'); + } const formData = await request.formData(); const comment = formData.get('comment') as string; - database.createComment(versionId, session.user.userId, comment); + await database.createComment(versionId, session.user.userId, comment); } }; diff --git a/plice/src/routes/track/[slug]/__snapshots__/page.server.test.ts.snap b/plice/src/routes/track/[slug]/__snapshots__/page.server.test.ts.snap new file mode 100644 index 0000000..fa8c177 --- /dev/null +++ b/plice/src/routes/track/[slug]/__snapshots__/page.server.test.ts.snap @@ -0,0 +1,30 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`create comment -- id 1`] = `undefined`; + +exports[`create comment -- id does not exist 1`] = ` +HttpError { + "body": { + "message": "Track not found", + }, + "status": 404, +} +`; + +exports[`create comment -- not logged in 1`] = ` +Redirect { + "location": "/login", + "status": 302, +} +`; + +exports[`create comment -- not uuid or id 1`] = ` +HttpError { + "body": { + "message": "Track or track version not found", + }, + "status": 404, +} +`; + +exports[`create comment -- uuid 1`] = `undefined`; diff --git a/plice/src/routes/track/[slug]/page.server.test.ts b/plice/src/routes/track/[slug]/page.server.test.ts new file mode 100644 index 0000000..664b6ed --- /dev/null +++ b/plice/src/routes/track/[slug]/page.server.test.ts @@ -0,0 +1,92 @@ +import { expect, test } from 'vitest'; +import { actions } from './+page.server'; + +import { v4 as uuidv4 } from 'uuid'; + +import { MockGenericDatabase } from '$lib/server/db/mock'; + +const baseLocals = { + // TODO: Replace with authentication adapter + authReq: { + validate: () => { + return { + user: { + userId: 1 + } + }; + } + }, + database: new MockGenericDatabase() +}; + +const baseRequest = { + formData: async () => { + let formData = new FormData(); + formData.append('comment', 'uwu'); + return formData; + } +}; + +test('create comment -- uuid', async () => { + expect( + await actions.comment({ + request: baseRequest, + params: { slug: uuidv4() }, + locals: baseLocals + }) + ).toMatchSnapshot(); +}); + +test('create comment -- id', async () => { + expect( + await actions.comment({ + request: baseRequest, + params: { slug: '1' }, + locals: baseLocals + }) + ).toMatchSnapshot(); +}); + +test('create comment -- id does not exist', async () => { + expect(async () => { + let locals = baseLocals; + locals.database.getLatestTrackVersion = async () => { + return null; + }; + + await actions.comment({ + request: baseRequest, + params: { slug: '1' }, + locals + }); + }).rejects.toThrowErrorMatchingSnapshot(); +}); + +test('create comment -- not uuid or id', async () => { + expect( + async () => + await actions.comment({ + request: baseRequest, + params: { slug: 'no' }, + locals: baseLocals + }) + ).rejects.toThrowErrorMatchingSnapshot(); +}); + +test('create comment -- not logged in', async () => { + let locals = baseLocals; + locals.authReq = { + validate: () => { + return null; + } + }; + + expect( + async () => + await actions.comment({ + request: baseRequest, + params: { slug: uuidv4() }, + locals: baseLocals + }) + ).rejects.toThrowErrorMatchingSnapshot(); +});