import { describe, it, expect, vi, beforeEach } from 'vitest'; import { canUseFeature } from '@/lib/entitlements'; vi.mock('@/lib/redis', () => ({ redis: { get: vi.fn(), mget: vi.fn(), eval: vi.fn(), }, })); vi.mock('@/lib/prisma', () => ({ prisma: { subscription: { findUnique: vi.fn() }, userAPIKey: { count: vi.fn() }, }, })); import { redis } from '@/lib/redis'; import { prisma } from '@/lib/prisma'; describe('BYOK entitlements', () => { beforeEach(() => { vi.clearAllMocks(); vi.mocked(prisma.subscription.findUnique).mockResolvedValue({ userId: 'user1', tier: 'BASIC', status: 'ACTIVE', currentPeriodStart: new Date(), currentPeriodEnd: new Date(), } as never); }); it('returns byokConfigured=true when BYOK active but quota exhausted (bypass is per-provider in routes)', async () => { vi.mocked(redis.get).mockResolvedValue('30'); vi.mocked(prisma.userAPIKey.count).mockResolvedValue(1); const result = await canUseFeature('user1', 'semantic_search'); expect(result.allowed).toBe(false); expect(result.byokConfigured).toBe(true); }); it('denies when quota exhausted and no BYOK', async () => { vi.mocked(redis.get).mockResolvedValue('30'); vi.mocked(prisma.userAPIKey.count).mockResolvedValue(0); const result = await canUseFeature('user1', 'semantic_search'); expect(result.allowed).toBe(false); expect(result.byokConfigured).toBe(false); }); });