This repository has been archived on 2026-06-01. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
json-cas/packages/cli-json-cas/tests/alias.test.ts
T
xiaoju 1d08c1bf4d refactor: reorganize CLI E2E tests into tests/ by scenario
Move 4 monolithic test files (cli.test.ts, e2e.test.ts, template.test.ts,
var.test.ts) from src/ into tests/ directory, split by scenario:

  tests/helpers.ts           - shared utilities (envValue, stripVolatile)
  tests/put-get-has.test.ts  - basic CAS storage operations
  tests/verify-refs-walk.test.ts - graph traversal
  tests/schema-validation.test.ts - Issue #50 schema validation
  tests/alias.test.ts        - @ alias resolution
  tests/variable.test.ts     - var set/get/delete/list/tag
  tests/template.test.ts     - template set/get/list/delete
  tests/render.test.ts       - render + render -p
  tests/pipe.test.ts         - pipe composition + stdin input
  tests/gc.test.ts           - garbage collection
  tests/edge-cases.test.ts   - help, --store, error paths

All 516 tests pass (159 CLI + 357 core/fs).
2026-06-01 03:38:35 +00:00

192 lines
4.7 KiB
TypeScript

import { afterEach, beforeEach, describe, expect, test } from "bun:test";
import {
mkdirSync,
rmSync,
writeFileSync,
} from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
// ---- @ Alias Resolution Tests ----
let testDir: string;
let storePath: string;
let cliPath: string;
beforeEach(() => {
// Create unique temp directory for each test
testDir = join(
tmpdir(),
`json-cas-cli-test-${Date.now()}-${Math.random().toString(36).slice(2)}`,
);
storePath = join(testDir, "store");
cliPath = join(import.meta.dir, "../src/index.ts");
mkdirSync(testDir, { recursive: true });
mkdirSync(storePath, { recursive: true });
});
afterEach(() => {
// Clean up test directory
try {
rmSync(testDir, { recursive: true, force: true });
} catch {
// Ignore cleanup errors
}
});
/**
* Run CLI command and return stdout, stderr, and exit code
*/
async function runCliAlias(...args: string[]): Promise<{
stdout: string;
stderr: string;
exitCode: number;
}> {
const proc = Bun.spawn(
["bun", "run", cliPath, "--store", storePath, ...args],
{
stdout: "pipe",
stderr: "pipe",
},
);
const [stdout, stderr] = await Promise.all([
new Response(proc.stdout).text(),
new Response(proc.stderr).text(),
]);
await proc.exited;
return {
stdout: stdout.trim(),
stderr: stderr.trim(),
exitCode: proc.exitCode ?? 0,
};
}
/** Extract the `value` field from a { type, value } envelope JSON string. */
function envValue(json: string): unknown {
return (JSON.parse(json.trim()) as { value: unknown }).value;
}
describe("@ Alias Resolution - put", () => {
test("ucas put @string <file> should resolve alias", async () => {
await runCliAlias("init");
const payloadFile = join(testDir, "payload.json");
writeFileSync(payloadFile, JSON.stringify("hello world"));
const { stdout, stderr, exitCode } = await runCliAlias(
"put",
"@string",
payloadFile,
);
expect(exitCode).toBe(0);
expect(stderr).toBe("");
// Should output an envelope whose value is a valid hash (13 chars)
expect(envValue(stdout)).toMatch(/^[0-9A-HJKMNP-TV-Z]{13}$/);
});
test("ucas put @number <file> should resolve alias", async () => {
await runCliAlias("init");
const payloadFile = join(testDir, "payload.json");
writeFileSync(payloadFile, "42");
const { stdout, exitCode } = await runCliAlias(
"put",
"@number",
payloadFile,
);
expect(exitCode).toBe(0);
expect(envValue(stdout)).toMatch(/^[0-9A-HJKMNP-TV-Z]{13}$/);
});
test("ucas put @object <file> should resolve alias", async () => {
await runCliAlias("init");
const payloadFile = join(testDir, "payload.json");
writeFileSync(payloadFile, JSON.stringify({ foo: "bar" }));
const { stdout, exitCode } = await runCliAlias(
"put",
"@object",
payloadFile,
);
expect(exitCode).toBe(0);
expect(envValue(stdout)).toMatch(/^[0-9A-HJKMNP-TV-Z]{13}$/);
});
test("ucas put @invalid <file> should fail", async () => {
await runCliAlias("init");
const payloadFile = join(testDir, "payload.json");
writeFileSync(payloadFile, "{}");
const { stderr, exitCode } = await runCliAlias(
"put",
"@invalid",
payloadFile,
);
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", () => {
test("ucas hash @string <file> should compute hash without storing", async () => {
await runCliAlias("init");
const payloadFile = join(testDir, "payload.json");
writeFileSync(payloadFile, JSON.stringify("test"));
const { stdout, stderr, exitCode } = await runCliAlias(
"hash",
"@string",
payloadFile,
);
expect(exitCode).toBe(0);
expect(stderr).toBe("");
expect(envValue(stdout)).toMatch(/^[0-9A-HJKMNP-TV-Z]{13}$/);
});
});