147 lines
4.6 KiB
TypeScript
147 lines
4.6 KiB
TypeScript
import { Prisma, PrismaClient } from "@prisma/client";
|
|
import { afterAll, beforeAll, beforeEach, describe, expect, it } from "vitest";
|
|
import { PrismaService } from "../../apps/api/src/database/prisma.service";
|
|
|
|
const databaseUrl =
|
|
process.env.DATABASE_URL ??
|
|
"postgresql://drift:drift_test_only@127.0.0.1:55432/drift_bottle_test?schema=public";
|
|
const prisma = new PrismaClient({ datasources: { db: { url: databaseUrl } } });
|
|
|
|
async function expectUniqueViolation(operation: Promise<unknown>) {
|
|
await expect(operation).rejects.toMatchObject({
|
|
code: "P2002",
|
|
} satisfies Partial<Prisma.PrismaClientKnownRequestError>);
|
|
}
|
|
|
|
async function createAccount(suffix: string) {
|
|
return prisma.account.create({
|
|
data: {
|
|
phoneCiphertext: Buffer.from(`ciphertext-${suffix}`),
|
|
phoneHmac: `hmac-${suffix}`,
|
|
anonymousProfile: { create: { nickname: `漂友-${suffix}` } },
|
|
},
|
|
});
|
|
}
|
|
|
|
async function createBottle(authorId: string, suffix: string) {
|
|
return prisma.bottle.create({
|
|
data: { authorId, contentText: `测试瓶子-${suffix}`, status: "IN_POOL" },
|
|
});
|
|
}
|
|
|
|
describe("database authority constraints", () => {
|
|
beforeAll(async () => prisma.$connect());
|
|
beforeEach(async () => {
|
|
await prisma.$executeRawUnsafe(`TRUNCATE TABLE "accounts" CASCADE`);
|
|
});
|
|
afterAll(async () => prisma.$disconnect());
|
|
|
|
it("rejects a second conversation for the same source bottle with P2002", async () => {
|
|
const author = await createAccount("conversation-author");
|
|
const bottle = await createBottle(author.id, "conversation");
|
|
await prisma.conversation.create({ data: { sourceBottleId: bottle.id } });
|
|
|
|
await expectUniqueViolation(
|
|
prisma.conversation.create({ data: { sourceBottleId: bottle.id } }),
|
|
);
|
|
});
|
|
|
|
it("rejects duplicate bottle pick history with P2002", async () => {
|
|
const author = await createAccount("history-author");
|
|
const picker = await createAccount("history-picker");
|
|
const bottle = await createBottle(author.id, "history");
|
|
await prisma.bottlePickHistory.create({
|
|
data: { bottleId: bottle.id, pickerId: picker.id },
|
|
});
|
|
|
|
await expectUniqueViolation(
|
|
prisma.bottlePickHistory.create({
|
|
data: { bottleId: bottle.id, pickerId: picker.id },
|
|
}),
|
|
);
|
|
});
|
|
|
|
it("rejects duplicate directed blocks with P2002", async () => {
|
|
const blocker = await createAccount("blocker");
|
|
const blocked = await createAccount("blocked");
|
|
await prisma.block.create({
|
|
data: { blockerId: blocker.id, blockedId: blocked.id },
|
|
});
|
|
|
|
await expectUniqueViolation(
|
|
prisma.block.create({
|
|
data: { blockerId: blocker.id, blockedId: blocked.id },
|
|
}),
|
|
);
|
|
});
|
|
|
|
it("rejects duplicate client message ids within a conversation with P2002", async () => {
|
|
const author = await createAccount("client-message-author");
|
|
const bottle = await createBottle(author.id, "client-message");
|
|
const conversation = await prisma.conversation.create({
|
|
data: { sourceBottleId: bottle.id },
|
|
});
|
|
await prisma.message.create({
|
|
data: {
|
|
conversationId: conversation.id,
|
|
senderId: author.id,
|
|
clientMsgId: "client-1",
|
|
seq: 1,
|
|
contentText: "一",
|
|
},
|
|
});
|
|
|
|
await expectUniqueViolation(
|
|
prisma.message.create({
|
|
data: {
|
|
conversationId: conversation.id,
|
|
senderId: author.id,
|
|
clientMsgId: "client-1",
|
|
seq: 2,
|
|
contentText: "二",
|
|
},
|
|
}),
|
|
);
|
|
});
|
|
|
|
it("rejects duplicate message sequences within a conversation with P2002", async () => {
|
|
const author = await createAccount("sequence-author");
|
|
const bottle = await createBottle(author.id, "sequence");
|
|
const conversation = await prisma.conversation.create({
|
|
data: { sourceBottleId: bottle.id },
|
|
});
|
|
await prisma.message.create({
|
|
data: {
|
|
conversationId: conversation.id,
|
|
senderId: author.id,
|
|
clientMsgId: "client-a",
|
|
seq: 1,
|
|
contentText: "一",
|
|
},
|
|
});
|
|
|
|
await expectUniqueViolation(
|
|
prisma.message.create({
|
|
data: {
|
|
conversationId: conversation.id,
|
|
senderId: author.id,
|
|
clientMsgId: "client-b",
|
|
seq: 1,
|
|
contentText: "二",
|
|
},
|
|
}),
|
|
);
|
|
});
|
|
});
|
|
|
|
describe("PrismaService lifecycle", () => {
|
|
it("connects and disconnects through module lifecycle hooks", async () => {
|
|
const service = new PrismaService({
|
|
datasources: { db: { url: databaseUrl } },
|
|
});
|
|
await service.onModuleInit();
|
|
await expect(service.$queryRaw`SELECT 1`).resolves.toBeDefined();
|
|
await service.onModuleDestroy();
|
|
});
|
|
});
|