web: protect config schema wrapper
This commit is contained in:
@@ -72,4 +72,32 @@ Deno.test("Decodal editor follows readonly prop changes after mount", async () =
|
||||
!source.includes("--border-subtle"),
|
||||
"CodeMirror theme must use workspace tokens that actually exist",
|
||||
);
|
||||
assert(
|
||||
source.includes("fixedSchemaWrapperCompartment.reconfigure") &&
|
||||
source.includes("fixedSchemaWrapperExtension()") &&
|
||||
source.includes("filter: false"),
|
||||
"fixed schema wrapper protection should react to the main source and permit authoritative synchronization",
|
||||
);
|
||||
});
|
||||
|
||||
Deno.test("main config wrapper is an explicit canonical draft conversion", async () => {
|
||||
const source = await Deno.readTextFile(
|
||||
new URL(
|
||||
"../../src/lib/workspace/config-source/ConfigSourceEditor.svelte",
|
||||
import.meta.url,
|
||||
),
|
||||
);
|
||||
|
||||
assert(
|
||||
source.includes("Wrap with WorkspaceConfigSchema") &&
|
||||
source.includes("wrapMainWithWorkspaceSchema") &&
|
||||
source.includes("fixedSchemaWrapper={mainSchemaWrapped}"),
|
||||
"legacy main sources should offer an explicit conversion before fixed wrapper mode is enabled",
|
||||
);
|
||||
assert(
|
||||
source.includes("source.trim()") &&
|
||||
source.includes("toolchain.format") &&
|
||||
source.includes("wrapper staged. Preview and Commit"),
|
||||
"conversion should remain a formatted local draft until the normal commit flow",
|
||||
);
|
||||
});
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
declare const Deno: {
|
||||
test(name: string, fn: () => Promise<void> | void): void;
|
||||
};
|
||||
|
||||
import { EditorState } from "@codemirror/state";
|
||||
import {
|
||||
fixedSchemaWrapperExtension,
|
||||
fixedWrapperBounds,
|
||||
} from "../../src/lib/workspace/config-source/fixed-schema-wrapper.ts";
|
||||
|
||||
function assertEquals(actual: unknown, expected: unknown, message: string) {
|
||||
if (actual !== expected) {
|
||||
throw new Error(
|
||||
`${message}: expected ${String(expected)}, got ${String(actual)}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function wrappedState(source = "{} as WorkspaceConfigSchema\n") {
|
||||
return EditorState.create({
|
||||
doc: source,
|
||||
extensions: [fixedSchemaWrapperExtension()],
|
||||
});
|
||||
}
|
||||
|
||||
Deno.test("fixed schema wrapper allows edits only inside the asserted object", () => {
|
||||
const state = wrappedState();
|
||||
const bounds = fixedWrapperBounds(state);
|
||||
if (!bounds) throw new Error("canonical wrapper should be recognized");
|
||||
assertEquals(bounds.bodyFrom, 1, "body should start after the opening brace");
|
||||
assertEquals(
|
||||
bounds.bodyTo,
|
||||
1,
|
||||
"empty body should end before the closing brace",
|
||||
);
|
||||
|
||||
const inserted = state.update({
|
||||
changes: { from: bounds.bodyFrom, insert: " profile = {}; " },
|
||||
});
|
||||
assertEquals(
|
||||
inserted.newDoc.toString(),
|
||||
"{ profile = {}; } as WorkspaceConfigSchema\n",
|
||||
"body insertion should be accepted",
|
||||
);
|
||||
|
||||
const prefixEdit = state.update({ changes: { from: 0, to: 1, insert: "[" } });
|
||||
assertEquals(
|
||||
prefixEdit.newDoc.toString(),
|
||||
state.doc.toString(),
|
||||
"opening wrapper edit should be rejected",
|
||||
);
|
||||
|
||||
const suffixEdit = state.update({
|
||||
changes: { from: bounds.bodyTo, to: bounds.bodyTo + 1, insert: "]" },
|
||||
});
|
||||
assertEquals(
|
||||
suffixEdit.newDoc.toString(),
|
||||
state.doc.toString(),
|
||||
"schema assertion edit should be rejected",
|
||||
);
|
||||
|
||||
const replaceAll = state.update({
|
||||
changes: { from: 0, to: state.doc.length, insert: "{}" },
|
||||
});
|
||||
assertEquals(
|
||||
replaceAll.newDoc.toString(),
|
||||
state.doc.toString(),
|
||||
"a replacement touching fixed and editable ranges should be rejected atomically",
|
||||
);
|
||||
});
|
||||
|
||||
Deno.test("authoritative synchronization can bypass the fixed wrapper filter", () => {
|
||||
const state = wrappedState();
|
||||
const synchronized = state.update({
|
||||
changes: { from: 0, to: state.doc.length, insert: "{}" },
|
||||
filter: false,
|
||||
});
|
||||
assertEquals(
|
||||
synchronized.newDoc.toString(),
|
||||
"{}",
|
||||
"programmatic source replacement should bypass user transaction filters",
|
||||
);
|
||||
assertEquals(
|
||||
fixedWrapperBounds(synchronized.state),
|
||||
null,
|
||||
"bare source should not expose fixed wrapper ranges",
|
||||
);
|
||||
});
|
||||
Reference in New Issue
Block a user