1import {
2 DEFAULT_SERVER_SETTINGS,
3 type EnvironmentId,
4 type ProjectId,
5 type ServerSettings,
6} from "@t3tools/contracts";
7import { describe, expect, it } from "vite-plus/test";
8
9import type { SettingsTarget } from "./settings-environment-filter";
10import {
11 planMobileScopedSettingsClear,
12 planMobileScopedSettingsPatch,
13 resolveMobileSettingsTargets,
14 uniformMobileSetting,
15} from "./settings-scoped-server";
16
17const firstId = "first" as EnvironmentId;
18const secondId = "second" as EnvironmentId;
19const firstProject = "first-project" as ProjectId;
20const secondProject = "second-project" as ProjectId;
21
22describe("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
58function 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
68describe("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});