Files
waggle-os/packages/server/tests/routes/health.test.ts
Oleg Maslov b20b138fe4 moving
2026-09-02 10:14:22 +02:00

145 lines
4.7 KiB
TypeScript

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 } from '@waggle/core';
import { buildLocalServer } from '../../src/local/index.js';
import type { FastifyInstance } from 'fastify';
describe('Health Endpoint', () => {
let server: FastifyInstance;
let tmpDir: string;
beforeAll(async () => {
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'waggle-health-test-'));
const personalPath = path.join(tmpDir, 'personal.mind');
const mind = new MindDB(personalPath);
mind.close();
server = await buildLocalServer({
dataDir: tmpDir,
port: 38123,
instanceId: 'desktop-instance-test',
});
});
afterAll(async () => {
await server.close();
fs.rmSync(tmpDir, { recursive: true, force: true });
});
it('returns structured health with llm and database status', async () => {
const res = await server.inject({ method: 'GET', url: '/health' });
expect(res.statusCode).toBe(200);
const body = JSON.parse(res.payload);
expect(body.mode).toBe('local');
expect(body.timestamp).toBeTruthy();
// LLM section
expect(body.llm).toBeDefined();
expect(body.llm.provider).toMatch(/^(litellm|anthropic-proxy|ollama)$/);
expect(body.llm.health).toMatch(/^(healthy|degraded|unavailable)$/);
expect(body.llm.detail).toBeTruthy();
expect(body.llm.checkedAt).toBeTruthy();
// Database section
expect(body.database).toBeDefined();
expect(body.database.healthy).toBe(true);
});
it('reports the desktop launch identity used for sidecar ownership checks', async () => {
vi.stubEnv('WAGGLE_INSTANCE_ID', 'mutated-after-launch');
try {
const res = await server.inject({ method: 'GET', url: '/health' });
expect(res.statusCode).toBe(200);
expect(JSON.parse(res.payload)).toMatchObject({
instanceId: 'desktop-instance-test',
port: 38123,
});
} finally {
vi.unstubAllEnvs();
}
});
it('overall status reflects LLM health', async () => {
// Default: llmProvider was set to unavailable (no init via startService)
const res = await server.inject({ method: 'GET', url: '/health' });
const body = JSON.parse(res.payload);
// Since we didn't go through startService, llmProvider defaults to unavailable
// Overall status should be 'unavailable' or 'degraded', not 'ok'
expect(body.status).not.toBe('ok');
});
it('reports healthy when LLM provider is marked healthy', async () => {
// Simulate a healthy provider
server.agentState.llmProvider = {
provider: 'litellm',
health: 'healthy',
detail: 'LiteLLM on port 4000',
checkedAt: new Date().toISOString(),
};
const res = await server.inject({ method: 'GET', url: '/health' });
const body = JSON.parse(res.payload);
expect(body.status).toBe('ok');
expect(body.llm.provider).toBe('litellm');
expect(body.llm.health).toBe('healthy');
expect(body.llm.reachable).toBe(true);
});
it('reports a healthy resolved provider as reachable even when the offline probe is stale', async () => {
const offlineProbe = server.offlineManager as unknown as { _offline: boolean };
const previousOffline = offlineProbe._offline;
offlineProbe._offline = true;
server.agentState.llmProvider = {
provider: 'ollama',
health: 'healthy',
detail: 'Local Ollama model',
checkedAt: new Date().toISOString(),
};
try {
const res = await server.inject({ method: 'GET', url: '/health' });
const body = JSON.parse(res.payload);
expect(body.status).toBe('ok');
expect(body.llm.health).toBe('healthy');
expect(body.llm.reachable).toBe(true);
} finally {
offlineProbe._offline = previousOffline;
}
});
it('reports degraded when LLM provider is configured but not verified', async () => {
server.agentState.llmProvider = {
provider: 'anthropic-proxy',
health: 'degraded',
detail: 'Built-in Anthropic proxy (no API key)',
checkedAt: new Date().toISOString(),
};
const res = await server.inject({ method: 'GET', url: '/health' });
const body = JSON.parse(res.payload);
expect(body.status).toBe('degraded');
expect(body.llm.health).toBe('degraded');
});
it('reports unavailable when no LLM path works', async () => {
server.agentState.llmProvider = {
provider: 'anthropic-proxy',
health: 'unavailable',
detail: 'No working LLM path',
checkedAt: new Date().toISOString(),
};
const res = await server.inject({ method: 'GET', url: '/health' });
const body = JSON.parse(res.payload);
expect(body.status).toBe('unavailable');
});
});