UNPKG

accounts

Version:

Tempo Accounts SDK

165 lines (137 loc) 5.56 kB
import { hc } from 'hono/client' import type { ExtractSchema } from 'hono/types' import type { Address } from 'viem' import { describe, expectTypeOf, test } from 'vp/test' import { compose } from '../../Handler.js' import { exchange } from './exchange.js' describe('exchange handler', () => { test('the route schema captures `POST /exchange/quote` input + output', () => { type Schema = ExtractSchema<ReturnType<typeof exchange>> type QuoteRoute = Schema['/exchange/quote']['$post'] expectTypeOf<QuoteRoute['input']>().toMatchTypeOf<{ json: { amount: string chainId?: number | undefined pairToken: string slippage: number token: string type: 'buy' | 'sell' } }>() }) test('typed client has direct access at `client.exchange.quote.$post`', () => { const handler = exchange() const client = hc<typeof handler>('http://localhost') // Direct access — autocomplete-friendly. No `NonNullable<>` workaround. expectTypeOf(client.exchange.quote.$post).toBeFunction() type Args = Parameters<typeof client.exchange.quote.$post>[0] expectTypeOf<Args['json']>().toMatchTypeOf<{ amount: string chainId?: number | undefined pairToken: string slippage: number token: string type: 'buy' | 'sell' }>() }) test('typed client exposes `client.exchange.tokens.$get` with optional `chainId` query', async () => { const handler = exchange() const client = hc<typeof handler>('http://localhost') expectTypeOf(client.exchange.tokens.$get).toBeFunction() type Args = Parameters<typeof client.exchange.tokens.$get>[0] expectTypeOf<Args['query']>().toMatchTypeOf<{ chainId?: string | undefined }>() const res = await client.exchange.tokens.$get({ query: {} }) if (res.status === 200) { const body = await res.json() expectTypeOf(body).toMatchTypeOf<{ tokens: readonly { address: `0x${string}` decimals: number logoUri?: string | undefined name: string symbol: string }[] }>() } }) test('success response is inferred from `schema.quote.returns`', () => { const handler = exchange() const client = hc<typeof handler>('http://localhost') type Response = Awaited<ReturnType<typeof client.exchange.quote.$post>> type Body = Response extends { json: () => Promise<infer B> } ? B : never expectTypeOf<Body>().toMatchTypeOf< | { calls: readonly { data: `0x${string}`; to: Address }[] pairToken: { address: Address; amount: string; name: string; symbol: string } slippage: number token: { address: Address; amount: string; name: string; symbol: string } type: 'buy' | 'sell' } | { error: string; issues: { message: string; path: string }[] } | { error: string; data?: unknown } >() }) }) describe('compose', () => { test('preserves the route schema under the mount path', () => { const composed = compose([exchange()], { path: '/api' }) type Schema = ExtractSchema<typeof composed> type QuoteRoute = Schema['/api/exchange/quote']['$post'] expectTypeOf<QuoteRoute['input']>().toMatchTypeOf<{ json: { amount: string chainId?: number | undefined pairToken: string slippage: number token: string type: 'buy' | 'sell' } }>() }) test('typed client has direct access at `client.api.exchange.quote.$post`', () => { const composed = compose([exchange()], { path: '/api' }) const client = hc<typeof composed>('http://localhost') // Direct access — autocomplete-friendly. expectTypeOf(client.api.exchange.quote.$post).toBeFunction() type Args = Parameters<typeof client.api.exchange.quote.$post>[0] expectTypeOf<Args['json']>().toMatchTypeOf<{ amount: string chainId?: number | undefined pairToken: string slippage: number token: string type: 'buy' | 'sell' }>() }) test('default mount path `/` exposes routes at the root', () => { const composed = compose([exchange()]) const client = hc<typeof composed>('http://localhost') expectTypeOf(client.exchange.quote.$post).toBeFunction() }) test('typed client merges multiple sub-handlers', () => { // Two exchange handlers at distinct paths to exercise schema merging. const composed = compose([exchange({ path: '/a' }), exchange({ path: '/b' })], { path: '/api', }) const client = hc<typeof composed>('http://localhost') expectTypeOf(client.api.a.quote.$post).toBeFunction() expectTypeOf(client.api.b.quote.$post).toBeFunction() }) test('success response inference flows through compose', () => { const composed = compose([exchange()], { path: '/api' }) const client = hc<typeof composed>('http://localhost') type Response = Awaited<ReturnType<typeof client.api.exchange.quote.$post>> type Body = Response extends { json: () => Promise<infer B> } ? B : never expectTypeOf<Body>().toMatchTypeOf< | { calls: readonly { data: `0x${string}`; to: Address }[] pairToken: { address: Address; amount: string; name: string; symbol: string } slippage: number token: { address: Address; amount: string; name: string; symbol: string } type: 'buy' | 'sell' } | { error: string; issues: { message: string; path: string }[] } | { error: string; data?: unknown } >() }) })