mirror of
https://github.com/OpenRouterTeam/spawn.git
synced 2026-04-28 11:59:29 +00:00
Restructure the repo as a Bun workspace monorepo: - Move cli/ → packages/cli/ - Create packages/shared/ (@openrouter/spawn-shared) with type-guards and parse utilities - Add root package.json with workspace configuration - Update all CLI imports to use @openrouter/spawn-shared - Deduplicate toRecord/toObjectArray helpers from 4 cloud modules - Update SPA (slack-bot) to use shared package instead of local toObj() - Update 48 agent shell scripts for new packages/cli/ path - Update install.sh, install.ps1, e2e, and test scripts - Update all GitHub workflows, .gitignore, pre-commit hooks - Update CLAUDE.md, README.md, and skill prompt references - Pin all dependency versions (no ^ ranges) - Bump CLI version 0.9.1 → 0.10.0 All 1908 tests pass. Lint clean. All 8 cloud bundles build. Co-authored-by: Claude <claude@anthropic.com> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
107 lines
2.8 KiB
TypeScript
107 lines
2.8 KiB
TypeScript
import { describe, it, expect } from "bun:test";
|
|
import * as v from "valibot";
|
|
import { parseJsonWith, parseJsonRaw } from "@openrouter/spawn-shared";
|
|
|
|
describe("parseJsonWith", () => {
|
|
const NumberSchema = v.object({
|
|
count: v.number(),
|
|
});
|
|
|
|
it("should return validated data for valid JSON matching the schema", () => {
|
|
const result = parseJsonWith('{"count": 42}', NumberSchema);
|
|
expect(result).toEqual({
|
|
count: 42,
|
|
});
|
|
});
|
|
|
|
it("should return null for valid JSON that doesn't match the schema", () => {
|
|
const result = parseJsonWith('{"count": "not a number"}', NumberSchema);
|
|
expect(result).toBeNull();
|
|
});
|
|
|
|
it("should return null for invalid JSON", () => {
|
|
const result = parseJsonWith("not json at all", NumberSchema);
|
|
expect(result).toBeNull();
|
|
});
|
|
|
|
it("should return null for empty string", () => {
|
|
const result = parseJsonWith("", NumberSchema);
|
|
expect(result).toBeNull();
|
|
});
|
|
|
|
it("should handle nested schemas", () => {
|
|
const NestedSchema = v.object({
|
|
user: v.object({
|
|
name: v.string(),
|
|
age: v.number(),
|
|
}),
|
|
});
|
|
const result = parseJsonWith('{"user": {"name": "Alice", "age": 30}}', NestedSchema);
|
|
expect(result).toEqual({
|
|
user: {
|
|
name: "Alice",
|
|
age: 30,
|
|
},
|
|
});
|
|
});
|
|
|
|
it("should handle optional fields", () => {
|
|
const OptSchema = v.object({
|
|
name: v.string(),
|
|
email: v.optional(v.string()),
|
|
});
|
|
const result = parseJsonWith('{"name": "Bob"}', OptSchema);
|
|
expect(result).toEqual({
|
|
name: "Bob",
|
|
});
|
|
});
|
|
|
|
it("should handle record schemas", () => {
|
|
const RecordSchema = v.record(v.string(), v.unknown());
|
|
const result = parseJsonWith('{"key": "value", "num": 1}', RecordSchema);
|
|
expect(result).toEqual({
|
|
key: "value",
|
|
num: 1,
|
|
});
|
|
});
|
|
|
|
it("should reject array when object schema expected", () => {
|
|
const result = parseJsonWith("[1, 2, 3]", NumberSchema);
|
|
expect(result).toBeNull();
|
|
});
|
|
});
|
|
|
|
describe("parseJsonRaw", () => {
|
|
it("should parse valid JSON to unknown", () => {
|
|
const result = parseJsonRaw('{"key": "value"}');
|
|
expect(result).toEqual({
|
|
key: "value",
|
|
});
|
|
});
|
|
|
|
it("should parse JSON arrays", () => {
|
|
const result = parseJsonRaw("[1, 2, 3]");
|
|
expect(result).toEqual([
|
|
1,
|
|
2,
|
|
3,
|
|
]);
|
|
});
|
|
|
|
it("should return null for invalid JSON", () => {
|
|
const result = parseJsonRaw("not json");
|
|
expect(result).toBeNull();
|
|
});
|
|
|
|
it("should return null for empty string", () => {
|
|
const result = parseJsonRaw("");
|
|
expect(result).toBeNull();
|
|
});
|
|
|
|
it("should parse primitive JSON values", () => {
|
|
expect(parseJsonRaw("42")).toBe(42);
|
|
expect(parseJsonRaw('"hello"')).toBe("hello");
|
|
expect(parseJsonRaw("true")).toBe(true);
|
|
expect(parseJsonRaw("null")).toBeNull();
|
|
});
|
|
});
|