Files
nexus/frontend/tests/server-state-queries.test.ts
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

237 lines
7.9 KiB
TypeScript

import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
import { QueryClient } from '@tanstack/vue-query'
import { createPinia, setActivePinia } from 'pinia'
import {
applyAgentFileWrite,
fetchAgentActivity,
fetchAgentDetail,
fetchAgentFile,
fetchAgentFiles,
fetchAgentSummary,
type AgentFileCollectionDto,
type AgentFileDto,
type AgentFileWriteDto,
} from '../src/api/agentDetail'
import {
fetchDoc,
fetchDocs,
fetchMemoryFile,
fetchMemoryFiles,
fetchMemorySearch,
} from '../src/api/knowledge'
import { fetchIncident, fetchIncidents } from '../src/api/incidents'
import { queryKeys } from '../src/api/queryClient'
import { fetchSecurityStatus } from '../src/api/security'
import { useAuthStore } from '../src/stores/auth'
function requestUrl(input: RequestInfo | URL): URL {
const raw = input instanceof Request ? input.url : String(input)
return new URL(raw, 'http://localhost')
}
function jsonResponse(value: unknown) {
return new Response(JSON.stringify(value), {
status: 200,
headers: { 'Content-Type': 'application/json' },
})
}
const file: AgentFileDto = {
agentId: 'iris',
name: 'SOUL.md',
missing: false,
size: 42,
updatedAt: '2026-07-31T10:00:00.000Z',
content: '# Iris',
contentHash: 'hash-1',
checkedAt: '2026-07-31T10:00:01.000Z',
}
beforeEach(() => {
setActivePinia(createPinia())
const auth = useAuthStore()
auth.initialized = true
auth.applySession({
accessToken: 'server-state-token',
expiresAt: '2026-07-31T12:00:00.000Z',
user: {
id: 'bao',
email: 'bao@nexus.local',
displayName: 'Bao',
role: 'owner',
},
})
})
afterEach(() => {
vi.unstubAllGlobals()
})
describe('remaining server-state fetchers', () => {
it('loads agent detail, activity, summary and generated OpenClaw file contracts', async () => {
const responses = new Map<string, unknown>([
['/api/v1/agents/iris', {
id: 'iris',
name: 'Iris',
role: 'orchestrator',
model: 'gpt-5',
status: 'Online',
}],
['/api/v1/agents/iris/activity', [{
id: 1,
type: 'task',
message: 'Delegated work',
at: '2026-07-31T09:00:00.000Z',
source: 'openclaw',
}]],
['/api/v1/agents/iris/summary', {
now: { text: 'Working', source: 'openclaw' },
today: { text: 'Completed review', source: 'openclaw' },
generatedAt: '2026-07-31T10:00:00.000Z',
}],
['/api/v1/openclaw/agents/iris/files', {
agentId: 'iris',
files: [{
name: file.name,
missing: file.missing,
size: file.size,
updatedAt: file.updatedAt,
contentHash: file.contentHash,
}],
checkedAt: file.checkedAt,
}],
['/api/v1/openclaw/agents/iris/files/SOUL.md', file],
])
const fetchMock = vi.fn(async (input: RequestInfo | URL, init?: RequestInit) => {
const value = responses.get(requestUrl(input).pathname)
return value === undefined
? new Response(null, { status: 404 })
: jsonResponse(value)
})
vi.stubGlobal('fetch', fetchMock)
await expect(fetchAgentDetail('iris')).resolves.toMatchObject({ id: 'iris' })
await expect(fetchAgentActivity('iris')).resolves.toHaveLength(1)
await expect(fetchAgentSummary('iris')).resolves.toMatchObject({
now: { text: 'Working' },
})
await expect(fetchAgentFiles('iris')).resolves.toMatchObject({
files: [{ name: 'SOUL.md' }],
})
await expect(fetchAgentFile('iris', 'SOUL.md')).resolves.toEqual(file)
expect(fetchMock).toHaveBeenCalledTimes(5)
for (const [, init] of fetchMock.mock.calls) {
expect(new Headers(init?.headers).get('Authorization'))
.toBe('Bearer server-state-token')
}
})
it('loads docs, memory, incidents and security through authenticated fetchers', async () => {
const fetchMock = vi.fn(async (input: RequestInfo | URL) => {
const url = requestUrl(input)
if (url.pathname === '/api/v1/docs') return jsonResponse([])
if (url.pathname === '/api/v1/docs/runbook%2Fdeploy.md') {
return jsonResponse({ name: 'deploy.md', path: 'runbook/deploy.md', content: '# Deploy' })
}
if (url.pathname === '/api/v1/memory') return jsonResponse([])
if (url.pathname === '/api/v1/memory/2026-07-31.md') {
return jsonResponse({ name: '2026-07-31.md', content: 'Memory' })
}
if (url.pathname === '/api/v1/memory/search') {
return jsonResponse([{ name: '2026-07-31.md', excerpt: 'Mission control' }])
}
if (url.pathname === '/api/v1/incidents') return jsonResponse([])
if (url.pathname === '/api/v1/incidents/gateway.md') {
return jsonResponse({ name: 'gateway.md', title: 'Gateway', content: '# Gateway' })
}
if (url.pathname === '/api/v1/security/status') {
return jsonResponse({
authMethod: 'JWT',
tokenConfig: {
issuer: 'nexus',
audience: 'nexus-web',
refreshTokenDays: 7,
accessTokenMinutes: 15,
},
rateLimit: 'enabled',
passwordPolicy: 'strong',
cookieConfig: { httpOnly: true, secure: true, sameSite: 'Strict' },
twoFactorEnabled: false,
passkeyEnabled: false,
})
}
return new Response(null, { status: 404 })
})
vi.stubGlobal('fetch', fetchMock)
await expect(fetchDocs()).resolves.toEqual([])
await expect(fetchDoc('runbook/deploy.md')).resolves.toMatchObject({ name: 'deploy.md' })
await expect(fetchMemoryFiles()).resolves.toEqual([])
await expect(fetchMemoryFile('2026-07-31.md')).resolves.toMatchObject({
content: 'Memory',
})
await expect(fetchMemorySearch('mission control')).resolves.toHaveLength(1)
await expect(fetchIncidents()).resolves.toEqual([])
await expect(fetchIncident('gateway.md')).resolves.toMatchObject({ title: 'Gateway' })
await expect(fetchSecurityStatus()).resolves.toMatchObject({ authMethod: 'JWT' })
const urls = fetchMock.mock.calls.map(([input]) => requestUrl(input))
expect(urls.find(url => url.pathname === '/api/v1/memory/search')
?.searchParams.get('q')).toBe('mission control')
})
})
describe('agent file cache synchronization', () => {
it('patches and invalidates only the written file and its collection', async () => {
const client = new QueryClient()
const collection: AgentFileCollectionDto = {
agentId: 'iris',
files: [{
name: file.name,
missing: true,
size: null,
updatedAt: null,
contentHash: null,
}],
checkedAt: '2026-07-31T09:00:00.000Z',
}
const previousFile: AgentFileDto = {
...file,
missing: true,
size: null,
updatedAt: null,
content: null,
contentHash: 'missing',
checkedAt: collection.checkedAt,
}
const writeResult: AgentFileWriteDto = {
ok: true,
state: 'saved',
message: 'Saved and verified.',
file,
verified: true,
idempotencyKey: 'idem-1',
correlationId: 'corr-1',
completedAt: file.checkedAt,
}
client.setQueryData(queryKeys.agentFiles('iris'), collection)
client.setQueryData(queryKeys.agentFile('iris', 'SOUL.md'), previousFile)
client.setQueryData(queryKeys.agentFile('iris', 'TOOLS.md'), { marker: 'other' })
await applyAgentFileWrite(client, 'iris', writeResult)
expect(client.getQueryData(queryKeys.agentFile('iris', 'SOUL.md'))).toEqual(file)
expect(client.getQueryData<AgentFileCollectionDto>(queryKeys.agentFiles('iris')))
.toMatchObject({
checkedAt: file.checkedAt,
files: [{ name: 'SOUL.md', missing: false, contentHash: 'hash-1' }],
})
expect(client.getQueryState(queryKeys.agentFiles('iris'))?.isInvalidated).toBe(true)
expect(client.getQueryState(queryKeys.agentFile('iris', 'SOUL.md'))?.isInvalidated)
.toBe(true)
expect(client.getQueryState(queryKeys.agentFile('iris', 'TOOLS.md'))?.isInvalidated)
.toBe(false)
})
})