fix: harden authoring schema and contract projections
This commit is contained in:
@@ -531,7 +531,9 @@ export const authoredRpcSchemas = {
|
||||
payload: Schema.Struct({
|
||||
workspace_id: Schema.String.pipe(Schema.minLength(1)),
|
||||
revision: PositiveIntegerSchema,
|
||||
selected_step_id: Schema.optional(Schema.NullOr(Schema.String)),
|
||||
selected_step_id: Schema.optional(
|
||||
Schema.NullOr(Schema.String.pipe(Schema.minLength(1))),
|
||||
),
|
||||
}),
|
||||
success: Schema.Union(AuthoringContractInventorySchema, DraftWorkspaceSchema),
|
||||
},
|
||||
|
||||
@@ -548,7 +548,11 @@ const parityCases: ReadonlyArray<ParityCase> = [
|
||||
revision: 7,
|
||||
selected_step_id: "render",
|
||||
},
|
||||
invalidPayload: { workspace_id: "console.demo", revision: 0 },
|
||||
invalidPayload: {
|
||||
workspace_id: "console.demo",
|
||||
revision: 7,
|
||||
selected_step_id: "",
|
||||
},
|
||||
validSuccess: authoringContractInventory,
|
||||
invalidSuccess: {
|
||||
...authoringContractInventory,
|
||||
|
||||
@@ -61,6 +61,35 @@ describe("run operation registry", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("preserves explicit null and empty contract replacement semantics", async () => {
|
||||
const { getOperationMeta } = await import("./method-registry.js");
|
||||
const operation = getOperationMeta("workflow.draft_workspaces.set_contract");
|
||||
if (operation === undefined) throw new Error("missing set contract operation");
|
||||
|
||||
const cli = operation.equivalentCli({
|
||||
workspace_id: "console.demo",
|
||||
revision: 7,
|
||||
input_schema: null,
|
||||
outcomes: [],
|
||||
});
|
||||
|
||||
expect(cli).toContain("input_schema=null (no equivalent CLI clear flag)");
|
||||
expect(cli).toContain("outcomes=[] (no equivalent CLI clear flag)");
|
||||
expect(cli).not.toContain("--outcome");
|
||||
});
|
||||
|
||||
it("renders non-empty contract outcomes as repeated CLI flags", async () => {
|
||||
const { getOperationMeta } = await import("./method-registry.js");
|
||||
const operation = getOperationMeta("workflow.draft_workspaces.set_contract");
|
||||
if (operation === undefined) throw new Error("missing set contract operation");
|
||||
|
||||
expect(operation.equivalentCli({
|
||||
workspace_id: "console.demo",
|
||||
revision: 7,
|
||||
outcomes: ["ok", "cancelled"],
|
||||
})).toContain("--outcome ok --outcome cancelled");
|
||||
});
|
||||
|
||||
it("decodes start results with the start success schema", async () => {
|
||||
vi.doMock("./rpcs.js", async () => {
|
||||
const actual = await vi.importActual<typeof import("./rpcs.js")>(
|
||||
|
||||
@@ -839,11 +839,31 @@ const operationEntries = defineOperationEntries([
|
||||
String(p.revision),
|
||||
];
|
||||
const unavailable: string[] = [];
|
||||
if (p.input_schema != null) unavailable.push("input_schema (use --input-schema-file)");
|
||||
if (p.state_schema != null) unavailable.push("state_schema (use --state-schema-file)");
|
||||
if (p.output_schema != null) unavailable.push("output_schema (use --output-schema-file)");
|
||||
for (const outcome of p.outcomes ?? []) {
|
||||
parts.push("--outcome", shellArg(outcome));
|
||||
if (p.input_schema !== undefined) {
|
||||
unavailable.push(p.input_schema === null
|
||||
? "input_schema=null (no equivalent CLI clear flag)"
|
||||
: "input_schema (use --input-schema-file)");
|
||||
}
|
||||
if (p.state_schema !== undefined) {
|
||||
unavailable.push(p.state_schema === null
|
||||
? "state_schema=null (no equivalent CLI clear flag)"
|
||||
: "state_schema (use --state-schema-file)");
|
||||
}
|
||||
if (p.output_schema !== undefined) {
|
||||
unavailable.push(p.output_schema === null
|
||||
? "output_schema=null (no equivalent CLI clear flag)"
|
||||
: "output_schema (use --output-schema-file)");
|
||||
}
|
||||
if (p.outcomes !== undefined) {
|
||||
if (p.outcomes === null) {
|
||||
unavailable.push("outcomes=null (no equivalent CLI clear flag)");
|
||||
} else if (p.outcomes.length === 0) {
|
||||
unavailable.push("outcomes=[] (no equivalent CLI clear flag)");
|
||||
} else {
|
||||
for (const outcome of p.outcomes) {
|
||||
parts.push("--outcome", shellArg(outcome));
|
||||
}
|
||||
}
|
||||
}
|
||||
return nonEquivalentCli(parts.join(" "), unavailable);
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user