mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-10-06 07:29:42 +02:00
test: repair route harness error rendering + stop AI-checker spawning real processes in tests (#97)
* fix(test): share route error handler with test harness + fix stale assertions
The route test harness built a bare Fastify instance without the production
global error handler (server.ts), so structured errors thrown by route helpers
(findSessionOrFail → 404, parseBody → 400) fell through to Fastify's default
handler — yielding a `{statusCode,error,message}` body instead of the
`{success:false,...}` shape, and the tests asserted the old implicit-200
behavior. 51 route tests across 7 files were red.
- Extract the handler into src/web/route-error-handler.ts; server.ts and the
test harness now install the identical handler (single source of truth).
- Correct stale assertions across route test files: throw-based error paths
now assert 404 (unknown session) / 400 (invalid body); genuine in-handler
`return createErrorResponse(...)` paths (200 + success:false) left untouched.
- Reformat a few test files prettier flagged (pre-existing non-compliance).
Route suite: 307/307 passing (was 256/307). No production behavior change.
* test(respawn): mock child_process so AI checker never spawns real processes
respawn-controller.test.ts drives the AI idle checker (ai-checker-base), whose
runCheck() spawns a real `tmux new-session` running `claude -p`. The AI-enabled
tests only assert the ai_checking state transition (then cancel/stop), so the
spawn produced stray real tmux sessions and claude processes on every run — the
reason `npm test` (full suite) was unsafe to run inside a managed session.
Mock node:child_process here (mirroring ai-idle-checker.test.ts), spreading the
real module so `exec` stays intact for transitively-imported modules
(tmux-manager calls promisify(exec) at load). With this, the full non-mobile
suite runs without spawning any real tmux/claude.
---------
Co-authored-by: Teigen <teigen@TeigendeMac-mini.local>
This commit is contained in:
@@ -7,6 +7,7 @@
|
||||
import Fastify, { type FastifyInstance } from 'fastify';
|
||||
import fastifyCookie from '@fastify/cookie';
|
||||
import { createMockRouteContext, type MockRouteContext } from '../mocks/index.js';
|
||||
import { installRouteErrorHandler } from '../../src/web/route-error-handler.js';
|
||||
|
||||
export interface RouteTestHarness {
|
||||
app: FastifyInstance;
|
||||
@@ -24,7 +25,7 @@ export interface RouteTestHarness {
|
||||
export async function createRouteTestHarness(
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
registerFn: (app: FastifyInstance, ctx: any) => void,
|
||||
ctxOptions?: { sessionId?: string },
|
||||
ctxOptions?: { sessionId?: string }
|
||||
): Promise<RouteTestHarness> {
|
||||
const app = Fastify({ logger: false });
|
||||
|
||||
@@ -34,6 +35,9 @@ export async function createRouteTestHarness(
|
||||
const ctx = createMockRouteContext(ctxOptions);
|
||||
|
||||
registerFn(app, ctx);
|
||||
// Mirror production: structured errors thrown by route helpers (findSessionOrFail,
|
||||
// parseBody) are rendered to {success:false} bodies at the right status.
|
||||
installRouteErrorHandler(app);
|
||||
await app.ready();
|
||||
|
||||
return { app, ctx };
|
||||
|
||||
@@ -104,9 +104,7 @@ describe('case-routes', () => {
|
||||
});
|
||||
|
||||
it('includes hasClaudeMd flag', async () => {
|
||||
mockedReaddir.mockResolvedValue([
|
||||
{ name: 'case-with-md', isDirectory: () => true },
|
||||
] as never);
|
||||
mockedReaddir.mockResolvedValue([{ name: 'case-with-md', isDirectory: () => true }] as never);
|
||||
mockedExistsSync.mockReturnValue(true);
|
||||
|
||||
const res = await harness.app.inject({
|
||||
@@ -120,9 +118,7 @@ describe('case-routes', () => {
|
||||
|
||||
it('includes linked cases from linked-cases.json', async () => {
|
||||
// CASES_DIR readdir returns one case
|
||||
mockedReaddir.mockResolvedValue([
|
||||
{ name: 'regular-case', isDirectory: () => true },
|
||||
] as never);
|
||||
mockedReaddir.mockResolvedValue([{ name: 'regular-case', isDirectory: () => true }] as never);
|
||||
// linked-cases.json is read second (after CASES_DIR readdir)
|
||||
let readCallCount = 0;
|
||||
mockedReadFile.mockImplementation(async () => {
|
||||
@@ -158,7 +154,7 @@ describe('case-routes', () => {
|
||||
url: '/api/cases',
|
||||
payload: { name: 'invalid case name!!' },
|
||||
});
|
||||
expect(res.statusCode).toBe(200);
|
||||
expect(res.statusCode).toBe(400);
|
||||
const body = JSON.parse(res.body);
|
||||
expect(body.success).toBe(false);
|
||||
});
|
||||
@@ -169,7 +165,7 @@ describe('case-routes', () => {
|
||||
url: '/api/cases',
|
||||
payload: {},
|
||||
});
|
||||
expect(res.statusCode).toBe(200);
|
||||
expect(res.statusCode).toBe(400);
|
||||
const body = JSON.parse(res.body);
|
||||
expect(body.success).toBe(false);
|
||||
});
|
||||
@@ -180,7 +176,7 @@ describe('case-routes', () => {
|
||||
url: '/api/cases',
|
||||
payload: { name: '../etc' },
|
||||
});
|
||||
expect(res.statusCode).toBe(200);
|
||||
expect(res.statusCode).toBe(400);
|
||||
const body = JSON.parse(res.body);
|
||||
expect(body.success).toBe(false);
|
||||
});
|
||||
@@ -230,7 +226,7 @@ describe('case-routes', () => {
|
||||
url: '/api/cases/link',
|
||||
payload: { name: 'bad name!' },
|
||||
});
|
||||
expect(res.statusCode).toBe(200);
|
||||
expect(res.statusCode).toBe(400);
|
||||
const body = JSON.parse(res.body);
|
||||
expect(body.success).toBe(false);
|
||||
});
|
||||
@@ -241,7 +237,7 @@ describe('case-routes', () => {
|
||||
url: '/api/cases/link',
|
||||
payload: {},
|
||||
});
|
||||
expect(res.statusCode).toBe(200);
|
||||
expect(res.statusCode).toBe(400);
|
||||
const body = JSON.parse(res.body);
|
||||
expect(body.success).toBe(false);
|
||||
});
|
||||
@@ -294,7 +290,10 @@ describe('case-routes', () => {
|
||||
const body = JSON.parse(res.body);
|
||||
expect(body.success).toBe(true);
|
||||
expect(body.data.case.name).toBe('linked-project');
|
||||
expect(harness.ctx.broadcast).toHaveBeenCalledWith('case:linked', expect.objectContaining({ name: 'linked-project' }));
|
||||
expect(harness.ctx.broadcast).toHaveBeenCalledWith(
|
||||
'case:linked',
|
||||
expect.objectContaining({ name: 'linked-project' })
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -312,7 +312,8 @@ describe('file-routes', () => {
|
||||
method: 'GET',
|
||||
url: `/api/sessions/${harness.ctx._sessionId}/file-raw?path=../../etc/shadow`,
|
||||
});
|
||||
expect(res.statusCode).toBe(400);
|
||||
// Path traversal returns 404 ("File not found") to avoid revealing the target exists.
|
||||
expect(res.statusCode).toBe(404);
|
||||
});
|
||||
|
||||
it('rejects overly large raw files', async () => {
|
||||
|
||||
@@ -38,7 +38,7 @@ describe('hook-event-routes', () => {
|
||||
expect(body.success).toBe(true);
|
||||
expect(harness.ctx.broadcast).toHaveBeenCalledWith(
|
||||
'hook:stop',
|
||||
expect.objectContaining({ sessionId: harness.ctx._sessionId }),
|
||||
expect.objectContaining({ sessionId: harness.ctx._sessionId })
|
||||
);
|
||||
});
|
||||
|
||||
@@ -55,7 +55,7 @@ describe('hook-event-routes', () => {
|
||||
expect(res.statusCode).toBe(200);
|
||||
expect(harness.ctx.sendPushNotifications).toHaveBeenCalledWith(
|
||||
'hook:idle_prompt',
|
||||
expect.objectContaining({ sessionId: harness.ctx._sessionId }),
|
||||
expect.objectContaining({ sessionId: harness.ctx._sessionId })
|
||||
);
|
||||
});
|
||||
|
||||
@@ -85,7 +85,7 @@ describe('hook-event-routes', () => {
|
||||
data: null,
|
||||
},
|
||||
});
|
||||
expect(res.statusCode).toBe(200);
|
||||
expect(res.statusCode).toBe(400);
|
||||
const body = JSON.parse(res.body);
|
||||
expect(body.success).toBe(false);
|
||||
});
|
||||
@@ -98,7 +98,7 @@ describe('hook-event-routes', () => {
|
||||
event: 'stop',
|
||||
},
|
||||
});
|
||||
expect(res.statusCode).toBe(200);
|
||||
expect(res.statusCode).toBe(400);
|
||||
const body = JSON.parse(res.body);
|
||||
expect(body.success).toBe(false);
|
||||
});
|
||||
@@ -180,10 +180,7 @@ describe('hook-event-routes', () => {
|
||||
},
|
||||
});
|
||||
expect(res.statusCode).toBe(200);
|
||||
expect(mockTracker.recordHookEvent).toHaveBeenCalledWith(
|
||||
'stop',
|
||||
expect.any(Object),
|
||||
);
|
||||
expect(mockTracker.recordHookEvent).toHaveBeenCalledWith('stop', expect.any(Object));
|
||||
});
|
||||
|
||||
it('starts transcript watcher when transcript_path is provided', async () => {
|
||||
@@ -199,7 +196,7 @@ describe('hook-event-routes', () => {
|
||||
expect(res.statusCode).toBe(200);
|
||||
expect(harness.ctx.startTranscriptWatcher).toHaveBeenCalledWith(
|
||||
harness.ctx._sessionId,
|
||||
'/home/user/.claude/transcript.jsonl',
|
||||
'/home/user/.claude/transcript.jsonl'
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@@ -103,7 +103,7 @@ describe('orchestrator-routes', () => {
|
||||
url: '/api/orchestrator/start',
|
||||
payload: { goal: '' },
|
||||
});
|
||||
expect(res.statusCode).toBe(200);
|
||||
expect(res.statusCode).toBe(400);
|
||||
const body = JSON.parse(res.body);
|
||||
expect(body.success).toBe(false);
|
||||
});
|
||||
@@ -114,7 +114,7 @@ describe('orchestrator-routes', () => {
|
||||
url: '/api/orchestrator/start',
|
||||
payload: {},
|
||||
});
|
||||
expect(res.statusCode).toBe(200);
|
||||
expect(res.statusCode).toBe(400);
|
||||
const body = JSON.parse(res.body);
|
||||
expect(body.success).toBe(false);
|
||||
});
|
||||
@@ -228,7 +228,7 @@ describe('orchestrator-routes', () => {
|
||||
url: '/api/orchestrator/reject',
|
||||
payload: {},
|
||||
});
|
||||
expect(res.statusCode).toBe(200);
|
||||
expect(res.statusCode).toBe(400);
|
||||
const body = JSON.parse(res.body);
|
||||
expect(body.success).toBe(false);
|
||||
});
|
||||
|
||||
@@ -80,7 +80,7 @@ describe('plan-routes', () => {
|
||||
url: '/api/cancel-plan-generation',
|
||||
payload: { orchestratorId: 12345 }, // should be string
|
||||
});
|
||||
expect(res.statusCode).toBe(200);
|
||||
expect(res.statusCode).toBe(400);
|
||||
const body = JSON.parse(res.body);
|
||||
expect(body.success).toBe(false);
|
||||
});
|
||||
@@ -129,7 +129,7 @@ describe('plan-routes', () => {
|
||||
expect(body.data.status).toBe('completed');
|
||||
expect(harness.ctx.broadcast).toHaveBeenCalledWith(
|
||||
'session:planTaskUpdate',
|
||||
expect.objectContaining({ sessionId: harness.ctx._sessionId, taskId: 'task-1' }),
|
||||
expect.objectContaining({ sessionId: harness.ctx._sessionId, taskId: 'task-1' })
|
||||
);
|
||||
});
|
||||
|
||||
@@ -159,7 +159,7 @@ describe('plan-routes', () => {
|
||||
url: `/api/sessions/${harness.ctx._sessionId}/plan/task/task-1`,
|
||||
payload: { status: 'invalid_status' },
|
||||
});
|
||||
expect(res.statusCode).toBe(200);
|
||||
expect(res.statusCode).toBe(400);
|
||||
const body = JSON.parse(res.body);
|
||||
expect(body.success).toBe(false);
|
||||
});
|
||||
@@ -208,7 +208,7 @@ describe('plan-routes', () => {
|
||||
expect(body.data.completedCount).toBe(5);
|
||||
expect(harness.ctx.broadcast).toHaveBeenCalledWith(
|
||||
'session:planCheckpoint',
|
||||
expect.objectContaining({ sessionId: harness.ctx._sessionId }),
|
||||
expect.objectContaining({ sessionId: harness.ctx._sessionId })
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -279,9 +279,7 @@ describe('plan-routes', () => {
|
||||
});
|
||||
|
||||
it('rolls back to a previous version', async () => {
|
||||
const mockPlan = [
|
||||
{ id: 'task-1', content: 'Step 1', status: 'pending' },
|
||||
];
|
||||
const mockPlan = [{ id: 'task-1', content: 'Step 1', status: 'pending' }];
|
||||
harness.ctx._session.ralphTracker = {
|
||||
rollbackToVersion: vi.fn(() => ({ success: true, plan: mockPlan })),
|
||||
} as never;
|
||||
@@ -296,7 +294,7 @@ describe('plan-routes', () => {
|
||||
expect(body.data).toHaveLength(1);
|
||||
expect(harness.ctx.broadcast).toHaveBeenCalledWith(
|
||||
'session:planRollback',
|
||||
expect.objectContaining({ sessionId: harness.ctx._sessionId, version: 1 }),
|
||||
expect.objectContaining({ sessionId: harness.ctx._sessionId, version: 1 })
|
||||
);
|
||||
});
|
||||
|
||||
@@ -358,7 +356,7 @@ describe('plan-routes', () => {
|
||||
expect(body.data.content).toBe('New task');
|
||||
expect(harness.ctx.broadcast).toHaveBeenCalledWith(
|
||||
'session:planTaskAdded',
|
||||
expect.objectContaining({ sessionId: harness.ctx._sessionId }),
|
||||
expect.objectContaining({ sessionId: harness.ctx._sessionId })
|
||||
);
|
||||
});
|
||||
|
||||
@@ -372,7 +370,7 @@ describe('plan-routes', () => {
|
||||
url: `/api/sessions/${harness.ctx._sessionId}/plan/task`,
|
||||
payload: { priority: 'P1' },
|
||||
});
|
||||
expect(res.statusCode).toBe(200);
|
||||
expect(res.statusCode).toBe(400);
|
||||
const body = JSON.parse(res.body);
|
||||
expect(body.success).toBe(false);
|
||||
});
|
||||
|
||||
@@ -65,7 +65,7 @@ describe('push-routes', () => {
|
||||
expect.objectContaining({
|
||||
endpoint: 'https://push.example.com/send/abc123',
|
||||
keys: { p256dh: 'test-p256dh-key', auth: 'test-auth-key' },
|
||||
}),
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
@@ -87,7 +87,7 @@ describe('push-routes', () => {
|
||||
expect.objectContaining({
|
||||
userAgent: 'TestBrowser/1.0',
|
||||
pushPreferences: { 'session:idle': true, 'session:error': false },
|
||||
}),
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
@@ -99,7 +99,7 @@ describe('push-routes', () => {
|
||||
keys: { p256dh: 'test-p256dh', auth: 'test-auth' },
|
||||
},
|
||||
});
|
||||
expect(res.statusCode).toBe(200);
|
||||
expect(res.statusCode).toBe(400);
|
||||
const body = JSON.parse(res.body);
|
||||
expect(body.success).toBe(false);
|
||||
});
|
||||
@@ -112,7 +112,7 @@ describe('push-routes', () => {
|
||||
endpoint: 'https://push.example.com/send/abc123',
|
||||
},
|
||||
});
|
||||
expect(res.statusCode).toBe(200);
|
||||
expect(res.statusCode).toBe(400);
|
||||
const body = JSON.parse(res.body);
|
||||
expect(body.success).toBe(false);
|
||||
});
|
||||
@@ -160,7 +160,7 @@ describe('push-routes', () => {
|
||||
url: '/api/push/subscribe/sub-123',
|
||||
payload: {},
|
||||
});
|
||||
expect(res.statusCode).toBe(200);
|
||||
expect(res.statusCode).toBe(400);
|
||||
const body = JSON.parse(res.body);
|
||||
expect(body.success).toBe(false);
|
||||
});
|
||||
|
||||
@@ -66,7 +66,9 @@ describe('ralph-routes', () => {
|
||||
});
|
||||
|
||||
it('enables ralph tracker', async () => {
|
||||
const tracker = (harness.ctx._session as Record<string, unknown>).ralphTracker as ReturnType<typeof createMockRalphTracker>;
|
||||
const tracker = (harness.ctx._session as Record<string, unknown>).ralphTracker as ReturnType<
|
||||
typeof createMockRalphTracker
|
||||
>;
|
||||
|
||||
const res = await harness.app.inject({
|
||||
method: 'POST',
|
||||
@@ -79,7 +81,9 @@ describe('ralph-routes', () => {
|
||||
});
|
||||
|
||||
it('disables ralph tracker', async () => {
|
||||
const tracker = (harness.ctx._session as Record<string, unknown>).ralphTracker as ReturnType<typeof createMockRalphTracker>;
|
||||
const tracker = (harness.ctx._session as Record<string, unknown>).ralphTracker as ReturnType<
|
||||
typeof createMockRalphTracker
|
||||
>;
|
||||
|
||||
const res = await harness.app.inject({
|
||||
method: 'POST',
|
||||
@@ -99,7 +103,7 @@ describe('ralph-routes', () => {
|
||||
});
|
||||
expect((harness.ctx.mux as Record<string, unknown>).updateRalphEnabled).toHaveBeenCalledWith(
|
||||
harness.ctx._sessionId,
|
||||
true,
|
||||
true
|
||||
);
|
||||
});
|
||||
|
||||
@@ -116,7 +120,9 @@ describe('ralph-routes', () => {
|
||||
});
|
||||
|
||||
it('handles reset option', async () => {
|
||||
const tracker = (harness.ctx._session as Record<string, unknown>).ralphTracker as ReturnType<typeof createMockRalphTracker>;
|
||||
const tracker = (harness.ctx._session as Record<string, unknown>).ralphTracker as ReturnType<
|
||||
typeof createMockRalphTracker
|
||||
>;
|
||||
|
||||
await harness.app.inject({
|
||||
method: 'POST',
|
||||
@@ -127,7 +133,9 @@ describe('ralph-routes', () => {
|
||||
});
|
||||
|
||||
it('configures completion phrase and max iterations', async () => {
|
||||
const tracker = (harness.ctx._session as Record<string, unknown>).ralphTracker as ReturnType<typeof createMockRalphTracker>;
|
||||
const tracker = (harness.ctx._session as Record<string, unknown>).ralphTracker as ReturnType<
|
||||
typeof createMockRalphTracker
|
||||
>;
|
||||
|
||||
await harness.app.inject({
|
||||
method: 'POST',
|
||||
@@ -138,7 +146,9 @@ describe('ralph-routes', () => {
|
||||
});
|
||||
|
||||
it('sets max iterations independently', async () => {
|
||||
const tracker = (harness.ctx._session as Record<string, unknown>).ralphTracker as ReturnType<typeof createMockRalphTracker>;
|
||||
const tracker = (harness.ctx._session as Record<string, unknown>).ralphTracker as ReturnType<
|
||||
typeof createMockRalphTracker
|
||||
>;
|
||||
|
||||
await harness.app.inject({
|
||||
method: 'POST',
|
||||
@@ -154,7 +164,7 @@ describe('ralph-routes', () => {
|
||||
url: '/api/sessions/nonexistent/ralph-config',
|
||||
payload: { enabled: true },
|
||||
});
|
||||
expect(res.statusCode).toBe(200);
|
||||
expect(res.statusCode).toBe(404);
|
||||
const body = JSON.parse(res.body);
|
||||
expect(body.success).toBe(false);
|
||||
});
|
||||
@@ -178,13 +188,15 @@ describe('ralph-routes', () => {
|
||||
url: `/api/sessions/${harness.ctx._sessionId}/ralph-config`,
|
||||
payload: { enabled: 'not-boolean' },
|
||||
});
|
||||
expect(res.statusCode).toBe(200);
|
||||
expect(res.statusCode).toBe(400);
|
||||
const body = JSON.parse(res.body);
|
||||
expect(body.success).toBe(false);
|
||||
});
|
||||
|
||||
it('handles disableAutoEnable flag', async () => {
|
||||
const tracker = (harness.ctx._session as Record<string, unknown>).ralphTracker as ReturnType<typeof createMockRalphTracker>;
|
||||
const tracker = (harness.ctx._session as Record<string, unknown>).ralphTracker as ReturnType<
|
||||
typeof createMockRalphTracker
|
||||
>;
|
||||
|
||||
await harness.app.inject({
|
||||
method: 'POST',
|
||||
@@ -208,7 +220,9 @@ describe('ralph-routes', () => {
|
||||
|
||||
describe('POST /api/sessions/:id/ralph-circuit-breaker/reset', () => {
|
||||
it('resets circuit breaker for valid session', async () => {
|
||||
const tracker = (harness.ctx._session as Record<string, unknown>).ralphTracker as ReturnType<typeof createMockRalphTracker>;
|
||||
const tracker = (harness.ctx._session as Record<string, unknown>).ralphTracker as ReturnType<
|
||||
typeof createMockRalphTracker
|
||||
>;
|
||||
|
||||
const res = await harness.app.inject({
|
||||
method: 'POST',
|
||||
@@ -225,7 +239,7 @@ describe('ralph-routes', () => {
|
||||
method: 'POST',
|
||||
url: '/api/sessions/nonexistent/ralph-circuit-breaker/reset',
|
||||
});
|
||||
expect(res.statusCode).toBe(200);
|
||||
expect(res.statusCode).toBe(404);
|
||||
const body = JSON.parse(res.body);
|
||||
expect(body.success).toBe(false);
|
||||
});
|
||||
@@ -253,7 +267,7 @@ describe('ralph-routes', () => {
|
||||
method: 'GET',
|
||||
url: '/api/sessions/nonexistent/ralph-status',
|
||||
});
|
||||
expect(res.statusCode).toBe(200);
|
||||
expect(res.statusCode).toBe(404);
|
||||
const body = JSON.parse(res.body);
|
||||
expect(body.success).toBe(false);
|
||||
});
|
||||
@@ -279,7 +293,7 @@ describe('ralph-routes', () => {
|
||||
method: 'GET',
|
||||
url: '/api/sessions/nonexistent/fix-plan',
|
||||
});
|
||||
expect(res.statusCode).toBe(200);
|
||||
expect(res.statusCode).toBe(404);
|
||||
const body = JSON.parse(res.body);
|
||||
expect(body.success).toBe(false);
|
||||
});
|
||||
@@ -315,7 +329,7 @@ describe('ralph-routes', () => {
|
||||
url: '/api/sessions/nonexistent/fix-plan/import',
|
||||
payload: { content: 'test' },
|
||||
});
|
||||
expect(res.statusCode).toBe(200);
|
||||
expect(res.statusCode).toBe(404);
|
||||
const body = JSON.parse(res.body);
|
||||
expect(body.success).toBe(false);
|
||||
});
|
||||
@@ -326,7 +340,7 @@ describe('ralph-routes', () => {
|
||||
url: `/api/sessions/${harness.ctx._sessionId}/fix-plan/import`,
|
||||
payload: {},
|
||||
});
|
||||
expect(res.statusCode).toBe(200);
|
||||
expect(res.statusCode).toBe(400);
|
||||
const body = JSON.parse(res.body);
|
||||
expect(body.success).toBe(false);
|
||||
});
|
||||
@@ -341,7 +355,7 @@ describe('ralph-routes', () => {
|
||||
url: '/api/sessions/nonexistent/ralph-prompt/write',
|
||||
payload: { content: 'test prompt' },
|
||||
});
|
||||
expect(res.statusCode).toBe(200);
|
||||
expect(res.statusCode).toBe(404);
|
||||
const body = JSON.parse(res.body);
|
||||
expect(body.success).toBe(false);
|
||||
});
|
||||
@@ -365,7 +379,7 @@ describe('ralph-routes', () => {
|
||||
url: `/api/sessions/${harness.ctx._sessionId}/ralph-prompt/write`,
|
||||
payload: {},
|
||||
});
|
||||
expect(res.statusCode).toBe(200);
|
||||
expect(res.statusCode).toBe(400);
|
||||
const body = JSON.parse(res.body);
|
||||
expect(body.success).toBe(false);
|
||||
});
|
||||
@@ -379,7 +393,7 @@ describe('ralph-routes', () => {
|
||||
method: 'POST',
|
||||
url: '/api/sessions/nonexistent/fix-plan/write',
|
||||
});
|
||||
expect(res.statusCode).toBe(200);
|
||||
expect(res.statusCode).toBe(404);
|
||||
const body = JSON.parse(res.body);
|
||||
expect(body.success).toBe(false);
|
||||
});
|
||||
@@ -405,7 +419,7 @@ describe('ralph-routes', () => {
|
||||
method: 'POST',
|
||||
url: '/api/sessions/nonexistent/fix-plan/read',
|
||||
});
|
||||
expect(res.statusCode).toBe(200);
|
||||
expect(res.statusCode).toBe(404);
|
||||
const body = JSON.parse(res.body);
|
||||
expect(body.success).toBe(false);
|
||||
});
|
||||
@@ -432,7 +446,7 @@ describe('ralph-routes', () => {
|
||||
url: '/api/ralph-loop/start',
|
||||
payload: {},
|
||||
});
|
||||
expect(res.statusCode).toBe(200);
|
||||
expect(res.statusCode).toBe(400);
|
||||
const body = JSON.parse(res.body);
|
||||
expect(body.success).toBe(false);
|
||||
});
|
||||
@@ -466,7 +480,7 @@ describe('ralph-routes', () => {
|
||||
caseName: '../escape-path',
|
||||
},
|
||||
});
|
||||
expect(res.statusCode).toBe(200);
|
||||
expect(res.statusCode).toBe(400);
|
||||
const body = JSON.parse(res.body);
|
||||
expect(body.success).toBe(false);
|
||||
});
|
||||
|
||||
@@ -172,7 +172,7 @@ describe('respawn-routes', () => {
|
||||
url: `/api/sessions/${harness.ctx._sessionId}/respawn/config`,
|
||||
payload: { idleTimeoutMs: 'not-a-number' },
|
||||
});
|
||||
expect(res.statusCode).toBe(200);
|
||||
expect(res.statusCode).toBe(400);
|
||||
const body = JSON.parse(res.body);
|
||||
expect(body.success).toBe(false);
|
||||
});
|
||||
@@ -196,7 +196,7 @@ describe('respawn-routes', () => {
|
||||
expect(mockController.updateConfig).toHaveBeenCalled();
|
||||
expect(harness.ctx.broadcast).toHaveBeenCalledWith(
|
||||
'respawn:configUpdated',
|
||||
expect.objectContaining({ sessionId: harness.ctx._sessionId }),
|
||||
expect.objectContaining({ sessionId: harness.ctx._sessionId })
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@@ -114,7 +114,7 @@ describe('scheduled-routes', () => {
|
||||
prompt: '',
|
||||
},
|
||||
});
|
||||
expect(res.statusCode).toBe(200);
|
||||
expect(res.statusCode).toBe(400);
|
||||
const body = JSON.parse(res.body);
|
||||
expect(body.success).toBe(false);
|
||||
});
|
||||
@@ -125,7 +125,7 @@ describe('scheduled-routes', () => {
|
||||
url: '/api/scheduled',
|
||||
payload: {},
|
||||
});
|
||||
expect(res.statusCode).toBe(200);
|
||||
expect(res.statusCode).toBe(400);
|
||||
const body = JSON.parse(res.body);
|
||||
expect(body.success).toBe(false);
|
||||
});
|
||||
@@ -139,7 +139,7 @@ describe('scheduled-routes', () => {
|
||||
workingDir: '/tmp/test;rm -rf /',
|
||||
},
|
||||
});
|
||||
expect(res.statusCode).toBe(200);
|
||||
expect(res.statusCode).toBe(400);
|
||||
const body = JSON.parse(res.body);
|
||||
expect(body.success).toBe(false);
|
||||
});
|
||||
@@ -171,11 +171,7 @@ describe('scheduled-routes', () => {
|
||||
const body = JSON.parse(res.body);
|
||||
expect(body.success).toBe(true);
|
||||
// Should default to 60 minutes
|
||||
expect(harness.ctx.startScheduledRun).toHaveBeenCalledWith(
|
||||
'test',
|
||||
expect.any(String),
|
||||
60,
|
||||
);
|
||||
expect(harness.ctx.startScheduledRun).toHaveBeenCalledWith('test', expect.any(String), 60);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -103,7 +103,7 @@ describe('session-routes', () => {
|
||||
method: 'GET',
|
||||
url: '/api/sessions/nonexistent',
|
||||
});
|
||||
expect(res.statusCode).toBe(200); // returns error in body, not HTTP 404
|
||||
expect(res.statusCode).toBe(404);
|
||||
const body = JSON.parse(res.body);
|
||||
expect(body.success).toBe(false);
|
||||
expect(body.error).toBeDefined();
|
||||
@@ -174,7 +174,7 @@ describe('session-routes', () => {
|
||||
url: '/api/sessions/nonexistent/name',
|
||||
payload: { name: 'test' },
|
||||
});
|
||||
expect(res.statusCode).toBe(200);
|
||||
expect(res.statusCode).toBe(404);
|
||||
const body = JSON.parse(res.body);
|
||||
expect(body.success).toBe(false);
|
||||
});
|
||||
@@ -227,7 +227,7 @@ describe('session-routes', () => {
|
||||
url: '/api/sessions/nonexistent/input',
|
||||
payload: { input: 'hello' },
|
||||
});
|
||||
expect(res.statusCode).toBe(200);
|
||||
expect(res.statusCode).toBe(404);
|
||||
const body = JSON.parse(res.body);
|
||||
expect(body.success).toBe(false);
|
||||
});
|
||||
@@ -238,7 +238,7 @@ describe('session-routes', () => {
|
||||
url: `/api/sessions/${harness.ctx._sessionId}/input`,
|
||||
payload: {},
|
||||
});
|
||||
expect(res.statusCode).toBe(200);
|
||||
expect(res.statusCode).toBe(400);
|
||||
const body = JSON.parse(res.body);
|
||||
expect(body.success).toBe(false);
|
||||
});
|
||||
@@ -265,7 +265,7 @@ describe('session-routes', () => {
|
||||
url: `/api/sessions/${harness.ctx._sessionId}/resize`,
|
||||
payload: { cols: 501, rows: 24 },
|
||||
});
|
||||
expect(res.statusCode).toBe(200);
|
||||
expect(res.statusCode).toBe(400);
|
||||
const body = JSON.parse(res.body);
|
||||
expect(body.success).toBe(false);
|
||||
});
|
||||
@@ -276,7 +276,7 @@ describe('session-routes', () => {
|
||||
url: `/api/sessions/${harness.ctx._sessionId}/resize`,
|
||||
payload: { cols: 80, rows: 201 },
|
||||
});
|
||||
expect(res.statusCode).toBe(200);
|
||||
expect(res.statusCode).toBe(400);
|
||||
const body = JSON.parse(res.body);
|
||||
expect(body.success).toBe(false);
|
||||
});
|
||||
@@ -287,7 +287,7 @@ describe('session-routes', () => {
|
||||
url: `/api/sessions/${harness.ctx._sessionId}/resize`,
|
||||
payload: { cols: 0, rows: 24 },
|
||||
});
|
||||
expect(res.statusCode).toBe(200);
|
||||
expect(res.statusCode).toBe(400);
|
||||
const body = JSON.parse(res.body);
|
||||
expect(body.success).toBe(false);
|
||||
});
|
||||
@@ -312,7 +312,7 @@ describe('session-routes', () => {
|
||||
method: 'GET',
|
||||
url: '/api/sessions/nonexistent/terminal',
|
||||
});
|
||||
expect(res.statusCode).toBe(200);
|
||||
expect(res.statusCode).toBe(404);
|
||||
const body = JSON.parse(res.body);
|
||||
expect(body.success).toBe(false);
|
||||
});
|
||||
@@ -338,7 +338,7 @@ describe('session-routes', () => {
|
||||
url: `/api/sessions/${harness.ctx._sessionId}/run`,
|
||||
payload: { prompt: '' },
|
||||
});
|
||||
expect(res.statusCode).toBe(200);
|
||||
expect(res.statusCode).toBe(400);
|
||||
const body = JSON.parse(res.body);
|
||||
expect(body.success).toBe(false);
|
||||
});
|
||||
@@ -349,7 +349,7 @@ describe('session-routes', () => {
|
||||
url: '/api/sessions/nonexistent/run',
|
||||
payload: { prompt: 'test' },
|
||||
});
|
||||
expect(res.statusCode).toBe(200);
|
||||
expect(res.statusCode).toBe(404);
|
||||
const body = JSON.parse(res.body);
|
||||
expect(body.success).toBe(false);
|
||||
});
|
||||
@@ -386,7 +386,7 @@ describe('session-routes', () => {
|
||||
method: 'POST',
|
||||
url: '/api/sessions/nonexistent/interactive',
|
||||
});
|
||||
expect(res.statusCode).toBe(200);
|
||||
expect(res.statusCode).toBe(404);
|
||||
const body = JSON.parse(res.body);
|
||||
expect(body.success).toBe(false);
|
||||
});
|
||||
@@ -450,7 +450,7 @@ describe('session-routes', () => {
|
||||
method: 'GET',
|
||||
url: '/api/sessions/nonexistent/output',
|
||||
});
|
||||
expect(res.statusCode).toBe(200);
|
||||
expect(res.statusCode).toBe(404);
|
||||
const body = JSON.parse(res.body);
|
||||
expect(body.success).toBe(false);
|
||||
});
|
||||
@@ -586,7 +586,7 @@ describe('session-routes', () => {
|
||||
resumeSessionId: 'not-a-uuid',
|
||||
},
|
||||
});
|
||||
expect(res.statusCode).toBe(200);
|
||||
expect(res.statusCode).toBe(400);
|
||||
const body = JSON.parse(res.body);
|
||||
expect(body.success).toBe(false);
|
||||
});
|
||||
|
||||
@@ -166,7 +166,7 @@ describe('system-routes', () => {
|
||||
url: '/api/config',
|
||||
payload: { unknownField: 'invalid' },
|
||||
});
|
||||
expect(res.statusCode).toBe(200);
|
||||
expect(res.statusCode).toBe(400);
|
||||
const body = JSON.parse(res.body);
|
||||
expect(body.success).toBe(false);
|
||||
});
|
||||
@@ -361,7 +361,7 @@ describe('system-routes', () => {
|
||||
url: '/api/settings',
|
||||
payload: { unknownField: 'bad' },
|
||||
});
|
||||
expect(res.statusCode).toBe(200);
|
||||
expect(res.statusCode).toBe(400);
|
||||
const body = JSON.parse(res.body);
|
||||
expect(body.success).toBe(false);
|
||||
});
|
||||
@@ -459,7 +459,7 @@ describe('system-routes', () => {
|
||||
url: '/api/subagent-window-states',
|
||||
payload: { minimized: { 'agent-1': 'not-a-boolean' } },
|
||||
});
|
||||
expect(res.statusCode).toBe(200);
|
||||
expect(res.statusCode).toBe(400);
|
||||
const body = JSON.parse(res.body);
|
||||
expect(body.success).toBe(false);
|
||||
});
|
||||
@@ -516,7 +516,7 @@ describe('system-routes', () => {
|
||||
url: '/api/subagent-parents',
|
||||
payload: { 'agent-1': 123 },
|
||||
});
|
||||
expect(res.statusCode).toBe(200);
|
||||
expect(res.statusCode).toBe(400);
|
||||
const body = JSON.parse(res.body);
|
||||
expect(body.success).toBe(false);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user