import { createServer } from "node:http"; import { describe, expect, it } from "@effect/vitest"; import * as Effect from "effect/Effect"; import { startOauthLoopbackRedirectServer } from "./oauth-loopback"; const withCompletionServer = async (handler: (input: { completionUrl: string; requests: string[]; }) => Promise): Promise => { const requests: string[] = []; const server = createServer((request, response) => { requests.push(request.url ?? "/"); response.statusCode = 200; response.end("ok"); }); await new Promise((resolve, reject) => { server.listen(0, "127.0.0.1", (error?: Error) => { if (error) { reject(error); return; } resolve(); }); }); try { const address = server.address(); if (!address || typeof address === "string") { throw new Error("Failed to resolve completion server address"); } return await handler({ completionUrl: `http://127.0.0.1:${address.port}/oauth/complete?sessionId=test-session`, requests, }); } finally { await new Promise((resolve, reject) => { server.close((error) => { if (error) { reject(error); return; } resolve(); }); }); } }; describe("oauth-loopback", () => { it("redirects loopback callbacks to the app completion URL with query params intact", async () => { await withCompletionServer(async ({ completionUrl, requests }) => { const receiver = await Effect.runPromise( startOauthLoopbackRedirectServer({ completionUrl, }), ); const response = await fetch( `${receiver.redirectUri}?state=oauth-state&code=oauth-code`, { redirect: "follow", signal: AbortSignal.timeout(10_000), }, ); expect(response.ok).toBe(true); expect(requests).toEqual([ "/oauth/complete?sessionId=test-session&state=oauth-state&code=oauth-code", ]); await Effect.runPromise(receiver.close); }); }); });