test(integration): add real-stack integration test suite with PGlite
- PGlite (in-memory WASM Postgres) hosted in a child process, exposed via TCP socket; real migrations applied with prisma migrate deploy - Vitest harness (vitest.integration.config.ts, global-setup spawns PGlite + built Nuxt server, per-test TRUNCATE, factories, cookie-jar API client); no mocks, zero production code changes - 50 tests across auth, hunt, team, submit, review, showcase, diashow incl. SuperJSON envelope check; *.itest.ts keeps bun test untouched - CI: parallel unit + integration jobs in .github/workflows/test.yml - tests/ covered by nuxt typecheck via tests/tsconfig.json reference
This commit is contained in:
@@ -0,0 +1,137 @@
|
||||
import { beforeEach, describe, expect, it } from 'vitest';
|
||||
import { newClient } from '../helpers/api';
|
||||
import { resetDatabase } from '../helpers/database';
|
||||
import {
|
||||
createAnswer,
|
||||
createFile,
|
||||
createHunt,
|
||||
createQuest,
|
||||
createTeam,
|
||||
createUser,
|
||||
TEST_PASSWORD
|
||||
} from '../helpers/factories';
|
||||
import { prisma } from './setup';
|
||||
|
||||
beforeEach(resetDatabase);
|
||||
|
||||
/**
|
||||
* Hunt with one quest and three finalized answers:
|
||||
* - a public picture (shown)
|
||||
* - a private picture (hidden)
|
||||
* - no picture at all (hidden)
|
||||
*/
|
||||
async function seedDiashow(reveal: boolean) {
|
||||
const creator = await createUser();
|
||||
const hunt = await createHunt(creator.id, {
|
||||
revealAnswers: reveal,
|
||||
revealQuests: reveal
|
||||
});
|
||||
const quest = await createQuest(hunt.id);
|
||||
const owner = await createUser();
|
||||
const team = await createTeam(hunt.id, owner.id);
|
||||
|
||||
const answerPublic = await createAnswer({
|
||||
teamId: team.id,
|
||||
questId: quest.id,
|
||||
memberId: owner.id
|
||||
});
|
||||
const publicFile = await createFile(owner.id, { answerId: answerPublic.id });
|
||||
await prisma.questAnswer.update({
|
||||
where: { id: answerPublic.id },
|
||||
data: { pictureId: publicFile.id, final: true }
|
||||
});
|
||||
|
||||
const answerPrivate = await createAnswer({
|
||||
teamId: team.id,
|
||||
questId: quest.id,
|
||||
memberId: owner.id
|
||||
});
|
||||
const privateFile = await createFile(owner.id, {
|
||||
answerId: answerPrivate.id,
|
||||
private: true
|
||||
});
|
||||
await prisma.questAnswer.update({
|
||||
where: { id: answerPrivate.id },
|
||||
data: { pictureId: privateFile.id, final: true }
|
||||
});
|
||||
|
||||
await createAnswer(
|
||||
{ teamId: team.id, questId: quest.id, memberId: owner.id },
|
||||
{ final: true, text: 'no picture' }
|
||||
);
|
||||
|
||||
return { hunt, quest, team, publicFile };
|
||||
}
|
||||
|
||||
describe('diashow (GET /api/hunt/[huntId]/diashow)', () => {
|
||||
it('returns 404 for unknown hunts', async () => {
|
||||
const res = await newClient().get('/api/hunt/999999/diashow');
|
||||
expect(res.status).toBe(404);
|
||||
});
|
||||
|
||||
it('blocks anonymous users before answers are revealed', async () => {
|
||||
const { hunt } = await seedDiashow(false);
|
||||
const res = await newClient().get(`/api/hunt/${hunt.id}/diashow`);
|
||||
expect(res.status).toBe(403);
|
||||
});
|
||||
|
||||
it('lets the hunt creator peek before reveal', async () => {
|
||||
const { hunt } = await seedDiashow(false);
|
||||
const creator = await prisma.user.findFirstOrThrow({
|
||||
where: { huntsCreated: { some: { id: hunt.id } } }
|
||||
});
|
||||
|
||||
const client = newClient();
|
||||
await client.login(creator.email, TEST_PASSWORD);
|
||||
const res = await client.get(`/api/hunt/${hunt.id}/diashow`);
|
||||
expect(res.status).toBe(200);
|
||||
});
|
||||
|
||||
it('only shows finalized answers with public pictures', async () => {
|
||||
const { hunt, quest, team, publicFile } = await seedDiashow(true);
|
||||
|
||||
const res = await newClient().get(`/api/hunt/${hunt.id}/diashow`);
|
||||
expect(res.status).toBe(200);
|
||||
const body = await res.json();
|
||||
|
||||
expect(body.id).toBe(hunt.id);
|
||||
expect(body.quests).toHaveLength(1);
|
||||
expect(body.quests[0].id).toBe(quest.id);
|
||||
expect(body.quests[0].answers).toHaveLength(1);
|
||||
expect(body.quests[0].answers[0].picture.url).toBe(publicFile.url);
|
||||
expect(body.quests[0].answers[0].team).toMatchObject({
|
||||
id: team.id,
|
||||
name: team.name
|
||||
});
|
||||
});
|
||||
|
||||
it('omits quests whose final answers are all private', async () => {
|
||||
const creator = await createUser();
|
||||
const hunt = await createHunt(creator.id, {
|
||||
revealAnswers: true,
|
||||
revealQuests: true
|
||||
});
|
||||
const quest = await createQuest(hunt.id);
|
||||
const owner = await createUser();
|
||||
const team = await createTeam(hunt.id, owner.id);
|
||||
|
||||
const answer = await createAnswer({
|
||||
teamId: team.id,
|
||||
questId: quest.id,
|
||||
memberId: owner.id
|
||||
});
|
||||
const privateFile = await createFile(owner.id, {
|
||||
answerId: answer.id,
|
||||
private: true
|
||||
});
|
||||
await prisma.questAnswer.update({
|
||||
where: { id: answer.id },
|
||||
data: { pictureId: privateFile.id, final: true }
|
||||
});
|
||||
|
||||
const res = await newClient().get(`/api/hunt/${hunt.id}/diashow`);
|
||||
expect(res.status).toBe(200);
|
||||
const body = await res.json();
|
||||
expect(body.quests).toEqual([]);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user