| 1 | import { |
| 2 | DEFAULT_SERVER_SETTINGS, |
| 3 | type EnvironmentId, |
| 4 | type ProjectId, |
| 5 | type ServerSettings, |
| 6 | } from "@t3tools/contracts"; |
| 7 | import { describe, expect, it } from "vite-plus/test"; |
| 8 | |
| 9 | import type { SettingsTarget } from "./settings-environment-filter"; |
| 10 | import { |
| 11 | planMobileScopedSettingsClear, |
| 12 | planMobileScopedSettingsPatch, |
| 13 | resolveMobileSettingsTargets, |
| 14 | uniformMobileSetting, |
| 15 | } from "./settings-scoped-server"; |
| 16 | |
| 17 | const firstId = "first" as EnvironmentId; |
| 18 | const secondId = "second" as EnvironmentId; |
| 19 | const firstProject = "first-project" as ProjectId; |
| 20 | const secondProject = "second-project" as ProjectId; |
| 21 | |
| 22 | describe("mobile usage-limit settings across environments", () => { |
| 23 | it.each(["autoResumeLimitedThreads", "snoozeLimitedThreads"] as const)( |
| 24 | "shows %s as mixed and can enable it everywhere without changing other settings", |
| 25 | (key) => { |
| 26 | const targets = resolveMobileSettingsTargets( |
| 27 | [ |
| 28 | environment(firstId, { ...DEFAULT_SERVER_SETTINGS, [key]: true }), |
| 29 | environment(secondId, { ...DEFAULT_SERVER_SETTINGS, [key]: false }), |
| 30 | ], |
| 31 | null, |
| 32 | ); |
| 33 | |
| 34 | expect(uniformMobileSetting(targets, key)).toBeNull(); |
| 35 | const writes = planMobileScopedSettingsPatch(targets, false, { [key]: true }); |
| 36 | expect(writes).toEqual([ |
| 37 | { environmentId: firstId, patch: { [key]: true } }, |
| 38 | { environmentId: secondId, patch: { [key]: true } }, |
| 39 | ]); |
| 40 | const updatedTargets = targets.map((target) => ({ |
| 41 | ...target, |
| 42 | settings: { |
| 43 | ...target.settings, |
| 44 | [key]: |
| 45 | writes.find((write) => write.environmentId === target.environment.environmentId)?.patch[ |
| 46 | key |
| 47 | ] ?? target.settings[key], |
| 48 | }, |
| 49 | })); |
| 50 | expect(uniformMobileSetting(updatedTargets, key)).toBe(true); |
| 51 | expect(uniformMobileSetting(targets.slice(0, 1), key)).toBe(true); |
| 52 | expect(uniformMobileSetting(targets.slice(1), key)).toBe(false); |
| 53 | expect(uniformMobileSetting([], key)).toBeNull(); |
| 54 | }, |
| 55 | ); |
| 56 | }); |
| 57 | |
| 58 | function environment(environmentId: EnvironmentId, settings: ServerSettings): SettingsTarget { |
| 59 | return { |
| 60 | environmentId, |
| 61 | serverConfig: { |
| 62 | settings, |
| 63 | environment: { capabilities: { projectSettingsOverrides: true } }, |
| 64 | }, |
| 65 | } as SettingsTarget; |
| 66 | } |
| 67 | |
| 68 | describe("mobile project settings scope", () => { |
| 69 | it("edits each checkout's own override without changing either environment default", () => { |
| 70 | const firstSettings: ServerSettings = { |
| 71 | ...DEFAULT_SERVER_SETTINGS, |
| 72 | responseStreamingMode: "paragraph", |
| 73 | projectSettingsOverrides: { [firstProject]: { defaultAutoPull: true } }, |
| 74 | }; |
| 75 | const secondSettings: ServerSettings = { |
| 76 | ...DEFAULT_SERVER_SETTINGS, |
| 77 | responseStreamingMode: "turn", |
| 78 | projectSettingsOverrides: {}, |
| 79 | }; |
| 80 | const targets = resolveMobileSettingsTargets( |
| 81 | [environment(firstId, firstSettings), environment(secondId, secondSettings)], |
| 82 | [ |
| 83 | { environmentId: firstId, id: firstProject }, |
| 84 | { environmentId: secondId, id: secondProject }, |
| 85 | ], |
| 86 | ); |
| 87 | |
| 88 | const writes = planMobileScopedSettingsPatch(targets, true, { |
| 89 | responseStreamingMode: "turn", |
| 90 | }); |
| 91 | expect(writes).toEqual([ |
| 92 | { |
| 93 | environmentId: firstId, |
| 94 | patch: { |
| 95 | projectSettingsOverrides: { |
| 96 | [firstProject]: { defaultAutoPull: true, responseStreamingMode: "turn" }, |
| 97 | }, |
| 98 | }, |
| 99 | }, |
| 100 | { |
| 101 | environmentId: secondId, |
| 102 | patch: { projectSettingsOverrides: { [secondProject]: { responseStreamingMode: "turn" } } }, |
| 103 | }, |
| 104 | ]); |
| 105 | expect(firstSettings.responseStreamingMode).toBe("paragraph"); |
| 106 | expect(secondSettings.responseStreamingMode).toBe("turn"); |
| 107 | }); |
| 108 | |
| 109 | it("removes a project override when a picker sends null for a key that cannot store it", () => { |
| 110 | const settings: ServerSettings = { |
| 111 | ...DEFAULT_SERVER_SETTINGS, |
| 112 | projectSettingsOverrides: { |
| 113 | [firstProject]: { defaultThreadEnvMode: "worktree", defaultAutoPull: true }, |
| 114 | }, |
| 115 | }; |
| 116 | const targets = resolveMobileSettingsTargets( |
| 117 | [environment(firstId, settings)], |
| 118 | [{ environmentId: firstId, id: firstProject }], |
| 119 | ); |
| 120 | expect(planMobileScopedSettingsPatch(targets, true, { defaultThreadEnvMode: null })).toEqual([ |
| 121 | { |
| 122 | environmentId: firstId, |
| 123 | patch: { projectSettingsOverrides: { [firstProject]: { defaultAutoPull: true } } }, |
| 124 | }, |
| 125 | ]); |
| 126 | expect(planMobileScopedSettingsPatch(targets, true, { defaultModelSelection: null })).toEqual([ |
| 127 | { |
| 128 | environmentId: firstId, |
| 129 | patch: { |
| 130 | projectSettingsOverrides: { |
| 131 | [firstProject]: { |
| 132 | defaultThreadEnvMode: "worktree", |
| 133 | defaultAutoPull: true, |
| 134 | defaultModelSelection: null, |
| 135 | }, |
| 136 | }, |
| 137 | }, |
| 138 | }, |
| 139 | ]); |
| 140 | }); |
| 141 | |
| 142 | it("resets only the selected page's override and rejects environment-wide writes", () => { |
| 143 | const settings: ServerSettings = { |
| 144 | ...DEFAULT_SERVER_SETTINGS, |
| 145 | projectSettingsOverrides: { |
| 146 | [firstProject]: { defaultAutoPull: true, responseStreamingMode: "turn" }, |
| 147 | }, |
| 148 | }; |
| 149 | const targets = resolveMobileSettingsTargets( |
| 150 | [environment(firstId, settings)], |
| 151 | [{ environmentId: firstId, id: firstProject }], |
| 152 | ); |
| 153 | |
| 154 | expect(planMobileScopedSettingsClear(targets, ["responseStreamingMode"])).toEqual([ |
| 155 | { |
| 156 | environmentId: firstId, |
| 157 | patch: { projectSettingsOverrides: { [firstProject]: { defaultAutoPull: true } } }, |
| 158 | }, |
| 159 | ]); |
| 160 | expect( |
| 161 | planMobileScopedSettingsPatch(targets, true, { enableProviderUpdateChecks: false }), |
| 162 | ).toEqual([]); |
| 163 | }); |
| 164 | }); |