1import * as NodeServices from "@effect/platform-node/NodeServices";
2import { assert, it } from "@effect/vitest";
3import { AuthProvidersManageScope, AuthOrchestrationReadScope } from "@t3tools/contracts";
4import * as Effect from "effect/Effect";
5import * as Context from "effect/Context";
6import * as Layer from "effect/Layer";
7import * as Schema from "effect/Schema";
8import { HttpRouter } from "effect/http";
9import * as ServerConfig from "../config.ts";
10import * as EnvironmentAuth from "../auth/EnvironmentAuth.ts";
11import { layerSharedAccountRoute } from "../http.ts";
12import * as SharedAccounts from "./SharedAccounts.ts";
13
14const encode = Schema.encodeSync(Schema.fromJsonString(Schema.Unknown));
15const layer = SharedAccounts.layer.pipe(
16 Layer.provideMerge(EnvironmentAuth.layerRuntime),
17 Layer.provide(ServerConfig.layerTest(process.cwd(), { prefix: "t3-shared-http-" })),
18 Layer.provide(NodeServices.layer),
19);
20
21it.live(
22 "requires provider management authority and never returns credential causes in HTTP errors",
23 () =>
24 Effect.gen(function* () {
25 const auth = yield* EnvironmentAuth.EnvironmentAuth;
26 const accounts = yield* SharedAccounts.SharedAccounts;
27 const manager = yield* auth.issueSession({ scopes: [AuthProvidersManageScope] });
28 const reader = yield* auth.issueSession({ scopes: [AuthOrchestrationReadScope] });
29 const requestContext = Context.make(EnvironmentAuth.EnvironmentAuth, auth).pipe(
30 Context.add(SharedAccounts.SharedAccounts, accounts),
31 );
32 const app = yield* Effect.acquireRelease(
33 Effect.sync(() =>
34 HttpRouter.toWebHandler(layerSharedAccountRoute, { disableLogger: true }),
35 ),
36 (app) => Effect.promise(() => app.dispose()),
37 );
38 const post = (body: unknown, token?: string) =>
39 Effect.promise(() =>
40 app.handler(
41 new Request("http://127.0.0.1/api/shared/accounts", {
42 method: "POST",
43 headers: {
44 "content-type": "application/json",
45 ...(token ? { authorization: `Bearer ${token}` } : {}),
46 },
47 body: encode(body),
48 }),
49 requestContext,
50 ),
51 );
52 assert.strictEqual((yield* post({ type: "list" })).status, 401);
53 assert.strictEqual((yield* post({ type: "list" }, reader.token)).status, 403);
54 const response = yield* post({ type: "list" }, manager.token);
55 assert.strictEqual(response.status, 200);
56 assert.strictEqual(response.headers.get("cache-control"), "no-store");
57 const invalid = yield* post(
58 {
59 type: "import",
60 label: "Personal",
61 credentials: { driver: "codex", accessToken: "private-value-in-malformed-input" },
62 },
63 manager.token,
64 );
65 assert.strictEqual(invalid.status, 503);
66 const error = yield* Effect.promise(() => invalid.text());
67 assert.isFalse(error.includes("private-value"));
68 assert.isFalse(error.includes("cause"));
69 }).pipe(Effect.provide(layer)),
70);