import test from "node:test"; import assert from "node:assert/strict"; // Import the executor directly (not via executors/index.ts) — index pulls in // the entire provider registry and DB layer which is slow and unnecessary for // the unit-level behavior we want to exercise here. const { TraeExecutor } = await import("../../open-sse/executors/trae.ts"); // ─── Helpers ──────────────────────────────────────────────────────────────── /** Build a Response whose body streams the given SSE frames (event/data pairs). */ function sseResponse(frames: Array<{ event: string; data: unknown }>): Response { const enc = new TextEncoder(); const stream = new ReadableStream({ start(controller) { for (const f of frames) { controller.enqueue(enc.encode(`event: ${f.event}\n`)); controller.enqueue(enc.encode(`data: ${JSON.stringify(f.data)}\n\n`)); } controller.close(); }, }); return new Response(stream, { status: 200, headers: { "Content-Type": "text/event-stream" }, }); } function jsonResponse(obj: unknown, status = 200): Response { return new Response(JSON.stringify(obj), { status, headers: { "Content-Type": "application/json" }, }); } /** * Install a mock fetch dispatching by URL: * POST /chat_sessions → session create * GET /chat_sessions/{}/events → SSE frames * Returns { calls, restore }. */ function installMockFetch({ sessionBody, frames, sessionStatus = 200, }: { sessionBody?: unknown; frames?: Array<{ event: string; data: unknown }>; sessionStatus?: number; } = {}) { const calls: { sessionBody?: any; sessionHeaders?: Record; eventsUrl?: string } = {}; const original = globalThis.fetch; globalThis.fetch = (async (input: any, init: any = {}) => { const url = typeof input === "string" ? input : input.url; if (url.includes("/chat_sessions") && url.includes("/events")) { calls.eventsUrl = url; return sseResponse(frames ?? [{ event: "done", data: { status: "completed" } }]); } if (url.endsWith("/chat_sessions")) { calls.sessionBody = init.body ? JSON.parse(init.body) : undefined; calls.sessionHeaders = init.headers; return jsonResponse( sessionBody ?? { code: 0, data: { chat_session_id: "sess1", status: 2, message_id: "msg1" }, message: "success", }, sessionStatus ); } throw new Error(`unexpected fetch ${url}`); }) as typeof fetch; return { calls, restore: () => (globalThis.fetch = original) }; } const CREDS = { accessToken: "JWT.test.token", providerSpecificData: { webId: "WID", bizUserId: "BUID", userUniqueId: "UUID", scope: "marscode-us", tenant: "marscode", region: "US-East", }, }; async function readAll(res: Response): Promise { return await res.text(); } // ─── Tests ────────────────────────────────────────────────────────────────── // Registration (`getExecutor("trae")` returns a TraeExecutor) is covered by // typecheck + a server-startup smoke test. Importing executors/index.ts here // would pull in the DB layer and leave async handles open at process exit. test("buildHeaders uses Cloud-IDE-JWT auth scheme + web client headers", () => { const ex = new TraeExecutor(); const h = ex.buildHeaders(CREDS); assert.equal(h.Authorization, "Cloud-IDE-JWT JWT.test.token"); assert.equal(h["X-Trae-Client-Type"], "web"); }); test("non-stream: accumulates plan_item.thought and maps usage", async () => { const { calls, restore } = installMockFetch({ frames: [ { event: "metadata", data: { message_id: "m" } }, { event: "plan_item", data: { id: "p1", thought: "Па" } }, { event: "plan_item", data: { id: "p1", thought: "Париж" } }, // trailing finish plan_item with empty thought must NOT wipe accumulated text { event: "plan_item", data: { id: "p2", thought: "" } }, { event: "token_usage", data: { prompt_tokens: 10, completion_tokens: 3, total_tokens: 13 }, }, { event: "done", data: { status: "completed" } }, ], }); try { const ex = new TraeExecutor(); const { response } = await ex.execute({ model: "auto", body: { messages: [{ role: "user", content: "столица Франции?" }] }, stream: false, credentials: CREDS, }); const json = JSON.parse(await readAll(response)); assert.equal(json.choices[0].message.content, "Париж"); assert.equal(json.choices[0].finish_reason, "stop"); assert.deepEqual(json.usage, { prompt_tokens: 10, completion_tokens: 3, total_tokens: 13 }); // query is JSON-encoded content blocks; model "auto" → auto strategy assert.equal(calls.sessionBody.initial_message.model_selection_strategy, "auto"); assert.match(calls.sessionBody.initial_message.query, /столица Франции/); } finally { restore(); } }); test("manual model → manual strategy + model_name passed through", async () => { const { calls, restore } = installMockFetch({ frames: [{ event: "done", data: { status: "completed" } }], }); try { const ex = new TraeExecutor(); await ex.execute({ model: "gpt-5.2", body: { messages: [{ role: "user", content: "hi" }] }, stream: false, credentials: CREDS, }); assert.equal(calls.sessionBody.initial_message.model_selection_strategy, "manual"); assert.equal(calls.sessionBody.initial_message.model_name, "gpt-5.2"); } finally { restore(); } }); test('model "work" → work session mode, auto strategy, empty model_name', async () => { const { calls, restore } = installMockFetch({ frames: [{ event: "done", data: { status: "completed" } }], }); try { const ex = new TraeExecutor(); await ex.execute({ model: "work", body: { messages: [{ role: "user", content: "hi" }] }, stream: false, credentials: CREDS, }); const im = calls.sessionBody.initial_message; assert.equal(calls.sessionBody.mode, "work"); assert.equal(im.model_selection_strategy, "auto"); assert.equal(im.model_name, ""); // common_params.solo_chat_mode must follow the session mode assert.equal(JSON.parse(im.common_params).solo_chat_mode, "work"); } finally { restore(); } }); test('model "auto" stays in code mode (solo_chat_mode=code)', async () => { const { calls, restore } = installMockFetch({ frames: [{ event: "done", data: { status: "completed" } }], }); try { const ex = new TraeExecutor(); await ex.execute({ model: "auto", body: { messages: [{ role: "user", content: "hi" }] }, stream: false, credentials: CREDS, }); assert.equal(calls.sessionBody.mode, "code"); assert.equal( JSON.parse(calls.sessionBody.initial_message.common_params).solo_chat_mode, "code" ); } finally { restore(); } }); test("stream: emits OpenAI chunks with deltas, finish_reason stop and [DONE]", async () => { const { restore } = installMockFetch({ frames: [ { event: "plan_item", data: { id: "p1", thought: "крас" } }, { event: "plan_item", data: { id: "p1", thought: "красный" } }, { event: "done", data: { status: "completed" } }, ], }); try { const ex = new TraeExecutor(); const { response } = await ex.execute({ model: "auto", body: { messages: [{ role: "user", content: "цвет" }] }, stream: true, credentials: CREDS, }); const text = await readAll(response); // role chunk first, then content deltas, then finish, then DONE assert.match(text, /"delta":\{"role":"assistant"\}/); assert.match(text, /"content":"крас"/); assert.match(text, /"content":"ный"/); // incremental delta after "крас" assert.match(text, /"finish_reason":"stop"/); assert.match(text, /data: \[DONE\]/); } finally { restore(); } }); test("upstream error event surfaces as 502 (non-stream)", async () => { const { restore } = installMockFetch({ frames: [{ event: "error", data: { code: 4001, message: "config item is empty" } }], }); try { const ex = new TraeExecutor(); const { response } = await ex.execute({ model: "deepseek-v3.1", body: { messages: [{ role: "user", content: "hi" }] }, stream: false, credentials: CREDS, }); assert.equal(response.status, 502); const json = JSON.parse(await readAll(response)); assert.match(json.error.message, /4001/); // error responses must not leak stack traces assert.ok(!json.error.message.includes("at /")); } finally { restore(); } }); test("session create failure returns 502", async () => { const { restore } = installMockFetch({ sessionBody: "nope", sessionStatus: 500 }); try { const ex = new TraeExecutor(); const { response } = await ex.execute({ model: "auto", body: { messages: [{ role: "user", content: "hi" }] }, stream: false, credentials: CREDS, }); assert.equal(response.status, 502); } finally { restore(); } }); // ─── refreshCredentials ──────────────────────────────────────────────────── function installRefreshMock(opts: { result?: any; errorCode?: string; httpStatus?: number } = {}) { const calls: { url?: string; body?: any } = {}; const original = globalThis.fetch; globalThis.fetch = (async (input: any, init: any = {}) => { const url = typeof input === "string" ? input : input.url; calls.url = url; calls.body = init.body ? JSON.parse(init.body) : undefined; const payload = opts.errorCode ? { ResponseMetadata: { Error: { Code: opts.errorCode, Message: "bad" } } } : { ResponseMetadata: {}, Result: opts.result }; return new Response(JSON.stringify(payload), { status: opts.httpStatus ?? 200, headers: { "Content-Type": "application/json" }, }); }) as typeof fetch; return { calls, restore: () => (globalThis.fetch = original) }; } test("refreshCredentials posts ExchangeToken with ClientID/RefreshToken and parses Result", async () => { const newToken = "NEW_TOKEN_eyJhbGc"; const newRefresh = "NEW_REFRESH"; const expMs = Date.UTC(2027, 0, 1, 12, 0, 0); // arbitrary future timestamp const { calls, restore } = installRefreshMock({ result: { Token: newToken, RefreshToken: newRefresh, TokenExpireAt: expMs }, }); try { const ex = new TraeExecutor(); const out = await ex.refreshCredentials({ ...CREDS, refreshToken: "OLD_REFRESH", providerSpecificData: { ...CREDS.providerSpecificData, host: "https://api-us-east.trae.ai", clientId: "en1oxy7wnw8j9n", }, }); assert.equal(calls.url, "https://api-us-east.trae.ai/cloudide/api/v3/trae/oauth/ExchangeToken"); assert.deepEqual(calls.body, { ClientID: "en1oxy7wnw8j9n", RefreshToken: "OLD_REFRESH", ClientSecret: "-", UserID: "", }); assert.equal(out?.accessToken, newToken); assert.equal(out?.refreshToken, newRefresh); assert.equal(out?.expiresAt, new Date(expMs).toISOString()); } finally { restore(); } }); test("refreshCredentials returns null when no refresh token is stored", async () => { // No fetch should happen; guard catches missing credential up front. const original = globalThis.fetch; let called = false; globalThis.fetch = (async () => { called = true; return new Response("{}"); }) as typeof fetch; try { const ex = new TraeExecutor(); const out = await ex.refreshCredentials({ ...CREDS, refreshToken: undefined }); assert.equal(out, null); assert.equal(called, false); } finally { globalThis.fetch = original; } }); test("refreshCredentials throws on RefreshTokenInvalid so the next call surfaces auth failure", async () => { const { restore } = installRefreshMock({ errorCode: "RefreshTokenInvalid" }); try { const ex = new TraeExecutor(); await assert.rejects( ex.refreshCredentials({ ...CREDS, refreshToken: "BAD" }), /RefreshTokenInvalid/ ); } finally { restore(); } }); test("refreshCredentials keeps the old refresh token when upstream omits a new one", async () => { // Trae occasionally returns just Token + TokenExpireAt without rotating // RefreshToken — we must preserve the existing one rather than null it out. const { restore } = installRefreshMock({ result: { Token: "NEW", TokenExpireAt: Date.UTC(2027, 0, 1) }, }); try { const ex = new TraeExecutor(); const out = await ex.refreshCredentials({ ...CREDS, refreshToken: "STAYS" }); assert.equal(out?.refreshToken, "STAYS"); } finally { restore(); } }); // ─── /authorize callback parser ──────────────────────────────────────────── // // The HTTP route in src/app/authorize/route.ts is a thin wrapper: parser + // createProviderConnection + HTML response. The parser is pure and is what we // unit-test here — DB persistence and the HTML shell are exercised manually // during the live OAuth flow. const { parseTraeCallbackQuery } = await import("../../src/app/authorize/parseCallback.ts"); test("parseTraeCallbackQuery extracts the full credential bundle from the Trae callback", () => { const userJwt = JSON.stringify({ ClientID: "en1oxy7wnw8j9n", Token: "ACCESS_TOKEN_eyJ", RefreshToken: "REFRESH", TokenExpireAt: Date.UTC(2026, 5, 6), RefreshExpireAt: Date.UTC(2026, 11, 19), TokenExpireDuration: 1209600000, }); const userInfo = JSON.stringify({ UserID: "76428", TenantID: "abc", Region: "US-East", AIRegion: "US", ScreenName: "tester", NonPlainTextEmail: "u***r@example.com", }); const q = new URLSearchParams({ isRedirect: "true", scope: "solo", loginTraceID: "trace-123", host: "https://api-us-east.trae.ai", userJwt, userInfo, }); const result = parseTraeCallbackQuery(q); assert.equal(result.ok, true); if (!result.ok) return; const rec = result.record; assert.equal(rec.provider, "trae"); assert.equal(rec.authType, "oauth"); assert.equal(rec.accessToken, "ACCESS_TOKEN_eyJ"); assert.equal(rec.refreshToken, "REFRESH"); assert.equal(rec.email, "u***r@example.com"); assert.equal(rec.expiresAt, new Date(Date.UTC(2026, 5, 6)).toISOString()); assert.equal(rec.providerSpecificData.userId, "76428"); assert.equal(rec.providerSpecificData.tenantId, "abc"); assert.equal(rec.providerSpecificData.region, "US-East"); assert.equal(rec.providerSpecificData.clientId, "en1oxy7wnw8j9n"); assert.equal(rec.providerSpecificData.host, "https://api-us-east.trae.ai"); assert.equal(rec.providerSpecificData.authMethod, "oauth_callback"); // common_params identity must mirror UserID for all three id fields assert.equal(rec.providerSpecificData.bizUserId, "76428"); assert.equal(rec.providerSpecificData.userUniqueId, "76428"); assert.equal(rec.providerSpecificData.webId, "76428"); }); test("parseTraeCallbackQuery returns an error when userJwt is missing", () => { const result = parseTraeCallbackQuery(new URLSearchParams({ scope: "solo" })); assert.equal(result.ok, false); if (result.ok) return; assert.match(result.error, /Missing userJwt/); }); test("parseTraeCallbackQuery returns an error when userJwt is not valid JSON", () => { const result = parseTraeCallbackQuery(new URLSearchParams({ userJwt: "not-json{{{" })); assert.equal(result.ok, false); if (result.ok) return; assert.match(result.error, /Malformed userJwt/); }); test("parseTraeCallbackQuery falls back to flat refreshToken/refreshExpireAt when nested ones are absent", () => { // Real Trae callbacks always nest the bundle inside userJwt, but the spec also // exposes flat duplicates. Confirm we fall back cleanly if only the flat copies // are present (defensive — protects against schema drift). const userJwt = JSON.stringify({ ClientID: "en1oxy7wnw8j9n", Token: "T" }); const q = new URLSearchParams({ userJwt, refreshToken: "FLAT_REFRESH", refreshExpireAt: String(Date.UTC(2027, 0, 1)), }); const result = parseTraeCallbackQuery(q); assert.equal(result.ok, true); if (!result.ok) return; assert.equal(result.record.refreshToken, "FLAT_REFRESH"); assert.equal(result.record.providerSpecificData.refreshExpireAt, Date.UTC(2027, 0, 1)); });