import type { Context } from 'hono' import * as TestApp from '../../test/App.js' import type * as App from '../App.js' import * as Db from '../db/Db.js' import * as core_VerifiedTokens from '../db/tables/verifiedTokens.js' import * as VerifiedTokens from './VerifiedTokens.js' // Publishes a list at an explicit version so `read`/`snapshot` can load it // (the write API generates monotonic versions; tests pin readable ones). Skips // a matching version so vitest retries on a shared db stay idempotent. async function seed( db: Db.Db, chainId: number, version: string, list: readonly VerifiedTokens.Token[], ) { if ((await core_VerifiedTokens.head(db, chainId))?.version === version) return await core_VerifiedTokens.publish(db, chainId, { tokens: list, updatedAt: '2024-01-01T00:00:00.000Z', version, }) } // A database whose every query fails (nothing listens on port 1), proving a // code path performed zero database I/O when a test passes with it. const unreachable = () => Db.postgres({ connectionString: 'postgresql://postgres:postgres@127.0.0.1:1/none' }) // Minimal context serving the verified-tokens feature config and the app db // (both the fresh and cached bindings, as `App.create` sets them). function context(db: Db.Db, config?: App.data.VerifiedTokens): Context { return { get: (key: string) => (key === 'verifiedTokens' ? (config ?? {}) : key === 'db' || key === 'dbCached' ? db : undefined), // prettier-ignore } as unknown as Context } const flush = () => new Promise((resolve) => setTimeout(resolve, 0)) const tokens: readonly VerifiedTokens.Token[] = [ { address: '0xAAA0000000000000000000000000000000000001', currency: 'USD', decimals: 6, name: 'Alpha USD', symbol: 'aUSD' }, // prettier-ignore { address: '0xBBB0000000000000000000000000000000000002', currency: 'EUR', decimals: 2, name: 'Beta EUR', symbol: 'bEUR' }, // prettier-ignore { address: '0xCCC0000000000000000000000000000000000003', currency: 'usd', decimals: 6, name: 'Gamma USD', symbol: 'cUSD' }, // prettier-ignore ] describe('compile', () => { const snapshot = VerifiedTokens.compile({ chainId: 4217, tokens, updatedAt: '2024-01-01T00:00:00.000Z', version: 'test', }) test('preserves list order', () => { expect(snapshot.list.map((token) => token.symbol)).toMatchInlineSnapshot(` [ "aUSD", "bEUR", "cUSD", ] `) }) test('indexes by lowercased address', () => { expect(snapshot.byAddress.has('0xaaa0000000000000000000000000000000000001')).toBe(true) // Lookups normalize, so the original mixed-case key is not present verbatim. expect(snapshot.byAddress.has('0xAAA0000000000000000000000000000000000001')).toBe(false) }) test('indexes by lowercased symbol', () => { expect(snapshot.bySymbol.get('ausd')?.address).toBe( '0xAAA0000000000000000000000000000000000001', ) }) test('groups by lowercased currency, preserving order', () => { expect(snapshot.byCurrency.get('usd')?.map((token) => token.symbol)).toMatchInlineSnapshot(` [ "aUSD", "cUSD", ] `) expect(snapshot.byCurrency.get('eur')?.map((token) => token.symbol)).toMatchInlineSnapshot(` [ "bEUR", ] `) }) test('derives sorted, distinct (case-sensitive) currencies', () => { expect(snapshot.currencies).toMatchInlineSnapshot(` [ "EUR", "USD", "usd", ] `) }) }) describe('read', () => { test('returns null when the chain has no list', async () => { expect(await VerifiedTokens.read(TestApp.database(), 4217)).toBe(null) }) test('compiles the latest snapshot', async () => { const db = TestApp.database() await seed(db, 4217, 'v1', tokens) const snapshot = await VerifiedTokens.read(db, 4217) expect(snapshot?.version).toBe('v1') expect(snapshot?.list.map((token) => token.symbol)).toMatchInlineSnapshot(` [ "aUSD", "bEUR", "cUSD", ] `) }) test('serves the latest publish as the head', async () => { const db = TestApp.database() await seed(db, 4217, 'v1', tokens) await seed(db, 4217, 'v2', tokens.slice(0, 1)) const snapshot = await VerifiedTokens.read(db, 4217) expect(snapshot?.version).toBe('v2') expect(snapshot?.list.map((token) => token.symbol)).toEqual(['aUSD']) }) test('round-trips a Zone chain id above the signed 32-bit range', async () => { const chainId = 421_700_001 const db = TestApp.database() await seed(db, chainId, 'zone', tokens) const [head, rows, snapshot] = await Promise.all([ core_VerifiedTokens.head(db, chainId), core_VerifiedTokens.list(db, chainId), VerifiedTokens.read(db, chainId), ]) expect(head?.chainId).toBe(chainId) expect(rows.map((row) => row.chainId)).toEqual(tokens.map(() => chainId)) expect(snapshot?.chainId).toBe(chainId) }) }) describe('snapshot', () => { // Minimal context stub: `Timing.time` only reads the optional `serverTiming`. const unconfigured = { get: () => undefined } as unknown as Context test('serves an empty snapshot when the feature is unconfigured', async () => { const snapshot = await VerifiedTokens.snapshot(unconfigured, 4217) expect(snapshot.version).toBe('empty') expect(snapshot.list).toEqual([]) }) // The following cases run in order and share the module-level per-isolate // cache for chain 42431 (and one seeded db): cold load → fresh → soft-stale // unchanged → soft-stale changed. const db = TestApp.database() test('cold-loads from the database when a list exists', async () => { await seed(db, 42431, 'v1', tokens) const snapshot = await VerifiedTokens.snapshot(context(db), 42431) expect(snapshot.version).toBe('v1') expect(snapshot.byAddress.size).toBe(tokens.length) }) test('serves fresh lookups with zero database I/O', async () => { // Every query against this db fails, so a pass proves the fresh window // never touched the database. const snapshot = await VerifiedTokens.snapshot( context(unreachable(), { refreshMs: 60_000 }), 42431, ) expect(snapshot.version).toBe('v1') }) test('soft-stale keeps serving an unchanged version', async () => { // refreshMs 0 forces the soft-stale path; the current snapshot is returned // immediately and the head-only refresh runs in the background. const snapshot = await VerifiedTokens.snapshot(context(db, { refreshMs: 0 }), 42431) expect(snapshot.version).toBe('v1') await flush() }) test('soft-stale recompiles when the version changed', async () => { await seed(db, 42431, 'v2', tokens.slice(0, 1)) // Stale call returns the existing v1 immediately, refreshes to v2 in background. const stale = await VerifiedTokens.snapshot(context(db, { refreshMs: 0 }), 42431) expect(stale.version).toBe('v1') // The refresh round-trips Postgres, so poll the (fresh-window, zero-I/O) // cell until it lands rather than racing a single macrotask. await vi.waitFor(async () => { const fresh = await VerifiedTokens.snapshot(context(db, { refreshMs: 60_000 }), 42431) expect(fresh.version).toBe('v2') }) const fresh = await VerifiedTokens.snapshot(context(db, { refreshMs: 60_000 }), 42431) expect(fresh.list.map((token) => token.symbol)).toEqual(['aUSD']) }) test('cold-loads an empty snapshot when the chain has no rows', async () => { // Chain 4217 has no configured cell yet and an empty database → empty snapshot. const snapshot = await VerifiedTokens.snapshot(context(TestApp.database()), 4217) expect(snapshot.version).toBe('empty') expect(snapshot.list).toEqual([]) }) }) const newToken = { address: '0xDDD0000000000000000000000000000000000004', currency: 'GBP', decimals: 8, name: 'Delta GBP', symbol: 'dGBP', } satisfies VerifiedTokens.Token describe('replace', () => { test('publishes the list and round-trips through read', async () => { const db = TestApp.database() const { snapshot } = await VerifiedTokens.replace(db, 42431, tokens) const read = await VerifiedTokens.read(db, 42431) expect(read?.version).toBe(snapshot.version) expect(read?.list.map((token) => token.symbol)).toEqual(['aUSD', 'bEUR', 'cUSD']) // Addresses are normalized to lowercase on write. expect(read?.list[0]?.address).toBe('0xaaa0000000000000000000000000000000000001') }) test('advances the head version on each publish', async () => { const db = TestApp.database() const first = await VerifiedTokens.replace(db, 42431, tokens) const second = await VerifiedTokens.replace(db, 42431, tokens.slice(0, 1)) expect(second.snapshot.version > first.snapshot.version).toBe(true) }) test('rejects a duplicate address in the input', async () => { const db = TestApp.database() await expect( VerifiedTokens.replace(db, 42431, [tokens[0]!, tokens[0]!]), ).rejects.toThrowErrorMatchingInlineSnapshot( `[VerifiedTokens.DuplicateAddressError: verified token 0xaaa0000000000000000000000000000000000001 already exists on chain 42431.]`, ) }) }) describe('create', () => { test('appends a token preserving order', async () => { const db = TestApp.database() await VerifiedTokens.replace(db, 42431, tokens) const { snapshot, token } = await VerifiedTokens.create(db, { ...newToken, chainId: 42431 }) expect(token.address).toBe('0xddd0000000000000000000000000000000000004') expect(snapshot.list.map((t) => t.symbol)).toEqual(['aUSD', 'bEUR', 'cUSD', 'dGBP']) }) test('rejects a duplicate address', async () => { const db = TestApp.database() await VerifiedTokens.replace(db, 42431, tokens) await expect(VerifiedTokens.create(db, { ...tokens[0]!, chainId: 42431 })).rejects.toThrowError( VerifiedTokens.DuplicateAddressError, ) }) test('rejects a case-insensitive duplicate symbol', async () => { const db = TestApp.database() await VerifiedTokens.replace(db, 42431, tokens) await expect( VerifiedTokens.create(db, { ...newToken, chainId: 42431, symbol: 'AUSD' }), ).rejects.toThrowError(VerifiedTokens.DuplicateSymbolError) }) }) describe('patch', () => { test('edits fields in place, preserving order', async () => { const db = TestApp.database() await VerifiedTokens.replace(db, 42431, tokens) const { snapshot, token } = await VerifiedTokens.patch(db, 42431, tokens[1]!.address, { name: 'Beta Euro', }) expect(token.name).toBe('Beta Euro') // Unchanged fields are preserved; order is preserved. expect(token.symbol).toBe('bEUR') expect(snapshot.list.map((t) => t.symbol)).toEqual(['aUSD', 'bEUR', 'cUSD']) }) test('throws NotFoundError for an unknown address', async () => { const db = TestApp.database() await VerifiedTokens.replace(db, 42431, tokens) await expect( VerifiedTokens.patch(db, 42431, newToken.address, { name: 'x' }), ).rejects.toThrowError(VerifiedTokens.NotFoundError) }) test('rejects an If-Match version mismatch', async () => { const db = TestApp.database() await VerifiedTokens.replace(db, 42431, tokens) await expect( VerifiedTokens.patch(db, 42431, tokens[0]!.address, { name: 'x' }, { ifMatch: 'stale' }), ).rejects.toThrowError(VerifiedTokens.VersionMismatchError) }) }) describe('remove', () => { test('removes a token and publishes', async () => { const db = TestApp.database() await VerifiedTokens.replace(db, 42431, tokens) const { snapshot } = await VerifiedTokens.remove(db, 42431, tokens[0]!.address) expect(snapshot.list.map((t) => t.symbol)).toEqual(['bEUR', 'cUSD']) expect(snapshot.byAddress.has(tokens[0]!.address.toLowerCase())).toBe(false) }) test('throws NotFoundError for an unknown address', async () => { const db = TestApp.database() await VerifiedTokens.replace(db, 42431, tokens) await expect(VerifiedTokens.remove(db, 42431, newToken.address)).rejects.toThrowError( VerifiedTokens.NotFoundError, ) }) }) describe('prime', () => { test('gives read-your-write for the priming isolate', async () => { const primed = VerifiedTokens.compile({ chainId: 42431, tokens: tokens.slice(0, 2), updatedAt: '2024-02-02T00:00:00.000Z', version: 'primed', }) VerifiedTokens.prime(42431, primed) // Within the fresh window the primed snapshot is served with no database read. const snapshot = await VerifiedTokens.snapshot(context(unreachable(), { refreshMs: 60_000 }), 42431) // prettier-ignore expect(snapshot).toBe(primed) }) })