Files
nexus/frontend/tests/openclaw-runtime.test.ts
T
AzuTear f5552218bc
CI - Build & Test / Backend (.NET) (push) Successful in 42s
CI - Build & Test / Frontend (Vue/TS) (push) Successful in 2m46s
CI - Build & Test / Security Check (push) Successful in 3s
CI - Build & Test / Deploy Nexus (push) Successful in 56s
feat: ship agent-first mission control v0.2.57
2026-07-31 22:39:47 +02:00

218 lines
5.8 KiB
TypeScript

import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
import { createPinia, setActivePinia } from 'pinia'
import {
fetchOpenClawAgents,
fetchOpenClawCapabilities,
openClawOverviewQueryOptions,
projectOpenClawAgentNodes,
type OpenClawOverviewDto,
} from '../src/api/openclawRuntime'
import { queryClient } from '../src/api/queryClient'
import { useAuthStore } from '../src/stores/auth'
import type {
OpenClawAgent,
OpenClawCapability,
OpenClawCollection,
} from '../src/types/openclaw'
const now = '2026-07-31T10:00:00.000Z'
function collection<T>(items: T[]): OpenClawCollection<T> {
return {
state: 'ready',
items,
nextCursor: null,
message: null,
recovery: null,
checkedAt: now,
}
}
function overviewFixture(): OpenClawOverviewDto {
return {
connection: {
state: 'connected',
configured: true,
credentialConfigured: true,
connected: true,
endpoint: 'ws://openclaw-gateway:18789',
gatewayVersion: '2026.7.1',
requiredVersion: '2026.7.1',
versionPinned: true,
versionMatches: true,
protocolVersion: 4,
grantedScopes: ['operator.read'],
advertisedEvents: ['agent', 'task'],
lastConnectedAt: now,
lastEventAt: now,
reconnectAttempts: 0,
message: 'Connected',
recovery: null,
checkedAt: now,
deviceId: 'nexus-test',
pairingRequired: false,
pairingRequestId: null,
},
capabilities: [],
tasks: collection([{
id: 'task-1',
title: 'Prepare release',
status: 'running',
kind: 'agent',
runtime: 'openclaw',
agentId: 'iris',
sessionKey: 'agent:iris:main',
runId: 'run-1',
flowId: null,
parentTaskId: null,
createdAt: now,
startedAt: now,
updatedAt: now,
finishedAt: null,
progress: 42,
summary: 'Checks the release gates.',
error: null,
canCancel: true,
}]),
sessions: collection([{
key: 'agent:iris:main',
sessionId: 'session-1',
agentId: 'iris',
title: 'Release session',
status: 'running',
kind: 'agent',
channel: null,
model: 'openai/gpt-5.4',
provider: 'openai',
runId: 'run-1',
updatedAt: now,
inputTokens: 800,
outputTokens: 400,
totalTokens: 1_200,
canAbort: true,
}]),
cronJobs: collection([]),
approvals: collection([]),
activity: collection([]),
models: collection([{
id: 'openai/gpt-5.4',
name: 'GPT-5.4',
provider: 'openai',
configured: true,
available: true,
contextWindow: 1_000_000,
reason: null,
}]),
agents: collection([{
id: 'iris',
name: 'Iris',
description: 'Coordinates the team.',
model: null,
provider: 'openai',
workspace: '/managed/agents/iris',
status: 'ready',
}]),
generatedAt: now,
}
}
function authenticate(): void {
const auth = useAuthStore()
auth.initialized = true
auth.applySession({
accessToken: 'runtime-access-token',
expiresAt: '2026-07-31T12:00:00.000Z',
user: {
id: 'bao',
email: 'bao@nexus.local',
displayName: 'Bao',
role: 'owner',
},
})
}
function pathOf(input: RequestInfo | URL): string {
const raw = input instanceof Request ? input.url : String(input)
return new URL(raw, 'http://localhost').pathname
}
beforeEach(() => {
setActivePinia(createPinia())
authenticate()
queryClient.clear()
})
afterEach(() => {
queryClient.clear()
vi.unstubAllGlobals()
})
describe('shared OpenClaw runtime queries', () => {
it('deduplicates simultaneous overview consumers at the authenticated boundary', async () => {
const fetchMock = vi.fn(async () => new Response(JSON.stringify(overviewFixture()), {
status: 200,
headers: { 'Content-Type': 'application/json' },
}))
vi.stubGlobal('fetch', fetchMock)
const options = openClawOverviewQueryOptions()
const results = await Promise.all([
queryClient.fetchQuery(options),
queryClient.fetchQuery(options),
queryClient.fetchQuery(options),
])
expect(results.every(result => result.connection.connected)).toBe(true)
expect(fetchMock).toHaveBeenCalledTimes(1)
const [input, init] = fetchMock.mock.calls[0]!
expect(pathOf(input)).toBe('/api/v1/openclaw/overview')
expect(new Headers(init?.headers).get('Authorization')).toBe('Bearer runtime-access-token')
})
it('uses the dedicated capability and agent contracts', async () => {
const capabilities: OpenClawCapability[] = [{
id: 'agents-list',
label: 'List agents',
method: 'agents.list',
requiredScope: 'operator.read',
available: true,
state: 'ready',
reason: null,
}]
const agents: OpenClawCollection<OpenClawAgent> = collection([
overviewFixture().agents.items[0]!,
])
const fetchMock = vi.fn(async (input: RequestInfo | URL) => {
const path = pathOf(input)
return new Response(JSON.stringify(
path.endsWith('/capabilities') ? capabilities : agents,
), {
status: 200,
headers: { 'Content-Type': 'application/json' },
})
})
vi.stubGlobal('fetch', fetchMock)
await expect(fetchOpenClawCapabilities()).resolves.toEqual(capabilities)
await expect(fetchOpenClawAgents()).resolves.toEqual(agents)
expect(fetchMock.mock.calls.map(call => pathOf(call[0]))).toEqual([
'/api/v1/openclaw/capabilities',
'/api/v1/openclaw/agents',
])
})
it('projects task, session, model and token state without a dashboard facade', () => {
expect(projectOpenClawAgentNodes(overviewFixture())).toEqual([
expect.objectContaining({
id: 'iris',
status: 'work',
statusLabel: 'Arbeitet',
task: 'Prepare release',
progress: 42,
model: 'openai/gpt-5.4',
tokens: '1.2k',
}),
])
})
})