1import * as NodeServices from "@effect/platform-node/NodeServices";
2import { assert, it } from "@effect/vitest";
3import { ProviderInstanceId } from "@t3tools/contracts";
4import { AcpRegistrySettings } from "../settings.ts";
5import * as Effect from "effect/Effect";
6import * as FileSystem from "effect/FileSystem";
7import * as Schema from "effect/Schema";
8
9import { makeAcpRegistryAuthenticationState } from "./authenticationState.ts";
10
11const settings = Schema.decodeSync(AcpRegistrySettings)({ agentId: "devin" });
12
13it.layer(NodeServices.layer)("ACP sign-in confirmation", (it) => {
14 it.effect("restores explicit sign-in across driver recreation and server restart", () =>
15 Effect.gen(function* () {
16 const fs = yield* FileSystem.FileSystem;
17 const cacheDir = yield* fs.makeTempDirectoryScoped();
18 const input = {
19 cacheDir,
20 instanceId: ProviderInstanceId.make("acp_devin"),
21 settings,
22 environment: [],
23 processEnvironment: { HOME: "/home/test" },
24 };
25 const first = yield* makeAcpRegistryAuthenticationState(input);
26 assert.isFalse(yield* first.get);
27 yield* first.set(true);
28 const recreated = yield* makeAcpRegistryAuthenticationState({
29 ...input,
30 settings: { ...settings, customModels: ["new-model"] },
31 });
32 assert.isTrue(yield* recreated.get);
33 // Both logout and an explicit authentication failure revoke confirmation.
34 yield* recreated.set(false);
35 const restarted = yield* makeAcpRegistryAuthenticationState(input);
36 assert.isFalse(yield* restarted.get);
37 }),
38 );
39
40 it.effect(
41 "does not carry confirmation to a different agent, method, executable, or credential environment",
42 () =>
43 Effect.gen(function* () {
44 const fs = yield* FileSystem.FileSystem;
45 const cacheDir = yield* fs.makeTempDirectoryScoped();
46 const input = {
47 cacheDir,
48 instanceId: ProviderInstanceId.make("acp_devin"),
49 settings,
50 environment: [{ name: "DEVIN_TOKEN", value: "test-only-token", sensitive: true }],
51 processEnvironment: { HOME: "/home/test" },
52 };
53 for (const change of [
54 { settings: { ...settings, agentId: "other-agent" } },
55 { settings: { ...settings, authMethodId: "enterprise" } },
56 { settings: { ...settings, commandPath: "/other/devin" } },
57 { environment: [{ name: "DEVIN_TOKEN", value: "different-token", sensitive: true }] },
58 { processEnvironment: { HOME: "/home/other" } },
59 ]) {
60 const signedIn = yield* makeAcpRegistryAuthenticationState(input);
61 yield* signedIn.set(true);
62 const changed = yield* makeAcpRegistryAuthenticationState({ ...input, ...change });
63 assert.isFalse(yield* changed.get);
64 const restoredConfig = yield* makeAcpRegistryAuthenticationState(input);
65 assert.isFalse(yield* restoredConfig.get);
66 }
67 for (const name of yield* fs.readDirectory(cacheDir)) {
68 const contents = yield* fs.readFileString(`${cacheDir}/${name}`);
69 assert.isFalse(contents.includes("test-only-token"));
70 assert.isFalse(contents.includes("different-token"));
71 }
72 }),
73 );
74
75 it.effect("ignores damaged confirmation and keeps another instance independent", () =>
76 Effect.gen(function* () {
77 const fs = yield* FileSystem.FileSystem;
78 const cacheDir = yield* fs.makeTempDirectoryScoped();
79 const input = {
80 cacheDir,
81 instanceId: ProviderInstanceId.make("acp_devin"),
82 settings,
83 environment: [],
84 processEnvironment: {},
85 };
86 const first = yield* makeAcpRegistryAuthenticationState(input);
87 yield* first.set(true);
88 const other = yield* makeAcpRegistryAuthenticationState({
89 ...input,
90 instanceId: ProviderInstanceId.make("acp_other"),
91 });
92 assert.isFalse(yield* other.get);
93 for (const name of yield* fs.readDirectory(cacheDir)) {
94 yield* fs.writeFileString(`${cacheDir}/${name}`, "invalid");
95 }
96 const restored = yield* makeAcpRegistryAuthenticationState(input);
97 assert.isFalse(yield* restored.get);
98 }),
99 );
100});