accounts
Version:
Tempo Accounts SDK
124 lines (102 loc) • 4.44 kB
text/typescript
import { hc } from 'hono/client'
import type { ExtractSchema } from 'hono/types'
import { describe, expectTypeOf, test } from 'vp/test'
import { Store } from 'wata/host'
import * as Handler from './Handler.js'
describe('deviceCode', () => {
test('accepts host deployment options', () => {
const handler = Handler.deviceCode({
baseUrl: (request) => new URL(request.url).origin,
html: {
render: () => new Response('verify'),
},
store: Store.memory(),
validate(options) {
expectTypeOf(options.record).toEqualTypeOf<import('wata/host').DeviceCode.PendingRecord>()
expectTypeOf(options.request.id).toEqualTypeOf<string | number>()
expectTypeOf(options.result).toBeUnknown()
expectTypeOf(options.userCode).toBeString()
return undefined
},
})
expectTypeOf(handler).toMatchTypeOf<Handler.Handler>()
})
})
describe('auth identity (OIDC)', () => {
test('Options.identity opts into issuer-based verification', () => {
expectTypeOf<Handler.auth.Options>().toMatchTypeOf<{
identity?:
| {
issuer?: string | undefined
required?: boolean | undefined
}
| undefined
}>()
})
})
describe('compose', () => {
// Two ad-hoc schema-typed handlers to exercise schema merging.
function makeAlpha() {
const router = Handler.from()
const app = router.post('/alpha/ping', async (c) => c.json({ pong: 'alpha' as const }))
return app as Handler.Handler<typeof app>
}
function makeBeta() {
const router = Handler.from()
const app = router.get('/beta/echo', async (c) => c.json({ echo: 'beta' as const }))
return app as Handler.Handler<typeof app>
}
test('merges route schemas under the mount path', () => {
const composed = Handler.compose([makeAlpha(), makeBeta()], { path: '/api' })
type Schema = ExtractSchema<typeof composed>
type AlphaPost = Schema['/api/alpha/ping']['$post']
type BetaGet = Schema['/api/beta/echo']['$get']
// Both routes survive the merge.
expectTypeOf<AlphaPost>().toBeObject()
expectTypeOf<BetaGet>().toBeObject()
})
test('typed client exposes both handlers via direct property access', () => {
const composed = Handler.compose([makeAlpha(), makeBeta()], { path: '/api' })
const client = hc<typeof composed>('http://localhost')
// Direct, autocomplete-friendly access.
expectTypeOf(client.api.alpha.ping.$post).toBeFunction()
expectTypeOf(client.api.beta.echo.$get).toBeFunction()
})
test('default mount path `/` exposes routes at the root', () => {
const composed = Handler.compose([makeAlpha(), makeBeta()])
const client = hc<typeof composed>('http://localhost')
expectTypeOf(client.alpha.ping.$post).toBeFunction()
expectTypeOf(client.beta.echo.$get).toBeFunction()
})
test('response bodies stay typed end-to-end after compose', async () => {
const composed = Handler.compose([makeAlpha(), makeBeta()], { path: '/api' })
const client = hc<typeof composed>('http://localhost')
const alphaRes = await client.api.alpha.ping.$post()
if (alphaRes.status === 200) {
const body = await alphaRes.json()
expectTypeOf(body).toMatchTypeOf<{ pong: 'alpha' }>()
}
const betaRes = await client.api.beta.echo.$get()
if (betaRes.status === 200) {
const body = await betaRes.json()
expectTypeOf(body).toMatchTypeOf<{ echo: 'beta' }>()
}
})
test('different mount paths are reflected in the composed schema', () => {
const a = Handler.compose([makeAlpha()], { path: '/v1' })
const b = Handler.compose([makeBeta()], { path: '/v2' })
type SchemaA = ExtractSchema<typeof a>
type SchemaB = ExtractSchema<typeof b>
expectTypeOf<SchemaA['/v1/alpha/ping']['$post']>().toBeObject()
expectTypeOf<SchemaB['/v2/beta/echo']['$get']>().toBeObject()
})
test('handlers without a route schema (legacy `Handler`) compose without errors', () => {
// `Handler.from` returns plain `Handler` (default `Hono`), no route
// schema. Mixing it with typed handlers must not break inference for the
// typed siblings.
const plain = Handler.from() as unknown as Handler.Handler
const composed = Handler.compose([plain, makeAlpha()], { path: '/api' })
const client = hc<typeof composed>('http://localhost')
expectTypeOf(client.api.alpha.ping.$post).toBeFunction()
})
})