131 lines
3.8 KiB
TypeScript
131 lines
3.8 KiB
TypeScript
import { describe, it, expect } from "vitest";
|
|
import { Value } from "@alkdev/typebox/value";
|
|
import {
|
|
OperationNodeAttrs,
|
|
type OperationNodeAttrs as OperationNodeAttrsType,
|
|
CallNodeAttrs,
|
|
type CallNodeAttrs as CallNodeAttrsType,
|
|
} from "../../src/schema/node";
|
|
|
|
describe("OperationNodeAttrs", () => {
|
|
const valid: OperationNodeAttrsType = {
|
|
name: "classify",
|
|
namespace: "task",
|
|
version: "1.0.0",
|
|
type: "query",
|
|
inputSchema: { type: "object" },
|
|
outputSchema: { type: "object" },
|
|
};
|
|
|
|
it("accepts valid attributes without optional fields", () => {
|
|
expect(Value.Check(OperationNodeAttrs, valid)).toBe(true);
|
|
});
|
|
|
|
it("accepts valid attributes with all optional fields", () => {
|
|
const withOptional: OperationNodeAttrsType = {
|
|
...valid,
|
|
description: "Classifies input",
|
|
tags: ["ml", "nlp"],
|
|
};
|
|
expect(Value.Check(OperationNodeAttrs, withOptional)).toBe(true);
|
|
});
|
|
|
|
it("accepts all operation types", () => {
|
|
for (const type of ["query", "mutation", "subscription"] as const) {
|
|
expect(Value.Check(OperationNodeAttrs, { ...valid, type })).toBe(true);
|
|
}
|
|
});
|
|
|
|
it("rejects missing required fields", () => {
|
|
expect(Value.Check(OperationNodeAttrs, {})).toBe(false);
|
|
expect(Value.Check(OperationNodeAttrs, { name: "classify" })).toBe(false);
|
|
});
|
|
|
|
it("rejects invalid operation type", () => {
|
|
expect(
|
|
Value.Check(OperationNodeAttrs, { ...valid, type: "invalid" }),
|
|
).toBe(false);
|
|
});
|
|
|
|
it("rejects wrong types for optional tags", () => {
|
|
expect(Value.Check(OperationNodeAttrs, { ...valid, tags: [1, 2] })).toBe(
|
|
false,
|
|
);
|
|
});
|
|
});
|
|
|
|
describe("CallNodeAttrs", () => {
|
|
const valid: CallNodeAttrsType = {
|
|
requestId: "req_abc123",
|
|
operationId: "task.classify",
|
|
status: "pending",
|
|
input: { text: "hello" },
|
|
};
|
|
|
|
it("accepts valid attributes without optional fields", () => {
|
|
expect(Value.Check(CallNodeAttrs, valid)).toBe(true);
|
|
});
|
|
|
|
it("accepts valid attributes with all optional fields", () => {
|
|
const withOptional: CallNodeAttrsType = {
|
|
...valid,
|
|
parentRequestId: "req_parent",
|
|
output: { label: "greeting" },
|
|
error: {
|
|
code: "INTERNAL",
|
|
message: "Something went wrong",
|
|
details: { stack: "..." },
|
|
},
|
|
identity: {
|
|
id: "user_1",
|
|
scopes: ["read", "write"],
|
|
resources: { org: ["org_1"] },
|
|
},
|
|
startedAt: "2026-05-21T10:00:00Z",
|
|
completedAt: "2026-05-21T10:00:01Z",
|
|
};
|
|
expect(Value.Check(CallNodeAttrs, withOptional)).toBe(true);
|
|
});
|
|
|
|
it("accepts all call statuses", () => {
|
|
for (const status of ["pending", "running", "completed", "failed", "aborted"] as const) {
|
|
expect(Value.Check(CallNodeAttrs, { ...valid, status })).toBe(true);
|
|
}
|
|
});
|
|
|
|
it("accepts error without optional details", () => {
|
|
const withError: CallNodeAttrsType = {
|
|
...valid,
|
|
error: { code: "NOT_FOUND", message: "Operation not found" },
|
|
};
|
|
expect(Value.Check(CallNodeAttrs, withError)).toBe(true);
|
|
});
|
|
|
|
it("accepts identity without optional resources", () => {
|
|
const withIdentity: CallNodeAttrsType = {
|
|
...valid,
|
|
identity: { id: "user_1", scopes: ["read"] },
|
|
};
|
|
expect(Value.Check(CallNodeAttrs, withIdentity)).toBe(true);
|
|
});
|
|
|
|
it("rejects missing required fields", () => {
|
|
expect(Value.Check(CallNodeAttrs, {})).toBe(false);
|
|
expect(Value.Check(CallNodeAttrs, { requestId: "req_1" })).toBe(false);
|
|
});
|
|
|
|
it("rejects invalid call status", () => {
|
|
expect(
|
|
Value.Check(CallNodeAttrs, { ...valid, status: "idle" }),
|
|
).toBe(false);
|
|
});
|
|
|
|
it("rejects error with missing required fields", () => {
|
|
expect(
|
|
Value.Check(CallNodeAttrs, { ...valid, error: { code: "X" } }),
|
|
).toBe(false);
|
|
expect(
|
|
Value.Check(CallNodeAttrs, { ...valid, error: { message: "X" } }),
|
|
).toBe(false);
|
|
});
|
|
}); |