import { ExecutionContext, ForbiddenException } from "@nestjs/common"; import { Reflector } from "@nestjs/core"; import { REQUIRED_PLAN_KEY, SubscriptionGuard } from "../src/stripe/subscription.guard"; import { IS_PUBLIC_KEY } from "../src/common/guards/jwt-auth.guard"; import { createPrismaMock } from "./utils/mock-prisma"; const createContext = (userId?: string) => ({ getHandler: jest.fn(), getClass: jest.fn(), switchToHttp: () => ({ getRequest: () => ({ user: userId ? { sub: userId } : undefined }), }), }) as unknown as ExecutionContext; describe("SubscriptionGuard", () => { it("blocks free users from pro-only exports", async () => { const prisma = createPrismaMock(); prisma.subscription.findUnique.mockResolvedValue({ userId: "user_1", plan: "free" }); const reflector = { getAllAndOverride: jest.fn((key: string) => key === REQUIRED_PLAN_KEY ? "pro" : undefined), } as unknown as Reflector; const guard = new SubscriptionGuard(reflector, prisma as any); await expect(guard.canActivate(createContext("user_1"))).rejects.toBeInstanceOf(ForbiddenException); expect(reflector.getAllAndOverride).toHaveBeenCalledWith(REQUIRED_PLAN_KEY, expect.any(Array)); }); it("allows pro users through pro-only exports", async () => { const prisma = createPrismaMock(); prisma.subscription.findUnique.mockResolvedValue({ userId: "user_1", plan: "pro" }); const reflector = { getAllAndOverride: jest.fn((key: string) => key === REQUIRED_PLAN_KEY ? "pro" : undefined), } as unknown as Reflector; const guard = new SubscriptionGuard(reflector, prisma as any); await expect(guard.canActivate(createContext("user_1"))).resolves.toBe(true); }); it("skips subscription checks for public signed download routes", async () => { const prisma = createPrismaMock(); const reflector = { getAllAndOverride: jest.fn((key: string) => key === IS_PUBLIC_KEY ? true : "pro"), } as unknown as Reflector; const guard = new SubscriptionGuard(reflector, prisma as any); await expect(guard.canActivate(createContext())).resolves.toBe(true); expect(prisma.subscription.findUnique).not.toHaveBeenCalled(); }); });