mirror of
https://github.com/morluto/rea
synced 2026-10-27 06:33:32 -04:00
5112fe07ae
Reject special files without blocking, cap the properties payload at 1 MiB before allocation, and verify descriptor extent and the selected name. Preserve Latin-1 data, selected symlinks, physical filesystem facts, completed observations and exact failed-close ownership. Reuse the stable bounded reader for profile artifacts and consolidate Ghidra file failure projection. Distinguish missing-at-open from disappearance or read failure, and attribute typed prerequisite failures at the consuming operation. Validation: 384 focused tests across 14 files, 42 seed/extension controls, 32 final metadata/profile/CLI controls, check:fast and docs:check. Controlled macOS file/process fixtures; no real Ghidra/JVM or Windows/Linux lifecycle verification. Bounded FIFO reproduction: baseline direct and public doctor required external cleanup around 2.2s; candidate returned independently around 0.4/0.57s, all fixture groups verified empty. Warm normal doctor baseline/candidate: elapsed 1.211/1.230s, CPU 1.239/1.247s, sampled family RSS 524/524MB, reply tokens 3116/3116. No performance improvement claimed; true cold and whole-agent costs unmeasured. Refs #4672
430 lines
13 KiB
TypeScript
430 lines
13 KiB
TypeScript
import { expect, it } from "vitest";
|
|
import { DeferredAnalysisClient } from "../../providers/DeferredAnalysisClient.js";
|
|
import {
|
|
createAnalysisExecution,
|
|
type AnalysisClient,
|
|
} from "../AnalysisProvider.js";
|
|
import {
|
|
AnalysisCancelledError,
|
|
AnalysisAccessDeniedError,
|
|
AnalysisArtifactChangedError,
|
|
AnalysisResourceConstraintError,
|
|
} from "../../domain/analysisErrorCore.js";
|
|
import { ProviderAdapterError } from "../../domain/providerAdapterError.js";
|
|
import { ProviderCleanupError } from "../../domain/providerCleanupError.js";
|
|
import { projectAnalysisError } from "../../domain/analysisErrorProjection.js";
|
|
import { resolvedAnalysisError } from "../../domain/analysisErrorBase.js";
|
|
import { err, ok } from "../../domain/result.js";
|
|
|
|
const identity = { id: "controlled", name: "Controlled client", version: "1" };
|
|
|
|
it.each([
|
|
new AnalysisAccessDeniedError(
|
|
"ghidra:installation",
|
|
"/selected/properties",
|
|
"EACCES",
|
|
),
|
|
new AnalysisArtifactChangedError(
|
|
"ghidra:installation",
|
|
"/selected/properties",
|
|
"selected name changed",
|
|
),
|
|
new AnalysisResourceConstraintError(
|
|
"ghidra:installation",
|
|
"file-size",
|
|
"metadata budget",
|
|
{ maximum_bytes: 1024 },
|
|
{ retryable: false },
|
|
),
|
|
])(
|
|
"attributes prerequisite $_tag to the requested operation without changing its facts",
|
|
async (physical) => {
|
|
const client = new DeferredAnalysisClient(async () => {
|
|
throw physical;
|
|
}, identity.id);
|
|
try {
|
|
const result = await client.execute("analyze_function", {});
|
|
if (result.ok) throw new Error("Expected prerequisite failure");
|
|
expect(result.error).toMatchObject({
|
|
_tag: physical._tag,
|
|
operation: "analyze_function",
|
|
});
|
|
expect(result.error.cause).toBe(physical);
|
|
if (physical instanceof AnalysisResourceConstraintError)
|
|
expect(result.error).toMatchObject({
|
|
resource: "file-size",
|
|
reportedLimits: { maximum_bytes: 1024 },
|
|
retryable: false,
|
|
});
|
|
else expect(result.error).toMatchObject({ path: "/selected/properties" });
|
|
} finally {
|
|
await client.close();
|
|
}
|
|
},
|
|
);
|
|
const gate = <T = void>() => {
|
|
let resolve!: (value: T) => void;
|
|
const promise = new Promise<T>((finish) => {
|
|
resolve = finish;
|
|
});
|
|
return { promise, resolve };
|
|
};
|
|
const child = (
|
|
close: AnalysisClient["close"] = async () => ok(null),
|
|
): AnalysisClient => ({
|
|
execute: async () => ok(createAnalysisExecution(null, identity)),
|
|
close,
|
|
operationHealthSnapshot: () => ({
|
|
state: "idle",
|
|
stage: null,
|
|
retryAction: null,
|
|
exitCode: null,
|
|
requests: [],
|
|
}),
|
|
});
|
|
|
|
it("refuses dispatch when close starts after a ready client was acquired", async () => {
|
|
let operations = 0;
|
|
const acquired: AnalysisClient = {
|
|
...child(),
|
|
execute: async () => {
|
|
operations++;
|
|
return ok(createAnalysisExecution(null, identity));
|
|
},
|
|
};
|
|
const client = new DeferredAnalysisClient(async () => acquired, identity.id);
|
|
expect((await client.execute("health", {})).ok).toBe(true);
|
|
const pending = client.execute("analyze_function", {});
|
|
const closing = client.close();
|
|
try {
|
|
expect(await pending).toMatchObject({
|
|
ok: false,
|
|
error: {
|
|
_tag: "ProviderAdapterError",
|
|
operation: "analyze_function",
|
|
diagnostics: { reason: "client_closed" },
|
|
},
|
|
});
|
|
expect(operations).toBe(1);
|
|
expect(
|
|
await client.executeIfReady?.("analyze_function", {}),
|
|
).toBeUndefined();
|
|
expect(await closing).toEqual(ok(null));
|
|
} finally {
|
|
await pending;
|
|
await closing;
|
|
await client.close();
|
|
}
|
|
});
|
|
|
|
it.each([
|
|
null,
|
|
"caller reason",
|
|
new ProviderCleanupError("foreign", ["foreign resource"], {
|
|
reason: "caller metadata",
|
|
}),
|
|
])(
|
|
"does not acquire or adopt resources for pre-aborted opaque reasons (%s)",
|
|
async (reason) => {
|
|
let creates = 0;
|
|
const controller = new AbortController();
|
|
controller.abort(reason);
|
|
const client = new DeferredAnalysisClient(async () => {
|
|
creates++;
|
|
return child();
|
|
}, identity.id);
|
|
const result = await client.execute(
|
|
"health",
|
|
{},
|
|
{ signal: controller.signal },
|
|
);
|
|
if (result.ok) throw new Error("Expected cancellation");
|
|
expect(result.error).toBeInstanceOf(AnalysisCancelledError);
|
|
expect(result.error.cause).toBe(reason);
|
|
expect(result.error.cleanupIncomplete).toBe(false);
|
|
expect(result.error.cleanupResources).toEqual([]);
|
|
expect(creates).toBe(0);
|
|
expect(await client.close()).toEqual(ok(null));
|
|
},
|
|
);
|
|
|
|
it("keeps a shared creation alive for an interested peer", async () => {
|
|
const entered = gate(),
|
|
loading = gate<AnalysisClient>();
|
|
let observed: AbortSignal | undefined,
|
|
creates = 0,
|
|
closes = 0;
|
|
const client = new DeferredAnalysisClient(async (signal) => {
|
|
creates++;
|
|
observed = signal;
|
|
entered.resolve();
|
|
return loading.promise;
|
|
}, identity.id);
|
|
const controller = new AbortController(),
|
|
reason = { request: "one peer leaves" };
|
|
const first = client.execute("health", {}, { signal: controller.signal });
|
|
const second = client.execute("health", {});
|
|
try {
|
|
await entered.promise;
|
|
controller.abort(reason);
|
|
const cancelled = await first;
|
|
if (cancelled.ok) throw new Error("Expected peer cancellation");
|
|
expect(cancelled.error.cause).toBe(reason);
|
|
expect(observed?.aborted).toBe(false);
|
|
loading.resolve(
|
|
child(async () => {
|
|
closes++;
|
|
return ok(null);
|
|
}),
|
|
);
|
|
expect((await second).ok).toBe(true);
|
|
expect(creates).toBe(1);
|
|
expect(await client.close()).toEqual(ok(null));
|
|
expect(closes).toBe(1);
|
|
} finally {
|
|
loading.resolve(child());
|
|
await Promise.allSettled([first, second]);
|
|
await client.close();
|
|
}
|
|
});
|
|
|
|
it("joins a late acquired client's final cleanup before cancellation settles", async () => {
|
|
const entered = gate(),
|
|
loading = gate<AnalysisClient>(),
|
|
closeEntered = gate(),
|
|
closing = gate();
|
|
let closes = 0,
|
|
settled = false;
|
|
const client = new DeferredAnalysisClient(async () => {
|
|
entered.resolve();
|
|
return loading.promise;
|
|
}, identity.id);
|
|
const controller = new AbortController(),
|
|
reason = { request: "late acquisition" };
|
|
const pending = client
|
|
.execute("analyze_function", {}, { signal: controller.signal })
|
|
.then((result) => {
|
|
settled = true;
|
|
return result;
|
|
});
|
|
try {
|
|
await entered.promise;
|
|
controller.abort(reason);
|
|
loading.resolve(
|
|
child(async () => {
|
|
closes++;
|
|
closeEntered.resolve();
|
|
await closing.promise;
|
|
return ok(null);
|
|
}),
|
|
);
|
|
await closeEntered.promise;
|
|
expect(settled).toBe(false);
|
|
expect(client.operationHealthSnapshot()?.state).toBe("idle");
|
|
expect(client.reuseReadiness()).toBe("blocked");
|
|
closing.resolve();
|
|
const result = await pending;
|
|
if (result.ok) throw new Error("Expected cancellation");
|
|
expect(result.error.cause).toBe(reason);
|
|
expect(result.error.cleanupIncomplete).toBe(false);
|
|
expect(await client.close()).toEqual(ok(null));
|
|
expect(closes).toBe(1);
|
|
} finally {
|
|
loading.resolve(child());
|
|
closing.resolve();
|
|
await pending;
|
|
await client.close();
|
|
}
|
|
});
|
|
|
|
it("keeps a producer-authored physical failure primary even when it is the caller's abort reason", async () => {
|
|
const entered = gate(),
|
|
finishing = gate();
|
|
const physical = new ProviderAdapterError(
|
|
identity.id,
|
|
"create_analysis_client",
|
|
{ diagnostics: { reason: "physical fixture fault" } },
|
|
);
|
|
const client = new DeferredAnalysisClient(async () => {
|
|
entered.resolve();
|
|
await finishing.promise;
|
|
throw physical;
|
|
}, identity.id);
|
|
const controller = new AbortController();
|
|
const pending = client.execute(
|
|
"analyze_function",
|
|
{},
|
|
{ signal: controller.signal },
|
|
);
|
|
try {
|
|
await entered.promise;
|
|
controller.abort(physical);
|
|
finishing.resolve();
|
|
const result = await pending;
|
|
if (result.ok) throw new Error("Expected physical failure");
|
|
expect(result.error).toMatchObject({
|
|
_tag: "ProviderAdapterError",
|
|
operation: "analyze_function",
|
|
diagnostics: {
|
|
reason: "physical fixture fault",
|
|
},
|
|
});
|
|
expect(result.error.cause).toBe(physical);
|
|
expect(physical.cause).toBeUndefined();
|
|
expect(projectAnalysisError(result.error).code).toBe("execution_failure");
|
|
} finally {
|
|
finishing.resolve();
|
|
await pending;
|
|
await client.close();
|
|
}
|
|
});
|
|
|
|
it("retains a failed late client's exact close owner and restores cancellation after confirmed retry", async () => {
|
|
let denied = true,
|
|
closes = 0;
|
|
const controller = new AbortController(),
|
|
reason = { request: "late cleanup refusal" };
|
|
const client = new DeferredAnalysisClient(async () => {
|
|
controller.abort(reason);
|
|
return child(async () => {
|
|
closes++;
|
|
return denied
|
|
? err(
|
|
new ProviderCleanupError(identity.id, ["controlled child"], {
|
|
reason: "controlled close refusal",
|
|
}),
|
|
)
|
|
: ok(null);
|
|
});
|
|
}, identity.id);
|
|
const result = await client.execute(
|
|
"analyze_function",
|
|
{},
|
|
{ signal: controller.signal },
|
|
);
|
|
if (result.ok) throw new Error("Expected cancellation with cleanup");
|
|
try {
|
|
expect(result.error.cleanupIncomplete).toBe(true);
|
|
expect(result.error.cleanupResources).toEqual(["controlled child"]);
|
|
expect(client.reuseReadiness()).toBe("blocked");
|
|
const firstClose = await client.close();
|
|
if (firstClose.ok) throw new Error("Expected retained close failure");
|
|
expect(firstClose.error.cleanupIncomplete).toBe(true);
|
|
expect(closes).toBe(2);
|
|
denied = false;
|
|
expect(await client.close()).toEqual(ok(null));
|
|
expect(result.error.cleanupIncomplete).toBe(false);
|
|
expect(firstClose.error.cleanupIncomplete).toBe(false);
|
|
expect(projectAnalysisError(result.error).code).toBe("cancelled");
|
|
expect(resolvedAnalysisError(result.error).cause).toBe(reason);
|
|
expect(await client.close()).toEqual(ok(null));
|
|
expect(closes).toBe(3);
|
|
} finally {
|
|
denied = false;
|
|
await client.close();
|
|
}
|
|
});
|
|
|
|
it("retains a failed prerequisite owner without creating a native client", async () => {
|
|
let denied = true,
|
|
releases = 0;
|
|
const cleanup = new ProviderCleanupError(
|
|
identity.id,
|
|
["controlled prerequisite"],
|
|
{ reason: "controlled refusal" },
|
|
);
|
|
const reason = { request: "prerequisite finalization" };
|
|
const client = new DeferredAnalysisClient(async (_signal, resources) => {
|
|
resources.retain(
|
|
{
|
|
close: async () => {
|
|
releases++;
|
|
if (denied) throw cleanup;
|
|
cleanup.resolveCleanup();
|
|
},
|
|
},
|
|
"controlled prerequisite",
|
|
);
|
|
throw new AnalysisCancelledError("create_analysis_client", {
|
|
cause: reason,
|
|
cleanup: {
|
|
reason: "controlled refusal",
|
|
resources: cleanup.cleanupResources,
|
|
},
|
|
}).bindCleanupDependencies([cleanup]);
|
|
}, identity.id);
|
|
const result = await client.execute("analyze_function", {});
|
|
if (result.ok) throw new Error("Expected prerequisite failure");
|
|
try {
|
|
expect(result.error.cleanupIncomplete).toBe(true);
|
|
expect((await client.close()).ok).toBe(false);
|
|
denied = false;
|
|
expect(await client.close()).toEqual(ok(null));
|
|
expect(result.error.cleanupIncomplete).toBe(false);
|
|
expect(projectAnalysisError(result.error).code).toBe("cancelled");
|
|
expect(resolvedAnalysisError(result.error).cause).toBe(reason);
|
|
expect(releases).toBe(3);
|
|
} finally {
|
|
denied = false;
|
|
await client.close();
|
|
}
|
|
});
|
|
|
|
it("retries only unresolved prerequisite and native owners across mixed cleanup outcomes", async () => {
|
|
let denyPrerequisite = true,
|
|
denyChild = true,
|
|
prerequisites = 0,
|
|
closes = 0;
|
|
const prerequisiteFailure = new ProviderCleanupError(
|
|
identity.id,
|
|
["mixed prerequisite"],
|
|
{ reason: "controlled prerequisite refusal" },
|
|
);
|
|
const client = new DeferredAnalysisClient(async (_signal, resources) => {
|
|
resources.retain(
|
|
{
|
|
close: async () => {
|
|
prerequisites++;
|
|
if (denyPrerequisite) throw prerequisiteFailure;
|
|
prerequisiteFailure.resolveCleanup();
|
|
},
|
|
},
|
|
"mixed prerequisite",
|
|
);
|
|
return child(async () => {
|
|
closes++;
|
|
return denyChild
|
|
? err(
|
|
new ProviderCleanupError(identity.id, ["mixed child"], {
|
|
reason: "controlled child refusal",
|
|
}),
|
|
)
|
|
: ok(null);
|
|
});
|
|
}, identity.id);
|
|
const result = await client.execute("analyze_function", {});
|
|
if (result.ok)
|
|
throw new Error("Expected mixed initialization cleanup failure");
|
|
try {
|
|
expect(result.error.cleanupResources).toEqual(
|
|
expect.arrayContaining(["mixed prerequisite", "mixed child"]),
|
|
);
|
|
expect(client.reuseReadiness()).toBe("blocked");
|
|
denyPrerequisite = false;
|
|
const first = await client.close();
|
|
expect(first.ok).toBe(false);
|
|
expect(result.error.cleanupIncomplete).toBe(true);
|
|
expect(result.error.cleanupResources).toEqual(["mixed child"]);
|
|
expect(prerequisites).toBe(2);
|
|
denyChild = false;
|
|
expect(await client.close()).toEqual(ok(null));
|
|
expect(result.error.cleanupIncomplete).toBe(false);
|
|
expect(prerequisites).toBe(2);
|
|
expect(closes).toBe(3);
|
|
} finally {
|
|
denyPrerequisite = false;
|
|
denyChild = false;
|
|
await client.close();
|
|
}
|
|
});
|