1import * as Schema from "effect/Schema";
2import { describe, expect, it } from "vite-plus/test";
3
4import { ExecutionEnvironmentDescriptor } from "./environment.ts";
5
6const decodeDescriptor = Schema.decodeUnknownSync(ExecutionEnvironmentDescriptor);
7
8const descriptor = {
9 environmentId: "environment-1",
10 label: "Local",
11 platform: { os: "darwin", arch: "arm64" },
12 serverVersion: "0.0.32",
13 capabilities: { repositoryIdentity: true },
14} as const;
15
16describe("ExecutionEnvironmentDescriptor", () => {
17 it("keeps settled cleanup optional alongside current worktree capabilities", () => {
18 expect(decodeDescriptor(descriptor).capabilities.settledWorktreeCleanup).toBeUndefined();
19 const capabilities = {
20 settledWorktreeCleanup: true,
21 storageCleanupRun: true,
22 projectWorktreeCleanup: true,
23 worktreesDirectory: true,
24 threadAutoSettlementScope: true,
25 };
26 expect(decodeDescriptor({ ...descriptor, capabilities }).capabilities).toMatchObject(
27 capabilities,
28 );
29 });
30
31 it("decodes old, recognized and future manual installation descriptors", () => {
32 expect(decodeDescriptor(descriptor).capabilities.serverInstallation).toBeUndefined();
33 for (const installation of [{ kind: "npx" }, { kind: "npm-global", prefix: "/opt/node" }]) {
34 expect(
35 decodeDescriptor({
36 ...descriptor,
37 capabilities: { ...descriptor.capabilities, serverInstallation: installation },
38 }).capabilities.serverInstallation,
39 ).toEqual(installation);
40 }
41 for (const installation of [{ kind: "future-manager" }, { kind: "npm-global" }]) {
42 expect(
43 decodeDescriptor({
44 ...descriptor,
45 capabilities: { ...descriptor.capabilities, serverInstallation: installation },
46 }).capabilities.serverInstallation,
47 ).toBeUndefined();
48 }
49 });
50 it("requires an advertised required-worktree bootstrap capability", () => {
51 expect(decodeDescriptor(descriptor).capabilities.requiredWorktreeBootstrap).toBeUndefined();
52 expect(
53 decodeDescriptor({
54 ...descriptor,
55 capabilities: { ...descriptor.capabilities, requiredWorktreeBootstrap: true },
56 }).capabilities.requiredWorktreeBootstrap,
57 ).toBe(true);
58 });
59
60 it("treats a missing pull-request capability as unsupported under version skew", () => {
61 expect(decodeDescriptor(descriptor).capabilities.pullRequests).toBeUndefined();
62 });
63
64 it("preserves an advertised pull-request capability", () => {
65 expect(
66 decodeDescriptor({
67 ...descriptor,
68 capabilities: { ...descriptor.capabilities, pullRequests: true },
69 }).capabilities.pullRequests,
70 ).toBe(true);
71 });
72
73 it("treats a missing attachment upload capability as unsupported", () => {
74 expect(decodeDescriptor(descriptor).capabilities.attachmentUploads).toBeUndefined();
75 });
76
77 it("preserves an advertised attachment upload capability", () => {
78 expect(
79 decodeDescriptor({
80 ...descriptor,
81 capabilities: { ...descriptor.capabilities, attachmentUploads: true },
82 }).capabilities.attachmentUploads,
83 ).toBe(true);
84 });
85
86 it("preserves the server's generic attachment upload limit", () => {
87 expect(
88 decodeDescriptor({
89 ...descriptor,
90 capabilities: {
91 ...descriptor.capabilities,
92 fileAttachments: { maxUploadBytes: 50 * 1024 * 1024 },
93 },
94 }).capabilities.fileAttachments,
95 ).toEqual({ maxUploadBytes: 50 * 1024 * 1024 });
96 });
97
98 it("treats missing server-resolved command context as unsupported", () => {
99 expect(decodeDescriptor(descriptor).capabilities.serverResolvedCommandContext).toBeUndefined();
100 });
101
102 it("preserves advertised server-resolved command context", () => {
103 expect(
104 decodeDescriptor({
105 ...descriptor,
106 capabilities: {
107 ...descriptor.capabilities,
108 serverResolvedCommandContext: true,
109 },
110 }).capabilities.serverResolvedCommandContext,
111 ).toBe(true);
112 });
113});