124 lines
4.4 KiB
TypeScript
124 lines
4.4 KiB
TypeScript
/**
|
|
* Client-side fetch helpers that the AgentBridge maintenance card uses to drive
|
|
* the already-shipped backend routes (#4084 repair + DELETE cert, #4093
|
|
* diagnose, #4094 config import/export). Pure integration logic — no DOM — so
|
|
* it is unit-testable by stubbing global.fetch: each helper must parse the
|
|
* success body and surface the sanitized server error message on !res.ok.
|
|
*/
|
|
import test from "node:test";
|
|
import assert from "node:assert/strict";
|
|
|
|
const {
|
|
runDiagnose,
|
|
removeCaCert,
|
|
repairMitmState,
|
|
fetchAgentBridgeConfig,
|
|
importAgentBridgeConfig,
|
|
} = await import("../../src/lib/inspector/agentBridgeMaintenanceApi.ts");
|
|
|
|
type FetchCall = { url: string; init?: RequestInit };
|
|
|
|
function stubFetch(handler: (call: FetchCall) => { ok: boolean; status?: number; body: unknown }) {
|
|
const calls: FetchCall[] = [];
|
|
const original = global.fetch;
|
|
global.fetch = (async (url: string, init?: RequestInit) => {
|
|
const call = { url: String(url), init };
|
|
calls.push(call);
|
|
const { ok, status = ok ? 200 : 500, body } = handler(call);
|
|
return {
|
|
ok,
|
|
status,
|
|
json: async () => body,
|
|
} as unknown as Response;
|
|
}) as typeof fetch;
|
|
return {
|
|
calls,
|
|
restore() {
|
|
global.fetch = original;
|
|
},
|
|
};
|
|
}
|
|
|
|
test("runDiagnose returns the report and hits GET /diagnose", async () => {
|
|
const report = { healthy: false, checks: [{ name: "cert-trusted", ok: false, hint: "trust it" }], port: 443 };
|
|
const f = stubFetch(() => ({ ok: true, body: report }));
|
|
try {
|
|
const result = await runDiagnose();
|
|
assert.equal(result.healthy, false);
|
|
assert.equal(result.port, 443);
|
|
assert.equal(result.checks[0].name, "cert-trusted");
|
|
assert.equal(f.calls[0].url, "/api/tools/agent-bridge/diagnose");
|
|
} finally {
|
|
f.restore();
|
|
}
|
|
});
|
|
|
|
test("removeCaCert DELETEs /cert and returns trusted flag", async () => {
|
|
const f = stubFetch(() => ({ ok: true, body: { ok: true, trusted: false } }));
|
|
try {
|
|
const result = await removeCaCert();
|
|
assert.equal(result.trusted, false);
|
|
assert.equal(f.calls[0].url, "/api/tools/agent-bridge/cert");
|
|
assert.equal(f.calls[0].init?.method, "DELETE");
|
|
} finally {
|
|
f.restore();
|
|
}
|
|
});
|
|
|
|
test("repairMitmState POSTs /repair and returns the repaired list", async () => {
|
|
const f = stubFetch(() => ({ ok: true, body: { ok: true, repaired: ["dns", "system-proxy"] } }));
|
|
try {
|
|
const result = await repairMitmState();
|
|
assert.deepEqual(result.repaired, ["dns", "system-proxy"]);
|
|
assert.equal(f.calls[0].url, "/api/tools/agent-bridge/repair");
|
|
assert.equal(f.calls[0].init?.method, "POST");
|
|
} finally {
|
|
f.restore();
|
|
}
|
|
});
|
|
|
|
test("fetchAgentBridgeConfig GETs /config and returns the portable blob", async () => {
|
|
const cfg = { version: 1, bypassPatterns: ["*.corp"], customHosts: [], agentMappings: {} };
|
|
const f = stubFetch(() => ({ ok: true, body: cfg }));
|
|
try {
|
|
const result = await fetchAgentBridgeConfig();
|
|
assert.equal(result.version, 1);
|
|
assert.deepEqual(result.bypassPatterns, ["*.corp"]);
|
|
assert.equal(f.calls[0].url, "/api/tools/agent-bridge/config");
|
|
} finally {
|
|
f.restore();
|
|
}
|
|
});
|
|
|
|
test("importAgentBridgeConfig POSTs the config and returns the counts", async () => {
|
|
const cfg = { version: 1 as const, bypassPatterns: ["*.corp"], customHosts: [], agentMappings: {} };
|
|
const f = stubFetch(() => ({ ok: true, body: { ok: true, bypassPatterns: 1, customHosts: 0, agents: 0 } }));
|
|
try {
|
|
const result = await importAgentBridgeConfig(cfg);
|
|
assert.equal(result.bypassPatterns, 1);
|
|
assert.equal(f.calls[0].url, "/api/tools/agent-bridge/config");
|
|
assert.equal(f.calls[0].init?.method, "POST");
|
|
assert.equal(JSON.parse(String(f.calls[0].init?.body)).version, 1);
|
|
} finally {
|
|
f.restore();
|
|
}
|
|
});
|
|
|
|
test("each helper surfaces the sanitized server error message on !res.ok", async () => {
|
|
const f = stubFetch(() => ({ ok: false, status: 400, body: { error: { message: "Invalid AgentBridge config" } } }));
|
|
try {
|
|
await assert.rejects(() => importAgentBridgeConfig({ version: 1, bypassPatterns: [], customHosts: [], agentMappings: {} }), /Invalid AgentBridge config/);
|
|
} finally {
|
|
f.restore();
|
|
}
|
|
});
|
|
|
|
test("helpers fall back to HTTP status when the error body has no message", async () => {
|
|
const f = stubFetch(() => ({ ok: false, status: 503, body: null }));
|
|
try {
|
|
await assert.rejects(() => repairMitmState(), /HTTP 503/);
|
|
} finally {
|
|
f.restore();
|
|
}
|
|
});
|