Files
nexmoe--vidbee/apps/cli/test/automation-client.test.ts
wehub-resource-sync d18ada4ee7
Docker Publish / docker (web, apps/web/Dockerfile, web) (push) Failing after 0s
Docker Publish / docker (api, apps/api/Dockerfile, api) (push) Failing after 1s
Publish Extension / detect-version (push) Has been skipped
Publish Extension / submit (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:24:41 +08:00

118 lines
4.1 KiB
TypeScript

import { describe, expect, it } from 'vitest'
import { AutomationClient } from '../src/transport/automation-client'
interface FakeResponse {
status: number
body: unknown
}
function fakeFetch(routes: Record<string, FakeResponse | ((init: RequestInit) => FakeResponse)>): typeof fetch {
const calls: { url: string; init: RequestInit }[] = []
const f = (async (url: RequestInfo | URL, init: RequestInit = {}) => {
const u = String(url)
calls.push({ url: u, init })
const path = new URL(u).pathname
const route = routes[path]
const r = typeof route === 'function' ? route(init) : route
if (!r) {
return new Response('not found', { status: 404 })
}
const body = typeof r.body === 'string' ? r.body : JSON.stringify(r.body)
return new Response(body, {
status: r.status,
headers: { 'content-type': 'application/json' }
})
}) as typeof fetch
;(f as unknown as { calls: typeof calls }).calls = calls
return f
}
describe('AutomationClient handshake', () => {
it('handshakes once then reuses token', async () => {
let handshakes = 0
const fetchImpl = fakeFetch({
'/automation/v1/handshake': () => {
handshakes += 1
return {
status: 200,
body: { token: 'tok', expiresAt: Date.now() + 600_000, ttlMs: 600_000, schemaVersion: '1.0.0' }
}
},
'/automation/v1/stats': { status: 200, body: { running: 0 } }
})
const c = new AutomationClient({ baseUrl: 'http://127.0.0.1:27100', fetch: fetchImpl })
await c.stats()
await c.stats()
expect(handshakes).toBe(1)
})
it('re-handshakes on 401', async () => {
let handshakes = 0
let nextStats: 401 | 200 = 401
const fetchImpl = fakeFetch({
'/automation/v1/handshake': () => {
handshakes += 1
return { status: 200, body: { token: 't', expiresAt: Date.now() + 600_000, ttlMs: 600_000 } }
},
'/automation/v1/stats': () => {
if (nextStats === 401) {
nextStats = 200
return { status: 401, body: { error: 'unauthorized' } }
}
return { status: 200, body: { ok: true } }
}
})
const c = new AutomationClient({ baseUrl: 'http://127.0.0.1:27100', fetch: fetchImpl })
const result = await c.stats()
expect(result).toEqual({ ok: true })
expect(handshakes).toBe(2) // initial + re-handshake on 401
})
it('skipHandshake uses provided token', async () => {
const fetchImpl = fakeFetch({
'/automation/v1/stats': (init) => {
const auth = (init.headers as Record<string, string>).Authorization
return { status: auth === 'Bearer t' ? 200 : 401, body: { auth } }
}
})
const c = new AutomationClient({
baseUrl: 'http://127.0.0.1:27100',
fetch: fetchImpl,
token: 't',
skipHandshake: true
})
const r = await c.stats()
expect(r).toEqual({ auth: 'Bearer t' })
})
it('list maps {tasks} → {items}', async () => {
const fetchImpl = fakeFetch({
'/automation/v1/handshake': { status: 200, body: { token: 't', expiresAt: Date.now() + 600_000, ttlMs: 600_000 } },
'/automation/v1/list': {
status: 200,
body: {
tasks: [{ task: { id: 'a', status: 'queued' }, projection: {} }],
nextCursor: null
}
}
})
const c = new AutomationClient({ baseUrl: 'http://127.0.0.1:27100', fetch: fetchImpl })
const r = await c.list({})
expect(r.items).toHaveLength(1)
expect((r.items[0] as { id: string }).id).toBe('a')
})
it('add() chases get() to assemble {id, task}', async () => {
const fetchImpl = fakeFetch({
'/automation/v1/handshake': { status: 200, body: { token: 't', expiresAt: Date.now() + 600_000, ttlMs: 600_000 } },
'/automation/v1/add': { status: 200, body: { id: 'id1' } },
'/automation/v1/get': { status: 200, body: { task: { id: 'id1', status: 'queued' }, projection: {} } }
})
const c = new AutomationClient({ baseUrl: 'http://127.0.0.1:27100', fetch: fetchImpl })
const r = await c.add({ input: { url: 'https://x', kind: 'video' } })
expect(r.id).toBe('id1')
expect((r.task as { status: string }).status).toBe('queued')
})
})