Files
root-org/src/lib/api/sponsors.test.ts

288 lines
10 KiB
TypeScript

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' });
});
});