import { describe, expect, it } from "@effect/vitest";import { EnvironmentId, ORCHESTRATION_PROTOCOL_HEADER, ORCHESTRATION_PROTOCOL_VERSION_TEXT, ProjectId, type AuthSessionState, type OrchestrationV2ShellSnapshot, OrchestrationV2ThreadDetailSnapshot, OrchestrationV2ThreadBoundedSnapshot, type OrchestrationV2ThreadHistoryPage,} from "@t3tools/contracts";import * as Deferred from "effect/Deferred";import * as Effect from "effect/Effect";import * as Fiber from "effect/Fiber";import * as Layer from "effect/Layer";import * as Option from "effect/Option";import * as Schema from "effect/Schema";import { TestClock } from "effect/testing";import type { HttpClient } from "effect/unstable/http"; import { RemoteEnvironmentAuthorization } from "../authorization/service.ts";import { ConnectionTransientError, RelayConnectionTarget, type PreparedConnection, type PreparedHttpAuthorization,} from "../connection/model.ts";import { ManagedRelayDpopSigner, type ManagedRelayDpopProofInput } from "../relay/managedRelay.ts";import { remoteHttpClientLayer, type RemoteEnvironmentRequestError } from "../rpc/http.ts";import { fetchEnvironmentPullRequestDiff, type PullRequestDiffCredentialRejectedError, PullRequestDiffLoader, pullRequestDiffLoaderLayer,} from "./pullRequestDiffHttp.ts";import { withOrchestrationProtocolHeader } from "./environmentHttpAuth.ts";import { fetchEnvironmentSessionState } from "./session.ts";import { fetchEnvironmentShellSnapshot } from "./shellSnapshotHttp.ts";import { fetchEnvironmentThreadSnapshot } from "./threadSnapshotHttp.ts";import { boundedThreadSnapshotLoaderLayer, fetchEnvironmentBoundedThreadSnapshot,} from "./boundedThreadSnapshotHttp.ts";import { ThreadSnapshotLoader } from "./threadSnapshotHttp.ts";import { fetchEnvironmentThreadHistoryPage } from "./threadHistoryHttp.ts";import { v2Projection } from "./orchestrationV2TestFixtures.ts"; const encodeThreadSnapshot = Schema.encodeSync(OrchestrationV2ThreadDetailSnapshot);const encodeBoundedSnapshot = Schema.encodeSync(OrchestrationV2ThreadBoundedSnapshot); const TARGET = new RelayConnectionTarget({ environmentId: EnvironmentId.make("environment-1"), label: "Remote environment",});const PREPARED: PreparedConnection = { environmentId: TARGET.environmentId, label: TARGET.label, httpBaseUrl: "https://previous.example.test", socketUrl: "wss://previous.example.test/ws", httpAuthorization: { _tag: "Dpop", accessToken: "expired-token", expiresAtEpochMs: 0 }, target: TARGET,};const CURRENT_ORIGIN = "https://current.example.test";const RENEWED_ORIGIN = "https://renewed.example.test";const DIFF = { projectId: ProjectId.make("project-1"), repository: "owner/repository", number: 42,};const DIFF_RESULT = { patch: "diff --git a/file.ts b/file.ts", truncated: false, nextCursor: null };const AUTH = { policy: "remote-reachable", bootstrapMethods: ["one-time-token"], sessionMethods: ["dpop-access-token"], sessionCookieName: "t3_session",} satisfies AuthSessionState["auth"];const SESSION = { authenticated: true, auth: AUTH, scopes: ["orchestration:read", "orchestration:operate"], sessionMethod: "dpop-access-token",} satisfies AuthSessionState;const UNAUTHENTICATED_SESSION = { authenticated: false, auth: AUTH } satisfies AuthSessionState;const SHELL = { schemaVersion: 1, snapshotSequence: 1, projects: [], threads: [], archivedThreads: [],} satisfies OrchestrationV2ShellSnapshot;const THREAD = { snapshotSequence: 2, projection: v2Projection,} satisfies OrchestrationV2ThreadDetailSnapshot;const BOUNDED_THREAD = { ...THREAD, historyCursor: "older-page", hasMoreHistory: true, latestLocalTurnOrdinal: 3,} satisfies OrchestrationV2ThreadBoundedSnapshot;const THREAD_HISTORY = { snapshotSequence: 2, items: [], nextCursor: null, hasMoreHistory: false,} satisfies OrchestrationV2ThreadHistoryPage; function credentialRejectedResponse(reason = "invalid_credential") { return Response.json( { _tag: "EnvironmentAuthInvalidError", code: "auth_invalid", reason, traceId: "trace-rejected", }, { status: 401 }, );} function makeHarness(reply: (requestNumber: number) => Response | Promise<Response>) { const calls: Array<{ readonly url: string; readonly init: RequestInit }> = []; const authorizations: Array< Parameters<RemoteEnvironmentAuthorization["Service"]["authorizeDpopHttp"]>[0] > = []; const proofs: Array<ManagedRelayDpopProofInput> = []; const remoteAuthorization = RemoteEnvironmentAuthorization.of({ authorizeBearer: () => Effect.die("Unexpected bearer connection preparation."), authorizeDpop: () => Effect.die("HTTP requests must not prepare a WebSocket connection."), authorizeDpopHttp: (input) => Effect.sync(() => { authorizations.push(input); const rejected = input.rejectedAccessToken !== undefined; return { environmentId: TARGET.environmentId, label: TARGET.label, httpBaseUrl: rejected ? RENEWED_ORIGIN : CURRENT_ORIGIN, httpAuthorization: { _tag: "Dpop" as const, accessToken: rejected ? "renewed-token" : "current-token", expiresAtEpochMs: 3_600_000, }, }; }), }); const signer = ManagedRelayDpopSigner.of({ thumbprint: Effect.succeed("test-thumbprint"), createProof: (input) => Effect.sync(() => { proofs.push(input); return `proof-${proofs.length}`; }), }); const fetchFn: typeof fetch = async (request, init) => { calls.push({ url: String(request), init: init ?? {} }); return reply(calls.length); }; return { calls, authorizations, proofs, remoteAuthorization, input: { prepared: PREPARED, signer: Option.some(signer), remoteAuthorization: Option.some(remoteAuthorization), }, httpLayer: remoteHttpClientLayer(fetchFn), };} type HttpInput = ReturnType<typeof makeHarness>["input"];const LOADERS: ReadonlyArray<{ readonly name: string; readonly method: string; readonly path: string; readonly response: unknown; readonly expected: unknown; readonly load: ( input: HttpInput, ) => Effect.Effect< unknown, RemoteEnvironmentRequestError | PullRequestDiffCredentialRejectedError, HttpClient.HttpClient >;}> = [ { name: "PR diff", method: "POST", path: "/api/pull-requests/diff", response: DIFF_RESULT, expected: DIFF_RESULT, load: (input: HttpInput) => fetchEnvironmentPullRequestDiff({ ...input, diff: DIFF }), }, { name: "session permissions", method: "GET", path: "/api/auth/session", response: SESSION, expected: SESSION, load: fetchEnvironmentSessionState, }, { name: "shell snapshot", method: "GET", path: "/api/orchestration/shell", response: SHELL, expected: SHELL, load: fetchEnvironmentShellSnapshot, }, { name: "thread snapshot", method: "GET", path: `/api/orchestration/threads/${THREAD.projection.thread.id}`, response: encodeThreadSnapshot(THREAD), expected: THREAD, load: (input: HttpInput) => fetchEnvironmentThreadSnapshot({ ...input, threadId: THREAD.projection.thread.id }), }, { name: "bounded thread snapshot", method: "GET", path: `/api/orchestration/threads/${THREAD.projection.thread.id}/bounded`, response: encodeBoundedSnapshot(BOUNDED_THREAD), expected: BOUNDED_THREAD, load: (input: HttpInput) => fetchEnvironmentBoundedThreadSnapshot({ ...input, threadId: THREAD.projection.thread.id }), }, { name: "older thread history", method: "GET", path: `/api/orchestration/threads/${THREAD.projection.thread.id}/history`, response: THREAD_HISTORY, expected: THREAD_HISTORY, load: (input: HttpInput) => fetchEnvironmentThreadHistoryPage({ ...input, threadId: THREAD.projection.thread.id, cursor: "older-page", }), },]; describe("authenticated environment HTTP requests", () => { it.effect.each(LOADERS)("uses current relay authorization and endpoint for $name", (loader) => Effect.gen(function* () { const harness = makeHarness(() => Response.json(loader.response)); const result = yield* loader.load(harness.input).pipe(Effect.provide(harness.httpLayer)); expect(result).toEqual(loader.expected); expect(harness.calls).toHaveLength(1); const call = harness.calls[0]!; const url = new URL(call.url); expect(url.origin).toBe(CURRENT_ORIGIN); expect(url.pathname).toBe(loader.path); expect(call.init.method).toBe(loader.method); expect(new Headers(call.init.headers).get("authorization")).toBe("DPoP current-token"); expect(new Headers(call.init.headers).get("dpop")).toBe("proof-1"); expect(call.init.credentials).toBeUndefined(); if (loader.path.startsWith("/api/orchestration/")) { expect(new Headers(call.init.headers).get(ORCHESTRATION_PROTOCOL_HEADER)).toBe( ORCHESTRATION_PROTOCOL_VERSION_TEXT, ); } expect(harness.authorizations).toEqual([{ expectedEnvironmentId: TARGET.environmentId }]); expect(harness.proofs).toEqual([ { method: loader.method, url: `${CURRENT_ORIGIN}${loader.path}`, accessToken: "current-token", }, ]); if (loader.name === "older thread history") { expect(url.searchParams.get("cursor")).toBe("older-page"); } expect(PREPARED.httpAuthorization).toMatchObject({ accessToken: "expired-token" }); }), ); it.effect("retries a rejected diff once with a new token, endpoint, and proof", () => Effect.gen(function* () { const harness = makeHarness((requestNumber) => requestNumber === 1 ? credentialRejectedResponse() : Response.json(DIFF_RESULT), ); const result = yield* fetchEnvironmentPullRequestDiff({ ...harness.input, diff: DIFF }).pipe( Effect.provide(harness.httpLayer), ); expect(result).toEqual(DIFF_RESULT); expect(harness.authorizations).toEqual([ { expectedEnvironmentId: TARGET.environmentId }, { expectedEnvironmentId: TARGET.environmentId, rejectedAccessToken: "current-token" }, ]); expect(harness.calls.map((call) => call.url)).toEqual([ `${CURRENT_ORIGIN}/api/pull-requests/diff`, `${RENEWED_ORIGIN}/api/pull-requests/diff`, ]); expect( harness.calls.map((call) => new Headers(call.init.headers).get("authorization")), ).toEqual(["DPoP current-token", "DPoP renewed-token"]); expect(harness.calls.map((call) => new Headers(call.init.headers).get("dpop"))).toEqual([ "proof-1", "proof-2", ]); expect(harness.proofs[1]).toEqual({ method: "POST", url: `${RENEWED_ORIGIN}/api/pull-requests/diff`, accessToken: "renewed-token", }); expect(harness.calls[1]!.init.body).toEqual(harness.calls[0]!.init.body); }), ); it.effect.each(LOADERS.filter((loader) => loader.path.startsWith("/api/orchestration/")))( "renews a rejected credential without losing the v2 protocol or query for $name", (loader) => Effect.gen(function* () { const harness = makeHarness((requestNumber) => requestNumber === 1 ? credentialRejectedResponse() : Response.json(loader.response), ); const result = yield* loader.load(harness.input).pipe(Effect.provide(harness.httpLayer)); expect(result).toEqual(loader.expected); expect(harness.calls).toHaveLength(2); const retried = harness.calls[1]!; const url = new URL(retried.url); expect(url.origin).toBe(RENEWED_ORIGIN); expect(url.pathname).toBe(loader.path); expect(new Headers(retried.init.headers).get("authorization")).toBe("DPoP renewed-token"); expect(new Headers(retried.init.headers).get("dpop")).toBe("proof-2"); expect(new Headers(retried.init.headers).get(ORCHESTRATION_PROTOCOL_HEADER)).toBe( ORCHESTRATION_PROTOCOL_VERSION_TEXT, ); expect(harness.proofs[1]).toEqual({ method: "GET", url: `${RENEWED_ORIGIN}${loader.path}`, accessToken: "renewed-token", }); if (loader.name === "older thread history") { expect(url.searchParams.get("cursor")).toBe("older-page"); } }), ); it.effect("renews credentials captured by the bounded snapshot loader", () => Effect.gen(function* () { const harness = makeHarness((requestNumber) => requestNumber === 1 ? credentialRejectedResponse() : Response.json(encodeBoundedSnapshot(BOUNDED_THREAD)), ); const loaderLayer = boundedThreadSnapshotLoaderLayer.pipe( Layer.provide( Layer.mergeAll( harness.httpLayer, Layer.succeed(ManagedRelayDpopSigner, Option.getOrThrow(harness.input.signer)), Layer.succeed(RemoteEnvironmentAuthorization, harness.remoteAuthorization), ), ), ); const loader = yield* ThreadSnapshotLoader.pipe(Effect.provide(loaderLayer)); const result = yield* loader.load(PREPARED, THREAD.projection.thread.id); expect(result).toEqual({ _tag: "present", snapshot: { ...THREAD, latestLocalTurnOrdinal: BOUNDED_THREAD.latestLocalTurnOrdinal }, history: { historyCursor: BOUNDED_THREAD.historyCursor, hasMoreHistory: true, latestLocalTurnOrdinal: BOUNDED_THREAD.latestLocalTurnOrdinal, }, }); expect(harness.calls).toHaveLength(2); expect(new Headers(harness.calls[1]!.init.headers).get("authorization")).toBe( "DPoP renewed-token", ); }), ); it.effect("uses the authorization service captured by the diff loader layer", () => Effect.gen(function* () { const harness = makeHarness(() => Response.json(DIFF_RESULT)); const loaderLayer = pullRequestDiffLoaderLayer.pipe( Layer.provide( Layer.mergeAll( harness.httpLayer, Layer.succeed(ManagedRelayDpopSigner, Option.getOrThrow(harness.input.signer)), Layer.succeed(RemoteEnvironmentAuthorization, harness.remoteAuthorization), ), ), ); const loader = yield* PullRequestDiffLoader.pipe(Effect.provide(loaderLayer)); const result = yield* loader.load(PREPARED, DIFF); expect(result).toEqual(DIFF_RESULT); expect(new Headers(harness.calls[0]!.init.headers).get("authorization")).toBe( "DPoP current-token", ); }), ); it.effect("preserves the credential rejection after the one recovery attempt fails", () => Effect.gen(function* () { const harness = makeHarness(() => credentialRejectedResponse()); const error = yield* fetchEnvironmentPullRequestDiff({ ...harness.input, diff: DIFF }).pipe( Effect.provide(harness.httpLayer), Effect.flip, ); expect(error).toMatchObject({ _tag: "PullRequestDiffCredentialRejectedError", traceId: "trace-rejected", }); expect(harness.calls).toHaveLength(2); expect(harness.authorizations).toHaveLength(2); }), ); it.effect.each([ { name: "insufficient scope", reply: () => Response.json( { _tag: "EnvironmentScopeRequiredError", code: "insufficient_scope", requiredScope: "orchestration:read", traceId: "trace-scope", }, { status: 403 }, ), errorTag: "EnvironmentScopeRequiredError", }, { name: "missing credential", reply: () => credentialRejectedResponse("missing_credential"), errorTag: "EnvironmentAuthInvalidError", }, { name: "Cloudflare 530", reply: () => new Response("error code: 1033", { status: 530 }), errorTag: "RemoteEnvironmentAuthUndeclaredStatusError", }, { name: "network failure", reply: () => Promise.reject(new Error("Network unreachable")), errorTag: "RemoteEnvironmentAuthFetchError", }, ])("does not renew or retry on $name", ({ reply, errorTag }) => Effect.gen(function* () { const harness = makeHarness(reply); const error = yield* fetchEnvironmentPullRequestDiff({ ...harness.input, diff: DIFF }).pipe( Effect.provide(harness.httpLayer), Effect.flip, ); expect(error._tag).toBe(errorTag); expect(harness.calls).toHaveLength(1); expect(harness.authorizations).toEqual([{ expectedEnvironmentId: TARGET.environmentId }]); }), ); it.effect("recovers a session's unauthenticated 200 response before checking permissions", () => Effect.gen(function* () { const harness = makeHarness((requestNumber) => Response.json(requestNumber === 1 ? UNAUTHENTICATED_SESSION : SESSION), ); const result = yield* fetchEnvironmentSessionState(harness.input).pipe( Effect.provide(harness.httpLayer), ); expect(result).toEqual(SESSION); expect(harness.authorizations[1]).toEqual({ expectedEnvironmentId: TARGET.environmentId, rejectedAccessToken: "current-token", }); expect(harness.calls).toHaveLength(2); }), ); it.effect("reports persistent session rejection instead of showing missing permissions", () => Effect.gen(function* () { const harness = makeHarness(() => Response.json(UNAUTHENTICATED_SESSION)); const error = yield* fetchEnvironmentSessionState(harness.input).pipe( Effect.provide(harness.httpLayer), Effect.flip, ); expect(error).toMatchObject({ _tag: "RemoteEnvironmentAuthFetchError", message: "The environment rejected the renewed session authorization.", }); expect(harness.calls).toHaveLength(2); }), ); it.effect.each([ { name: "cookie", authorization: null }, { name: "bearer", authorization: { _tag: "Bearer", token: "bearer-token" } }, ] satisfies ReadonlyArray<{ name: string; authorization: PreparedHttpAuthorization | null }>)( "leaves $name sessions unchanged without relay services", ({ authorization }) =>Lines 1–500 of 642. Load more
import { describe, expect, it } from "@effect/vitest"; import { EnvironmentId, ORCHESTRATION_PROTOCOL_HEADER, ORCHESTRATION_PROTOCOL_VERSION_TEXT, ProjectId, type AuthSessionState, type OrchestrationV2ShellSnapshot, OrchestrationV2ThreadDetailSnapshot, OrchestrationV2ThreadBoundedSnapshot, type OrchestrationV2ThreadHistoryPage, } from "@t3tools/contracts"; import * as Deferred from "effect/Deferred"; import * as Effect from "effect/Effect"; import * as Fiber from "effect/Fiber"; import * as Layer from "effect/Layer"; import * as Option from "effect/Option"; import * as Schema from "effect/Schema"; import { TestClock } from "effect/testing"; import type { HttpClient } from "effect/unstable/http"; import { RemoteEnvironmentAuthorization } from "../authorization/service.ts"; import { ConnectionTransientError, RelayConnectionTarget, type PreparedConnection, type PreparedHttpAuthorization, } from "../connection/model.ts"; import { ManagedRelayDpopSigner, type ManagedRelayDpopProofInput } from "../relay/managedRelay.ts"; import { remoteHttpClientLayer, type RemoteEnvironmentRequestError } from "../rpc/http.ts"; import { fetchEnvironmentPullRequestDiff, type PullRequestDiffCredentialRejectedError, PullRequestDiffLoader, pullRequestDiffLoaderLayer, } from "./pullRequestDiffHttp.ts"; import { withOrchestrationProtocolHeader } from "./environmentHttpAuth.ts"; import { fetchEnvironmentSessionState } from "./session.ts"; import { fetchEnvironmentShellSnapshot } from "./shellSnapshotHttp.ts"; import { fetchEnvironmentThreadSnapshot } from "./threadSnapshotHttp.ts"; import { boundedThreadSnapshotLoaderLayer, fetchEnvironmentBoundedThreadSnapshot, } from "./boundedThreadSnapshotHttp.ts"; import { ThreadSnapshotLoader } from "./threadSnapshotHttp.ts"; import { fetchEnvironmentThreadHistoryPage } from "./threadHistoryHttp.ts"; import { v2Projection } from "./orchestrationV2TestFixtures.ts"; const encodeThreadSnapshot = Schema.encodeSync(OrchestrationV2ThreadDetailSnapshot); const encodeBoundedSnapshot = Schema.encodeSync(OrchestrationV2ThreadBoundedSnapshot); const TARGET = new RelayConnectionTarget({ environmentId: EnvironmentId.make("environment-1"), label: "Remote environment", }); const PREPARED: PreparedConnection = { environmentId: TARGET.environmentId, label: TARGET.label, httpBaseUrl: "https://previous.example.test", socketUrl: "wss://previous.example.test/ws", httpAuthorization: { _tag: "Dpop", accessToken: "expired-token", expiresAtEpochMs: 0 }, target: TARGET, }; const CURRENT_ORIGIN = "https://current.example.test"; const RENEWED_ORIGIN = "https://renewed.example.test"; const DIFF = { projectId: ProjectId.make("project-1"), repository: "owner/repository", number: 42, }; const DIFF_RESULT = { patch: "diff --git a/file.ts b/file.ts", truncated: false, nextCursor: null }; const AUTH = { policy: "remote-reachable", bootstrapMethods: ["one-time-token"], sessionMethods: ["dpop-access-token"], sessionCookieName: "t3_session", } satisfies AuthSessionState["auth"]; const SESSION = { authenticated: true, auth: AUTH, scopes: ["orchestration:read", "orchestration:operate"], sessionMethod: "dpop-access-token", } satisfies AuthSessionState; const UNAUTHENTICATED_SESSION = { authenticated: false, auth: AUTH } satisfies AuthSessionState; const SHELL = { schemaVersion: 1, snapshotSequence: 1, projects: [], threads: [], archivedThreads: [], } satisfies OrchestrationV2ShellSnapshot; const THREAD = { snapshotSequence: 2, projection: v2Projection, } satisfies OrchestrationV2ThreadDetailSnapshot; const BOUNDED_THREAD = { ...THREAD, historyCursor: "older-page", hasMoreHistory: true, latestLocalTurnOrdinal: 3, } satisfies OrchestrationV2ThreadBoundedSnapshot; const THREAD_HISTORY = { snapshotSequence: 2, items: [], nextCursor: null, hasMoreHistory: false, } satisfies OrchestrationV2ThreadHistoryPage; function credentialRejectedResponse(reason = "invalid_credential") { return Response.json( { _tag: "EnvironmentAuthInvalidError", code: "auth_invalid", reason, traceId: "trace-rejected", }, { status: 401 }, ); } function makeHarness(reply: (requestNumber: number) => Response | Promise<Response>) { const calls: Array<{ readonly url: string; readonly init: RequestInit }> = []; const authorizations: Array< Parameters<RemoteEnvironmentAuthorization["Service"]["authorizeDpopHttp"]>[0] > = []; const proofs: Array<ManagedRelayDpopProofInput> = []; const remoteAuthorization = RemoteEnvironmentAuthorization.of({ authorizeBearer: () => Effect.die("Unexpected bearer connection preparation."), authorizeDpop: () => Effect.die("HTTP requests must not prepare a WebSocket connection."), authorizeDpopHttp: (input) => Effect.sync(() => { authorizations.push(input); const rejected = input.rejectedAccessToken !== undefined; return { environmentId: TARGET.environmentId, label: TARGET.label, httpBaseUrl: rejected ? RENEWED_ORIGIN : CURRENT_ORIGIN, httpAuthorization: { _tag: "Dpop" as const, accessToken: rejected ? "renewed-token" : "current-token", expiresAtEpochMs: 3_600_000, }, }; }), }); const signer = ManagedRelayDpopSigner.of({ thumbprint: Effect.succeed("test-thumbprint"), createProof: (input) => Effect.sync(() => { proofs.push(input); return `proof-${proofs.length}`; }), }); const fetchFn: typeof fetch = async (request, init) => { calls.push({ url: String(request), init: init ?? {} }); return reply(calls.length); }; return { calls, authorizations, proofs, remoteAuthorization, input: { prepared: PREPARED, signer: Option.some(signer), remoteAuthorization: Option.some(remoteAuthorization), }, httpLayer: remoteHttpClientLayer(fetchFn), }; } type HttpInput = ReturnType<typeof makeHarness>["input"]; const LOADERS: ReadonlyArray<{ readonly name: string; readonly method: string; readonly path: string; readonly response: unknown; readonly expected: unknown; readonly load: ( input: HttpInput, ) => Effect.Effect< unknown, RemoteEnvironmentRequestError | PullRequestDiffCredentialRejectedError, HttpClient.HttpClient >; }> = [ { name: "PR diff", method: "POST", path: "/api/pull-requests/diff", response: DIFF_RESULT, expected: DIFF_RESULT, load: (input: HttpInput) => fetchEnvironmentPullRequestDiff({ ...input, diff: DIFF }), }, { name: "session permissions", method: "GET", path: "/api/auth/session", response: SESSION, expected: SESSION, load: fetchEnvironmentSessionState, }, { name: "shell snapshot", method: "GET", path: "/api/orchestration/shell", response: SHELL, expected: SHELL, load: fetchEnvironmentShellSnapshot, }, { name: "thread snapshot", method: "GET", path: `/api/orchestration/threads/${THREAD.projection.thread.id}`, response: encodeThreadSnapshot(THREAD), expected: THREAD, load: (input: HttpInput) => fetchEnvironmentThreadSnapshot({ ...input, threadId: THREAD.projection.thread.id }), }, { name: "bounded thread snapshot", method: "GET", path: `/api/orchestration/threads/${THREAD.projection.thread.id}/bounded`, response: encodeBoundedSnapshot(BOUNDED_THREAD), expected: BOUNDED_THREAD, load: (input: HttpInput) => fetchEnvironmentBoundedThreadSnapshot({ ...input, threadId: THREAD.projection.thread.id }), }, { name: "older thread history", method: "GET", path: `/api/orchestration/threads/${THREAD.projection.thread.id}/history`, response: THREAD_HISTORY, expected: THREAD_HISTORY, load: (input: HttpInput) => fetchEnvironmentThreadHistoryPage({ ...input, threadId: THREAD.projection.thread.id, cursor: "older-page", }), }, ]; describe("authenticated environment HTTP requests", () => { it.effect.each(LOADERS)("uses current relay authorization and endpoint for $name", (loader) => Effect.gen(function* () { const harness = makeHarness(() => Response.json(loader.response)); const result = yield* loader.load(harness.input).pipe(Effect.provide(harness.httpLayer)); expect(result).toEqual(loader.expected); expect(harness.calls).toHaveLength(1); const call = harness.calls[0]!; const url = new URL(call.url); expect(url.origin).toBe(CURRENT_ORIGIN); expect(url.pathname).toBe(loader.path); expect(call.init.method).toBe(loader.method); expect(new Headers(call.init.headers).get("authorization")).toBe("DPoP current-token"); expect(new Headers(call.init.headers).get("dpop")).toBe("proof-1"); expect(call.init.credentials).toBeUndefined(); if (loader.path.startsWith("/api/orchestration/")) { expect(new Headers(call.init.headers).get(ORCHESTRATION_PROTOCOL_HEADER)).toBe( ORCHESTRATION_PROTOCOL_VERSION_TEXT, ); } expect(harness.authorizations).toEqual([{ expectedEnvironmentId: TARGET.environmentId }]); expect(harness.proofs).toEqual([ { method: loader.method, url: `${CURRENT_ORIGIN}${loader.path}`, accessToken: "current-token", }, ]); if (loader.name === "older thread history") { expect(url.searchParams.get("cursor")).toBe("older-page"); } expect(PREPARED.httpAuthorization).toMatchObject({ accessToken: "expired-token" }); }), ); it.effect("retries a rejected diff once with a new token, endpoint, and proof", () => Effect.gen(function* () { const harness = makeHarness((requestNumber) => requestNumber === 1 ? credentialRejectedResponse() : Response.json(DIFF_RESULT), ); const result = yield* fetchEnvironmentPullRequestDiff({ ...harness.input, diff: DIFF }).pipe( Effect.provide(harness.httpLayer), ); expect(result).toEqual(DIFF_RESULT); expect(harness.authorizations).toEqual([ { expectedEnvironmentId: TARGET.environmentId }, { expectedEnvironmentId: TARGET.environmentId, rejectedAccessToken: "current-token" }, ]); expect(harness.calls.map((call) => call.url)).toEqual([ `${CURRENT_ORIGIN}/api/pull-requests/diff`, `${RENEWED_ORIGIN}/api/pull-requests/diff`, ]); expect( harness.calls.map((call) => new Headers(call.init.headers).get("authorization")), ).toEqual(["DPoP current-token", "DPoP renewed-token"]); expect(harness.calls.map((call) => new Headers(call.init.headers).get("dpop"))).toEqual([ "proof-1", "proof-2", ]); expect(harness.proofs[1]).toEqual({ method: "POST", url: `${RENEWED_ORIGIN}/api/pull-requests/diff`, accessToken: "renewed-token", }); expect(harness.calls[1]!.init.body).toEqual(harness.calls[0]!.init.body); }), ); it.effect.each(LOADERS.filter((loader) => loader.path.startsWith("/api/orchestration/")))( "renews a rejected credential without losing the v2 protocol or query for $name", (loader) => Effect.gen(function* () { const harness = makeHarness((requestNumber) => requestNumber === 1 ? credentialRejectedResponse() : Response.json(loader.response), ); const result = yield* loader.load(harness.input).pipe(Effect.provide(harness.httpLayer)); expect(result).toEqual(loader.expected); expect(harness.calls).toHaveLength(2); const retried = harness.calls[1]!; const url = new URL(retried.url); expect(url.origin).toBe(RENEWED_ORIGIN); expect(url.pathname).toBe(loader.path); expect(new Headers(retried.init.headers).get("authorization")).toBe("DPoP renewed-token"); expect(new Headers(retried.init.headers).get("dpop")).toBe("proof-2"); expect(new Headers(retried.init.headers).get(ORCHESTRATION_PROTOCOL_HEADER)).toBe( ORCHESTRATION_PROTOCOL_VERSION_TEXT, ); expect(harness.proofs[1]).toEqual({ method: "GET", url: `${RENEWED_ORIGIN}${loader.path}`, accessToken: "renewed-token", }); if (loader.name === "older thread history") { expect(url.searchParams.get("cursor")).toBe("older-page"); } }), ); it.effect("renews credentials captured by the bounded snapshot loader", () => Effect.gen(function* () { const harness = makeHarness((requestNumber) => requestNumber === 1 ? credentialRejectedResponse() : Response.json(encodeBoundedSnapshot(BOUNDED_THREAD)), ); const loaderLayer = boundedThreadSnapshotLoaderLayer.pipe( Layer.provide( Layer.mergeAll( harness.httpLayer, Layer.succeed(ManagedRelayDpopSigner, Option.getOrThrow(harness.input.signer)), Layer.succeed(RemoteEnvironmentAuthorization, harness.remoteAuthorization), ), ), ); const loader = yield* ThreadSnapshotLoader.pipe(Effect.provide(loaderLayer)); const result = yield* loader.load(PREPARED, THREAD.projection.thread.id); expect(result).toEqual({ _tag: "present", snapshot: { ...THREAD, latestLocalTurnOrdinal: BOUNDED_THREAD.latestLocalTurnOrdinal }, history: { historyCursor: BOUNDED_THREAD.historyCursor, hasMoreHistory: true, latestLocalTurnOrdinal: BOUNDED_THREAD.latestLocalTurnOrdinal, }, }); expect(harness.calls).toHaveLength(2); expect(new Headers(harness.calls[1]!.init.headers).get("authorization")).toBe( "DPoP renewed-token", ); }), ); it.effect("uses the authorization service captured by the diff loader layer", () => Effect.gen(function* () { const harness = makeHarness(() => Response.json(DIFF_RESULT)); const loaderLayer = pullRequestDiffLoaderLayer.pipe( Layer.provide( Layer.mergeAll( harness.httpLayer, Layer.succeed(ManagedRelayDpopSigner, Option.getOrThrow(harness.input.signer)), Layer.succeed(RemoteEnvironmentAuthorization, harness.remoteAuthorization), ), ), ); const loader = yield* PullRequestDiffLoader.pipe(Effect.provide(loaderLayer)); const result = yield* loader.load(PREPARED, DIFF); expect(result).toEqual(DIFF_RESULT); expect(new Headers(harness.calls[0]!.init.headers).get("authorization")).toBe( "DPoP current-token", ); }), ); it.effect("preserves the credential rejection after the one recovery attempt fails", () => Effect.gen(function* () { const harness = makeHarness(() => credentialRejectedResponse()); const error = yield* fetchEnvironmentPullRequestDiff({ ...harness.input, diff: DIFF }).pipe( Effect.provide(harness.httpLayer), Effect.flip, ); expect(error).toMatchObject({ _tag: "PullRequestDiffCredentialRejectedError", traceId: "trace-rejected", }); expect(harness.calls).toHaveLength(2); expect(harness.authorizations).toHaveLength(2); }), ); it.effect.each([ { name: "insufficient scope", reply: () => Response.json( { _tag: "EnvironmentScopeRequiredError", code: "insufficient_scope", requiredScope: "orchestration:read", traceId: "trace-scope", }, { status: 403 }, ), errorTag: "EnvironmentScopeRequiredError", }, { name: "missing credential", reply: () => credentialRejectedResponse("missing_credential"), errorTag: "EnvironmentAuthInvalidError", }, { name: "Cloudflare 530", reply: () => new Response("error code: 1033", { status: 530 }), errorTag: "RemoteEnvironmentAuthUndeclaredStatusError", }, { name: "network failure", reply: () => Promise.reject(new Error("Network unreachable")), errorTag: "RemoteEnvironmentAuthFetchError", }, ])("does not renew or retry on $name", ({ reply, errorTag }) => Effect.gen(function* () { const harness = makeHarness(reply); const error = yield* fetchEnvironmentPullRequestDiff({ ...harness.input, diff: DIFF }).pipe( Effect.provide(harness.httpLayer), Effect.flip, ); expect(error._tag).toBe(errorTag); expect(harness.calls).toHaveLength(1); expect(harness.authorizations).toEqual([{ expectedEnvironmentId: TARGET.environmentId }]); }), ); it.effect("recovers a session's unauthenticated 200 response before checking permissions", () => Effect.gen(function* () { const harness = makeHarness((requestNumber) => Response.json(requestNumber === 1 ? UNAUTHENTICATED_SESSION : SESSION), ); const result = yield* fetchEnvironmentSessionState(harness.input).pipe( Effect.provide(harness.httpLayer), ); expect(result).toEqual(SESSION); expect(harness.authorizations[1]).toEqual({ expectedEnvironmentId: TARGET.environmentId, rejectedAccessToken: "current-token", }); expect(harness.calls).toHaveLength(2); }), ); it.effect("reports persistent session rejection instead of showing missing permissions", () => Effect.gen(function* () { const harness = makeHarness(() => Response.json(UNAUTHENTICATED_SESSION)); const error = yield* fetchEnvironmentSessionState(harness.input).pipe( Effect.provide(harness.httpLayer), Effect.flip, ); expect(error).toMatchObject({ _tag: "RemoteEnvironmentAuthFetchError", message: "The environment rejected the renewed session authorization.", }); expect(harness.calls).toHaveLength(2); }), ); it.effect.each([ { name: "cookie", authorization: null }, { name: "bearer", authorization: { _tag: "Bearer", token: "bearer-token" } }, ] satisfies ReadonlyArray<{ name: string; authorization: PreparedHttpAuthorization | null }>)( "leaves $name sessions unchanged without relay services", ({ authorization }) =>