532 lines · 18.9 KB · TypeScript
RawDownload
import { describe, expect, it } from "@effect/vitest";import {  EnvironmentId,  ProjectId,  ProviderInstanceId,  ThreadId,  type AuthSessionState,  type OrchestrationShellSnapshot,  type OrchestrationThreadDetailSnapshot,} 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 { 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 { fetchEnvironmentSessionState } from "./session.ts";import { fetchEnvironmentShellSnapshot } from "./shellSnapshotHttp.ts";import { fetchEnvironmentThreadSnapshot } from "./threadSnapshotHttp.ts"; 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 = {  snapshotSequence: 1,  projects: [],  threads: [],  updatedAt: "2026-09-04T00:00:00.000Z",} satisfies OrchestrationShellSnapshot;const THREAD = {  snapshotSequence: 2,  thread: {    id: ThreadId.make("thread-1"),    projectId: ProjectId.make("project-1"),    title: "Thread",    modelSelection: { instanceId: ProviderInstanceId.make("codex"), model: "gpt-5.4" },    runtimeMode: "full-access",    interactionMode: "default",    branch: null,    pullRequests: [],    worktreePath: null,    latestTurn: null,    createdAt: "2026-09-04T00:00:00.000Z",    updatedAt: "2026-09-04T00:00:00.000Z",    archivedAt: null,    settledOverride: null,    settledAt: null,    deletedAt: null,    messages: [],    proposedPlans: [],    activities: [],    checkpoints: [],    session: null,  },  page: { beforeCursor: null, hasMore: false, snapshotSequence: 2 },} satisfies OrchestrationThreadDetailSnapshot; 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 load: (    input: HttpInput,  ) => Effect.Effect<    unknown,    RemoteEnvironmentRequestError | PullRequestDiffCredentialRejectedError,    HttpClient.HttpClient  >;}> = [  {    name: "PR diff",    method: "POST",    path: "/api/pull-requests/diff",    response: DIFF_RESULT,    load: (input: HttpInput) => fetchEnvironmentPullRequestDiff({ ...input, diff: DIFF }),  },  {    name: "session permissions",    method: "GET",    path: "/api/auth/session",    response: SESSION,    load: fetchEnvironmentSessionState,  },  {    name: "shell snapshot",    method: "GET",    path: "/api/orchestration/shell",    response: SHELL,    load: fetchEnvironmentShellSnapshot,  },  {    name: "older thread history",    method: "GET",    path: "/api/orchestration/threads/thread-1",    response: THREAD,    load: (input: HttpInput) =>      fetchEnvironmentThreadSnapshot({        ...input,        threadId: THREAD.thread.id,        window: { turnLimit: 20, beforeCursor: "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.response);      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();      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("turnLimit")).toBe("20");        expect(url.searchParams.get("beforeCursor")).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("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 }) =>      Effect.gen(function* () {        const harness = makeHarness(() => Response.json(UNAUTHENTICATED_SESSION));        const result = yield* fetchEnvironmentSessionState({          prepared: { ...PREPARED, httpAuthorization: authorization },          signer: Option.none(),        }).pipe(Effect.provide(harness.httpLayer));         expect(result).toEqual(UNAUTHENTICATED_SESSION);        expect(harness.calls).toHaveLength(1);        expect(harness.authorizations).toEqual([]);        expect(new Headers(harness.calls[0]!.init.headers).get("authorization")).toBe(          authorization === null ? null : "Bearer bearer-token",        );        expect(harness.calls[0]!.init.credentials).toBe(          authorization === null ? "include" : undefined,        );      }),  );   it.effect("keeps the caller's timeout while waiting for renewal", () =>    Effect.gen(function* () {      const harness = makeHarness(() => Response.json(SESSION));      const authorizing = yield* Deferred.make<void>();      const remoteAuthorization = RemoteEnvironmentAuthorization.of({        ...harness.remoteAuthorization,        authorizeDpopHttp: () =>          Deferred.succeed(authorizing, undefined).pipe(Effect.andThen(Effect.never)),      });      const pending = yield* fetchEnvironmentSessionState({        ...harness.input,        remoteAuthorization: Option.some(remoteAuthorization),        timeoutMs: 100,      }).pipe(Effect.provide(harness.httpLayer), Effect.flip, Effect.forkChild);      yield* Deferred.await(authorizing);      yield* TestClock.adjust(100);       expect(yield* Fiber.join(pending)).toMatchObject({        _tag: "RemoteEnvironmentAuthTimeoutError",        requestUrl: `${PREPARED.httpBaseUrl}/api/auth/session`,        timeoutMs: 100,      });      expect(harness.calls).toEqual([]);    }),  );   it.effect("reports the current endpoint when the total timeout expires during HTTP", () =>    Effect.gen(function* () {      const requested = Promise.withResolvers<void>();      const response = Promise.withResolvers<Response>();      const harness = makeHarness(() => {        requested.resolve();        return response.promise;      });      const authorizing = yield* Deferred.make<void>();      const authorize = yield* Deferred.make<void>();      const remoteAuthorization = RemoteEnvironmentAuthorization.of({        ...harness.remoteAuthorization,        authorizeDpopHttp: (input) =>          Deferred.succeed(authorizing, undefined).pipe(            Effect.andThen(Deferred.await(authorize)),            Effect.andThen(harness.remoteAuthorization.authorizeDpopHttp(input)),          ),      });      const pending = yield* fetchEnvironmentSessionState({        ...harness.input,        remoteAuthorization: Option.some(remoteAuthorization),        timeoutMs: 100,      }).pipe(Effect.provide(harness.httpLayer), Effect.flip, Effect.forkChild);      yield* Deferred.await(authorizing);      yield* TestClock.adjust(25);      yield* Deferred.succeed(authorize, undefined);      yield* Effect.promise(() => requested.promise);      yield* TestClock.adjust(75);       expect(yield* Fiber.join(pending)).toMatchObject({        _tag: "RemoteEnvironmentAuthTimeoutError",        requestUrl: `${CURRENT_ORIGIN}/api/auth/session`,        timeoutMs: 100,      });      expect(harness.calls.map((call) => call.url)).toEqual([`${CURRENT_ORIGIN}/api/auth/session`]);      expect(harness.authorizations).toHaveLength(1);      response.resolve(Response.json(SESSION));    }),  );   it.effect("reports renewal failure without sending the expired prepared token", () =>    Effect.gen(function* () {      const harness = makeHarness(() => Response.json(SESSION));      const failure = new ConnectionTransientError({        reason: "transport",        detail: "Relay unavailable",      });      const remoteAuthorization = RemoteEnvironmentAuthorization.of({        ...harness.remoteAuthorization,        authorizeDpopHttp: () => Effect.fail(failure),

Lines 1–500 of 532.