mirror of
https://github.com/morluto/rea.git
synced 2026-11-07 05:56:19 -10:00
fix(desktop): preserve significant keyfile value whitespace (#9077)
* fix(desktop): preserve significant keyfile value whitespace * fix(desktop): preserve keyfile whitespace in exported verifier * test(process): align symlink fixture with filesystem checkpoints * test(frida): assert the CLI spawn Evidence contract * test(mcp): use an unsupported analysis-view parent * test(browser): separate source-location line bounds from parity
This commit is contained in:
1 parent
c7b0e0dec6
commit
c92cbcaf28
4 files changed
+61
-6
No files matched your search
@@ -205,8 +205,10 @@ def _keyfile_line(raw: str) -> tuple:
|
||||
header = GROUP_HEADER.match(line)
|
||||
if header:
|
||||
return ("group", header.group(1))
|
||||
key, equals, value = line.partition("=")
|
||||
return ("entry", key.strip(), value.strip()) if equals and key else ("other",)
|
||||
key, equals, value = raw.partition("=")
|
||||
key = key.strip()
|
||||
# Only ASCII space and tab immediately after '=' are padding.
|
||||
return ("entry", key, value.lstrip(" \t")) if equals and key else ("other",)
|
||||
|
||||
|
||||
def _each_line(text: str):
|
||||
|
||||
@@ -2,6 +2,7 @@ import { describe, expect, it } from "vitest";
|
||||
|
||||
import type { JsonValue } from "../jsonValue.js";
|
||||
import { diffStateViews } from "./stateChanges.js";
|
||||
import { keyfileView } from "./stateFormats.js";
|
||||
import { resolvePointer } from "./viewPointer.js";
|
||||
|
||||
describe("diffStateViews", () => {
|
||||
@@ -84,6 +85,33 @@ describe("diffStateViews", () => {
|
||||
});
|
||||
|
||||
describe("diffStateViews fidelity", () => {
|
||||
it("preserves significant keyfile whitespace in views and differences", () => {
|
||||
const view = (suffix: string, unicode: string, padding: string) =>
|
||||
keyfileView(
|
||||
`[ui]\r\nsuffix=${suffix}\r\nunicode=${unicode}\r\npadded=${padding}\r\n`,
|
||||
);
|
||||
const before = view("hello ", "\u00a0hello\u00a0", " hello");
|
||||
const after = view("hello ", "\u00a0hello\u00a0\u00a0", "\thello");
|
||||
expect(before.ui).toEqual({
|
||||
suffix: "hello ",
|
||||
unicode: "\u00a0hello\u00a0",
|
||||
padded: "hello",
|
||||
});
|
||||
expect(after.ui).toEqual({
|
||||
suffix: "hello ",
|
||||
unicode: "\u00a0hello\u00a0\u00a0",
|
||||
padded: "hello",
|
||||
});
|
||||
expect(
|
||||
diffStateViews("keyfile", { groups: before }, { groups: after }),
|
||||
).toMatchObject({
|
||||
kind: "keys",
|
||||
changed: ["ui/suffix", "ui/unicode"],
|
||||
changed_count: 2,
|
||||
complete: true,
|
||||
});
|
||||
});
|
||||
|
||||
it("keeps empty containers as values", () => {
|
||||
expect(diffStateViews("json", {}, { items: [] })).toMatchObject({
|
||||
added: ["/items"],
|
||||
|
||||
@@ -49,13 +49,16 @@ const keyfileLine = (raw: string): KeyfileLine => {
|
||||
return { kind: "skip" };
|
||||
const name = /^\[(?<name>[^\]]+)\]$/u.exec(line)?.groups?.name;
|
||||
if (name !== undefined) return { kind: "group", name };
|
||||
const equals = line.indexOf("=");
|
||||
return equals <= 0
|
||||
const equals = raw.indexOf("=");
|
||||
if (equals < 0) return { kind: "other" };
|
||||
const key = raw.slice(0, equals).trim();
|
||||
return key.length === 0
|
||||
? { kind: "other" }
|
||||
: {
|
||||
kind: "entry",
|
||||
key: line.slice(0, equals).trim(),
|
||||
value: line.slice(equals + 1).trim(),
|
||||
key,
|
||||
// Only ASCII space and tab immediately after '=' are padding.
|
||||
value: raw.slice(equals + 1).replace(/^[ \t]+/u, ""),
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ import {
|
||||
} from "../../../src/domain/desktop/appExportPython.js";
|
||||
import {
|
||||
detectStateFormat,
|
||||
keyfileView,
|
||||
isJsonText,
|
||||
jsonView,
|
||||
textualView,
|
||||
@@ -397,3 +398,24 @@ it("spells infinite and negative-zero SQLite reals as REA's SQLite reader does",
|
||||
|
||||
expect(JSON.parse(cells)).toEqual(["Infinity", "-Infinity", "-0", 0, 1.5]);
|
||||
});
|
||||
|
||||
it("keeps the same keyfile whitespace as the retained state view", async () => {
|
||||
const inputs = [
|
||||
"[ui]\nsuffix=hello \nunicode=\u00a0hello\u00a0\npadded= hello\n",
|
||||
"[ui]\r\nsuffix=hello \r\nunicode=\u00a0hello\u00a0\u00a0\r\npadded=\thello\r\n",
|
||||
];
|
||||
const imports = PYTHON_IMPORTS.split("\n")
|
||||
.filter((line) => !line.includes("agentenv_protocol"))
|
||||
.join("\n");
|
||||
const program = `${imports}${PYTHON_HELPERS}
|
||||
print(json.dumps([_text_view("settings.ini", text, len(text.encode()))["view"]["groups"] for text in json.load(sys.stdin)]))
|
||||
`;
|
||||
const root = await createTestTempDirectory("rea-keyfile-python-");
|
||||
const script = join(root, "fixture.py");
|
||||
await writeFile(script, program, "utf8");
|
||||
const output = execFileSync("python3", ["-I", "-B", "-X", "utf8", script], {
|
||||
input: JSON.stringify(inputs),
|
||||
encoding: "utf8",
|
||||
});
|
||||
expect(JSON.parse(output)).toEqual(inputs.map(keyfileView));
|
||||
});
|
||||
Reference in new issue
Block a user