Repository navigation
Expand file tree
/
Copy pathdrive.test.ts
More file actions
190 lines (150 loc) · 7.31 KB
/
Copy pathdrive.test.ts
File metadata and controls
190 lines (150 loc) · 7.31 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
/**
* The durable transport's drive callback (#329 T9).
*
* This is the one path a caller reaches without a user session, so what it refuses
* matters more than what it does. It authenticates with its own single-purpose
* credential; a user session is not one, and the run it drives is named by the
* credential rather than by the request body.
*
* `installMocks` is re-applied in `beforeEach` rather than only at import time, and
* that is not belt-and-braces: `mock.module` replaces a module process-wide, and
* `tests/api/agent/runs.test.ts` mocks the same runtime module in the same process
* under `bun run test`. Re-registering makes the file that is currently running own
* the module, whatever order the runner loaded them in.
*/
import { describe, test, expect, mock, beforeEach, afterEach, spyOn } from "bun:test";
import { SignJWT } from "jose";
import { configureAgentModel, restoreAgentModel } from "../../helpers/agent-model-env";
import { createMockRequest, parseResponseJSON } from "../../helpers/mock-next";
import { AGENT_DRIVE_HEADER, mintAgentDriveToken } from "@/lib/agent/drive-token";
import { AGENT_ENABLED_ENV } from "@/lib/agent/config";
import { AgentRunServiceError } from "@/lib/agent/run-service";
import { clearRateLimitState } from "@/lib/api/rate-limit";
const mockDriveAgentRun = mock(async (runId: string) => ({
runId,
status: "succeeded",
stopReason: "report-composed",
turns: 3,
text: "done",
}));
function installMocks(): void {
mock.module("@/lib/agent/runtime", () => ({
driveAgentRun: mockDriveAgentRun,
getAgentRunService: mock(async () => ({})),
// Unused here, and listed anyway: `mock.module` replaces the module for the whole
// process, so an export this file forgets is an export the NEXT file's route
// cannot import (`tests/api/agent/artifacts.test.ts` reaches for exactly this one).
readAgentArtifact: mock(() => undefined),
}));
}
installMocks();
const { POST } = await import("@/app/api/agent/drive/route");
const RUN_ID = "arun_0123456789abcdef";
function driveRequest(options: { token?: string; body?: unknown } = {}): Request {
const headers: Record<string, string> = {};
if (options.token !== undefined) headers[AGENT_DRIVE_HEADER] = options.token;
return createMockRequest("/api/agent/drive", { method: "POST", headers, body: options.body ?? {} });
}
beforeEach(() => {
installMocks();
clearRateLimitState();
mockDriveAgentRun.mockClear();
// A configured model is what makes the surface exist since #331 T5; the flag is
// only the off-switch, so the absence test below sets it to a negative value.
delete process.env[AGENT_ENABLED_ENV];
configureAgentModel();
});
afterEach(() => {
restoreAgentModel();
});
describe("POST /api/agent/drive", () => {
test("a valid credential drives the run it names", async () => {
const res = await POST(driveRequest({ token: await mintAgentDriveToken(RUN_ID) }));
const body = await parseResponseJSON<{ runId: string; status: string }>(res);
expect(res.status).toBe(200);
expect(body.runId).toBe(RUN_ID);
expect(body.status).toBe("succeeded");
expect(mockDriveAgentRun).toHaveBeenCalledWith(RUN_ID);
});
test("the run driven is the credential's, never the body's", async () => {
await POST(driveRequest({ token: await mintAgentDriveToken(RUN_ID), body: { runId: "arun_someoneelse" } }));
expect(mockDriveAgentRun).toHaveBeenCalledWith(RUN_ID);
});
test("no credential drives nothing", async () => {
const res = await POST(driveRequest());
expect(res.status).toBe(401);
expect(mockDriveAgentRun).not.toHaveBeenCalled();
});
test("a token signed with the session secret is not a drive credential", async () => {
// It carries a well-formed `runId` as well as session claims, so the only thing
// that can refuse it is the key separation — a probe missing the runId would
// still be rejected on shape if the two keys were ever collapsed into one.
const session = await new SignJWT({ runId: RUN_ID, role: "admin", username: "root" })
.setProtectedHeader({ alg: "HS256" })
.setIssuedAt()
.setExpirationTime("1h")
.sign(new TextEncoder().encode(process.env.JWT_SECRET as string));
const res = await POST(driveRequest({ token: session }));
expect(res.status).toBe(401);
expect(mockDriveAgentRun).not.toHaveBeenCalled();
});
test("a broken audit sink still refuses the caller", async () => {
// The emit is isolated in its own try/catch: the 401 is already decided, and a
// failing audit sink must not turn a refusal into an unrelated 500.
const spy = spyOn(console, "log").mockImplementation(() => {
throw new Error("audit sink unavailable");
});
try {
const res = await POST(driveRequest());
expect(res.status).toBe(401);
} finally {
spy.mockRestore();
}
});
test("an expired credential drives nothing", async () => {
const stale = await mintAgentDriveToken(RUN_ID, () => Date.now() - 120_000);
const res = await POST(driveRequest({ token: stale }));
expect(res.status).toBe(401);
expect(mockDriveAgentRun).not.toHaveBeenCalled();
});
test("the callback does not exist once the operator switches the agent off", async () => {
process.env[AGENT_ENABLED_ENV] = "false";
const res = await POST(driveRequest({ token: await mintAgentDriveToken(RUN_ID) }));
expect(res.status).toBe(404);
expect(mockDriveAgentRun).not.toHaveBeenCalled();
});
test("a run that does not exist is a 404", async () => {
mockDriveAgentRun.mockRejectedValueOnce(new AgentRunServiceError("RUN_NOT_FOUND", "gone"));
const res = await POST(driveRequest({ token: await mintAgentDriveToken(RUN_ID) }));
expect(res.status).toBe(404);
});
test("a run that has already ended is a conflict, not a failure", async () => {
mockDriveAgentRun.mockRejectedValueOnce(new AgentRunServiceError("RUN_NOT_RESUMABLE", "already succeeded"));
const res = await POST(driveRequest({ token: await mintAgentDriveToken(RUN_ID) }));
expect(res.status).toBe(409);
});
test("a delivery for a run another drive is already carrying is a conflict, not a failure", async () => {
// The duplicate-delivery case, and the one place the distinction matters: the run
// is HEALTHY and in flight, so this must not read as a server fault the queue
// retries forever against a run someone else owns. 409 tells it to drop the
// delivery; the drive that holds the claim finishes the run.
mockDriveAgentRun.mockRejectedValueOnce(
new AgentRunServiceError("RUN_ALREADY_DRIVEN", "already being driven in this process"),
);
const res = await POST(driveRequest({ token: await mintAgentDriveToken(RUN_ID) }));
expect(res.status).toBe(409);
});
test("a drive that fails for any other reason reports a failure", async () => {
mockDriveAgentRun.mockRejectedValueOnce(new Error("model unreachable"));
const res = await POST(driveRequest({ token: await mintAgentDriveToken(RUN_ID) }));
expect(res.status).toBeGreaterThanOrEqual(500);
});
test("deliveries for one run are rate limited like every other model-reaching route", async () => {
const token = await mintAgentDriveToken(RUN_ID);
let last = 0;
for (let attempt = 0; attempt < 21; attempt++) {
last = (await POST(driveRequest({ token }))).status;
}
expect(last).toBe(429);
});
});