Files
spoon/apps/agent-worker/tests/unit/box-route.test.ts
T
Gabriel Brown 192f547be5 feat(worker): /box status, lifecycle, tree, and file HTTP routes
Wire the box helpers into the worker HTTP server as /box/* routes,
authed by the internal bearer token like every other worker route.
Adds a boxRoute matcher (/^\/box\/(status|lifecycle|tree|file)$/) and
dispatches before jobRoute; missing user -> 400, unknown lifecycle
action -> 400, containment throws -> 400 (server keeps serving).

Manual verification (harness on :3922, internal token, user gabriel):
  GET  /box/status?user=gabriel        -> 200 {"status":{...running:false...}}
  GET  /box/status  (no user)          -> 400 {"error":"Missing user"}
  (no Authorization header)            -> 401
  POST /box/lifecycle {"action":"bogus"} -> 400 {"error":"Unknown action"}
  GET  /box/tree?user=gabriel          -> 200 {"tree":{"name":"~",...}}
  PUT  /box/file {"path":"spoon-test.txt","content":"hi from task4"} -> 200 {"success":true}
  GET  /box/file?path=spoon-test.txt   -> 200 {"path":...,"content":"hi from task4"}
  GET  /box/file?path=../../../../../etc/passwd -> 400 {"error":"Refusing to access path outside home: ..."}
  GET  /box/status after escape        -> 200 (no crash)
  GET  /box/other?user=gabriel         -> 404 {"error":"Not found"}
2026-07-11 12:13:50 -04:00

33 lines
1.2 KiB
TypeScript

import { afterEach, describe, expect, test, vi } from 'vitest';
const load = async () => {
vi.resetModules();
process.env.SPOON_WORKER_TOKEN = 'test-worker-token';
process.env.GITHUB_APP_ID = '123';
process.env.GITHUB_APP_PRIVATE_KEY =
'-----BEGIN PRIVATE KEY-----\ntest\n-----END PRIVATE KEY-----';
process.env.SPOON_AGENT_WORKDIR = '/work';
return await import('../../src/server');
};
describe('boxRoute', () => {
afterEach(() => vi.resetModules());
test('matches the four box actions', async () => {
const { boxRoute } = await load();
expect(boxRoute('/box/status')).toEqual({ action: 'status' });
expect(boxRoute('/box/lifecycle')).toEqual({ action: 'lifecycle' });
expect(boxRoute('/box/tree')).toEqual({ action: 'tree' });
expect(boxRoute('/box/file')).toEqual({ action: 'file' });
});
test('rejects unknown, nested, and trailing paths', async () => {
const { boxRoute } = await load();
expect(boxRoute('/box/other')).toBeNull();
expect(boxRoute('/box/file/extra')).toBeNull();
expect(boxRoute('/box/status/')).toBeNull();
expect(boxRoute('/box')).toBeNull();
expect(boxRoute('/jobs/x/file')).toBeNull();
});
});