This commit is contained in:
111
packages/server/tests/cockpit-health.test.ts
Normal file
111
packages/server/tests/cockpit-health.test.ts
Normal file
@@ -0,0 +1,111 @@
|
||||
/**
|
||||
* Cockpit health endpoint enhancements — memoryStats, serviceHealth, defaultModel.
|
||||
*/
|
||||
import { describe, it, expect, beforeAll, afterAll, vi } from 'vitest';
|
||||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import os from 'node:os';
|
||||
import { MindDB, FrameStore, SessionStore } from '@waggle/core';
|
||||
import { buildLocalServer } from '../src/local/index.js';
|
||||
import type { FastifyInstance } from 'fastify';
|
||||
|
||||
describe('Cockpit health endpoint enhancements', () => {
|
||||
let server: FastifyInstance;
|
||||
let tmpDir: string;
|
||||
|
||||
beforeAll(async () => {
|
||||
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'waggle-cockpit-test-'));
|
||||
|
||||
// Create personal.mind with test data
|
||||
const personalPath = path.join(tmpDir, 'personal.mind');
|
||||
const mind = new MindDB(personalPath);
|
||||
const sessions = new SessionStore(mind);
|
||||
const frames = new FrameStore(mind);
|
||||
const s1 = sessions.create('cockpit-test');
|
||||
frames.createIFrame(s1.gop_id, 'Test frame 1', 'normal');
|
||||
frames.createIFrame(s1.gop_id, 'Test frame 2', 'important');
|
||||
frames.createPFrame(s1.gop_id, 'Test P-frame', 1, 'normal');
|
||||
mind.close();
|
||||
|
||||
server = await buildLocalServer({ dataDir: tmpDir });
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
await server.close();
|
||||
fs.rmSync(tmpDir, { recursive: true, force: true });
|
||||
});
|
||||
|
||||
it('returns memoryStats in health response', async () => {
|
||||
const res = await server.inject({ method: 'GET', url: '/health' });
|
||||
expect(res.statusCode).toBe(200);
|
||||
const body = JSON.parse(res.body);
|
||||
|
||||
expect(body.memoryStats).toBeDefined();
|
||||
expect(typeof body.memoryStats.frameCount).toBe('number');
|
||||
expect(body.memoryStats.frameCount).toBeGreaterThanOrEqual(3); // We created 3 frames
|
||||
expect(typeof body.memoryStats.mindSizeBytes).toBe('number');
|
||||
expect(body.memoryStats.mindSizeBytes).toBeGreaterThan(0);
|
||||
expect(typeof body.memoryStats.embeddingCoverage).toBe('number');
|
||||
expect(body.memoryStats.embeddingCoverage).toBeGreaterThanOrEqual(0);
|
||||
expect(body.memoryStats.embeddingCoverage).toBeLessThanOrEqual(100);
|
||||
});
|
||||
|
||||
it('returns serviceHealth in health response', async () => {
|
||||
const res = await server.inject({ method: 'GET', url: '/health' });
|
||||
expect(res.statusCode).toBe(200);
|
||||
const body = JSON.parse(res.body);
|
||||
|
||||
expect(body.serviceHealth).toBeDefined();
|
||||
expect(typeof body.serviceHealth.watchdogRunning).toBe('boolean');
|
||||
expect(body.serviceHealth.watchdogRunning).toBe(true); // scheduler starts on server build
|
||||
expect(typeof body.serviceHealth.notificationSSEActive).toBe('boolean');
|
||||
});
|
||||
|
||||
it('returns defaultModel in health response', async () => {
|
||||
const res = await server.inject({ method: 'GET', url: '/health' });
|
||||
expect(res.statusCode).toBe(200);
|
||||
const body = JSON.parse(res.body);
|
||||
|
||||
expect(body.defaultModel).toBeDefined();
|
||||
expect(typeof body.defaultModel).toBe('string');
|
||||
expect(body.defaultModel.length).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
it('preserves existing health fields', async () => {
|
||||
const res = await server.inject({ method: 'GET', url: '/health' });
|
||||
expect(res.statusCode).toBe(200);
|
||||
const body = JSON.parse(res.body);
|
||||
|
||||
// All original fields still present
|
||||
expect(['ok', 'degraded', 'unavailable']).toContain(body.status);
|
||||
expect(body.mode).toBe('local');
|
||||
expect(body.timestamp).toBeDefined();
|
||||
expect(body.llm).toBeDefined();
|
||||
expect(body.llm.provider).toBeDefined();
|
||||
expect(body.llm.health).toBeDefined();
|
||||
expect(body.database).toBeDefined();
|
||||
expect(body.database.healthy).toBe(true);
|
||||
});
|
||||
|
||||
it('does not block first health response on slow live provider validation', async () => {
|
||||
server.agentState.llmProvider = {
|
||||
provider: 'anthropic-proxy',
|
||||
health: 'healthy',
|
||||
detail: 'test provider',
|
||||
checkedAt: new Date().toISOString(),
|
||||
};
|
||||
const fetchSpy = vi.spyOn(globalThis, 'fetch').mockImplementation(async () => {
|
||||
await new Promise((resolve) => setTimeout(resolve, 250));
|
||||
return { status: 200 } as Response;
|
||||
});
|
||||
const started = Date.now();
|
||||
try {
|
||||
const res = await server.inject({ method: 'GET', url: '/health' });
|
||||
const elapsed = Date.now() - started;
|
||||
expect(res.statusCode).toBe(200);
|
||||
expect(elapsed).toBeLessThan(100);
|
||||
} finally {
|
||||
fetchSpy.mockRestore();
|
||||
}
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user