import { describe, it, expect } from 'vitest' import { monthlyCost, annualCost, totalMonthlyCost, totalAnnualCost, daysUntil, subscriptionsExpiringSoon, subscriptionsToCSV, expensesToCSV, generateId, formatCurrency } from '../src/shared/storage' import type { Subscription, Expense } from '../src/shared/storage' describe('monthlyCost', () => { it('returns monthly cost for monthly interval', () => { const s: Subscription = { id: '1', name: 'Test', cost: 10, interval: 'monthly', startedAt: '2024-01-01', category: 'Other' } expect(monthlyCost(s)).toBe(10) }) it('returns monthly cost for yearly interval', () => { const s: Subscription = { id: '1', name: 'Test', cost: 120, interval: 'yearly', startedAt: '2024-01-01', category: 'Other' } expect(monthlyCost(s)).toBe(10) }) it('returns monthly cost for weekly interval', () => { const s: Subscription = { id: '1', name: 'Test', cost: 10, interval: 'weekly', startedAt: '2024-01-01', category: 'Other' } expect(monthlyCost(s)).toBeCloseTo(43.3, 1) }) }) describe('annualCost', () => { it('returns annual cost for monthly interval', () => { const s: Subscription = { id: '1', name: 'Test', cost: 10, interval: 'monthly', startedAt: '2024-01-01', category: 'Other' } expect(annualCost(s)).toBe(120) }) it('returns annual cost for yearly interval', () => { const s: Subscription = { id: '1', name: 'Test', cost: 120, interval: 'yearly', startedAt: '2024-01-01', category: 'Other' } expect(annualCost(s)).toBe(120) }) }) describe('totalMonthlyCost', () => { it('sums only active subscriptions', () => { const subs: Subscription[] = [ { id: '1', name: 'A', cost: 10, interval: 'monthly', startedAt: '2024-01-01', category: 'Other' }, { id: '2', name: 'B', cost: 20, interval: 'monthly', startedAt: '2024-01-01', category: 'Other', cancelled: true }, { id: '3', name: 'C', cost: 120, interval: 'yearly', startedAt: '2024-01-01', category: 'Other' } ] expect(totalMonthlyCost(subs)).toBe(20) }) }) describe('daysUntil', () => { it('returns days until a future date', () => { const future = new Date(Date.now() + 3 * 86400000).toISOString().slice(0, 10) expect(daysUntil(future)).toBe(3) }) it('returns 0 for today', () => { const today = new Date().toISOString().slice(0, 10) expect(daysUntil(today)).toBe(0) }) }) describe('subscriptionsExpiringSoon', () => { it('returns trials expiring within given days', () => { const today = new Date().toISOString().slice(0, 10) const tomorrow = new Date(Date.now() + 86400000).toISOString().slice(0, 10) const nextWeek = new Date(Date.now() + 7 * 86400000).toISOString().slice(0, 10) const subs: Subscription[] = [ { id: '1', name: 'A', cost: 10, interval: 'monthly', startedAt: '2024-01-01', category: 'Other', trialEndsAt: today }, { id: '2', name: 'B', cost: 10, interval: 'monthly', startedAt: '2024-01-01', category: 'Other', trialEndsAt: tomorrow }, { id: '3', name: 'C', cost: 10, interval: 'monthly', startedAt: '2024-01-01', category: 'Other', trialEndsAt: nextWeek } ] expect(subscriptionsExpiringSoon(subs, 3).length).toBe(2) }) }) describe('formatCurrency', () => { it('formats dollars correctly', () => { expect(formatCurrency(10)).toBe('$10.00') expect(formatCurrency(10.5)).toBe('$10.50') expect(formatCurrency(0)).toBe('$0.00') }) }) describe('generateId', () => { it('generates unique ids', () => { const id1 = generateId() const id2 = generateId() expect(id1).not.toBe(id2) expect(id1.length).toBeGreaterThan(0) }) }) describe('subscriptionsToCSV', () => { it('produces valid CSV', () => { const subs: Subscription[] = [ { id: '1', name: 'Netflix', cost: 15.99, interval: 'monthly', startedAt: '2024-01-01', category: 'Entertainment' } ] const csv = subscriptionsToCSV(subs) expect(csv).toContain('Name') expect(csv).toContain('Netflix') expect(csv).toContain('15.99') }) }) describe('expensesToCSV', () => { it('produces valid CSV', () => { const expenses: Expense[] = [ { id: '1', amount: 25.50, description: 'Lunch', category: 'Food', date: '2024-01-01' } ] const csv = expensesToCSV(expenses) expect(csv).toContain('Description') expect(csv).toContain('Lunch') expect(csv).toContain('25.50') }) })