fix(web): apply the whole live surface, and track write outcomes per server
Review round 9 on #2091, all three findings. Every live-apply now goes through one `applyLiveServerSettings`: the settings object, the Network log size, and the roots diff together. A close with a pending debounce applies the draft to all three, so reconciling only `setServerSettings` when that save rejected left the server advertising roots that were never persisted and the log sized for them. The "this server's draft is unpersisted" flag moves from a single id in `App.tsx` into the tracker, which already orders writes by issue and keeps state per server. One id could be overwritten by a failure for a different server, or cleared by a stale success, either of which made a close apply a rejected draft. And when nothing has ever landed for a server, close falls back to the empty settings the toggle rollback uses rather than to the draft — the old fallback re-applied the very values that had just failed. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UL9retfvAXRgvi6EWk4SY4 Signed-off-by: cliffhall <cliff@futurescale.com>
This commit is contained in:
parent
ba633fc8d2
commit
db84823262
1 changed files with 159 additions and 56 deletions
|
|
@ -1762,6 +1762,50 @@ describe("App roots live-apply on settings-dialog close", () => {
|
|||
// …and the rejected roots edit is not advertised either.
|
||||
expect(client.setRoots).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("restores the roots and log size too when a flushed save rejects (#2089)", async () => {
|
||||
// Close flushes a pending save and live-applies the draft immediately —
|
||||
// roots and the Network log size, not just the settings object. If that
|
||||
// save then rejects, reconciling only `setServerSettings` would leave the
|
||||
// server advertising roots that were never persisted.
|
||||
const user = userEvent.setup();
|
||||
updateServerSettingsSpy.mockClear();
|
||||
const rejectedDraft: InspectorServerSettings = {
|
||||
...settingsWithRoots([{ uri: "file:///rejected" }]),
|
||||
maxFetchRequests: 10,
|
||||
};
|
||||
const client = await openSettingsForConnectedServer(rejectedDraft);
|
||||
client.getRoots.mockReturnValue([]);
|
||||
|
||||
const draftOptions = vi.mocked(useSettingsDraft).mock.calls.at(-1)?.[0];
|
||||
if (!draftOptions) throw new Error("useSettingsDraft was never called");
|
||||
const persistedSettings = settingsWithRoots([]);
|
||||
await act(async () => {
|
||||
await draftOptions.onPersist("A", persistedSettings);
|
||||
});
|
||||
|
||||
// Close applies the draft live — the state the flush leaves behind.
|
||||
await closeModal(user);
|
||||
await waitFor(() =>
|
||||
expect(client.setRoots).toHaveBeenCalledWith([
|
||||
{ uri: "file:///rejected" },
|
||||
]),
|
||||
);
|
||||
client.getRoots.mockReturnValue([{ uri: "file:///rejected" }]);
|
||||
client.setRoots.mockClear();
|
||||
|
||||
// …and only now does the flushed save reject.
|
||||
updateServerSettingsSpy.mockRejectedValueOnce(new Error("disk full"));
|
||||
await act(async () => {
|
||||
await draftOptions.onPersist("A", rejectedDraft).catch(() => undefined);
|
||||
});
|
||||
|
||||
// The whole live surface goes back to what landed, roots included.
|
||||
await waitFor(() => expect(client.setRoots).toHaveBeenCalledWith([]));
|
||||
expect(client.setServerSettings).toHaveBeenLastCalledWith(
|
||||
persistedSettings,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("App history pin/replay", () => {
|
||||
|
|
|
|||
Loading…
Reference in a new issue