mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-09 21:10:59 +00:00
120 lines
3.7 KiB
TypeScript
120 lines
3.7 KiB
TypeScript
import { readFileSync } from "node:fs";
|
|
import { resolve } from "node:path";
|
|
import { beforeEach, describe, expect, it } from "vitest";
|
|
|
|
import { getPrismaClient, truncateAllTables } from "./setup";
|
|
const WHOOP_OWNER_MIGRATION_SQL = readFileSync(
|
|
resolve(
|
|
process.cwd(),
|
|
"prisma/migrations/0264_whoop_owner_identity/migration.sql",
|
|
),
|
|
"utf8",
|
|
);
|
|
|
|
async function createUser(tag: string) {
|
|
const prisma = getPrismaClient();
|
|
return prisma.user.create({
|
|
data: {
|
|
username: `whoop-owner-${tag}`,
|
|
email: `whoop-owner-${tag}@example.test`,
|
|
role: "USER",
|
|
},
|
|
});
|
|
}
|
|
|
|
describe("WHOOP provider identity ownership — integration", () => {
|
|
beforeEach(async () => {
|
|
await truncateAllTables(getPrismaClient());
|
|
});
|
|
|
|
it("prevents one WHOOP user id from belonging to two local users", async () => {
|
|
const prisma = getPrismaClient();
|
|
const firstUser = await createUser("first");
|
|
const secondUser = await createUser("second");
|
|
const tokenExpiresAt = new Date("2026-08-01T00:00:00.000Z");
|
|
|
|
await prisma.whoopConnection.create({
|
|
data: {
|
|
userId: firstUser.id,
|
|
whoopUserId: "shared-provider-user",
|
|
accessToken: "encrypted-access-1",
|
|
refreshToken: "encrypted-refresh-1",
|
|
tokenExpiresAt,
|
|
},
|
|
});
|
|
|
|
await expect(
|
|
prisma.whoopConnection.create({
|
|
data: {
|
|
userId: secondUser.id,
|
|
whoopUserId: "shared-provider-user",
|
|
accessToken: "encrypted-access-2",
|
|
refreshToken: "encrypted-refresh-2",
|
|
tokenExpiresAt,
|
|
},
|
|
}),
|
|
).rejects.toMatchObject({ code: "P2002" });
|
|
});
|
|
|
|
it("keeps the earliest duplicate deterministically when the migration is applied", async () => {
|
|
const prisma = getPrismaClient();
|
|
const winnerUser = await createUser("migration-winner");
|
|
const tiedLoserUser = await createUser("migration-tied-loser");
|
|
const laterLoserUser = await createUser("migration-later-loser");
|
|
const earliest = new Date("2026-01-01T00:00:00.000Z");
|
|
const later = new Date("2026-01-02T00:00:00.000Z");
|
|
const tokenExpiresAt = new Date("2026-08-01T00:00:00.000Z");
|
|
|
|
await prisma.$executeRawUnsafe(
|
|
'DROP INDEX "whoop_connections_whoop_user_id_key"',
|
|
);
|
|
await prisma.whoopConnection.createMany({
|
|
data: [
|
|
{
|
|
id: "a-earliest",
|
|
userId: winnerUser.id,
|
|
whoopUserId: "duplicate-provider-user",
|
|
accessToken: "encrypted-access-1",
|
|
refreshToken: "encrypted-refresh-1",
|
|
tokenExpiresAt,
|
|
createdAt: earliest,
|
|
updatedAt: earliest,
|
|
},
|
|
{
|
|
id: "z-earliest",
|
|
userId: tiedLoserUser.id,
|
|
whoopUserId: "duplicate-provider-user",
|
|
accessToken: "encrypted-access-2",
|
|
refreshToken: "encrypted-refresh-2",
|
|
tokenExpiresAt,
|
|
createdAt: earliest,
|
|
updatedAt: earliest,
|
|
},
|
|
{
|
|
id: "0-later",
|
|
userId: laterLoserUser.id,
|
|
whoopUserId: "duplicate-provider-user",
|
|
accessToken: "encrypted-access-3",
|
|
refreshToken: "encrypted-refresh-3",
|
|
tokenExpiresAt,
|
|
createdAt: later,
|
|
updatedAt: later,
|
|
},
|
|
],
|
|
});
|
|
|
|
await prisma.$executeRawUnsafe(WHOOP_OWNER_MIGRATION_SQL);
|
|
|
|
const connections = await prisma.whoopConnection.findMany({
|
|
where: { id: { in: ["a-earliest", "z-earliest", "0-later"] } },
|
|
orderBy: { id: "asc" },
|
|
select: { id: true, whoopUserId: true },
|
|
});
|
|
expect(connections).toEqual([
|
|
{ id: "0-later", whoopUserId: null },
|
|
{ id: "a-earliest", whoopUserId: "duplicate-provider-user" },
|
|
{ id: "z-earliest", whoopUserId: null },
|
|
]);
|
|
});
|
|
});
|