feat: add agent API and operational analytics
This commit is contained in:
1 parent
720ea0652a
commit
e8b730b592
65 files changed
+2650
-1214
No files matched your search
@@ -6,6 +6,7 @@ import { afterEach, describe, expect, it } from 'vitest';
|
||||
import type { BrowserClient } from '../src/browser/client.js';
|
||||
import { ActivityLogRepository } from '../src/database/repositories.js';
|
||||
import { WebServer } from '../src/web/server.js';
|
||||
import { OverviewResponseSchema } from '../src/web/agent-api-contracts.js';
|
||||
import { createMockLogger } from './mockLogger.js';
|
||||
import { createTestDb, createTestRepositories, getTestConfig } from './testUtils.js';
|
||||
|
||||
@@ -31,13 +32,25 @@ function createWorkspace(includeReactBuild = true): string {
|
||||
return workspace;
|
||||
}
|
||||
|
||||
async function startServer(options: { debugEnabled?: boolean; includeReactBuild?: boolean } = {}): Promise<RunningServer> {
|
||||
async function startServer(options: {
|
||||
debugEnabled?: boolean;
|
||||
includeReactBuild?: boolean;
|
||||
agentReadToken?: string;
|
||||
agentControlToken?: string;
|
||||
} = {}): Promise<RunningServer> {
|
||||
const database = createTestDb();
|
||||
const repositories = createTestRepositories(database);
|
||||
const workspace = createWorkspace(options.includeReactBuild);
|
||||
const server = new WebServer(
|
||||
0,
|
||||
getTestConfig({ web: { enabled: true, port: 0, authToken: AUTH_TOKEN, debugEnabled: options.debugEnabled ?? false } }),
|
||||
getTestConfig({ web: {
|
||||
enabled: true,
|
||||
port: 0,
|
||||
authToken: AUTH_TOKEN,
|
||||
debugEnabled: options.debugEnabled ?? false,
|
||||
agentReadToken: options.agentReadToken,
|
||||
agentControlToken: options.agentControlToken,
|
||||
} }),
|
||||
repositories.postsRepo,
|
||||
repositories.messagesRepo,
|
||||
repositories.successfulMessagesRepo,
|
||||
@@ -110,4 +123,44 @@ describe('authenticated React dashboard cutover', () => {
|
||||
expect(upload.status).toBe(404);
|
||||
});
|
||||
|
||||
it('exposes public liveness and scoped discovery/control APIs', async () => {
|
||||
const running = await startServer({
|
||||
agentReadToken: 'agent-read-token',
|
||||
agentControlToken: 'agent-control-token',
|
||||
});
|
||||
runningServers.push(running);
|
||||
running.server.setAccountAuth({
|
||||
isFullyAuthenticated: () => true,
|
||||
} as never);
|
||||
|
||||
const health = await fetch(`${running.baseUrl}/healthz`);
|
||||
const openapi = await fetch(`${running.baseUrl}/api/openapi.json`, {
|
||||
headers: { Authorization: 'Bearer agent-read-token' },
|
||||
});
|
||||
const overview = await fetch(`${running.baseUrl}/api/v1/overview`, {
|
||||
headers: { Authorization: 'Bearer agent-read-token' },
|
||||
});
|
||||
const forbiddenStart = await fetch(`${running.baseUrl}/api/v1/control/start`, {
|
||||
method: 'POST',
|
||||
headers: { Authorization: 'Bearer agent-read-token' },
|
||||
});
|
||||
const allowedStart = await fetch(`${running.baseUrl}/api/v1/control/start`, {
|
||||
method: 'POST',
|
||||
headers: { Authorization: 'Bearer agent-control-token' },
|
||||
});
|
||||
const allowedStop = await fetch(`${running.baseUrl}/api/v1/control/stop`, {
|
||||
method: 'POST',
|
||||
headers: { Authorization: 'Bearer agent-control-token' },
|
||||
});
|
||||
|
||||
expect(health.status).toBe(200);
|
||||
expect(await health.json()).toMatchObject({ status: 'ok', service: 'reddit-bot' });
|
||||
expect(openapi.status).toBe(200);
|
||||
expect(await openapi.json()).toMatchObject({ openapi: '3.1.0' });
|
||||
expect(OverviewResponseSchema.safeParse(await overview.json()).success).toBe(true);
|
||||
expect(forbiddenStart.status).toBe(403);
|
||||
expect(await allowedStart.json()).toMatchObject({ success: true, running: true, changed: true });
|
||||
expect(await allowedStop.json()).toMatchObject({ success: true, running: false, changed: true });
|
||||
});
|
||||
|
||||
});
|
||||
Reference in new issue
Block a user