import { describe, it, expect, vi } from 'vitest'; import { SPONSOR_STATUSES, STATUS_LABELS, STATUS_COLORS, fetchSponsorTiers, createSponsorTier, updateSponsorTier, deleteSponsorTier, fetchSponsors, createSponsor, updateSponsor, deleteSponsor, fetchDeliverables, fetchAllDeliverables, createDeliverable, updateDeliverable, deleteDeliverable, } from './sponsors'; // ── Supabase mock builder ──────────────────────────────────────────────────── function mockChain(resolvedValue: { data: any; error: any }) { const chain: any = {}; const methods = ['from', 'select', 'insert', 'update', 'delete', 'eq', 'in', 'order', 'single']; for (const m of methods) { chain[m] = vi.fn(() => chain); } chain.single = vi.fn(() => Promise.resolve(resolvedValue)); chain.then = (resolve: any) => resolve(resolvedValue); return chain; } function mockSupabase(resolvedValue: { data: any; error: any }) { const chain = mockChain(resolvedValue); return { from: vi.fn(() => chain), _chain: chain } as any; } // ── Constants ──────────────────────────────────────────────────────────────── describe('sponsor constants', () => { it('SPONSOR_STATUSES has expected entries', () => { expect(SPONSOR_STATUSES).toContain('prospect'); expect(SPONSOR_STATUSES).toContain('confirmed'); expect(SPONSOR_STATUSES).toContain('declined'); expect(SPONSOR_STATUSES).toContain('active'); expect(SPONSOR_STATUSES.length).toBe(5); }); it('STATUS_LABELS has a label for every status', () => { for (const s of SPONSOR_STATUSES) { expect(STATUS_LABELS[s]).toBeDefined(); expect(typeof STATUS_LABELS[s]).toBe('string'); } }); it('STATUS_COLORS has a color for every status', () => { for (const s of SPONSOR_STATUSES) { expect(STATUS_COLORS[s]).toBeDefined(); expect(STATUS_COLORS[s]).toMatch(/^#/); } }); }); // ── Sponsor Tiers ──────────────────────────────────────────────────────────── describe('fetchSponsorTiers', () => { it('returns tiers for a department', async () => { const tiers = [{ id: 't1', name: 'Gold', department_id: 'd1' }]; const sb = mockSupabase({ data: tiers, error: null }); expect(await fetchSponsorTiers(sb, 'd1')).toEqual(tiers); }); it('returns empty array when null', async () => { const sb = mockSupabase({ data: null, error: null }); expect(await fetchSponsorTiers(sb, 'd1')).toEqual([]); }); it('throws on error', async () => { const sb = mockSupabase({ data: null, error: { message: 'fail' } }); await expect(fetchSponsorTiers(sb, 'd1')).rejects.toEqual({ message: 'fail' }); }); }); describe('createSponsorTier', () => { it('creates with default color', async () => { const tier = { id: 't1', name: 'Silver', amount: 0, color: '#F59E0B' }; const sb = mockSupabase({ data: tier, error: null }); expect(await createSponsorTier(sb, 'd1', 'Silver')).toEqual(tier); }); it('creates with custom amount and color', async () => { const tier = { id: 't2', name: 'Platinum', amount: 10000, color: '#00ff00' }; const sb = mockSupabase({ data: tier, error: null }); expect(await createSponsorTier(sb, 'd1', 'Platinum', 10000, '#00ff00')).toEqual(tier); }); it('throws on error', async () => { const sb = mockSupabase({ data: null, error: { message: 'fail' } }); await expect(createSponsorTier(sb, 'd1', 'X')).rejects.toEqual({ message: 'fail' }); }); }); describe('updateSponsorTier', () => { it('updates and returns tier', async () => { const tier = { id: 't1', name: 'Updated' }; const sb = mockSupabase({ data: tier, error: null }); expect(await updateSponsorTier(sb, 't1', { name: 'Updated' })).toEqual(tier); }); it('throws on error', async () => { const sb = mockSupabase({ data: null, error: { message: 'fail' } }); await expect(updateSponsorTier(sb, 't1', { name: 'X' })).rejects.toEqual({ message: 'fail' }); }); }); describe('deleteSponsorTier', () => { it('deletes without error', async () => { const sb = mockSupabase({ data: null, error: null }); await expect(deleteSponsorTier(sb, 't1')).resolves.toBeUndefined(); }); it('throws on error', async () => { const sb = mockSupabase({ data: null, error: { message: 'fail' } }); await expect(deleteSponsorTier(sb, 't1')).rejects.toEqual({ message: 'fail' }); }); }); // ── Sponsors ───────────────────────────────────────────────────────────────── describe('fetchSponsors', () => { it('returns sponsors for a department', async () => { const sponsors = [{ id: 's1', name: 'Acme', department_id: 'd1' }]; const sb = mockSupabase({ data: sponsors, error: null }); expect(await fetchSponsors(sb, 'd1')).toEqual(sponsors); }); it('returns empty array when null', async () => { const sb = mockSupabase({ data: null, error: null }); expect(await fetchSponsors(sb, 'd1')).toEqual([]); }); it('throws on error', async () => { const sb = mockSupabase({ data: null, error: { message: 'fail' } }); await expect(fetchSponsors(sb, 'd1')).rejects.toEqual({ message: 'fail' }); }); }); describe('createSponsor', () => { it('creates with minimal params', async () => { const sponsor = { id: 's1', name: 'Acme', status: 'prospect', amount: 0 }; const sb = mockSupabase({ data: sponsor, error: null }); expect(await createSponsor(sb, 'd1', { name: 'Acme' })).toEqual(sponsor); }); it('creates with all params', async () => { const sponsor = { id: 's2', name: 'BigCo', status: 'confirmed', amount: 5000 }; const sb = mockSupabase({ data: sponsor, error: null }); expect(await createSponsor(sb, 'd1', { name: 'BigCo', tier_id: 't1', contact_name: 'John', contact_email: 'john@bigco.com', contact_phone: '+1234', website: 'https://bigco.com', logo_url: 'https://bigco.com/logo.png', status: 'confirmed', amount: 5000, notes: 'VIP sponsor', })).toEqual(sponsor); }); it('throws on error', async () => { const sb = mockSupabase({ data: null, error: { message: 'fail' } }); await expect(createSponsor(sb, 'd1', { name: 'X' })).rejects.toEqual({ message: 'fail' }); }); }); describe('updateSponsor', () => { it('updates and returns sponsor', async () => { const sponsor = { id: 's1', name: 'Updated' }; const sb = mockSupabase({ data: sponsor, error: null }); expect(await updateSponsor(sb, 's1', { name: 'Updated' })).toEqual(sponsor); }); it('throws on error', async () => { const sb = mockSupabase({ data: null, error: { message: 'fail' } }); await expect(updateSponsor(sb, 's1', { name: 'X' })).rejects.toEqual({ message: 'fail' }); }); }); describe('deleteSponsor', () => { it('deletes without error', async () => { const sb = mockSupabase({ data: null, error: null }); await expect(deleteSponsor(sb, 's1')).resolves.toBeUndefined(); }); it('throws on error', async () => { const sb = mockSupabase({ data: null, error: { message: 'fail' } }); await expect(deleteSponsor(sb, 's1')).rejects.toEqual({ message: 'fail' }); }); }); // ── Deliverables ───────────────────────────────────────────────────────────── describe('fetchDeliverables', () => { it('returns deliverables for a sponsor', async () => { const delivs = [{ id: 'dl1', description: 'Logo placement', sponsor_id: 's1' }]; const sb = mockSupabase({ data: delivs, error: null }); expect(await fetchDeliverables(sb, 's1')).toEqual(delivs); }); it('returns empty array when null', async () => { const sb = mockSupabase({ data: null, error: null }); expect(await fetchDeliverables(sb, 's1')).toEqual([]); }); it('throws on error', async () => { const sb = mockSupabase({ data: null, error: { message: 'fail' } }); await expect(fetchDeliverables(sb, 's1')).rejects.toEqual({ message: 'fail' }); }); }); describe('fetchAllDeliverables', () => { it('returns empty array for empty sponsor list', async () => { const sb = mockSupabase({ data: [], error: null }); expect(await fetchAllDeliverables(sb, [])).toEqual([]); }); it('returns deliverables for multiple sponsors', async () => { const delivs = [ { id: 'dl1', sponsor_id: 's1' }, { id: 'dl2', sponsor_id: 's2' }, ]; const sb = mockSupabase({ data: delivs, error: null }); expect(await fetchAllDeliverables(sb, ['s1', 's2'])).toEqual(delivs); }); it('throws on error', async () => { const sb = mockSupabase({ data: null, error: { message: 'fail' } }); await expect(fetchAllDeliverables(sb, ['s1'])).rejects.toEqual({ message: 'fail' }); }); }); describe('createDeliverable', () => { it('creates with description only', async () => { const deliv = { id: 'dl1', description: 'Banner', sponsor_id: 's1' }; const sb = mockSupabase({ data: deliv, error: null }); expect(await createDeliverable(sb, 's1', 'Banner')).toEqual(deliv); }); it('creates with due date', async () => { const deliv = { id: 'dl2', description: 'Video', sponsor_id: 's1', due_date: '2025-06-01' }; const sb = mockSupabase({ data: deliv, error: null }); expect(await createDeliverable(sb, 's1', 'Video', '2025-06-01')).toEqual(deliv); }); it('throws on error', async () => { const sb = mockSupabase({ data: null, error: { message: 'fail' } }); await expect(createDeliverable(sb, 's1', 'X')).rejects.toEqual({ message: 'fail' }); }); }); describe('updateDeliverable', () => { it('updates and returns deliverable', async () => { const deliv = { id: 'dl1', description: 'Updated' }; const sb = mockSupabase({ data: deliv, error: null }); expect(await updateDeliverable(sb, 'dl1', { description: 'Updated' })).toEqual(deliv); }); it('throws on error', async () => { const sb = mockSupabase({ data: null, error: { message: 'fail' } }); await expect(updateDeliverable(sb, 'dl1', { description: 'X' })).rejects.toEqual({ message: 'fail' }); }); }); describe('deleteDeliverable', () => { it('deletes without error', async () => { const sb = mockSupabase({ data: null, error: null }); await expect(deleteDeliverable(sb, 'dl1')).resolves.toBeUndefined(); }); it('throws on error', async () => { const sb = mockSupabase({ data: null, error: { message: 'fail' } }); await expect(deleteDeliverable(sb, 'dl1')).rejects.toEqual({ message: 'fail' }); }); });