1import { assert, it } from "@effect/vitest";
2import * as Effect from "effect/Effect";
3import * as Layer from "effect/Layer";
4import * as Redacted from "effect/Redacted";
5import * as HttpClient from "effect/http/HttpClient";
6import * as HttpClientResponse from "effect/http/HttpClientResponse";
7import { describe } from "vite-plus/test";
8
9import * as OpenCode2Client from "./OpenCode2Client.ts";
10
11const INFO_BODY = '{"version":"2.0.18","pid":4242,"urls":[],"paths":{"tmp":"/tmp"}}';
12
13/** Sends one request through the client and returns the Authorization header it carried. */
14const authorizationFor = (password: string | Redacted.Redacted) => {
15 const seen: Array<string | undefined> = [];
16 const layerCapturing = Layer.succeed(
17 HttpClient.HttpClient,
18 HttpClient.make((request) =>
19 Effect.sync(() => {
20 seen.push(request.headers.authorization);
21 return HttpClientResponse.fromWeb(
22 request,
23 new Response(INFO_BODY, { headers: { "content-type": "application/json" } }),
24 );
25 }),
26 ),
27 );
28 return Effect.gen(function* () {
29 const opencode = yield* OpenCode2Client.OpenCode2Client;
30 const { client } = yield* opencode.connect({ baseUrl: "http://127.0.0.1:4096", password });
31 yield* client.server.info();
32 return seen[0];
33 }).pipe(Effect.provide(OpenCode2Client.layer.pipe(Layer.provide(layerCapturing))));
34};
35
36const utf8Basic = (password: string) =>
37 `Basic ${Buffer.from(`opencode:${password}`, "utf8").toString("base64")}`;
38
39describe("OpenCode2Client authorization", () => {
40 // OpenCode decodes Basic credentials as UTF-8. Latin-1 (`btoa`) encoding gets
41 // `pässwörd` rejected with a 401 and throws on characters such as `€`.
42 it.effect("encodes non-ASCII passwords as UTF-8", () =>
43 Effect.gen(function* () {
44 assert.strictEqual(yield* authorizationFor("pässwörd"), utf8Basic("pässwörd"));
45 assert.strictEqual(yield* authorizationFor("pass€word"), utf8Basic("pass€word"));
46 assert.strictEqual(yield* authorizationFor(Redacted.make("pässwörd")), utf8Basic("pässwörd"));
47 }),
48 );
49});