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,183 @@
|
||||
import { beforeEach, describe, expect, it } from 'vitest';
|
||||
import { newClient } from '../helpers/api';
|
||||
import { resetDatabase } from '../helpers/database';
|
||||
import {
|
||||
createHunt,
|
||||
createHuntMember,
|
||||
createQuest,
|
||||
createTeam,
|
||||
createTeamMember,
|
||||
createUser,
|
||||
TEST_PASSWORD
|
||||
} from '../helpers/factories';
|
||||
import { prisma } from './setup';
|
||||
|
||||
beforeEach(resetDatabase);
|
||||
|
||||
describe('hunt listing (GET /api/home)', () => {
|
||||
it('lists only public, joinable hunts that have not started yet', async () => {
|
||||
const creator = await createUser();
|
||||
await createHunt(creator.id, { allowJoin: true });
|
||||
await createHunt(creator.id, { allowJoin: true, public: false });
|
||||
await createHunt(creator.id, { allowJoin: false });
|
||||
await createHunt(creator.id, {
|
||||
allowJoin: true,
|
||||
start: new Date('2020-01-01')
|
||||
});
|
||||
await createHunt(creator.id, {
|
||||
allowJoin: true,
|
||||
start: new Date(Date.now() + 24 * 60 * 60 * 1000)
|
||||
});
|
||||
await createHunt(creator.id, {
|
||||
allowJoin: true,
|
||||
deletedAt: new Date()
|
||||
});
|
||||
|
||||
const res = await newClient().get('/api/home');
|
||||
expect(res.status).toBe(200);
|
||||
const body = await res.json();
|
||||
expect(body.own).toBeNull();
|
||||
// one hunt without start + one future hunt
|
||||
expect(body.others).toHaveLength(2);
|
||||
});
|
||||
|
||||
it('lists hunts the user participates in under own', async () => {
|
||||
const creator = await createUser();
|
||||
const hunt = await createHunt(creator.id, { allowJoin: true });
|
||||
const player = await createUser({ email: 'player@test.dev' });
|
||||
const team = await createTeam(hunt.id, creator.id);
|
||||
await createTeamMember(team.id, player.id);
|
||||
|
||||
const client = newClient();
|
||||
await client.login('player@test.dev', TEST_PASSWORD);
|
||||
const res = await client.get('/api/home');
|
||||
const body = await res.json();
|
||||
|
||||
expect(body.own).toHaveLength(1);
|
||||
expect(body.own[0].id).toBe(hunt.id);
|
||||
// the hunt is excluded from others because the user already plays it
|
||||
expect(body.others).toHaveLength(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('hunt detail (GET /api/hunt/[huntId])', () => {
|
||||
it('returns 404 for unknown hunts', async () => {
|
||||
const res = await newClient().get('/api/hunt/999999');
|
||||
expect(res.status).toBe(404);
|
||||
});
|
||||
|
||||
it('hides quests from anonymous users when revealQuests is false', async () => {
|
||||
const creator = await createUser();
|
||||
const hunt = await createHunt(creator.id);
|
||||
await createQuest(hunt.id);
|
||||
|
||||
const res = await newClient().get(`/api/hunt/${hunt.id}`);
|
||||
expect(res.status).toBe(200);
|
||||
const body = await res.json();
|
||||
expect(body.quests).toBeNull();
|
||||
expect(body.isMember).toBe(false);
|
||||
expect(body.loggedIn).toBe(false);
|
||||
expect(body.ownTeam).toBeNull();
|
||||
});
|
||||
|
||||
it('shows quests to hunt members even without revealQuests', async () => {
|
||||
const creator = await createUser();
|
||||
const hunt = await createHunt(creator.id);
|
||||
await createQuest(hunt.id);
|
||||
const member = await createUser({ email: 'member@test.dev' });
|
||||
await createHuntMember(hunt.id, member.id);
|
||||
|
||||
const client = newClient();
|
||||
await client.login('member@test.dev', TEST_PASSWORD);
|
||||
const res = await client.get(`/api/hunt/${hunt.id}`);
|
||||
const body = await res.json();
|
||||
expect(body.isMember).toBe(true);
|
||||
expect(body.quests).toHaveLength(1);
|
||||
});
|
||||
|
||||
it('exposes the own team to team owners', async () => {
|
||||
const creator = await createUser();
|
||||
const hunt = await createHunt(creator.id, { revealQuests: true });
|
||||
const team = await createTeam(hunt.id, creator.id, { name: 'Owners' });
|
||||
|
||||
const client = newClient();
|
||||
await client.login(creator.email, TEST_PASSWORD);
|
||||
const res = await client.get(`/api/hunt/${hunt.id}`);
|
||||
const body = await res.json();
|
||||
expect(body.ownTeam).toMatchObject({ id: team.id, name: 'Owners' });
|
||||
});
|
||||
});
|
||||
|
||||
describe('join hunt (POST /api/hunt/[huntId]/join)', () => {
|
||||
it('requires a session', async () => {
|
||||
const res = await newClient().post('/api/hunt/1/join', {
|
||||
team: 1,
|
||||
password: 'x'
|
||||
});
|
||||
expect(res.status).toBe(401);
|
||||
});
|
||||
|
||||
it('joins a team with the correct invite password', async () => {
|
||||
const creator = await createUser();
|
||||
const hunt = await createHunt(creator.id, { allowJoin: true });
|
||||
const team = await createTeam(hunt.id, creator.id, { password: 'abc123' });
|
||||
const player = await createUser({ email: 'joiner@test.dev' });
|
||||
|
||||
const client = newClient();
|
||||
await client.login('joiner@test.dev', TEST_PASSWORD);
|
||||
const res = await client.post(`/api/hunt/${hunt.id}/join`, {
|
||||
team: team.id,
|
||||
password: 'abc123'
|
||||
});
|
||||
expect(res.status).toBe(200);
|
||||
expect(await res.json()).toEqual({ success: true });
|
||||
|
||||
expect(
|
||||
await prisma.teamMember.count({
|
||||
where: { teamId: team.id, memberId: player.id }
|
||||
})
|
||||
).toBe(1);
|
||||
});
|
||||
|
||||
it('rejects wrong team or password with 404', async () => {
|
||||
const creator = await createUser();
|
||||
const hunt = await createHunt(creator.id, { allowJoin: true });
|
||||
const team = await createTeam(hunt.id, creator.id, { password: 'abc123' });
|
||||
await createUser({ email: 'joiner@test.dev' });
|
||||
|
||||
const client = newClient();
|
||||
await client.login('joiner@test.dev', TEST_PASSWORD);
|
||||
expect(
|
||||
(
|
||||
await client.post(`/api/hunt/${hunt.id}/join`, {
|
||||
team: team.id,
|
||||
password: 'wrong'
|
||||
})
|
||||
).status
|
||||
).toBe(404);
|
||||
expect(
|
||||
(
|
||||
await client.post(`/api/hunt/${hunt.id}/join`, {
|
||||
team: team.id + 999,
|
||||
password: 'abc123'
|
||||
})
|
||||
).status
|
||||
).toBe(404);
|
||||
});
|
||||
|
||||
it('rejects joining twice with 409', async () => {
|
||||
const creator = await createUser();
|
||||
const hunt = await createHunt(creator.id, { allowJoin: true });
|
||||
const team = await createTeam(hunt.id, creator.id, { password: 'abc123' });
|
||||
const player = await createUser({ email: 'joiner@test.dev' });
|
||||
await createTeamMember(team.id, player.id);
|
||||
|
||||
const client = newClient();
|
||||
await client.login('joiner@test.dev', TEST_PASSWORD);
|
||||
const res = await client.post(`/api/hunt/${hunt.id}/join`, {
|
||||
team: team.id,
|
||||
password: 'abc123'
|
||||
});
|
||||
expect(res.status).toBe(409);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user