import { sql } from 'kysely' import * as TestApp from '../../../test/App.js' import * as RoutesCatalog from './routesCatalog.js' // Matches the table module's private `fcat` advisory-lock key. const publishLockKey = 0x66636174 const catalog = { chains: [ { aliases: ['eth'], id: 'eip155:1', name: 'Ethereum', parentChainId: null, rpcUrls: ['https://ethereum.example'], slug: 'ethereum', }, { aliases: [], id: 'eip155:4217', name: 'Tempo', parentChainId: null, rpcUrls: ['https://tempo.example'], slug: 'tempo', }, ], chainTokens: [ { address: '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48', chainId: 'eip155:1', decimals: 6, name: null, standard: 'ERC-20', tokenId: 'usdc', }, { address: '0x20c000000000000000000000b9537d11c60e8b50', chainId: 'eip155:4217', decimals: 6, name: null, standard: 'TIP-20', tokenId: 'usdce', }, ], routes: [ { destinationChainId: 'eip155:4217', destinationTokenId: 'usdce', providerId: 'relay', sourceChainId: 'eip155:1', sourceTokenId: 'usdc', }, ], tokens: [ { currency: 'USD', id: 'usdc', name: 'USD Coin', symbol: 'USDC', }, { currency: 'USD', id: 'usdce', name: 'Bridged USDC (Stargate)', symbol: 'USDC.e', }, ], } as const satisfies RoutesCatalog.publish.Input describe('read', () => { test('runs concurrently with another reader', async () => { const factory = TestApp.databaseFactory() await RoutesCatalog.publish(factory(), catalog) const locked = Promise.withResolvers() const release = Promise.withResolvers() const holder = factory().transaction(async (tx) => { await sql`SELECT pg_advisory_xact_lock_shared(${publishLockKey})`.execute(tx.kysely) locked.resolve() await release.promise }) await locked.promise const read = RoutesCatalog.read(factory()) const timeout = Promise.withResolvers<'waiting'>() const timer = setTimeout(() => timeout.resolve('waiting'), 1_000) let state: 'read' | 'waiting' try { state = await Promise.race([read.then(() => 'read' as const), timeout.promise]) } finally { clearTimeout(timer) release.resolve() await holder await read } expect(state).toBe('read') }) test('waits for a publisher lock', async () => { const factory = TestApp.databaseFactory() await RoutesCatalog.publish(factory(), catalog) const locked = Promise.withResolvers() const release = Promise.withResolvers() const holder = factory().transaction(async (tx) => { await sql`SELECT pg_advisory_xact_lock(${publishLockKey})`.execute(tx.kysely) locked.resolve() await release.promise }) await locked.promise const read = RoutesCatalog.read(factory()) const timeout = Promise.withResolvers<'waiting'>() const timer = setTimeout(() => timeout.resolve('waiting'), 100) let state: 'read' | 'waiting' try { state = await Promise.race([read.then(() => 'read' as const), timeout.promise]) } finally { clearTimeout(timer) release.resolve() await holder await read } expect(state).toBe('waiting') }) }) describe('publish', () => { test('round-trips a complete route catalog', async () => { const db = TestApp.database() const head = await RoutesCatalog.publish(db, catalog) expect(await RoutesCatalog.read(db)).toStrictEqual({ catalog: head, chainTokens: catalog.chainTokens, chains: catalog.chains, // Optional route JSON persists as null. routes: catalog.routes.map((route) => ({ ...route, capabilities: null, configuration: null, })), tokens: catalog.tokens, }) }) test('round-trips route capabilities', async () => { const db = TestApp.database() const capabilities = { transfer: { modes: ['exactSource'] } } as const await RoutesCatalog.publish(db, { ...catalog, routes: [{ ...catalog.routes[0]!, capabilities }], }) expect((await RoutesCatalog.read(db)).routes[0]?.capabilities).toStrictEqual(capabilities) }) test('round-trips route configuration', async () => { const db = TestApp.database() const configuration = { poolAddress: `0x${'11'.repeat(20)}` } await RoutesCatalog.publish(db, { ...catalog, routes: [{ ...catalog.routes[0]!, configuration }], }) expect((await RoutesCatalog.read(db)).routes[0]?.configuration).toStrictEqual(configuration) }) test('replaces rows and advances the catalog version', async () => { const db = TestApp.database() const first = await RoutesCatalog.publish(db, catalog) const second = await RoutesCatalog.publish(db, { chainTokens: [], chains: catalog.chains.slice(0, 1), routes: [], tokens: [], }) expect(second.version > first.version).toBe(true) expect(await RoutesCatalog.read(db)).toStrictEqual({ catalog: second, chainTokens: [], chains: catalog.chains.slice(0, 1), routes: [], tokens: [], }) }) })