/* eslint-disable @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-unsafe-return */ import "reflect-metadata"; import { type INestApplication } from "@nestjs/common"; import { Test } from "@nestjs/testing"; import { PrismaClient } from "@prisma/client"; import { randomUUID } from "node:crypto"; import request from "supertest"; import { afterAll, beforeAll, beforeEach, describe, expect, it } from "vitest"; import { assertSafeTestDatabaseUrl } from "../../../../prisma/database-safety.js"; import { AppModule } from "../app.module.js"; import { TokenService } from "../auth/token.service.js"; import { configureApp } from "../main.js"; import { CandidateRepository } from "./candidate.repository.js"; process.env.NODE_ENV = "test"; process.env.WEB_ORIGIN = "http://localhost:3000"; process.env.PHONE_ENCRYPTION_KEY = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="; process.env.PHONE_HMAC_KEY = "test-phone-hmac-key-with-at-least-32-bytes"; process.env.VERIFICATION_CODE_HMAC_KEY = "test-code-hmac-key-with-at-least-32-bytes"; process.env.JWT_SECRET = "test-jwt-secret-with-at-least-thirty-two-bytes"; process.env.REFRESH_TOKEN_HMAC_KEY = "test-refresh-hmac-key-with-at-least-32-bytes"; process.env.LEASE_TOKEN_HMAC_KEY = "test-lease-hmac-key-with-at-least-32-bytes"; process.env.LEASE_TOKEN_ENCRYPTION_KEY = "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB="; const prisma = new PrismaClient(); describe("match API with real PostgreSQL", () => { let app: INestApplication; beforeAll(async () => { assertSafeTestDatabaseUrl(process.env.DATABASE_URL ?? ""); await prisma.$connect(); const module = await Test.createTestingModule({ imports: [AppModule], }).compile(); app = module.createNestApplication(); configureApp(app); await app.init(); }); beforeEach(async () => { await prisma.$executeRawUnsafe(`TRUNCATE TABLE "accounts" CASCADE`); }); afterAll(async () => { await app?.close(); await prisma.$disconnect(); }); async function actor( deviceId: string, profileStatus: "APPROVED" | "REVIEWING" = "APPROVED", ) { const account = await prisma.account.create({ data: { phoneCiphertext: Buffer.from("cipher"), phoneHmac: randomUUID(), anonymousProfile: { create: { nickname: `anon-${deviceId}`, avatarColor: "#123456", bio: `bio-${deviceId}`, reviewStatus: profileStatus, }, }, }, include: { anonymousProfile: true }, }); const session = await prisma.session.create({ data: { accountId: account.id, refreshTokenHash: randomUUID(), deviceId, expiresAt: new Date(Date.now() + 60_000), tokenFamily: randomUUID(), }, }); const token = app.get(TokenService).issueAccess({ sub: account.id, session_id: session.id, device_id: deviceId, scopes: ["user"], token_version: 0, }); return { id: account.id, authorization: `Bearer ${token}`, profile: account.anonymousProfile!, }; } async function bottle(authorId: string, contentText = "message in bottle") { return prisma.bottle.create({ data: { authorId, clientRequestId: randomUUID(), contentText, reviewStatus: "APPROVED", poolStatus: "IN_POOL", approvedAt: new Date(), }, }); } const pick = (authorization: string, key: string = randomUUID()) => request(app.getHttpServer()) .post("/api/v1/bottles/pick") .set("Authorization", authorization) .set("Idempotency-Key", key) .send({}); const returnBottle = ( authorization: string, bottleId: string, leaseId: string, token: string, ) => request(app.getHttpServer()) .post(`/api/v1/bottles/${bottleId}/return`) .set("Authorization", authorization) .send({ leaseId, token }); it("requires authentication for both match endpoints", async () => { await request(app.getHttpServer()) .post("/api/v1/bottles/pick") .set("Idempotency-Key", randomUUID()) .send({}) .expect(401); await request(app.getHttpServer()) .post(`/api/v1/bottles/${randomUUID()}/return`) .send({ leaseId: randomUUID(), token: "x".repeat(43) }) .expect(401); }); it("uses a bounded UUID keyset pivot so every region beyond the oldest 32 is reachable", async () => { const author = await actor("author-device"); const picker = await actor("picker-device"); const ids = Array.from({ length: 96 }, (_, index) => { const prefix = index.toString(16).padStart(8, "0"); return `${prefix}-0000-4000-8000-000000000000`; }); await prisma.bottle.createMany({ data: ids.map((id, index) => ({ id, authorId: author.id, clientRequestId: `pivot-${index}`, contentText: `pivot-${index}`, reviewStatus: "APPROVED" as const, poolStatus: "IN_POOL" as const, approvedAt: new Date(), })), }); const repository = app.get(CandidateRepository); const front = await repository.findBatch(prisma, picker.id, 32, ids[0]); const middle = await repository.findBatch(prisma, picker.id, 32, ids[40]); const back = await repository.findBatch(prisma, picker.id, 32, ids[88]); expect(front).toHaveLength(32); expect(middle[0]?.id).toBe(ids[40]); expect(back.map(({ id }) => id)).toContain(ids[95]); expect(back.map(({ id }) => id)).toContain(ids[0]); expect(new Set(back.map(({ id }) => id)).size).toBe(back.length); expect(middle.map(({ id }) => id)).not.toEqual(front.map(({ id }) => id)); }); it("picks an approved in-pool bottle and exposes only an anonymous public snapshot", async () => { const author = await actor("author-device"); const picker = await actor("picker-device"); const source = await bottle(author.id, "hello from sea"); const response = await pick(picker.authorization).expect(201); expect(response.body.data).toMatchObject({ bottle: { id: source.id, contentText: "hello from sea" }, author: { publicId: author.profile.publicId, nickname: author.profile.nickname, avatarColor: author.profile.avatarColor, bio: author.profile.bio, }, lease: { id: expect.any(String), token: expect.stringMatching(/^[A-Za-z0-9_-]{32,}$/), expiresAt: expect.any(String), }, }); expect(JSON.stringify(response.body.data)).not.toContain(author.id); expect(JSON.stringify(response.body.data)).not.toContain("phone"); expect( await prisma.bottle.findUniqueOrThrow({ where: { id: source.id } }), ).toMatchObject({ poolStatus: "LEASED", version: 2 }); expect( await prisma.bottlePickHistory.count({ where: { bottleId: source.id, pickerId: picker.id }, }), ).toBe(1); expect( ( await prisma.dailyUsage.findFirstOrThrow({ where: { accountId: picker.id }, }) ).bottlesPicked, ).toBe(1); }); it("excludes own, history, both block directions, unapproved profile and unavailable bottle states", async () => { const picker = await actor("picker-device"); const own = await bottle(picker.id, "own"); const historyAuthor = await actor("history-author"); const historical = await bottle(historyAuthor.id, "history"); await prisma.bottlePickHistory.create({ data: { bottleId: historical.id, pickerId: picker.id }, }); const blockedByPicker = await actor("blocked-one"); await bottle(blockedByPicker.id, "blocked outgoing"); await prisma.block.create({ data: { blockerId: picker.id, blockedId: blockedByPicker.id }, }); const blocksPicker = await actor("blocked-two"); await bottle(blocksPicker.id, "blocked incoming"); await prisma.block.create({ data: { blockerId: blocksPicker.id, blockedId: picker.id }, }); const pendingProfile = await actor("pending-profile", "REVIEWING"); await bottle(pendingProfile.id, "pending profile"); const validAuthor = await actor("valid-author"); const valid = await bottle(validAuthor.id, "valid"); await prisma.bottle.create({ data: { authorId: validAuthor.id, clientRequestId: randomUUID(), contentText: "reviewing", reviewStatus: "REVIEWING", poolStatus: "IN_POOL", }, }); const closed = await bottle(validAuthor.id, "closed"); await prisma.bottle.update({ where: { id: closed.id }, data: { poolStatus: "CLOSED" }, }); const leased = await bottle(validAuthor.id, "leased"); await prisma.bottle.update({ where: { id: leased.id }, data: { poolStatus: "LEASED" }, }); const activeLease = await bottle(validAuthor.id, "active lease id"); const leaseId = randomUUID(); await prisma.$transaction(async (tx) => { await tx.bottle.update({ where: { id: activeLease.id }, data: { activeLeaseId: leaseId }, }); await tx.bottlePickLease.create({ data: { id: leaseId, bottleId: activeLease.id, pickerId: picker.id, leaseTokenHash: randomUUID(), leaseTokenCiphertext: Buffer.from("ciphertext"), expiresAt: new Date(Date.now() + 60_000), }, }); }); const picked = await pick(picker.authorization).expect(201); expect(picked.body.data.bottle.id).toBe(valid.id); expect(picked.body.data.bottle.id).not.toBe(own.id); }); it("returns pool empty without consuming quota", async () => { const picker = await actor("picker-device"); const response = await pick(picker.authorization).expect(404); expect(response.body.code).toBe("BOTTLE_POOL_EMPTY"); expect( await prisma.dailyUsage.count({ where: { accountId: picker.id } }), ).toBe(0); }); it("deduplicates concurrent pick retries into one lease and one quota use", async () => { const author = await actor("author-device"); const picker = await actor("picker-device"); await bottle(author.id); const key = randomUUID(); const results = await Promise.all( Array.from({ length: 8 }, () => pick(picker.authorization, key)), ); expect(results.map((x) => [x.status, x.body.code])).toEqual( Array.from({ length: 8 }, () => [201, "OK"]), ); expect(new Set(results.map((x) => x.body.data.lease.id))).toHaveLength(1); expect(new Set(results.map((x) => x.body.data.lease.token))).toHaveLength( 1, ); expect(await prisma.bottlePickLease.count()).toBe(1); expect((await prisma.dailyUsage.findFirstOrThrow()).bottlesPicked).toBe(1); const token = results[0]!.body.data.lease.token as string; const stored = await prisma.bottlePickLease.findFirstOrThrow(); expect(token).toMatch(/^[A-Za-z0-9_-]{43}$/); expect(stored.leaseTokenHash).not.toContain(token); expect( Buffer.from(stored.leaseTokenCiphertext).toString("utf8"), ).not.toContain(token); }); it("fails closed with a stable response when an idempotent lease token cannot be decrypted", async () => { const author = await actor("author-device"); const picker = await actor("picker-device"); await bottle(author.id); const key = randomUUID(); const first = await pick(picker.authorization, key).expect(201); await prisma.bottlePickLease.update({ where: { id: first.body.data.lease.id }, data: { leaseTokenCiphertext: Buffer.from("corrupt") }, }); const retry = await pick(picker.authorization, key).expect(503); expect(retry.body).toMatchObject({ code: "SERVICE_UNAVAILABLE", message: "Bottle pick temporarily unavailable", }); expect(JSON.stringify(retry.body)).not.toContain("cipher"); expect(await prisma.bottlePickLease.count()).toBe(1); expect((await prisma.dailyUsage.findFirstOrThrow()).bottlesPicked).toBe(1); }); it.each(["RETURNED", "EXPIRED"] as const)( "returns the original pick response when the idempotent lease is %s", async (status) => { const author = await actor("author-device"); const picker = await actor("picker-device"); const source = await bottle(author.id); const key = randomUUID(); const first = await pick(picker.authorization, key).expect(201); if (status === "RETURNED") { await returnBottle( picker.authorization, source.id, first.body.data.lease.id, first.body.data.lease.token, ).expect(201); } else { await prisma.bottlePickLease.update({ where: { id: first.body.data.lease.id }, data: { status: "EXPIRED", endedAt: new Date() }, }); } const retry = await pick(picker.authorization, key).expect(201); expect(retry.body.data).toEqual(first.body.data); expect(await prisma.bottlePickLease.count()).toBe(1); expect((await prisma.dailyUsage.findFirstOrThrow()).bottlesPicked).toBe( 1, ); }, ); it("issues independent random opaque tokens for different requests", async () => { const author = await actor("author-device"); const picker = await actor("picker-device"); await bottle(author.id, "one"); await bottle(author.id, "two"); const first = (await pick(picker.authorization, "request-one").expect(201)) .body.data.lease.token; const second = (await pick(picker.authorization, "request-two").expect(201)) .body.data.lease.token; expect(first).toMatch(/^[A-Za-z0-9_-]{43}$/); expect(second).toMatch(/^[A-Za-z0-9_-]{43}$/); expect(second).not.toBe(first); }); it("rejects malformed return UUIDs and non-canonical lease tokens at the DTO boundary", async () => { const picker = await actor("picker-device"); await returnBottle( picker.authorization, "not-a-uuid", randomUUID(), "x".repeat(43), ).expect(400); await returnBottle( picker.authorization, randomUUID(), "not-a-uuid", "x".repeat(43), ).expect(400); await returnBottle( picker.authorization, randomUUID(), randomUUID(), "x".repeat(42), ).expect(400); }); it("rejects malformed idempotency keys and canonicalizes surrounding whitespace", async () => { const author = await actor("author-device"); const picker = await actor("picker-device"); await bottle(author.id); await pick(picker.authorization, "bad key").expect(400); const first = await pick(picker.authorization, " stable-key ").expect(201); const retry = await pick(picker.authorization, "stable-key").expect(201); expect(retry.body.data.lease).toEqual(first.body.data.lease); }); it("allows only one of two users to concurrently lease the sole bottle", async () => { const author = await actor("author-device"); const a = await actor("picker-a-device"); const b = await actor("picker-b-device"); const source = await bottle(author.id); const results = await Promise.all([ pick(a.authorization), pick(b.authorization), ]); expect(results.filter((x) => x.status === 201)).toHaveLength(1); expect( results.filter( (x) => x.status === 404 && x.body.code === "BOTTLE_POOL_EMPTY", ), ).toHaveLength(1); expect( await prisma.bottlePickLease.count({ where: { bottleId: source.id, status: "ACTIVE" }, }), ).toBe(1); }); it("enforces twenty successful picks atomically and resets on another UTC+8 day", async () => { const author = await actor("author-device"); const picker = await actor("picker-device"); await prisma.bottle.createMany({ data: Array.from({ length: 22 }, (_, i) => ({ authorId: author.id, clientRequestId: randomUUID(), contentText: `bottle-${i}`, reviewStatus: "APPROVED", poolStatus: "IN_POOL", approvedAt: new Date(), })), }); const results = await Promise.all( Array.from({ length: 21 }, () => pick(picker.authorization)), ); expect(results.filter((x) => x.status === 201)).toHaveLength(20); expect( results.filter( (x) => x.status === 429 && x.body.code === "BOTTLE_DAILY_LIMIT", ), ).toHaveLength(1); expect((await prisma.dailyUsage.findFirstOrThrow()).bottlesPicked).toBe(20); await prisma.dailyUsage.updateMany({ data: { usageDate: new Date("2020-01-01T00:00:00.000Z") }, }); await pick(picker.authorization).expect(201); }, 15_000); it("sustains twenty rounds of 21 concurrent distinct requests without picker-level serialization", async () => { for (let round = 0; round < 20; round += 1) { await prisma.$executeRawUnsafe(`TRUNCATE TABLE "accounts" CASCADE`); const author = await actor(`stress-author-${round}`); const picker = await actor(`stress-picker-${round}`); await prisma.bottle.createMany({ data: Array.from({ length: 96 }, (_, index) => ({ authorId: author.id, clientRequestId: `stress-${round}-${index}`, contentText: `stress-${round}-${index}`, reviewStatus: "APPROVED" as const, poolStatus: "IN_POOL" as const, approvedAt: new Date(), })), }); const results = await Promise.all( Array.from({ length: 21 }, (_, index) => pick(picker.authorization, `stress-key-${round}-${index}`), ), ); expect(results.filter(({ status }) => status === 201)).toHaveLength(20); expect( results.filter( ({ status, body }) => status === 429 && body.code === "BOTTLE_DAILY_LIMIT", ), ).toHaveLength(1); expect(results.filter(({ status }) => status >= 500)).toHaveLength(0); expect( await prisma.dailyUsage.findFirstOrThrow({ where: { accountId: picker.id }, }), ).toMatchObject({ bottlesPicked: 20 }); expect( await prisma.bottlePickLease.count({ where: { pickerId: picker.id } }), ).toBe(20); expect( await prisma.bottlePickHistory.count({ where: { pickerId: picker.id }, }), ).toBe(20); expect( await prisma.bottlePickRequest.count({ where: { pickerId: picker.id }, }), ).toBe(20); } }, 120_000); it.each(["SUSPENSION", "BAN"] as const)( "rejects active %s sanction", async (type) => { const author = await actor("author-device"); const picker = await actor("picker-device"); await bottle(author.id); await prisma.sanction.create({ data: { accountId: picker.id, type, reason: "policy" }, }); const response = await pick(picker.authorization).expect(403); expect(response.body.code).toBe("ACCOUNT_SANCTIONED"); expect(await prisma.bottlePickLease.count()).toBe(0); }, ); it("returns an active lease, is idempotent, and permanently preserves pick history", async () => { const author = await actor("author-device"); const picker = await actor("picker-device"); const source = await bottle(author.id); const picked = (await pick(picker.authorization).expect(201)).body.data; const first = await returnBottle( picker.authorization, source.id, picked.lease.id, picked.lease.token, ).expect(201); const retry = await returnBottle( picker.authorization, source.id, picked.lease.id, picked.lease.token, ).expect(201); expect(first.body.data).toEqual({ bottleId: source.id, leaseId: picked.lease.id, status: "RETURNED", }); expect(retry.body.data).toEqual(first.body.data); expect( await prisma.bottle.findUniqueOrThrow({ where: { id: source.id } }), ).toMatchObject({ poolStatus: "IN_POOL", activeLeaseId: null, version: 3 }); await pick(picker.authorization).expect(404); }); it("hides another user's lease and rejects an invalid opaque token without mutation", async () => { const author = await actor("author-device"); const picker = await actor("picker-device"); const stranger = await actor("stranger-device"); const source = await bottle(author.id); const picked = (await pick(picker.authorization).expect(201)).body.data; await returnBottle( stranger.authorization, source.id, picked.lease.id, picked.lease.token, ).expect(404); await returnBottle( picker.authorization, source.id, picked.lease.id, "x".repeat(43), ).expect(404); expect( await prisma.bottlePickLease.findUniqueOrThrow({ where: { id: picked.lease.id }, }), ).toMatchObject({ status: "ACTIVE", endedAt: null }); expect( (await prisma.bottle.findUniqueOrThrow({ where: { id: source.id } })) .poolStatus, ).toBe("LEASED"); }); it("rejects returning an expired lease without releasing it in the API", async () => { const author = await actor("author-device"); const picker = await actor("picker-device"); const source = await bottle(author.id); const picked = (await pick(picker.authorization).expect(201)).body.data; await prisma.bottlePickLease.update({ where: { id: picked.lease.id }, data: { expiresAt: new Date(Date.now() - 1) }, }); const response = await returnBottle( picker.authorization, source.id, picked.lease.id, picked.lease.token, ).expect(410); expect(response.body.code).toBe("BOTTLE_LEASE_EXPIRED"); }); });