mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-10-02 13:39:41 +02:00
Consolidate duplicated MockSession/MockStateStore into test/mocks/, migrate respawn tests to shared mocks, and add 58 route tests for session, system, and respawn endpoints using Fastify app.inject(). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
167 lines
5.4 KiB
TypeScript
167 lines
5.4 KiB
TypeScript
/**
|
|
* @fileoverview Tests for system route handlers.
|
|
*
|
|
* Uses app.inject() — no real HTTP ports needed.
|
|
* Port: N/A (app.inject doesn't open ports)
|
|
*
|
|
* Note: Some system routes access singleton modules (subagentWatcher, imageWatcher)
|
|
* directly. Those routes are tested lightly here since we can't easily mock singletons
|
|
* without vi.mock(). Focus is on routes that use the port context.
|
|
*/
|
|
|
|
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
import { createRouteTestHarness, type RouteTestHarness } from './_route-test-utils.js';
|
|
import { registerSystemRoutes } from '../../src/web/routes/system-routes.js';
|
|
|
|
describe('system-routes', () => {
|
|
let harness: RouteTestHarness;
|
|
|
|
beforeEach(async () => {
|
|
harness = await createRouteTestHarness(registerSystemRoutes);
|
|
});
|
|
|
|
afterEach(async () => {
|
|
await harness.app.close();
|
|
});
|
|
|
|
// ========== GET /api/status ==========
|
|
|
|
describe('GET /api/status', () => {
|
|
it('returns server status', async () => {
|
|
const res = await harness.app.inject({ method: 'GET', url: '/api/status' });
|
|
expect(res.statusCode).toBe(200);
|
|
expect(harness.ctx.getLightState).toHaveBeenCalled();
|
|
});
|
|
});
|
|
|
|
// ========== GET /api/config ==========
|
|
|
|
describe('GET /api/config', () => {
|
|
it('returns config', async () => {
|
|
const res = await harness.app.inject({ method: 'GET', url: '/api/config' });
|
|
expect(res.statusCode).toBe(200);
|
|
const body = JSON.parse(res.body);
|
|
expect(body.success).toBe(true);
|
|
expect(body.config).toBeDefined();
|
|
});
|
|
});
|
|
|
|
// ========== PUT /api/config ==========
|
|
|
|
describe('PUT /api/config', () => {
|
|
it('updates config with valid payload', async () => {
|
|
const res = await harness.app.inject({
|
|
method: 'PUT',
|
|
url: '/api/config',
|
|
payload: { maxConcurrentSessions: 10 },
|
|
});
|
|
expect(res.statusCode).toBe(200);
|
|
const body = JSON.parse(res.body);
|
|
expect(body.success).toBe(true);
|
|
expect(harness.ctx.store.setConfig).toHaveBeenCalled();
|
|
});
|
|
|
|
it('rejects unknown config fields (strict schema)', async () => {
|
|
const res = await harness.app.inject({
|
|
method: 'PUT',
|
|
url: '/api/config',
|
|
payload: { unknownField: 'invalid' },
|
|
});
|
|
expect(res.statusCode).toBe(200);
|
|
const body = JSON.parse(res.body);
|
|
expect(body.success).toBe(false);
|
|
});
|
|
});
|
|
|
|
// ========== GET /api/stats ==========
|
|
|
|
describe('GET /api/stats', () => {
|
|
it('returns aggregate stats', async () => {
|
|
const res = await harness.app.inject({ method: 'GET', url: '/api/stats' });
|
|
expect(res.statusCode).toBe(200);
|
|
const body = JSON.parse(res.body);
|
|
expect(body.success).toBe(true);
|
|
expect(body.stats).toBeDefined();
|
|
});
|
|
});
|
|
|
|
// ========== GET /api/token-stats ==========
|
|
|
|
describe('GET /api/token-stats', () => {
|
|
it('returns daily and aggregate token stats', async () => {
|
|
const res = await harness.app.inject({ method: 'GET', url: '/api/token-stats' });
|
|
expect(res.statusCode).toBe(200);
|
|
const body = JSON.parse(res.body);
|
|
expect(body.success).toBe(true);
|
|
expect(body.daily).toBeDefined();
|
|
expect(body.totals).toBeDefined();
|
|
});
|
|
});
|
|
|
|
// ========== GET /api/debug/memory ==========
|
|
|
|
describe('GET /api/debug/memory', () => {
|
|
it('returns memory and map usage info', async () => {
|
|
const res = await harness.app.inject({ method: 'GET', url: '/api/debug/memory' });
|
|
expect(res.statusCode).toBe(200);
|
|
const body = JSON.parse(res.body);
|
|
expect(body.memory).toBeDefined();
|
|
expect(body.memory.rssMB).toBeGreaterThan(0);
|
|
expect(body.mapSizes).toBeDefined();
|
|
expect(body.uptime).toBeDefined();
|
|
});
|
|
});
|
|
|
|
// ========== GET /api/system/stats ==========
|
|
|
|
describe('GET /api/system/stats', () => {
|
|
it('returns CPU and memory stats', async () => {
|
|
const res = await harness.app.inject({ method: 'GET', url: '/api/system/stats' });
|
|
expect(res.statusCode).toBe(200);
|
|
const body = JSON.parse(res.body);
|
|
expect(body).toHaveProperty('cpu');
|
|
expect(body).toHaveProperty('memory');
|
|
});
|
|
});
|
|
|
|
// ========== POST /api/cleanup-state ==========
|
|
|
|
describe('POST /api/cleanup-state', () => {
|
|
it('cleans up stale session state', async () => {
|
|
const res = await harness.app.inject({ method: 'POST', url: '/api/cleanup-state' });
|
|
expect(res.statusCode).toBe(200);
|
|
const body = JSON.parse(res.body);
|
|
expect(body.success).toBe(true);
|
|
expect(body.cleanedSessions).toBe(0);
|
|
expect(harness.ctx.store.cleanupStaleSessions).toHaveBeenCalled();
|
|
});
|
|
});
|
|
|
|
// ========== GET /api/subagents ==========
|
|
|
|
describe('GET /api/subagents', () => {
|
|
it('returns subagent list', async () => {
|
|
const res = await harness.app.inject({ method: 'GET', url: '/api/subagents' });
|
|
expect(res.statusCode).toBe(200);
|
|
const body = JSON.parse(res.body);
|
|
expect(body.success).toBe(true);
|
|
expect(body.data).toBeDefined();
|
|
});
|
|
});
|
|
|
|
// ========== POST /api/auth/revoke ==========
|
|
|
|
describe('POST /api/auth/revoke', () => {
|
|
it('returns success even without auth sessions', async () => {
|
|
const res = await harness.app.inject({
|
|
method: 'POST',
|
|
url: '/api/auth/revoke',
|
|
payload: {},
|
|
});
|
|
expect(res.statusCode).toBe(200);
|
|
const body = JSON.parse(res.body);
|
|
expect(body.success).toBe(true);
|
|
});
|
|
});
|
|
});
|