mirror of
https://github.com/morluto/rea.git
synced 2026-11-03 13:26:10 +00:00
feat(sqlite): inspect offline snapshots with selective evidence (#2271)
Add offline SQLite database/WAL inspection through the shared CLI/MCP. Use compact inventory, selected-table schema, exact ordered columns and bounded row samples while preserving source identity and lossless values. Retain completed Evidence through late cancellation and private-root cleanup failures, bound concurrent inspections, and recheck journal validity after acquisition. Reuse prepared CI runtime artifacts across supported Node lines. Co-authored-by: Y0UW1SH <29498139+Y0UW1SH@users.noreply.github.com> Assisted-by: Kogasa (gensokyo-skills:kogasa-surprise-testing)
This commit is contained in:
1 parent
916ac97bf9
commit
aeee5074d1
59 files changed
+5757
-24
No files matched your search
@@ -0,0 +1,147 @@
|
||||
import { expect, it } from "vitest";
|
||||
import { SqliteDatabaseService } from "./SqliteDatabaseService.js";
|
||||
import { AnalysisCapabilityUnavailableError } from "../../domain/analysisErrorCore.js";
|
||||
import { err } from "../../domain/result.js";
|
||||
import { ok } from "../../domain/result.js";
|
||||
import { createAnalysisExecution } from "../AnalysisProvider.js";
|
||||
|
||||
const fixture = () => ({
|
||||
artifact: { path: "/selected.db", sha256: "a".repeat(64), bytes: 4096 },
|
||||
wal: null,
|
||||
engine: { name: "SQLite", version: "3.50.2" },
|
||||
database_encoding: "UTF-8",
|
||||
schema: {
|
||||
scope: "inventory",
|
||||
completeness: "complete",
|
||||
tables: [],
|
||||
indexes: [],
|
||||
views: [],
|
||||
triggers: [],
|
||||
},
|
||||
rows: null,
|
||||
limitations: [],
|
||||
});
|
||||
|
||||
const execution = (value: unknown) =>
|
||||
createAnalysisExecution(
|
||||
value,
|
||||
{ id: "sqlite", name: "SQLite", version: "3.50.2" },
|
||||
{
|
||||
subject: { path: "/selected.db", sha256: "a".repeat(64), format: "file" },
|
||||
locations: [{ kind: "artifact-path", path: "/selected.db" }],
|
||||
},
|
||||
);
|
||||
|
||||
it.each([
|
||||
{ path: "relative.db" },
|
||||
{ path: "/selected.db", approval: true },
|
||||
{ path: "/selected.db", row_limit: 1 },
|
||||
{ path: "/selected.db", table: 1 },
|
||||
{ path: "/selected.db", table: "data", row_limit: 0 },
|
||||
{
|
||||
path: "/selected.db",
|
||||
table: "data",
|
||||
row_limit: Number.MAX_SAFE_INTEGER + 1,
|
||||
},
|
||||
])(
|
||||
"rejects invalid SQLite selection before provider effects: %j",
|
||||
async (input) => {
|
||||
const service = new SqliteDatabaseService({
|
||||
inspect: () => {
|
||||
throw new Error("Invalid selection must not read a database");
|
||||
},
|
||||
});
|
||||
expect(await service.inspect(input)).toMatchObject({
|
||||
ok: false,
|
||||
error: { _tag: "AnalysisInputError" },
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
it("preserves producer failures and rejects pre-cancelled requests before effects", async () => {
|
||||
const failure = new AnalysisCapabilityUnavailableError(
|
||||
"rea-sqlite",
|
||||
"inspect_sqlite_database",
|
||||
"Selected worker could not start",
|
||||
);
|
||||
const service = new SqliteDatabaseService({
|
||||
inspect: () => Promise.resolve(err(failure)),
|
||||
});
|
||||
expect(await service.inspect({ path: "/selected.db" })).toEqual(err(failure));
|
||||
const signal = AbortSignal.abort();
|
||||
const untouched = new SqliteDatabaseService({
|
||||
inspect: () => {
|
||||
throw new Error("Cancelled selection must not read a database");
|
||||
},
|
||||
});
|
||||
expect(
|
||||
await untouched.inspect({ path: "/selected.db" }, { signal }),
|
||||
).toMatchObject({
|
||||
ok: false,
|
||||
error: { _tag: "AnalysisCancelledError" },
|
||||
});
|
||||
});
|
||||
|
||||
it.each(["path", "digest", "subject", "wal", "table"])(
|
||||
"rejects producer results with changed caller binding: %s",
|
||||
async (problem) => {
|
||||
const value = fixture();
|
||||
if (problem === "path") value.artifact.path = "/other.db";
|
||||
if (problem === "digest") value.artifact.sha256 = "b".repeat(64);
|
||||
const observed = execution({
|
||||
...value,
|
||||
...(problem === "wal"
|
||||
? { wal: { path: "/other.db-wal", sha256: "b".repeat(64), bytes: 32 } }
|
||||
: {}),
|
||||
});
|
||||
const service = new SqliteDatabaseService({
|
||||
inspect: () =>
|
||||
Promise.resolve(
|
||||
ok({
|
||||
...observed,
|
||||
...(problem === "subject" ? { subject: null } : {}),
|
||||
}),
|
||||
),
|
||||
});
|
||||
expect(
|
||||
await service.inspect({
|
||||
path: "/selected.db",
|
||||
...(problem === "table" ? { table: "selected" } : {}),
|
||||
}),
|
||||
).toMatchObject({ ok: false, error: { _tag: "AnalysisOutputError" } });
|
||||
},
|
||||
);
|
||||
|
||||
it("preserves completed observed evidence when cancellation arrives after provider completion", async () => {
|
||||
const value = fixture();
|
||||
const service = new SqliteDatabaseService({
|
||||
inspect: () => Promise.resolve(ok(execution(value))),
|
||||
});
|
||||
expect(await service.inspect({ path: "/selected.db" })).toMatchObject({
|
||||
ok: true,
|
||||
value: {
|
||||
confidence: "observed",
|
||||
subject: {
|
||||
local_path: "/selected.db",
|
||||
digest: { sha256: "a".repeat(64) },
|
||||
},
|
||||
normalized_result: value,
|
||||
},
|
||||
});
|
||||
const controller = new AbortController();
|
||||
const cancelled = new SqliteDatabaseService({
|
||||
inspect: () => {
|
||||
controller.abort();
|
||||
return Promise.resolve(ok(execution(value)));
|
||||
},
|
||||
});
|
||||
expect(
|
||||
await cancelled.inspect(
|
||||
{ path: "/selected.db" },
|
||||
{ signal: controller.signal },
|
||||
),
|
||||
).toMatchObject({
|
||||
ok: true,
|
||||
value: { normalized_result: value },
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user