

























@@ -4,6 +4,7 @@ import {
44applyConfig,
55ensureAgentConfigEntry,
66findAgentConfigEntryIndex,
7+loadConfig,
78resetConfigPendingChanges,
89runUpdate,
910saveConfig,
@@ -33,6 +34,7 @@ function createState(): ConfigState {
3334configSchemaVersion: null,
3435configSearchQuery: "",
3536configSnapshot: null,
37+configDraftBaseHash: null,
3638configUiHints: {},
3739configValid: null,
3840connected: false,
@@ -115,6 +117,59 @@ describe("applyConfigSnapshot", () => {
115117expect(state.configFormOriginal).toEqual({ original: true });
116118});
117119120+it("keeps the draft base hash when preserving dirty edits across refreshes", () => {
121+const state = createState();
122+applyConfigSnapshot(state, {
123+hash: "hash-original",
124+config: { gateway: { mode: "local" } },
125+valid: true,
126+issues: [],
127+raw: '{ "gateway": { "mode": "local" } }',
128+});
129+130+updateConfigFormValue(state, ["gateway", "mode"], "remote");
131+applyConfigSnapshot(state, {
132+hash: "hash-refreshed",
133+config: { gateway: { mode: "external" } },
134+valid: true,
135+issues: [],
136+raw: '{ "gateway": { "mode": "external" } }',
137+});
138+139+expect(state.configSnapshot?.hash).toBe("hash-refreshed");
140+expect(state.configDraftBaseHash).toBe("hash-original");
141+expect(state.configForm).toEqual({ gateway: { mode: "remote" } });
142+});
143+144+it("discards dirty form edits when explicitly requested", () => {
145+const state = createState();
146+state.configFormMode = "form";
147+state.configFormDirty = true;
148+state.configForm = { gateway: { mode: "local", port: 18789 } };
149+state.configFormOriginal = { gateway: { mode: "local", port: 18789 } };
150+state.configRawOriginal =
151+'{\n "gateway": {\n "mode": "local",\n "port": 18789\n }\n}\n';
152+153+applyConfigSnapshot(
154+state,
155+{
156+hash: "hash-remote",
157+config: { gateway: { mode: "remote", port: 9999 } },
158+valid: true,
159+issues: [],
160+raw: '{\n "gateway": { "mode": "remote", "port": 9999 }\n}\n',
161+},
162+{ discardPendingChanges: true },
163+);
164+165+expect(state.configFormDirty).toBe(false);
166+expect(state.configForm).toEqual({ gateway: { mode: "remote", port: 9999 } });
167+expect(state.configFormOriginal).toEqual({ gateway: { mode: "remote", port: 9999 } });
168+expect(state.configRaw).toBe('{\n "gateway": { "mode": "remote", "port": 9999 }\n}\n');
169+expect(state.configRawOriginal).toBe('{\n "gateway": { "mode": "remote", "port": 9999 }\n}\n');
170+expect(state.configDraftBaseHash).toBe("hash-remote");
171+});
172+118173it("forces form mode when the snapshot does not include raw text", () => {
119174const state = createState();
120175state.configFormMode = "raw";
@@ -131,6 +186,28 @@ describe("applyConfigSnapshot", () => {
131186});
132187});
133188189+describe("loadConfig", () => {
190+it("passes explicit reload mode through to snapshot application", async () => {
191+const request = vi.fn().mockResolvedValue({
192+config: { gateway: { mode: "remote" } },
193+valid: true,
194+issues: [],
195+raw: '{\n "gateway": { "mode": "remote" }\n}\n',
196+});
197+const state = createState();
198+state.connected = true;
199+state.client = { request } as unknown as ConfigState["client"];
200+state.configFormDirty = true;
201+state.configForm = { gateway: { mode: "local" } };
202+203+await loadConfig(state, { discardPendingChanges: true });
204+205+expect(state.configFormDirty).toBe(false);
206+expect(state.configForm).toEqual({ gateway: { mode: "remote" } });
207+expect(state.configRawOriginal).toBe('{\n "gateway": { "mode": "remote" }\n}\n');
208+});
209+});
210+134211describe("updateConfigFormValue", () => {
135212it("seeds from snapshot when form is null", () => {
136213const state = createState();
@@ -469,6 +546,35 @@ describe("applyConfig", () => {
469546});
470547471548describe("saveConfig", () => {
549+it("submits the original draft base hash after a dirty config refresh", async () => {
550+const request = createRequestWithConfigGet();
551+const state = createState();
552+state.connected = true;
553+state.client = { request } as unknown as ConfigState["client"];
554+state.configFormMode = "form";
555+applyConfigSnapshot(state, {
556+hash: "hash-original",
557+config: { gateway: { mode: "local" } },
558+valid: true,
559+issues: [],
560+raw: '{\n "gateway": { "mode": "local" }\n}\n',
561+});
562+updateConfigFormValue(state, ["gateway", "mode"], "remote");
563+applyConfigSnapshot(state, {
564+hash: "hash-refreshed",
565+config: { gateway: { mode: "external" } },
566+valid: true,
567+issues: [],
568+raw: '{\n "gateway": { "mode": "external" }\n}\n',
569+});
570+571+await saveConfig(state);
572+573+expect(request.mock.calls[0]?.[0]).toBe("config.set");
574+const params = request.mock.calls[0]?.[1] as { raw: string; baseHash: string };
575+expect(params.baseHash).toBe("hash-original");
576+});
577+472578it("coerces schema-typed values before config.set in form mode", async () => {
473579const request = createRequestWithConfigGet();
474580const state = createState();
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。