import { describe, expect, it, beforeAll, afterAll } from "vitest"; import { eq, inArray } from "drizzle-orm"; import { createDatabase, closeDatabase, markMilestone, getActivationState, schema } from "@app/database"; describe("activation", () => { const { db } = createDatabase(); const emails = ["act-test-1@example.invalid", "act-test-2@example.invalid"]; const inviteCodes = ["ACT123", "ACT223"]; async function cleanup() { await db.delete(schema.householdMembers).where( inArray( schema.householdMembers.householdId, db.select({ id: schema.households.id }).from(schema.households).where(inArray(schema.households.inviteCode, inviteCodes)), ), ); await db.delete(schema.households).where(inArray(schema.households.inviteCode, inviteCodes)); await db.delete(schema.users).where(inArray(schema.users.email, emails)); } beforeAll(cleanup); afterAll(async () => { await cleanup(); await closeDatabase(); }); it("marks first scan and activates household", async () => { const [user] = await db .insert(schema.users) .values({ email: emails[0]!, displayName: "Act" }) .returning(); const [household] = await db .insert(schema.households) .values({ name: "Act family", inviteCode: inviteCodes[0]! }) .returning(); await db.insert(schema.householdMembers).values({ householdId: household.id, userId: user.id, role: "owner", }); const r1 = await markMilestone(db, household.id, "firstScanCompletedAt"); expect(r1.wasNew).toBe(true); expect(r1.activatedNow).toBe(false); await markMilestone(db, household.id, "fifthItemConfirmedAt"); await markMilestone(db, household.id, "firstRecipeRecommendationViewedAt"); const r2 = await markMilestone(db, household.id, "firstRecipeSavedOrStartedAt"); expect(r2.activatedNow).toBe(true); const state = await getActivationState(db, household.id); expect(state.activatedAt).not.toBeNull(); expect(state.valueConfirmedAt).toBeNull(); await markMilestone(db, household.id, "firstCookingSessionCompletedAt"); const r3 = await markMilestone(db, household.id, "inventoryUpdatedAfterCookingAt"); expect(r3.valueConfirmedNow).toBe(true); const state2 = await getActivationState(db, household.id); expect(state2.valueConfirmedAt).not.toBeNull(); }); it("idempotent: does not move timestamps backward", async () => { const [user] = await db .insert(schema.users) .values({ email: emails[1]!, displayName: "Act2" }) .returning(); const [household] = await db .insert(schema.households) .values({ name: "Act2 family", inviteCode: inviteCodes[1]! }) .returning(); await db.insert(schema.householdMembers).values({ householdId: household.id, userId: user.id, role: "owner", }); await markMilestone(db, household.id, "firstScanCompletedAt"); const state1 = await getActivationState(db, household.id); await new Promise((r) => setTimeout(r, 20)); await markMilestone(db, household.id, "firstScanCompletedAt"); const state2 = await getActivationState(db, household.id); expect(state2.firstScanCompletedAt?.getTime()).toBe(state1.firstScanCompletedAt?.getTime()); }); });