mirror of
https://github.com/morluto/rea.git
synced 2026-11-15 19:23:24 +00:00
* fix(pcap): report a refused tool launch as an unavailable tool When a capture read's tool launch was refused with EPERM, pcap reported it as unreadable tool output (AnalysisProtocolError) instead of an unavailable tool. Pcap also never told "not executable" apart from "missing". Add classifyLaunchFailure in src/process as the one owner of which spawn errnos mean missing (ENOENT, ENOTDIR) and which mean present but not executable (EACCES, EPERM, ENOEXEC, EISDIR). Pcap, apktool and gdb used three different errno lists; all three now use it. * refactor(process): classify owned command outcomes through one owner Add ProviderOutcome, read once from the supervisor's facts, and classifyProviderOutcome, the one mapping from an outcome to a typed error. Migrate pcap and the shared artifact producer command failure (TShark, sigrok). Add the rea/provider-failure-classifier lint rule with a shrink-only baseline of per-site (cause: unknown) => AnalysisError functions. * refactor(process): classify Java and adb launch refusals with the shared owner (#10598) The Ghidra and JADX Java probes and adb's version probe now take missing-versus-not-executable from classifyLaunchFailure instead of their own errno checks. ELOOP and ENAMETOOLONG join the owner's missing set, so adb keeps its existing classification and both Java probes now treat a non-directory path component as a missing runtime. * fix(pcap): preserve command launch attribution * test(pcap): expect an unknown failing stage when a limited capture tool exits unsuccessfully * refactor(process): classify owned command outcomes through one owner Add ProviderOutcome, read once from the supervisor's facts, and classifyProviderOutcome, the one mapping from an outcome to a typed error. Migrate pcap and the shared artifact producer command failure (TShark, sigrok). Add the rea/provider-failure-classifier lint rule with a shrink-only baseline of per-site (cause: unknown) => AnalysisError functions. * test(lint): cover baselined files in the provider-failure classifier rule * fix(pcap): attribute a limited capture's exit through the prlimit wrapper * fix(process): identify the refused executable for every launcher shape Name the executable on errnos Node throws synchronously from spawn (ENOEXEC, ENOTDIR, ...) and read the Windows native launcher's executable-launch constraint, so pcap attributes those refusals to the selected tool or limiter instead of an unknown stage. * test(pcap): prove synchronous launch naming with ENOTDIR on every POSIX host * refactor(process): build classified cancellations with cancelledBy and the carried caller reason * chore(lint): count runOwned's close-failure mapper from main in the classifier baseline
69 lines
2.6 KiB
TypeScript
69 lines
2.6 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
import { readFile } from "node:fs/promises";
|
|
import { fileURLToPath } from "node:url";
|
|
import { RuleTester } from "oxlint/plugins-dev";
|
|
import rule from "../../scripts/lint/rules/provider-failure-classifier.mjs";
|
|
|
|
/** A baselined file allowed exactly one per-site classifier. */
|
|
const baselinedFile = fileURLToPath(
|
|
new URL("../../src/adb/AdbProvider.ts", import.meta.url),
|
|
);
|
|
const classifier = "const failure = (cause: unknown): AnalysisError => error;";
|
|
|
|
RuleTester.describe = describe;
|
|
RuleTester.it = it;
|
|
|
|
new RuleTester({ languageOptions: { parserOptions: { lang: "ts" } } }).run(
|
|
"provider-failure-classifier",
|
|
rule,
|
|
{
|
|
valid: [
|
|
"const classify = (outcome: ProviderOutcome): AnalysisError => error;",
|
|
"const message = (cause: unknown): string => String(cause);",
|
|
"const recover = (cause: unknown): Result<number, AnalysisError> => ok(1);",
|
|
"function dialect(outcome: ProviderDialectOutcome, details: AnalysisErrorOptions): AnalysisError { return error; }",
|
|
{ code: classifier, filename: baselinedFile },
|
|
],
|
|
invalid: [
|
|
{
|
|
code: "const failure = (cause: unknown): AnalysisError => error;",
|
|
errors: [{ messageId: "classifier" }],
|
|
},
|
|
{
|
|
code: "function failure(operation: string, cause: unknown): AnalysisError | undefined { return undefined; }",
|
|
errors: [{ messageId: "classifier" }],
|
|
},
|
|
{
|
|
code: "class Provider { failure(cause: unknown, phase = 'read'): AnalysisError { return error; } }",
|
|
errors: [{ messageId: "classifier" }],
|
|
},
|
|
{
|
|
code: `${classifier}\n${classifier.replace("failure", "second")}`,
|
|
filename: baselinedFile,
|
|
errors: [{ messageId: "classifier" }, { messageId: "classifier" }],
|
|
},
|
|
{
|
|
code: "const message = (cause: unknown): string => String(cause);",
|
|
filename: baselinedFile,
|
|
errors: [{ messageId: "stale", data: { allowed: "1", actual: "0" } }],
|
|
},
|
|
],
|
|
},
|
|
);
|
|
|
|
it("keeps the classifier baseline to existing source files", async () => {
|
|
const baseline = JSON.parse(
|
|
await readFile(
|
|
"scripts/lint/rules/provider-failure-classifier-baseline.json",
|
|
"utf8",
|
|
),
|
|
) as Record<string, number>;
|
|
for (const [file, count] of Object.entries(baseline)) {
|
|
expect(file).toMatch(/^src\/.+\.ts$/u);
|
|
expect(count).toBeGreaterThan(0);
|
|
// The lint run reports an entry above a file's real count as stale; this
|
|
// catches entries whose file no longer exists, which lint never visits.
|
|
await expect(readFile(file, "utf8")).resolves.toBeTypeOf("string");
|
|
}
|
|
});
|