mirror of
https://github.com/hanzo-apps/hanzo.ai.git
synced 2026-10-29 03:30:33 +00:00
Every import, the catalog pin and the docs name @hanzo/app and github.com/hanzoai/app. lib/rooms.ts is lib/app.ts and components/rooms.tsx is components/app.tsx.
116 lines
3.7 KiB
TypeScript
116 lines
3.7 KiB
TypeScript
import { test, expect } from '@playwright/test'
|
|
import {
|
|
CodingRefusal,
|
|
dispatchable,
|
|
startCoding,
|
|
startRun,
|
|
listTargets,
|
|
type Target,
|
|
} from '@hanzo/app/lib/coding'
|
|
|
|
test.describe('hanzo coding client unit tests', () => {
|
|
test('CodingRefusal stores status and message', () => {
|
|
const refusal = new CodingRefusal(401, 'Sign in or use Free AI')
|
|
expect(refusal.status).toBe(401)
|
|
expect(refusal.message).toBe('Sign in or use Free AI')
|
|
expect(refusal).toBeInstanceOf(Error)
|
|
})
|
|
|
|
test('dispatchable reports true only for online machines', () => {
|
|
const onlineTgt: Target = { id: 'tgt_1', label: 'Box 1', kind: 'cloud', status: 'online' }
|
|
const offlineTgt: Target = { id: 'tgt_2', label: 'Box 2', kind: 'cloud', status: 'offline' }
|
|
const drainingTgt: Target = { id: 'tgt_3', label: 'Box 3', kind: 'cloud', status: 'draining' }
|
|
|
|
expect(dispatchable(onlineTgt)).toBe(true)
|
|
expect(dispatchable(offlineTgt)).toBe(false)
|
|
expect(dispatchable(drainingTgt)).toBe(false)
|
|
})
|
|
|
|
test('startRun aliases startCoding', () => {
|
|
expect(startRun).toBe(startCoding)
|
|
})
|
|
|
|
test('startCoding handles valid 202 session responses', async () => {
|
|
const origFetch = globalThis.fetch
|
|
try {
|
|
globalThis.fetch = async (url: string | URL | Request, init?: RequestInit) => {
|
|
return new Response(
|
|
JSON.stringify({
|
|
sessionId: 'ses_12345',
|
|
repo: 'hanzo/ai',
|
|
branch: 'feat/cool',
|
|
routed: true,
|
|
targetId: 'tgt_99',
|
|
}),
|
|
{ status: 202, headers: { 'Content-Type': 'application/json' } }
|
|
)
|
|
}
|
|
|
|
const run = await startCoding({ prompt: 'Build a widget', repo: 'hanzo/ai' })
|
|
expect(run.sessionId).toBe('ses_12345')
|
|
expect(run.repo).toBe('hanzo/ai')
|
|
expect(run.branch).toBe('feat/cool')
|
|
expect(run.routed).toBe(true)
|
|
expect(run.targetId).toBe('tgt_99')
|
|
} finally {
|
|
globalThis.fetch = origFetch
|
|
}
|
|
})
|
|
|
|
test('startCoding throws 502 CodingRefusal if accepted with no session', async () => {
|
|
const origFetch = globalThis.fetch
|
|
try {
|
|
globalThis.fetch = async () => {
|
|
return new Response(JSON.stringify({ repo: 'hanzo/ai' }), {
|
|
status: 202,
|
|
headers: { 'Content-Type': 'application/json' },
|
|
})
|
|
}
|
|
|
|
await expect(startCoding({ prompt: 'Test missing session' })).rejects.toThrow(CodingRefusal)
|
|
} finally {
|
|
globalThis.fetch = origFetch
|
|
}
|
|
})
|
|
|
|
test('startCoding propagates 401 unauthenticated refusal', async () => {
|
|
const origFetch = globalThis.fetch
|
|
try {
|
|
globalThis.fetch = async () => {
|
|
return new Response(JSON.stringify({ error: { message: 'Unauthorized' } }), {
|
|
status: 401,
|
|
headers: { 'Content-Type': 'application/json' },
|
|
})
|
|
}
|
|
|
|
await expect(startCoding({ prompt: 'Test 401' })).rejects.toThrow(CodingRefusal)
|
|
} finally {
|
|
globalThis.fetch = origFetch
|
|
}
|
|
})
|
|
|
|
test('listTargets returns array of claimed targets', async () => {
|
|
const origFetch = globalThis.fetch
|
|
try {
|
|
globalThis.fetch = async () => {
|
|
return new Response(
|
|
JSON.stringify({
|
|
targets: [
|
|
{ id: 'tgt_1', label: 'Rig 1', kind: 'laptop', status: 'online' },
|
|
{ id: 'tgt_2', label: 'Rig 2', kind: 'cloud', status: 'offline' },
|
|
],
|
|
}),
|
|
{ status: 200, headers: { 'Content-Type': 'application/json' } }
|
|
)
|
|
}
|
|
|
|
const targets = await listTargets()
|
|
expect(targets.length).toBe(2)
|
|
expect(targets[0].label).toBe('Rig 1')
|
|
expect(targets[1].status).toBe('offline')
|
|
} finally {
|
|
globalThis.fetch = origFetch
|
|
}
|
|
})
|
|
})
|