fix(config): prevent config.patch from destroying arrays when patch entries lack id (#18030)

Merged via /review-pr -> /prepare-pr -> /merge-pr.

Prepared head SHA: a857df9e32
Co-authored-by: stakeswky <64798754+stakeswky@users.noreply.github.com>
Co-authored-by: sebslight <19554889+sebslight@users.noreply.github.com>
Reviewed-by: @sebslight
This commit is contained in:
不做了睡大觉
2026-02-16 21:13:51 +08:00
committed by GitHub
parent 3a277e394e
commit cb391f4bdc
4 changed files with 130 additions and 7 deletions

View File

@@ -91,6 +91,47 @@ describe("gateway config methods", () => {
expect(primary?.workspace).toBe("/tmp/primary-updated");
expect(secondary?.workspace).toBe("/tmp/secondary");
});
it("rejects mixed-id agents.list patches without mutating persisted config", async () => {
const setRes = await rpcReq<{ ok?: boolean }>(ws, "config.set", {
raw: JSON.stringify({
agents: {
list: [
{ id: "primary", default: true, workspace: "/tmp/primary" },
{ id: "secondary", workspace: "/tmp/secondary" },
],
},
}),
});
expect(setRes.ok).toBe(true);
const beforeRes = await rpcReq<{ hash?: string }>(ws, "config.get", {});
expect(beforeRes.ok).toBe(true);
expect(typeof beforeRes.payload?.hash).toBe("string");
const patchRes = await rpcReq<{ ok?: boolean }>(ws, "config.patch", {
baseHash: beforeRes.payload?.hash,
raw: JSON.stringify({
agents: {
list: [
{
id: "primary",
workspace: "/tmp/primary-updated",
},
{
workspace: "/tmp/orphan-no-id",
},
],
},
}),
});
expect(patchRes.ok).toBe(false);
expect(patchRes.error?.message ?? "").toContain("invalid config");
const afterRes = await rpcReq<{ hash?: string }>(ws, "config.get", {});
expect(afterRes.ok).toBe(true);
expect(afterRes.payload?.hash).toBe(beforeRes.payload?.hash);
});
});
describe("gateway server sessions", () => {