mirror of
https://github.com/AutoMaker-Org/automaker.git
synced 2026-02-02 08:33:36 +00:00
feat: introduce debug panel for performance monitoring
- Added a debug panel to monitor server performance, including memory and CPU metrics. - Implemented debug services for real-time tracking of processes and performance metrics. - Created API endpoints for metrics collection and process management. - Enhanced UI components for displaying metrics and process statuses. - Updated documentation to include new debug API details. This feature is intended for development use and can be toggled with the `ENABLE_DEBUG_PANEL` environment variable.
This commit is contained in:
318
apps/server/tests/unit/routes/debug/metrics.test.ts
Normal file
318
apps/server/tests/unit/routes/debug/metrics.test.ts
Normal file
@@ -0,0 +1,318 @@
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||
import type { Request, Response } from 'express';
|
||||
import {
|
||||
createGetMetricsHandler,
|
||||
createStartMetricsHandler,
|
||||
createStopMetricsHandler,
|
||||
createForceGCHandler,
|
||||
createClearHistoryHandler,
|
||||
} from '@/routes/debug/routes/metrics.js';
|
||||
import type { PerformanceMonitorService } from '@/services/performance-monitor-service.js';
|
||||
import type { DebugMetricsConfig, DebugMetricsSnapshot } from '@automaker/types';
|
||||
import { DEFAULT_DEBUG_METRICS_CONFIG } from '@automaker/types';
|
||||
|
||||
describe('Debug Metrics Routes', () => {
|
||||
let mockPerformanceMonitor: Partial<PerformanceMonitorService>;
|
||||
let mockReq: Partial<Request>;
|
||||
let mockRes: Partial<Response>;
|
||||
let jsonFn: ReturnType<typeof vi.fn>;
|
||||
let statusFn: ReturnType<typeof vi.fn>;
|
||||
|
||||
const mockConfig: DebugMetricsConfig = { ...DEFAULT_DEBUG_METRICS_CONFIG };
|
||||
const mockSnapshot: DebugMetricsSnapshot = {
|
||||
timestamp: Date.now(),
|
||||
memory: {
|
||||
timestamp: Date.now(),
|
||||
server: {
|
||||
heapTotal: 100 * 1024 * 1024,
|
||||
heapUsed: 50 * 1024 * 1024,
|
||||
external: 5 * 1024 * 1024,
|
||||
rss: 150 * 1024 * 1024,
|
||||
arrayBuffers: 1 * 1024 * 1024,
|
||||
},
|
||||
},
|
||||
cpu: {
|
||||
timestamp: Date.now(),
|
||||
server: {
|
||||
percentage: 25.5,
|
||||
user: 1000,
|
||||
system: 500,
|
||||
},
|
||||
eventLoopLag: 5,
|
||||
},
|
||||
processes: [],
|
||||
processSummary: {
|
||||
total: 0,
|
||||
running: 0,
|
||||
idle: 0,
|
||||
stopped: 0,
|
||||
errored: 0,
|
||||
byType: { agent: 0, cli: 0, terminal: 0, worker: 0 },
|
||||
},
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
jsonFn = vi.fn();
|
||||
statusFn = vi.fn(() => ({ json: jsonFn }));
|
||||
|
||||
mockPerformanceMonitor = {
|
||||
getLatestSnapshot: vi.fn(() => mockSnapshot),
|
||||
getConfig: vi.fn(() => mockConfig),
|
||||
isActive: vi.fn(() => true),
|
||||
start: vi.fn(),
|
||||
stop: vi.fn(),
|
||||
updateConfig: vi.fn(),
|
||||
forceGC: vi.fn(() => true),
|
||||
clearHistory: vi.fn(),
|
||||
};
|
||||
|
||||
mockReq = {
|
||||
body: {},
|
||||
query: {},
|
||||
params: {},
|
||||
};
|
||||
|
||||
mockRes = {
|
||||
json: jsonFn,
|
||||
status: statusFn,
|
||||
};
|
||||
});
|
||||
|
||||
describe('GET /api/debug/metrics', () => {
|
||||
it('should return current metrics snapshot', () => {
|
||||
const handler = createGetMetricsHandler(mockPerformanceMonitor as PerformanceMonitorService);
|
||||
handler(mockReq as Request, mockRes as Response);
|
||||
|
||||
expect(jsonFn).toHaveBeenCalledWith({
|
||||
active: true,
|
||||
config: mockConfig,
|
||||
snapshot: mockSnapshot,
|
||||
});
|
||||
});
|
||||
|
||||
it('should return undefined snapshot when no data available', () => {
|
||||
(mockPerformanceMonitor.getLatestSnapshot as ReturnType<typeof vi.fn>).mockReturnValue(null);
|
||||
|
||||
const handler = createGetMetricsHandler(mockPerformanceMonitor as PerformanceMonitorService);
|
||||
handler(mockReq as Request, mockRes as Response);
|
||||
|
||||
expect(jsonFn).toHaveBeenCalledWith({
|
||||
active: true,
|
||||
config: mockConfig,
|
||||
snapshot: undefined,
|
||||
});
|
||||
});
|
||||
|
||||
it('should return active status correctly', () => {
|
||||
(mockPerformanceMonitor.isActive as ReturnType<typeof vi.fn>).mockReturnValue(false);
|
||||
|
||||
const handler = createGetMetricsHandler(mockPerformanceMonitor as PerformanceMonitorService);
|
||||
handler(mockReq as Request, mockRes as Response);
|
||||
|
||||
expect(jsonFn).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
active: false,
|
||||
})
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('POST /api/debug/metrics/start', () => {
|
||||
it('should start metrics collection', () => {
|
||||
const handler = createStartMetricsHandler(
|
||||
mockPerformanceMonitor as PerformanceMonitorService
|
||||
);
|
||||
handler(mockReq as Request, mockRes as Response);
|
||||
|
||||
expect(mockPerformanceMonitor.start).toHaveBeenCalled();
|
||||
expect(jsonFn).toHaveBeenCalledWith({
|
||||
active: true,
|
||||
config: mockConfig,
|
||||
});
|
||||
});
|
||||
|
||||
it('should apply config overrides when provided', () => {
|
||||
mockReq.body = {
|
||||
config: {
|
||||
collectionInterval: 5000,
|
||||
maxDataPoints: 500,
|
||||
},
|
||||
};
|
||||
|
||||
const handler = createStartMetricsHandler(
|
||||
mockPerformanceMonitor as PerformanceMonitorService
|
||||
);
|
||||
handler(mockReq as Request, mockRes as Response);
|
||||
|
||||
expect(mockPerformanceMonitor.updateConfig).toHaveBeenCalledWith({
|
||||
collectionInterval: 5000,
|
||||
maxDataPoints: 500,
|
||||
});
|
||||
});
|
||||
|
||||
it('should sanitize config values - clamp collectionInterval to min 100ms', () => {
|
||||
mockReq.body = {
|
||||
config: {
|
||||
collectionInterval: 10, // Below minimum of 100ms
|
||||
},
|
||||
};
|
||||
|
||||
const handler = createStartMetricsHandler(
|
||||
mockPerformanceMonitor as PerformanceMonitorService
|
||||
);
|
||||
handler(mockReq as Request, mockRes as Response);
|
||||
|
||||
expect(mockPerformanceMonitor.updateConfig).toHaveBeenCalledWith({
|
||||
collectionInterval: 100,
|
||||
});
|
||||
});
|
||||
|
||||
it('should sanitize config values - clamp collectionInterval to max 60000ms', () => {
|
||||
mockReq.body = {
|
||||
config: {
|
||||
collectionInterval: 100000, // Above maximum of 60000ms
|
||||
},
|
||||
};
|
||||
|
||||
const handler = createStartMetricsHandler(
|
||||
mockPerformanceMonitor as PerformanceMonitorService
|
||||
);
|
||||
handler(mockReq as Request, mockRes as Response);
|
||||
|
||||
expect(mockPerformanceMonitor.updateConfig).toHaveBeenCalledWith({
|
||||
collectionInterval: 60000,
|
||||
});
|
||||
});
|
||||
|
||||
it('should sanitize config values - clamp maxDataPoints to bounds', () => {
|
||||
mockReq.body = {
|
||||
config: {
|
||||
maxDataPoints: 5, // Below minimum of 10
|
||||
},
|
||||
};
|
||||
|
||||
const handler = createStartMetricsHandler(
|
||||
mockPerformanceMonitor as PerformanceMonitorService
|
||||
);
|
||||
handler(mockReq as Request, mockRes as Response);
|
||||
|
||||
expect(mockPerformanceMonitor.updateConfig).toHaveBeenCalledWith({
|
||||
maxDataPoints: 10,
|
||||
});
|
||||
});
|
||||
|
||||
it('should sanitize config values - clamp maxDataPoints to max', () => {
|
||||
mockReq.body = {
|
||||
config: {
|
||||
maxDataPoints: 50000, // Above maximum of 10000
|
||||
},
|
||||
};
|
||||
|
||||
const handler = createStartMetricsHandler(
|
||||
mockPerformanceMonitor as PerformanceMonitorService
|
||||
);
|
||||
handler(mockReq as Request, mockRes as Response);
|
||||
|
||||
expect(mockPerformanceMonitor.updateConfig).toHaveBeenCalledWith({
|
||||
maxDataPoints: 10000,
|
||||
});
|
||||
});
|
||||
|
||||
it('should ignore non-object config', () => {
|
||||
mockReq.body = {
|
||||
config: 'not-an-object',
|
||||
};
|
||||
|
||||
const handler = createStartMetricsHandler(
|
||||
mockPerformanceMonitor as PerformanceMonitorService
|
||||
);
|
||||
handler(mockReq as Request, mockRes as Response);
|
||||
|
||||
expect(mockPerformanceMonitor.updateConfig).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should ignore empty config object', () => {
|
||||
mockReq.body = {
|
||||
config: {},
|
||||
};
|
||||
|
||||
const handler = createStartMetricsHandler(
|
||||
mockPerformanceMonitor as PerformanceMonitorService
|
||||
);
|
||||
handler(mockReq as Request, mockRes as Response);
|
||||
|
||||
expect(mockPerformanceMonitor.updateConfig).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should only accept boolean flags as actual booleans', () => {
|
||||
mockReq.body = {
|
||||
config: {
|
||||
memoryEnabled: 'true', // String, not boolean - should be ignored
|
||||
cpuEnabled: true, // Boolean - should be accepted
|
||||
},
|
||||
};
|
||||
|
||||
const handler = createStartMetricsHandler(
|
||||
mockPerformanceMonitor as PerformanceMonitorService
|
||||
);
|
||||
handler(mockReq as Request, mockRes as Response);
|
||||
|
||||
expect(mockPerformanceMonitor.updateConfig).toHaveBeenCalledWith({
|
||||
cpuEnabled: true,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('POST /api/debug/metrics/stop', () => {
|
||||
it('should stop metrics collection', () => {
|
||||
const handler = createStopMetricsHandler(mockPerformanceMonitor as PerformanceMonitorService);
|
||||
handler(mockReq as Request, mockRes as Response);
|
||||
|
||||
expect(mockPerformanceMonitor.stop).toHaveBeenCalled();
|
||||
expect(jsonFn).toHaveBeenCalledWith({
|
||||
active: false,
|
||||
config: mockConfig,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('POST /api/debug/metrics/gc', () => {
|
||||
it('should trigger garbage collection when available', () => {
|
||||
const handler = createForceGCHandler(mockPerformanceMonitor as PerformanceMonitorService);
|
||||
handler(mockReq as Request, mockRes as Response);
|
||||
|
||||
expect(mockPerformanceMonitor.forceGC).toHaveBeenCalled();
|
||||
expect(jsonFn).toHaveBeenCalledWith({
|
||||
success: true,
|
||||
message: 'Garbage collection triggered',
|
||||
});
|
||||
});
|
||||
|
||||
it('should report when garbage collection is not available', () => {
|
||||
(mockPerformanceMonitor.forceGC as ReturnType<typeof vi.fn>).mockReturnValue(false);
|
||||
|
||||
const handler = createForceGCHandler(mockPerformanceMonitor as PerformanceMonitorService);
|
||||
handler(mockReq as Request, mockRes as Response);
|
||||
|
||||
expect(jsonFn).toHaveBeenCalledWith({
|
||||
success: false,
|
||||
message: 'Garbage collection not available (start Node.js with --expose-gc flag)',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('POST /api/debug/metrics/clear', () => {
|
||||
it('should clear metrics history', () => {
|
||||
const handler = createClearHistoryHandler(
|
||||
mockPerformanceMonitor as PerformanceMonitorService
|
||||
);
|
||||
handler(mockReq as Request, mockRes as Response);
|
||||
|
||||
expect(mockPerformanceMonitor.clearHistory).toHaveBeenCalled();
|
||||
expect(jsonFn).toHaveBeenCalledWith({
|
||||
success: true,
|
||||
message: 'Metrics history cleared',
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
293
apps/server/tests/unit/routes/debug/processes.test.ts
Normal file
293
apps/server/tests/unit/routes/debug/processes.test.ts
Normal file
@@ -0,0 +1,293 @@
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||
import type { Request, Response } from 'express';
|
||||
import {
|
||||
createGetProcessesHandler,
|
||||
createGetProcessHandler,
|
||||
createGetSummaryHandler,
|
||||
} from '@/routes/debug/routes/processes.js';
|
||||
import type { ProcessRegistryService } from '@/services/process-registry-service.js';
|
||||
import type { TrackedProcess, ProcessSummary } from '@automaker/types';
|
||||
|
||||
describe('Debug Processes Routes', () => {
|
||||
let mockProcessRegistry: Partial<ProcessRegistryService>;
|
||||
let mockReq: Partial<Request>;
|
||||
let mockRes: Partial<Response>;
|
||||
let jsonFn: ReturnType<typeof vi.fn>;
|
||||
let statusFn: ReturnType<typeof vi.fn>;
|
||||
|
||||
const mockProcesses: TrackedProcess[] = [
|
||||
{
|
||||
id: 'process-1',
|
||||
pid: 1234,
|
||||
type: 'agent',
|
||||
name: 'Agent 1',
|
||||
status: 'running',
|
||||
startedAt: Date.now() - 60000,
|
||||
featureId: 'feature-1',
|
||||
sessionId: 'session-1',
|
||||
},
|
||||
{
|
||||
id: 'process-2',
|
||||
pid: 5678,
|
||||
type: 'terminal',
|
||||
name: 'Terminal 1',
|
||||
status: 'idle',
|
||||
startedAt: Date.now() - 30000,
|
||||
sessionId: 'session-1',
|
||||
},
|
||||
{
|
||||
id: 'process-3',
|
||||
pid: 9012,
|
||||
type: 'cli',
|
||||
name: 'CLI 1',
|
||||
status: 'stopped',
|
||||
startedAt: Date.now() - 120000,
|
||||
stoppedAt: Date.now() - 60000,
|
||||
exitCode: 0,
|
||||
},
|
||||
];
|
||||
|
||||
const mockSummary: ProcessSummary = {
|
||||
total: 3,
|
||||
running: 1,
|
||||
idle: 1,
|
||||
stopped: 1,
|
||||
errored: 0,
|
||||
byType: {
|
||||
agent: 1,
|
||||
cli: 1,
|
||||
terminal: 1,
|
||||
worker: 0,
|
||||
},
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
jsonFn = vi.fn();
|
||||
statusFn = vi.fn(() => ({ json: jsonFn }));
|
||||
|
||||
mockProcessRegistry = {
|
||||
getProcesses: vi.fn(() => mockProcesses),
|
||||
getProcess: vi.fn((id: string) => mockProcesses.find((p) => p.id === id)),
|
||||
getProcessSummary: vi.fn(() => mockSummary),
|
||||
};
|
||||
|
||||
mockReq = {
|
||||
body: {},
|
||||
query: {},
|
||||
params: {},
|
||||
};
|
||||
|
||||
mockRes = {
|
||||
json: jsonFn,
|
||||
status: statusFn,
|
||||
};
|
||||
});
|
||||
|
||||
describe('GET /api/debug/processes', () => {
|
||||
it('should return list of processes with summary', () => {
|
||||
const handler = createGetProcessesHandler(mockProcessRegistry as ProcessRegistryService);
|
||||
handler(mockReq as Request, mockRes as Response);
|
||||
|
||||
expect(mockProcessRegistry.getProcesses).toHaveBeenCalled();
|
||||
expect(mockProcessRegistry.getProcessSummary).toHaveBeenCalled();
|
||||
expect(jsonFn).toHaveBeenCalledWith({
|
||||
processes: mockProcesses,
|
||||
summary: mockSummary,
|
||||
});
|
||||
});
|
||||
|
||||
it('should pass type filter to service', () => {
|
||||
mockReq.query = { type: 'agent' };
|
||||
|
||||
const handler = createGetProcessesHandler(mockProcessRegistry as ProcessRegistryService);
|
||||
handler(mockReq as Request, mockRes as Response);
|
||||
|
||||
expect(mockProcessRegistry.getProcesses).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
type: 'agent',
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it('should pass status filter to service', () => {
|
||||
mockReq.query = { status: 'running' };
|
||||
|
||||
const handler = createGetProcessesHandler(mockProcessRegistry as ProcessRegistryService);
|
||||
handler(mockReq as Request, mockRes as Response);
|
||||
|
||||
expect(mockProcessRegistry.getProcesses).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
status: 'running',
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it('should pass includeStopped flag when set to "true"', () => {
|
||||
mockReq.query = { includeStopped: 'true' };
|
||||
|
||||
const handler = createGetProcessesHandler(mockProcessRegistry as ProcessRegistryService);
|
||||
handler(mockReq as Request, mockRes as Response);
|
||||
|
||||
expect(mockProcessRegistry.getProcesses).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
includeStopped: true,
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it('should not pass includeStopped when not "true"', () => {
|
||||
mockReq.query = { includeStopped: 'false' };
|
||||
|
||||
const handler = createGetProcessesHandler(mockProcessRegistry as ProcessRegistryService);
|
||||
handler(mockReq as Request, mockRes as Response);
|
||||
|
||||
expect(mockProcessRegistry.getProcesses).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
includeStopped: undefined,
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it('should pass sessionId filter to service', () => {
|
||||
mockReq.query = { sessionId: 'session-1' };
|
||||
|
||||
const handler = createGetProcessesHandler(mockProcessRegistry as ProcessRegistryService);
|
||||
handler(mockReq as Request, mockRes as Response);
|
||||
|
||||
expect(mockProcessRegistry.getProcesses).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
sessionId: 'session-1',
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it('should pass featureId filter to service', () => {
|
||||
mockReq.query = { featureId: 'feature-1' };
|
||||
|
||||
const handler = createGetProcessesHandler(mockProcessRegistry as ProcessRegistryService);
|
||||
handler(mockReq as Request, mockRes as Response);
|
||||
|
||||
expect(mockProcessRegistry.getProcesses).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
featureId: 'feature-1',
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it('should handle multiple filters', () => {
|
||||
mockReq.query = {
|
||||
type: 'agent',
|
||||
status: 'running',
|
||||
sessionId: 'session-1',
|
||||
includeStopped: 'true',
|
||||
};
|
||||
|
||||
const handler = createGetProcessesHandler(mockProcessRegistry as ProcessRegistryService);
|
||||
handler(mockReq as Request, mockRes as Response);
|
||||
|
||||
expect(mockProcessRegistry.getProcesses).toHaveBeenCalledWith({
|
||||
type: 'agent',
|
||||
status: 'running',
|
||||
sessionId: 'session-1',
|
||||
includeStopped: true,
|
||||
featureId: undefined,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('GET /api/debug/processes/:id', () => {
|
||||
it('should return a specific process by ID', () => {
|
||||
mockReq.params = { id: 'process-1' };
|
||||
|
||||
const handler = createGetProcessHandler(mockProcessRegistry as ProcessRegistryService);
|
||||
handler(mockReq as Request, mockRes as Response);
|
||||
|
||||
expect(mockProcessRegistry.getProcess).toHaveBeenCalledWith('process-1');
|
||||
expect(jsonFn).toHaveBeenCalledWith(mockProcesses[0]);
|
||||
});
|
||||
|
||||
it('should return 404 for non-existent process', () => {
|
||||
mockReq.params = { id: 'non-existent' };
|
||||
(mockProcessRegistry.getProcess as ReturnType<typeof vi.fn>).mockReturnValue(undefined);
|
||||
|
||||
const handler = createGetProcessHandler(mockProcessRegistry as ProcessRegistryService);
|
||||
handler(mockReq as Request, mockRes as Response);
|
||||
|
||||
expect(statusFn).toHaveBeenCalledWith(404);
|
||||
expect(jsonFn).toHaveBeenCalledWith({
|
||||
error: 'Process not found',
|
||||
id: 'non-existent',
|
||||
});
|
||||
});
|
||||
|
||||
it('should return 400 for empty process ID', () => {
|
||||
mockReq.params = { id: '' };
|
||||
|
||||
const handler = createGetProcessHandler(mockProcessRegistry as ProcessRegistryService);
|
||||
handler(mockReq as Request, mockRes as Response);
|
||||
|
||||
expect(statusFn).toHaveBeenCalledWith(400);
|
||||
expect(jsonFn).toHaveBeenCalledWith({
|
||||
error: 'Invalid process ID format',
|
||||
});
|
||||
});
|
||||
|
||||
it('should return 400 for process ID exceeding max length', () => {
|
||||
mockReq.params = { id: 'a'.repeat(257) };
|
||||
|
||||
const handler = createGetProcessHandler(mockProcessRegistry as ProcessRegistryService);
|
||||
handler(mockReq as Request, mockRes as Response);
|
||||
|
||||
expect(statusFn).toHaveBeenCalledWith(400);
|
||||
expect(jsonFn).toHaveBeenCalledWith({
|
||||
error: 'Invalid process ID format',
|
||||
});
|
||||
});
|
||||
|
||||
it('should accept process ID at max length', () => {
|
||||
mockReq.params = { id: 'a'.repeat(256) };
|
||||
(mockProcessRegistry.getProcess as ReturnType<typeof vi.fn>).mockReturnValue(undefined);
|
||||
|
||||
const handler = createGetProcessHandler(mockProcessRegistry as ProcessRegistryService);
|
||||
handler(mockReq as Request, mockRes as Response);
|
||||
|
||||
// Should pass validation but process not found
|
||||
expect(statusFn).toHaveBeenCalledWith(404);
|
||||
});
|
||||
});
|
||||
|
||||
describe('GET /api/debug/processes/summary', () => {
|
||||
it('should return process summary', () => {
|
||||
const handler = createGetSummaryHandler(mockProcessRegistry as ProcessRegistryService);
|
||||
handler(mockReq as Request, mockRes as Response);
|
||||
|
||||
expect(mockProcessRegistry.getProcessSummary).toHaveBeenCalled();
|
||||
expect(jsonFn).toHaveBeenCalledWith(mockSummary);
|
||||
});
|
||||
|
||||
it('should return correct counts', () => {
|
||||
const customSummary: ProcessSummary = {
|
||||
total: 10,
|
||||
running: 5,
|
||||
idle: 2,
|
||||
stopped: 2,
|
||||
errored: 1,
|
||||
byType: {
|
||||
agent: 4,
|
||||
cli: 3,
|
||||
terminal: 2,
|
||||
worker: 1,
|
||||
},
|
||||
};
|
||||
|
||||
(mockProcessRegistry.getProcessSummary as ReturnType<typeof vi.fn>).mockReturnValue(
|
||||
customSummary
|
||||
);
|
||||
|
||||
const handler = createGetSummaryHandler(mockProcessRegistry as ProcessRegistryService);
|
||||
handler(mockReq as Request, mockRes as Response);
|
||||
|
||||
expect(jsonFn).toHaveBeenCalledWith(customSummary);
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user