import { convexTest } from "convex-test"; import { describe, it, expect } from "vitest"; import { api } from "./_generated/api"; import schema from "./schema"; const modules = import.meta.glob("./**/*.ts"); async function setupAdminUser(t: ReturnType) { const asAdmin = t.withIdentity({ name: "Admin", email: "admin@example.com", subject: "clerk_admin_123", }); const userId = await asAdmin.mutation(api.users.store, {}); await t.run(async (ctx) => { await ctx.db.patch(userId, { role: "admin" }); }); return asAdmin; } async function setupCategory(t: ReturnType) { let categoryId: import("./_generated/dataModel").Id<"categories">; await t.run(async (ctx) => { categoryId = await ctx.db.insert("categories", { name: "Pet Food", slug: "pet-food", }); }); return categoryId!; } async function setupProduct( t: ReturnType, asAdmin: ReturnType["withIdentity"]>, ) { const categoryId = await setupCategory(t); return await asAdmin.mutation(api.products.create, { name: "Wishlist Product", slug: "wishlist-product", status: "active", categoryId, tags: [], }); } describe("wishlists", () => { it("toggle adds then removes and list is empty after remove", async () => { const t = convexTest(schema, modules); const asAdmin = await setupAdminUser(t); const productId = await setupProduct(t, asAdmin); const asA = t.withIdentity({ name: "Alice", email: "alice@example.com", subject: "clerk_alice_123", }); await asA.mutation(api.users.store, {}); const addResult = await asA.mutation(api.wishlists.toggle, { productId }); expect(addResult).toEqual({ added: true, id: expect.anything() }); const listAfterAdd = await asA.query(api.wishlists.list, {}); expect(listAfterAdd).toHaveLength(1); const removeResult = await asA.mutation(api.wishlists.toggle, { productId }); expect(removeResult).toEqual({ removed: true }); const listAfterRemove = await asA.query(api.wishlists.list, {}); expect(listAfterRemove).toHaveLength(0); }); it("isWishlisted is true after add and false after remove", async () => { const t = convexTest(schema, modules); const asAdmin = await setupAdminUser(t); const productId = await setupProduct(t, asAdmin); const asA = t.withIdentity({ name: "Alice", email: "alice@example.com", subject: "clerk_alice_123", }); await asA.mutation(api.users.store, {}); expect(await asA.query(api.wishlists.isWishlisted, { productId })).toBe( false, ); await asA.mutation(api.wishlists.toggle, { productId }); expect(await asA.query(api.wishlists.isWishlisted, { productId })).toBe( true, ); await asA.mutation(api.wishlists.toggle, { productId }); expect(await asA.query(api.wishlists.isWishlisted, { productId })).toBe( false, ); }); it("count returns 0 for unauthenticated user", async () => { const t = convexTest(schema, modules); const count = await t.query(api.wishlists.count, {}); expect(count).toBe(0); }); it("count returns correct number after add and toggle", async () => { const t = convexTest(schema, modules); const asAdmin = await setupAdminUser(t); const productId1 = await setupProduct(t, asAdmin); const categoryId = await setupCategory(t); const productId2 = await asAdmin.mutation(api.products.create, { name: "Wishlist Product 2", slug: "wishlist-product-2", status: "active", categoryId, tags: [], }); const asA = t.withIdentity({ name: "Alice", email: "alice@example.com", subject: "clerk_alice_123", }); await asA.mutation(api.users.store, {}); expect(await asA.query(api.wishlists.count, {})).toBe(0); await asA.mutation(api.wishlists.toggle, { productId: productId1 }); await asA.mutation(api.wishlists.toggle, { productId: productId2 }); expect(await asA.query(api.wishlists.count, {})).toBe(2); await asA.mutation(api.wishlists.toggle, { productId: productId1 }); expect(await asA.query(api.wishlists.count, {})).toBe(1); }); it("remove throws if user does not own the wishlist item", async () => { const t = convexTest(schema, modules); const asAdmin = await setupAdminUser(t); const productId = await setupProduct(t, asAdmin); const asA = t.withIdentity({ name: "Alice", email: "alice@example.com", subject: "clerk_alice_123", }); const asB = t.withIdentity({ name: "Bob", email: "bob@example.com", subject: "clerk_bob_456", }); await asA.mutation(api.users.store, {}); await asB.mutation(api.users.store, {}); await asA.mutation(api.wishlists.add, { productId }); const listA = await asA.query(api.wishlists.list, {}); expect(listA).toHaveLength(1); const wishlistId = listA[0]._id; await expect( asB.mutation(api.wishlists.remove, { id: wishlistId }), ).rejects.toThrow(/Unauthorized|does not belong/); await asA.mutation(api.wishlists.remove, { id: wishlistId }); const listAfter = await asA.query(api.wishlists.list, {}); expect(listAfter).toHaveLength(0); }); });