import { describe, it, expect } from 'vitest' import { evaluateAlert, type SnapshotInput } from '@/lib/alerts/evaluate' function snap(price: number, daysAgo: number): SnapshotInput { const d = new Date() d.setDate(d.getDate() - daysAgo) return { price, scrapedAt: d } } describe('evaluateAlert', () => { describe('target_price', () => { it('triggers when current price <= threshold', () => { const r = evaluateAlert({ alert: { type: 'target_price', config: { threshold: 100 }, lastTriggeredAt: null }, currentPrice: 99, history: [], }) expect(r.triggered).toBe(true) }) it('does not trigger when price > threshold', () => { const r = evaluateAlert({ alert: { type: 'target_price', config: { threshold: 100 }, lastTriggeredAt: null }, currentPrice: 101, history: [], }) expect(r.triggered).toBe(false) }) }) describe('all_time_low', () => { it('triggers when current is below all previous successful prices', () => { const r = evaluateAlert({ alert: { type: 'all_time_low', config: {}, lastTriggeredAt: null }, currentPrice: 50, history: [snap(60, 1), snap(70, 5), snap(80, 10)], }) expect(r.triggered).toBe(true) expect(r.context.prevMin).toBe(60) }) it('does not trigger when current equals previous min', () => { const r = evaluateAlert({ alert: { type: 'all_time_low', config: {}, lastTriggeredAt: null }, currentPrice: 60, history: [snap(60, 1)], }) expect(r.triggered).toBe(false) }) it('does not trigger with no history (first scrape)', () => { const r = evaluateAlert({ alert: { type: 'all_time_low', config: {}, lastTriggeredAt: null }, currentPrice: 50, history: [], }) expect(r.triggered).toBe(false) }) }) describe('percent_drop', () => { it('triggers when price drops >= percent vs lookback avg', () => { const r = evaluateAlert({ alert: { type: 'percent_drop', config: { lookback_days: 7, percent: 10 }, lastTriggeredAt: null }, currentPrice: 80, history: [snap(100, 1), snap(100, 3), snap(100, 6), snap(50, 30)], }) expect(r.triggered).toBe(true) expect(r.context.percent).toBeGreaterThanOrEqual(10) }) it('does not trigger when drop is smaller than threshold', () => { const r = evaluateAlert({ alert: { type: 'percent_drop', config: { lookback_days: 7, percent: 10 }, lastTriggeredAt: null }, currentPrice: 95, history: [snap(100, 1), snap(100, 3)], }) expect(r.triggered).toBe(false) }) it('does not trigger with no history in lookback window', () => { const r = evaluateAlert({ alert: { type: 'percent_drop', config: { lookback_days: 7, percent: 10 }, lastTriggeredAt: null }, currentPrice: 50, history: [snap(100, 30)], }) expect(r.triggered).toBe(false) }) }) describe('dedup', () => { it('does not trigger if last triggered within 24h', () => { const recent = new Date(Date.now() - 1000 * 60 * 60 * 12) const r = evaluateAlert({ alert: { type: 'target_price', config: { threshold: 100 }, lastTriggeredAt: recent }, currentPrice: 50, history: [], }) expect(r.triggered).toBe(false) }) it('triggers if last triggered > 24h ago', () => { const old = new Date(Date.now() - 1000 * 60 * 60 * 25) const r = evaluateAlert({ alert: { type: 'target_price', config: { threshold: 100 }, lastTriggeredAt: old }, currentPrice: 50, history: [], }) expect(r.triggered).toBe(true) }) }) })