Fresh history cut from cynarAI/evoo antigravity-handoff/project-timeline (Voice First + project timeline). Brand docs/titles as evOo; keep @rakazo/* packages as tech debt. Excludes node_modules, caches, secrets, voice blobs.
113 lines
4.4 KiB
TypeScript
113 lines
4.4 KiB
TypeScript
import type { AdapterContext } from "@rakazo/adapter-kit";
|
|
import { CloudAgentRequestRejected } from "@rakazo/adapter-kit";
|
|
import { describe, expect, it } from "vitest";
|
|
import { CursorCloudAgentProvider } from "./cursor-cloud-agent.js";
|
|
import { CursorCloudAgentEmulator } from "./testing/cursor-cloud-agent-emulator.js";
|
|
|
|
const context: AdapterContext = {
|
|
operationId: "test",
|
|
traceId: "test",
|
|
spaceId: "test-space",
|
|
userId: "test-user",
|
|
signal: new AbortController().signal,
|
|
};
|
|
|
|
describe("Cursor HTTP boundary", () => {
|
|
it("keeps a duplicate create recoverable when the accepted agent is not yet readable", async () => {
|
|
const provider = new CursorCloudAgentProvider({
|
|
apiKey: "fake-key",
|
|
fetch: async (_url, init) =>
|
|
new Response(null, { status: init?.method === "POST" ? 409 : 404 }),
|
|
});
|
|
const error = await provider
|
|
.launch({ idempotencyKey: "operation", prompt: "Task" }, context)
|
|
.catch((error: Error) => error);
|
|
expect(error).toBeInstanceOf(Error);
|
|
expect(error).not.toBeInstanceOf(CloudAgentRequestRejected);
|
|
});
|
|
it("sends the documented create shape with a stable client agent ID", async () => {
|
|
const wire = new CursorCloudAgentEmulator();
|
|
const provider = new CursorCloudAgentProvider({ apiKey: "fake-key", fetch: wire.fetch });
|
|
const result = await provider.launch(
|
|
{
|
|
idempotencyKey: "operation",
|
|
prompt: "Task",
|
|
repository: "https://github.com/example/demo",
|
|
openPr: false,
|
|
images: [{ data: "ZmFrZQ==", mimeType: "image/png" }],
|
|
},
|
|
context,
|
|
);
|
|
expect(wire.requests[0]).toEqual({
|
|
method: "POST",
|
|
path: "/v1/agents",
|
|
body: {
|
|
agentId: result.id,
|
|
prompt: { text: "Task", images: [{ data: "ZmFrZQ==", mimeType: "image/png" }] },
|
|
repos: [{ url: "https://github.com/example/demo" }],
|
|
autoCreatePR: false,
|
|
},
|
|
});
|
|
expect(result.id).toMatch(
|
|
/^bc-[\da-f]{8}-[\da-f]{4}-4[\da-f]{3}-[89ab][\da-f]{3}-[\da-f]{12}$/,
|
|
);
|
|
});
|
|
it.each([400, 401, 403, 404, 422, 429, 500, 503])(
|
|
"never exposes a %s response body",
|
|
async (status) => {
|
|
const provider = new CursorCloudAgentProvider({
|
|
apiKey: "fake-key",
|
|
fetch: async () => new Response("fake-key and fake-secret", { status }),
|
|
});
|
|
const error = await provider.get("test-agent", context).catch((error: Error) => error);
|
|
expect(error).toBeInstanceOf(Error);
|
|
expect(String(error)).not.toMatch(/fake-key|fake-secret/);
|
|
},
|
|
);
|
|
it("rejects malformed and oversized response bodies", async () => {
|
|
for (const body of ['{"bad":true}', "invalid fake-secret", " ".repeat(1_000_001)]) {
|
|
const provider = new CursorCloudAgentProvider({
|
|
apiKey: "fake-key",
|
|
fetch: async () => new Response(body),
|
|
});
|
|
await expect(provider.get("test-agent", context)).rejects.toThrow();
|
|
}
|
|
});
|
|
it("bounds hanging body reads even if stream cancellation never settles", async () => {
|
|
const provider = new CursorCloudAgentProvider({
|
|
apiKey: "fake-key",
|
|
timeoutMs: 20,
|
|
fetch: async () =>
|
|
new Response(
|
|
new ReadableStream({
|
|
pull: () => new Promise(() => {}),
|
|
cancel: () => new Promise(() => {}),
|
|
}),
|
|
),
|
|
});
|
|
await expect(provider.get("test-agent", context)).rejects.toThrow();
|
|
});
|
|
it("uses a fixed credential origin and rejects redirects", async () => {
|
|
let redirect: RequestRedirect | undefined;
|
|
const provider = new CursorCloudAgentProvider({
|
|
apiKey: "fake-key",
|
|
fetch: async (url, init) => {
|
|
expect(String(url).startsWith("https://api.cursor.com/v1/agents/")).toBe(true);
|
|
redirect = init?.redirect;
|
|
return new Response(null, { status: 302 });
|
|
},
|
|
});
|
|
await expect(provider.get("test-agent", context)).rejects.toThrow();
|
|
expect(redirect).toBe("error");
|
|
});
|
|
it("does not turn a successful follow-up into failure by fetching metadata afterward", async () => {
|
|
const wire = new CursorCloudAgentEmulator();
|
|
const provider = new CursorCloudAgentProvider({ apiKey: "fake-key", fetch: wire.fetch });
|
|
const agent = await provider.launch({ idempotencyKey: "follow-up", prompt: "Task" }, context);
|
|
wire.complete(agent.id);
|
|
wire.requests.length = 0;
|
|
await provider.reply(agent.id, { prompt: "Tests" }, context);
|
|
expect(wire.requests.map((request) => request.method)).toEqual(["GET", "POST"]);
|
|
});
|
|
});
|