import * as EarnRates from './EarnRates.js' describe('schema.Window', () => { test('stays absent when omitted', () => { expect(EarnRates.schema.Window.parse(undefined)).toBeUndefined() }) test('accepts every selectable window and nothing else', () => { expect( ['1h', '1d', '7d', '30d'].map((value) => EarnRates.schema.Window.parse(value)), ).toStrictEqual(['1h', '1d', '7d', '30d']) }) test('rejects windows outside the selectable set', () => { expect( ['0h', '24h', '36h', '168h', '7x', '1w', '-1d', '1.5d', '721h', '31d', '90d', ''].map( (value) => EarnRates.schema.Window.safeParse(value).success, ), ).toStrictEqual([ false, false, false, false, false, false, false, false, false, false, false, false, ]) }) }) describe('asOf', () => { test('rounds down to a fifteen-minute UTC bucket', () => { vi.useFakeTimers() try { vi.setSystemTime(new Date('2026-07-21T09:14:59.999Z')) const inBucket = EarnRates.asOf() vi.setSystemTime(new Date('2026-07-21T09:29:59.000Z')) expect([inBucket, EarnRates.asOf()]).toMatchInlineSnapshot(` [ "2026-07-21T09:00:00.000Z", "2026-07-21T09:15:00.000Z", ] `) } finally { vi.useRealTimers() } }) test('withholds the newest thirty seconds', () => { vi.useFakeTimers() try { vi.setSystemTime(new Date('2026-07-21T09:15:20.000Z')) expect(EarnRates.asOf()).toMatchInlineSnapshot(`"2026-07-21T09:00:00.000Z"`) } finally { vi.useRealTimers() } }) }) describe('annualize', () => { const day = 24 * 60 * 60 test('annualizes share-price growth over the measured window', () => { expect([ EarnRates.annualize({ end: { price: 1_010_000n, timestamp: 7 * day }, start: { price: 1_000_000n, timestamp: 0 }, }), EarnRates.annualize({ end: { price: 1_000_500n, timestamp: 30 * day }, start: { price: 1_000_000n, timestamp: 0 }, }), ]).toMatchInlineSnapshot(` [ "0.680075", "0.006100", ] `) }) test('reports a drawdown as a negative ratio', () => { expect( EarnRates.annualize({ end: { price: 990_000n, timestamp: 30 * day }, start: { price: 1_000_000n, timestamp: 0 }, }), ).toMatchInlineSnapshot(`"-0.115099"`) }) test('leaves an unmeasurable window unannualized', () => { expect([ // Reversed and zero-length windows. EarnRates.annualize({ end: { price: 1_000_000n, timestamp: 0 }, start: { price: 1_000_000n, timestamp: day }, }), EarnRates.annualize({ end: { price: 1_000_000n, timestamp: 0 }, start: { price: 1_000_000n, timestamp: 0 }, }), // A vault with no starting price has no growth to measure. EarnRates.annualize({ end: { price: 1_000_000n, timestamp: day }, start: { price: 0n, timestamp: 0 }, }), // Growth too steep to express as a decimal ratio. EarnRates.annualize({ end: { price: 1_010_000n, timestamp: 3600 }, start: { price: 1_000_000n, timestamp: 0 }, }), ]).toMatchInlineSnapshot(` [ null, null, null, null, ] `) }) })