Files
united-workforce/packages/workflow/__tests__/cas.test.ts
T
xiaoju 495c000356 refactor(workflow): split @uncaged/workflow-runtime from engine (Phase 1)
Create packages/workflow-runtime with the minimal runtime subset:
- Types (WorkflowFn, RoleOutput, AgentBinding, etc.)
- createWorkflow (pure orchestration, zero I/O)
- validateWorkflowDescriptor
- Result/ok/err, START/END constants

Zero external dependencies (zod as peer only).
Zero node:fs/node:path imports.

Engine (@uncaged/workflow) now depends on workflow-runtime and
provides CAS/merkle/extract implementations via injection.

Refs #121, relates #122
2026-05-08 06:29:49 +00:00

105 lines
3.3 KiB
TypeScript

import { afterEach, beforeEach, describe, expect, test } from "bun:test";
import { mkdtemp, rm } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { createCasStore } from "../src/cas/cas.js";
import { hashString } from "../src/cas/hash.js";
import { createContentMerkleNode, serializeMerkleNode } from "../src/cas/merkle.js";
function casStoredForm(raw: string): string {
return serializeMerkleNode(createContentMerkleNode(raw));
}
describe("createCasStore", () => {
let casDir: string;
beforeEach(async () => {
casDir = await mkdtemp(join(tmpdir(), "cas-test-"));
});
afterEach(async () => {
await rm(casDir, { recursive: true, force: true });
});
test("put returns consistent hash for same content", async () => {
const cas = createCasStore(casDir);
const raw = "hello world";
const stored = casStoredForm(raw);
const h1 = await cas.put(raw);
const h2 = await cas.put(raw);
expect(h1).toBe(h2);
expect(h1).toBe(hashString(stored));
expect(h1).toHaveLength(13);
});
test("put returns hash matching hashString of merkle-stored form", async () => {
const cas = createCasStore(casDir);
const content = "some content to store";
const stored = casStoredForm(content);
const h = await cas.put(content);
expect(h).toBe(hashString(stored));
});
test("get returns merkle-serialized blob for raw puts", async () => {
const cas = createCasStore(casDir);
const content = "line1\nline2\nline3";
const stored = casStoredForm(content);
const h = await cas.put(content);
const retrieved = await cas.get(h);
expect(retrieved).toBe(stored);
});
test("get returns null for missing hash", async () => {
const cas = createCasStore(casDir);
const result = await cas.get("0000000000000");
expect(result).toBeNull();
});
test("delete removes entry", async () => {
const cas = createCasStore(casDir);
const h = await cas.put("to be deleted");
await cas.delete(h);
const result = await cas.get(h);
expect(result).toBeNull();
});
test("delete on missing hash does not throw", async () => {
const cas = createCasStore(casDir);
await cas.delete("0000000000000");
});
test("list returns all stored hashes", async () => {
const cas = createCasStore(casDir);
const h1 = await cas.put("aaa");
const h2 = await cas.put("bbb");
const h3 = await cas.put("ccc");
const hashes = await cas.list();
expect(hashes.sort()).toEqual([h1, h2, h3].sort());
});
test("list returns empty array when cas dir does not exist", async () => {
const cas = createCasStore(join(casDir, "nonexistent"));
const hashes = await cas.list();
expect(hashes).toEqual([]);
});
test("put is idempotent — same content written twice causes no error", async () => {
const cas = createCasStore(casDir);
const raw = "idempotent";
const stored = casStoredForm(raw);
const h1 = await cas.put(raw);
const h2 = await cas.put(raw);
expect(h1).toBe(h2);
const content = await cas.get(h1);
expect(content).toBe(stored);
});
test("different content produces different hashes", async () => {
const cas = createCasStore(casDir);
const h1 = await cas.put("alpha");
const h2 = await cas.put("beta");
expect(h1).not.toBe(h2);
});
});