feat: ship agent-first mission control v0.2.57
This commit is contained in:
@@ -0,0 +1,217 @@
|
||||
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',
|
||||
}),
|
||||
])
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user