fix: CLI put @schema uses putSchema() for recursive validation (#82)
CLI 'ucas put @schema' now detects meta-schema target and routes
through putSchema() which uses isValidSchema() — a proper recursive
validator — instead of ajv against the meta-schema (which cannot
express recursive constraints for nested property sub-schemas).
This fixes the core issue where schemas with typed properties like
{"type":"string","minLength":1} were rejected by the CLI path.
Added CLI E2E test: put @schema with nested constraints (minLength,
maximum, uniqueItems).
494 tests, 0 fail.
Refs #82
This commit is contained in:
@@ -224,6 +224,39 @@ describe("@ Alias Resolution - put", () => {
|
||||
expect(exitCode).not.toBe(0);
|
||||
expect(stderr.length).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
test("ucas put @schema with nested type constraints should succeed", async () => {
|
||||
await runCliAlias("init");
|
||||
|
||||
const schemaFile = join(testDir, "constrained-schema.json");
|
||||
writeFileSync(
|
||||
schemaFile,
|
||||
JSON.stringify({
|
||||
type: "object",
|
||||
properties: {
|
||||
name: { type: "string", minLength: 1, maxLength: 50 },
|
||||
age: { type: "number", minimum: 0, maximum: 150 },
|
||||
tags: {
|
||||
type: "array",
|
||||
items: { type: "string" },
|
||||
minItems: 1,
|
||||
uniqueItems: true,
|
||||
},
|
||||
},
|
||||
required: ["name"],
|
||||
}),
|
||||
);
|
||||
|
||||
const { stdout, stderr, exitCode } = await runCliAlias(
|
||||
"put",
|
||||
"@schema",
|
||||
schemaFile,
|
||||
);
|
||||
|
||||
expect(exitCode).toBe(0);
|
||||
expect(stderr).toBe("");
|
||||
expect(envValue(stdout)).toMatch(/^[0-9A-HJKMNP-TV-Z]{13}$/);
|
||||
});
|
||||
});
|
||||
|
||||
describe("@ Alias Resolution - hash", () => {
|
||||
|
||||
@@ -13,6 +13,7 @@ import {
|
||||
getSchema,
|
||||
InvalidTagFormatError,
|
||||
InvalidVariableNameError,
|
||||
putSchema,
|
||||
refs,
|
||||
renderAsync,
|
||||
renderDirect,
|
||||
@@ -187,6 +188,23 @@ async function cmdPut(args: string[]): Promise<void> {
|
||||
const payload = readJsonFile(file);
|
||||
const store = await openStore();
|
||||
|
||||
// Schema nodes: use putSchema() which validates via isValidSchema() (recursive)
|
||||
// instead of ajv against meta-schema (which can't express recursive constraints)
|
||||
const builtinSchemas = await bootstrap(store);
|
||||
const metaHash = builtinSchemas["@schema"];
|
||||
if (typeHash === metaHash) {
|
||||
try {
|
||||
const hash = await putSchema(store, payload as Record<string, unknown>);
|
||||
out(await wrapEnvelope(store, "@output/put", hash));
|
||||
} catch (e) {
|
||||
console.error(
|
||||
`Validation failed: payload in ${file} does not match schema ${typeHash}`,
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if schema exists
|
||||
const schema = getSchema(store, typeHash);
|
||||
if (schema === null) {
|
||||
|
||||
Reference in New Issue
Block a user