| 1 | import { |
| 2 | DEFAULT_SERVER_SETTINGS, |
| 3 | EnvironmentId, |
| 4 | ProjectId, |
| 5 | ProviderDriverKind, |
| 6 | ProviderInstanceId, |
| 7 | } from "@t3tools/contracts"; |
| 8 | import { describe, expect, it } from "@effect/vitest"; |
| 9 | |
| 10 | import { |
| 11 | filterSharedServerPatch, |
| 12 | findSharedSettingsMismatches, |
| 13 | pickSharedServerSettings, |
| 14 | splitSharedServerPatch, |
| 15 | supportsSharedSettingsSync, |
| 16 | } from "./sharedSettings.ts"; |
| 17 | |
| 18 | const primaryId = EnvironmentId.make("env-primary"); |
| 19 | const laptopId = EnvironmentId.make("env-laptop"); |
| 20 | const boxId = EnvironmentId.make("env-box"); |
| 21 | const restartCapabilities = { threadRestartContinuation: true }; |
| 22 | |
| 23 | describe("supportsSharedSettingsSync", () => { |
| 24 | it("accepts only connected servers that advertise the shared-settings capability", () => { |
| 25 | expect( |
| 26 | supportsSharedSettingsSync({ |
| 27 | connection: { phase: "connected" }, |
| 28 | serverConfig: { environment: { capabilities: { threadAutoSettlement: true } } }, |
| 29 | }), |
| 30 | ).toBe(true); |
| 31 | expect( |
| 32 | supportsSharedSettingsSync({ |
| 33 | connection: { phase: "connected" }, |
| 34 | serverConfig: { environment: { capabilities: {} } }, |
| 35 | }), |
| 36 | ).toBe(false); |
| 37 | expect( |
| 38 | supportsSharedSettingsSync({ |
| 39 | connection: { phase: "reconnecting" }, |
| 40 | serverConfig: { environment: { capabilities: { threadAutoSettlement: true } } }, |
| 41 | }), |
| 42 | ).toBe(false); |
| 43 | }); |
| 44 | }); |
| 45 | |
| 46 | describe("splitSharedServerPatch", () => { |
| 47 | it("keeps project overrides local: project ids belong to one environment", () => { |
| 48 | const patch = { |
| 49 | projectSettingsOverrides: { [ProjectId.make("project")]: { defaultAutoPull: true } }, |
| 50 | sidebarAutoSettleOnMerge: false, |
| 51 | }; |
| 52 | expect(splitSharedServerPatch(patch)).toEqual({ |
| 53 | sharedPatch: { sidebarAutoSettleOnMerge: false }, |
| 54 | localPatch: { projectSettingsOverrides: patch.projectSettingsOverrides }, |
| 55 | }); |
| 56 | }); |
| 57 | |
| 58 | it.each([ |
| 59 | { |
| 60 | instanceId: ProviderInstanceId.make("codex"), |
| 61 | model: "gpt-5.6-sol", |
| 62 | options: [{ id: "reasoningEffort", value: "low" }], |
| 63 | }, |
| 64 | { |
| 65 | instanceId: ProviderInstanceId.make("claudeAgent"), |
| 66 | model: "claude-sonnet-4-6", |
| 67 | options: [{ id: "effort", value: "high" }], |
| 68 | }, |
| 69 | DEFAULT_SERVER_SETTINGS.textGenerationModelSelection, |
| 70 | ])("shares the text generation model and options, including reset (%j)", (selection) => { |
| 71 | const patch = { textGenerationModelSelection: selection }; |
| 72 | expect(splitSharedServerPatch(patch)).toEqual({ sharedPatch: patch, localPatch: {} }); |
| 73 | expect(pickSharedServerSettings({ ...DEFAULT_SERVER_SETTINGS, ...patch })).toMatchObject(patch); |
| 74 | const environment = { |
| 75 | environmentId: boxId, |
| 76 | label: "Remote Box", |
| 77 | syncEligible: true, |
| 78 | settings: { |
| 79 | ...DEFAULT_SERVER_SETTINGS, |
| 80 | textGenerationModelSelection: { ...selection, model: "different-model" }, |
| 81 | }, |
| 82 | }; |
| 83 | const input = { |
| 84 | primaryEnvironmentId: primaryId, |
| 85 | primarySettings: { ...DEFAULT_SERVER_SETTINGS, ...patch }, |
| 86 | environments: [environment], |
| 87 | }; |
| 88 | expect(findSharedSettingsMismatches(input)).toEqual([ |
| 89 | { environmentId: boxId, label: "Remote Box" }, |
| 90 | ]); |
| 91 | expect( |
| 92 | findSharedSettingsMismatches({ |
| 93 | ...input, |
| 94 | environments: [{ ...environment, settings: input.primarySettings }], |
| 95 | }), |
| 96 | ).toEqual([]); |
| 97 | }); |
| 98 | |
| 99 | it("routes preference keys to the shared patch and machine keys to the local patch", () => { |
| 100 | const { sharedPatch, localPatch } = splitSharedServerPatch({ |
| 101 | sidebarAutoSettleAfterDays: 7, |
| 102 | sidebarAutoSettleOnMerge: false, |
| 103 | continueThreadsAfterServerUpdate: true, |
| 104 | enableAgentBrowserAccess: false, |
| 105 | defaultThreadEnvMode: "worktree", |
| 106 | newWorktreesStartFromOrigin: true, |
| 107 | }); |
| 108 | expect(sharedPatch).toEqual({ |
| 109 | sidebarAutoSettleAfterDays: 7, |
| 110 | sidebarAutoSettleOnMerge: false, |
| 111 | continueThreadsAfterServerUpdate: true, |
| 112 | newWorktreesStartFromOrigin: true, |
| 113 | }); |
| 114 | expect(localPatch).toEqual({ |
| 115 | enableAgentBrowserAccess: false, |
| 116 | defaultThreadEnvMode: "worktree", |
| 117 | }); |
| 118 | }); |
| 119 | }); |
| 120 | |
| 121 | describe("pickSharedServerSettings", () => { |
| 122 | it("returns only the shared keys", () => { |
| 123 | expect( |
| 124 | Object.keys(pickSharedServerSettings(DEFAULT_SERVER_SETTINGS, restartCapabilities)).sort(), |
| 125 | ).toEqual([ |
| 126 | "autoResumeLimitedThreads", |
| 127 | "continueThreadsAfterServerUpdate", |
| 128 | "newWorktreesStartFromOrigin", |
| 129 | "sidebarAutoSettleAfterDays", |
| 130 | "sidebarAutoSettleOnMerge", |
| 131 | "snoozeLimitedThreads", |
| 132 | "sourceControlWritingStyle", |
| 133 | "textGenerationModelSelection", |
| 134 | ]); |
| 135 | }); |
| 136 | }); |
| 137 | |
| 138 | describe("filterSharedServerPatch", () => { |
| 139 | it.each([true, false])( |
| 140 | "resets a disabled default provider only on the originating environment (%s)", |
| 141 | (targetIsSource) => { |
| 142 | const settings = { |
| 143 | ...DEFAULT_SERVER_SETTINGS, |
| 144 | providerInstances: { |
| 145 | codex: { driver: ProviderDriverKind.make("codex"), enabled: false, config: {} }, |
| 146 | claudeAgent: { |
| 147 | driver: ProviderDriverKind.make("claudeAgent"), |
| 148 | enabled: true, |
| 149 | config: {}, |
| 150 | }, |
| 151 | }, |
| 152 | textGenerationModelSelection: { |
| 153 | instanceId: ProviderInstanceId.make("claudeAgent"), |
| 154 | model: "claude-opus-4-6", |
| 155 | }, |
| 156 | }; |
| 157 | const patch = { |
| 158 | textGenerationModelSelection: DEFAULT_SERVER_SETTINGS.textGenerationModelSelection, |
| 159 | continueThreadsAfterServerUpdate: true, |
| 160 | sidebarAutoSettleAfterDays: 7, |
| 161 | }; |
| 162 | expect(filterSharedServerPatch(patch, undefined, settings, settings, targetIsSource)).toEqual( |
| 163 | { |
| 164 | ...(targetIsSource |
| 165 | ? { textGenerationModelSelection: DEFAULT_SERVER_SETTINGS.textGenerationModelSelection } |
| 166 | : {}), |
| 167 | sidebarAutoSettleAfterDays: 7, |
| 168 | }, |
| 169 | ); |
| 170 | }, |
| 171 | ); |
| 172 | |
| 173 | it.each(["missing", "disabled", "different-driver", "enabled"] as const)( |
| 174 | "shares a custom model only when its target provider is enabled (%s)", |
| 175 | (availability) => { |
| 176 | const instanceId = ProviderInstanceId.make("codex_personal"); |
| 177 | const selection = { |
| 178 | instanceId, |
| 179 | model: "gpt-5.6-luna", |
| 180 | options: [{ id: "reasoningEffort", value: "low" }], |
| 181 | }; |
| 182 | const instance = { |
| 183 | driver: ProviderDriverKind.make( |
| 184 | availability === "different-driver" ? "claudeAgent" : "codex", |
| 185 | ), |
| 186 | enabled: availability !== "disabled", |
| 187 | config: {}, |
| 188 | }; |
| 189 | const settings = { |
| 190 | ...DEFAULT_SERVER_SETTINGS, |
| 191 | providerInstances: availability === "missing" ? {} : { [instanceId]: instance }, |
| 192 | }; |
| 193 | const patch = { sidebarAutoSettleAfterDays: 7, textGenerationModelSelection: selection }; |
| 194 | const sourceSettings = { |
| 195 | ...settings, |
| 196 | providerInstances: { |
| 197 | [instanceId]: { ...instance, driver: ProviderDriverKind.make("codex"), enabled: true }, |
| 198 | }, |
| 199 | }; |
| 200 | expect(filterSharedServerPatch(patch, restartCapabilities, settings, sourceSettings)).toEqual( |
| 201 | availability === "enabled" ? patch : { sidebarAutoSettleAfterDays: 7 }, |
| 202 | ); |
| 203 | const primarySettings = { |
| 204 | ...sourceSettings, |
| 205 | textGenerationModelSelection: selection, |
| 206 | }; |
| 207 | expect( |
| 208 | findSharedSettingsMismatches({ |
| 209 | primaryEnvironmentId: primaryId, |
| 210 | primarySettings, |
| 211 | environments: [ |
| 212 | { environmentId: boxId, label: "Remote Box", syncEligible: true, settings }, |
| 213 | ], |
| 214 | }), |
| 215 | ).toEqual(availability === "enabled" ? [{ environmentId: boxId, label: "Remote Box" }] : []); |
| 216 | }, |
| 217 | ); |
| 218 | |
| 219 | it.each([true, false])("preserves supported restart preference %s", (enabled) => { |
| 220 | const patch = { continueThreadsAfterServerUpdate: enabled, sidebarAutoSettleAfterDays: 7 }; |
| 221 | expect(filterSharedServerPatch(patch, restartCapabilities)).toEqual(patch); |
| 222 | }); |
| 223 | |
| 224 | it.each([undefined, {}, { threadRestartContinuation: false }])( |
| 225 | "omits only the unsupported restart preference with capabilities %j", |
| 226 | (capabilities) => { |
| 227 | expect( |
| 228 | filterSharedServerPatch( |
| 229 | { continueThreadsAfterServerUpdate: true, sidebarAutoSettleAfterDays: 7 }, |
| 230 | capabilities, |
| 231 | ), |
| 232 | ).toEqual({ sidebarAutoSettleAfterDays: 7 }); |
| 233 | expect(pickSharedServerSettings(DEFAULT_SERVER_SETTINGS, capabilities)).not.toHaveProperty( |
| 234 | "continueThreadsAfterServerUpdate", |
| 235 | ); |
| 236 | }, |
| 237 | ); |
| 238 | }); |
| 239 | |
| 240 | describe("findSharedSettingsMismatches", () => { |
| 241 | const primarySettings = { ...DEFAULT_SERVER_SETTINGS, sidebarAutoSettleAfterDays: 7 }; |
| 242 | |
| 243 | it.each([true, false])( |
| 244 | "detects remote restart continuation drift when the preference is %s", |
| 245 | (enabled) => { |
| 246 | const settings = { ...primarySettings, continueThreadsAfterServerUpdate: enabled }; |
| 247 | const remoteSettings = { ...settings, continueThreadsAfterServerUpdate: !enabled }; |
| 248 | const environment = { |
| 249 | environmentId: boxId, |
| 250 | label: "Remote Box", |
| 251 | syncEligible: true, |
| 252 | settings: remoteSettings, |
| 253 | capabilities: restartCapabilities, |
| 254 | }; |
| 255 | expect( |
| 256 | findSharedSettingsMismatches({ |
| 257 | primaryEnvironmentId: primaryId, |
| 258 | primarySettings: settings, |
| 259 | primaryCapabilities: restartCapabilities, |
| 260 | environments: [environment], |
| 261 | }), |
| 262 | ).toEqual([{ environmentId: boxId, label: "Remote Box" }]); |
| 263 | expect( |
| 264 | findSharedSettingsMismatches({ |
| 265 | primaryEnvironmentId: primaryId, |
| 266 | primarySettings: settings, |
| 267 | primaryCapabilities: restartCapabilities, |
| 268 | environments: [ |
| 269 | { |
| 270 | ...environment, |
| 271 | settings: Object.assign( |
| 272 | {}, |
| 273 | remoteSettings, |
| 274 | pickSharedServerSettings(settings, restartCapabilities), |
| 275 | ), |
| 276 | }, |
| 277 | ], |
| 278 | }), |
| 279 | ).toEqual([]); |
| 280 | }, |
| 281 | ); |
| 282 | |
| 283 | it.each([ |
| 284 | [undefined, restartCapabilities], |
| 285 | [restartCapabilities, undefined], |
| 286 | [undefined, undefined], |
| 287 | ])( |
| 288 | "ignores restart drift unless both servers support it (%j, %j)", |
| 289 | (primaryCapabilities, capabilities) => { |
| 290 | const environment = { |
| 291 | environmentId: boxId, |
| 292 | label: "Remote Box", |
| 293 | syncEligible: true, |
| 294 | capabilities, |
| 295 | settings: { ...primarySettings, continueThreadsAfterServerUpdate: true }, |
| 296 | }; |
| 297 | const input = { |
| 298 | primaryEnvironmentId: primaryId, |
| 299 | primarySettings, |
| 300 | primaryCapabilities, |
| 301 | environments: [environment], |
| 302 | }; |
| 303 | expect(findSharedSettingsMismatches(input)).toEqual([]); |
| 304 | expect( |
| 305 | findSharedSettingsMismatches({ |
| 306 | ...input, |
| 307 | environments: [ |
| 308 | { |
| 309 | ...environment, |
| 310 | settings: { ...environment.settings, sidebarAutoSettleAfterDays: 14 }, |
| 311 | }, |
| 312 | ], |
| 313 | }), |
| 314 | ).toEqual([{ environmentId: boxId, label: "Remote Box" }]); |
| 315 | }, |
| 316 | ); |
| 317 | |
| 318 | it("lists sync-eligible environments whose shared settings differ", () => { |
| 319 | const mismatches = findSharedSettingsMismatches({ |
| 320 | primaryEnvironmentId: primaryId, |
| 321 | primarySettings, |
| 322 | environments: [ |
| 323 | { |
| 324 | environmentId: primaryId, |
| 325 | label: "Desktop", |
| 326 | syncEligible: true, |
| 327 | settings: primarySettings, |
| 328 | }, |
| 329 | { |
| 330 | environmentId: laptopId, |
| 331 | label: "Laptop", |
| 332 | syncEligible: true, |
| 333 | settings: primarySettings, |
| 334 | }, |
| 335 | { |
| 336 | environmentId: boxId, |
| 337 | label: "Remote Box", |
| 338 | syncEligible: true, |
| 339 | settings: DEFAULT_SERVER_SETTINGS, |
| 340 | }, |
| 341 | ], |
| 342 | }); |
| 343 | expect(mismatches).toEqual([{ environmentId: boxId, label: "Remote Box" }]); |
| 344 | }); |
| 345 | |
| 346 | it("ignores machine-only differences", () => { |
| 347 | const mismatches = findSharedSettingsMismatches({ |
| 348 | primaryEnvironmentId: primaryId, |
| 349 | primarySettings, |
| 350 | environments: [ |
| 351 | { |
| 352 | environmentId: boxId, |
| 353 | label: "Remote Box", |
| 354 | syncEligible: true, |
| 355 | settings: { |
| 356 | ...primarySettings, |
| 357 | enableAgentBrowserAccess: false, |
| 358 | defaultThreadEnvMode: |
| 359 | primarySettings.defaultThreadEnvMode === "local" ? "worktree" : "local", |
| 360 | }, |
| 361 | }, |
| 362 | ], |
| 363 | }); |
| 364 | expect(mismatches).toEqual([]); |
| 365 | }); |
| 366 | |
| 367 | it("reports nothing until the primary environment's settings are loaded", () => { |
| 368 | const environments = [ |
| 369 | { |
| 370 | environmentId: boxId, |
| 371 | label: "Remote Box", |
| 372 | syncEligible: true, |
| 373 | settings: primarySettings, |
| 374 | }, |
| 375 | ]; |
| 376 | expect( |
| 377 | findSharedSettingsMismatches({ primaryEnvironmentId: null, primarySettings, environments }), |
| 378 | ).toEqual([]); |
| 379 | expect( |
| 380 | findSharedSettingsMismatches({ |
| 381 | primaryEnvironmentId: primaryId, |
| 382 | primarySettings: null, |
| 383 | environments, |
| 384 | }), |
| 385 | ).toEqual([]); |
| 386 | }); |
| 387 | |
| 388 | it("skips ineligible environments and environments without a loaded config", () => { |
| 389 | const mismatches = findSharedSettingsMismatches({ |
| 390 | primaryEnvironmentId: primaryId, |
| 391 | primarySettings, |
| 392 | environments: [ |
| 393 | { |
| 394 | environmentId: laptopId, |
| 395 | label: "Laptop", |
| 396 | syncEligible: false, |
| 397 | settings: DEFAULT_SERVER_SETTINGS, |
| 398 | }, |
| 399 | { environmentId: boxId, label: "Remote Box", syncEligible: true, settings: null }, |
| 400 | ], |
| 401 | }); |
| 402 | expect(mismatches).toEqual([]); |
| 403 | }); |
| 404 | }); |