import * as FxOracle from './FxOracle.js' const set = { asOf: '2026-01-01T00:00:00.000Z', base: 'EUR', rates: { AUD: '1.6', USD: '1.25' }, } satisfies FxOracle.RateSet function feedUrl(usd: string) { const body = `` return `data:application/xml,${encodeURIComponent(body)}` } describe('from', () => { test('applies the default ttl and preserves overrides', () => { const oracle = FxOracle.from({ name: 'test', rates: async () => set }) expect(oracle.name).toBe('test') expect(oracle.ttl).toMatchInlineSnapshot(`3600000`) expect(FxOracle.from({ name: 'test', rates: async () => set, ttl: 5 }).ttl).toBe(5) }) test('resolves the definition rates', async () => { const oracle = FxOracle.from({ name: 'test', rates: async () => set }) await expect(oracle.rates()).resolves.toEqual(set) }) test('rethrows rates failures as RatesError', async () => { const oracle = FxOracle.from({ name: 'test', rates: async () => { throw new Error('boom') }, }) const error = await oracle.rates().catch((error: unknown) => error) expect(error).toBeInstanceOf(FxOracle.RatesError) expect(error).toMatchInlineSnapshot( `[FxOracle.RatesError: FX oracle "test" failed to produce rates]`, ) expect((error as Error).cause).toMatchInlineSnapshot(`[Error: boom]`) }) }) describe('ecb', () => { test('caches the daily feed for one day', () => { expect(FxOracle.ecb({ url: feedUrl('1.25') }).ttl).toMatchInlineSnapshot(`86400000`) }) test('rotates the cache key after the daily publication window', () => { vi.useFakeTimers() try { const oracle = FxOracle.ecb({ url: feedUrl('1.25') }) vi.setSystemTime('2026-01-02T15:59:00.000Z') expect(oracle.cacheKey?.()).toMatchInlineSnapshot(`"ecb:2026-01-01"`) vi.setSystemTime('2026-01-02T16:00:00.000Z') expect(oracle.cacheKey?.()).toMatchInlineSnapshot(`"ecb:2026-01-02"`) } finally { vi.useRealTimers() } }) test('fetches and parses the daily reference rates', async () => { const rates = await FxOracle.ecb({ url: feedUrl('1.25') }).rates() expect(rates).toMatchInlineSnapshot(` { "asOf": "2026-01-01T00:00:00.000Z", "base": "EUR", "rates": { "AUD": "1.6", "USD": "1.25", }, } `) }) test.each(['', '-1.25', '1..25'])('rejects malformed rate %j', async (value) => { const error = await FxOracle.ecb({ url: feedUrl(value) }) .rates() .catch((error: unknown) => error) expect(error).toBeInstanceOf(FxOracle.RatesError) expect((error as Error).cause).toMatchInlineSnapshot( `[FxOracle.ParseError: Rate feed returned an unrecognized payload]`, ) }) test('rejects non-positive rates', async () => { const error = await FxOracle.ecb({ url: feedUrl('0.0') }) .rates() .catch((error: unknown) => error) expect(error).toBeInstanceOf(FxOracle.RatesError) expect((error as Error).cause).toMatchInlineSnapshot( `[FxOracle.ParseError: Rate feed returned an unrecognized payload]`, ) }) test('rejects rates below fixed-point precision', async () => { const error = await FxOracle.ecb({ url: feedUrl('0.0000000000000000001') }) .rates() .catch((error: unknown) => error) expect(error).toBeInstanceOf(FxOracle.RatesError) expect((error as Error).cause).toMatchInlineSnapshot( `[FxOracle.ParseError: Rate feed returned an unrecognized payload]`, ) }) test('rejects with RatesError when the feed is unreachable', async () => { const oracle = FxOracle.ecb({ url: 'http://127.0.0.1:1' }) const error = await oracle.rates().catch((error: unknown) => error) expect(error).toBeInstanceOf(FxOracle.RatesError) expect(error).toMatchInlineSnapshot( `[FxOracle.RatesError: FX oracle "ecb" failed to produce rates]`, ) }) }) describe('rate', () => { test('derives cross rates through the base', () => { expect(FxOracle.rate(set, { from: 'USD', to: 'AUD' })).toMatchInlineSnapshot( `1280000000000000000n`, ) expect(FxOracle.rate(set, { from: 'EUR', to: 'USD' })).toMatchInlineSnapshot( `1250000000000000000n`, ) expect(FxOracle.rate(set, { from: 'USD', to: 'EUR' })).toMatchInlineSnapshot( `800000000000000000n`, ) expect(FxOracle.rate(set, { from: 'USD', to: 'USD' })).toMatchInlineSnapshot( `1000000000000000000n`, ) }) test('returns undefined for currencies the set does not price', () => { expect(FxOracle.rate(set, { from: 'BTC', to: 'USD' })).toBeUndefined() expect(FxOracle.rate(set, { from: 'USD', to: 'XXX' })).toBeUndefined() }) })