Files
2026-07-13 13:39:12 +08:00

176 lines
6.4 KiB
TypeScript

/**
* G-08 — CliproxyModelMappingEditor: parser/validator unit tests.
*
* Only exercises the pure `parseMappingJson` function — no React rendering.
*/
import { describe, it } from "node:test";
import assert from "node:assert/strict";
import {
parseMappingJson,
type MappingParseResult,
} from "../../../../../src/app/(dashboard)/dashboard/providers/services/components/CliproxyModelMappingEditor.tsx";
// ── Module shape ──────────────────────────────────────────────────────────────
describe("CliproxyModelMappingEditor — module shape", () => {
it("exports parseMappingJson as a function", async () => {
const mod =
await import("../../../../../src/app/(dashboard)/dashboard/providers/services/components/CliproxyModelMappingEditor.tsx");
assert.equal(typeof mod.parseMappingJson, "function");
});
it("exports CliproxyModelMappingEditor as a function", async () => {
const mod =
await import("../../../../../src/app/(dashboard)/dashboard/providers/services/components/CliproxyModelMappingEditor.tsx");
assert.equal(typeof mod.CliproxyModelMappingEditor, "function");
});
it("MappingParseResult ok-variant has value field", () => {
const result: MappingParseResult = parseMappingJson("{}");
assert.ok(result.ok);
if (result.ok) assert.ok("value" in result);
});
it("MappingParseResult error-variant has error field", () => {
const result: MappingParseResult = parseMappingJson("invalid");
assert.equal(result.ok, false);
if (!result.ok) assert.ok("error" in result);
});
});
// ── Valid inputs ───────────────────────────────────────────────────────────────
describe("parseMappingJson — valid inputs", () => {
it("accepts empty object", () => {
const result = parseMappingJson("{}");
assert.ok(result.ok);
if (result.ok) assert.deepEqual(result.value, {});
});
it("accepts single string-to-string entry", () => {
const result = parseMappingJson('{"gpt-4o": "openai-gpt-4o"}');
assert.ok(result.ok);
if (result.ok) assert.deepEqual(result.value, { "gpt-4o": "openai-gpt-4o" });
});
it("accepts multiple entries", () => {
const raw = JSON.stringify({
"gpt-4o": "openai-gpt-4o",
"claude-sonnet-4.5": "anthropic-claude-sonnet",
});
const result = parseMappingJson(raw);
assert.ok(result.ok);
if (result.ok) {
assert.equal(result.value["gpt-4o"], "openai-gpt-4o");
assert.equal(result.value["claude-sonnet-4.5"], "anthropic-claude-sonnet");
}
});
it("accepts pretty-printed JSON", () => {
const raw = `{
"gpt-4o": "openai-gpt-4o"
}`;
const result = parseMappingJson(raw);
assert.ok(result.ok);
});
it("accepts empty string values", () => {
const result = parseMappingJson('{"model-a": ""}');
assert.ok(result.ok);
if (result.ok) assert.equal(result.value["model-a"], "");
});
});
// ── Invalid JSON syntax ────────────────────────────────────────────────────────
describe("parseMappingJson — invalid JSON syntax", () => {
it("rejects bare text", () => {
const result = parseMappingJson("not json");
assert.equal(result.ok, false);
if (!result.ok) assert.match(result.error, /json|token|unexpected/i);
});
it("rejects trailing comma", () => {
const result = parseMappingJson('{"a": "b",}');
assert.equal(result.ok, false);
});
it("rejects unclosed brace", () => {
const result = parseMappingJson('{"a": "b"');
assert.equal(result.ok, false);
});
it("rejects empty string", () => {
const result = parseMappingJson("");
assert.equal(result.ok, false);
});
});
// ── Wrong shape ───────────────────────────────────────────────────────────────
describe("parseMappingJson — wrong shape", () => {
it("rejects JSON array", () => {
const result = parseMappingJson('["a", "b"]');
assert.equal(result.ok, false);
if (!result.ok) assert.match(result.error, /object|array/i);
});
it("rejects JSON string primitive", () => {
const result = parseMappingJson('"hello"');
assert.equal(result.ok, false);
});
it("rejects JSON number", () => {
const result = parseMappingJson("42");
assert.equal(result.ok, false);
});
it("rejects JSON null", () => {
const result = parseMappingJson("null");
assert.equal(result.ok, false);
});
it("rejects object with non-string value (number)", () => {
const result = parseMappingJson('{"model-a": 123}');
assert.equal(result.ok, false);
if (!result.ok) assert.match(result.error, /model-a/);
});
it("rejects object with non-string value (boolean)", () => {
const result = parseMappingJson('{"model-a": true}');
assert.equal(result.ok, false);
if (!result.ok) assert.match(result.error, /model-a/);
});
it("rejects object with non-string value (array)", () => {
const result = parseMappingJson('{"model-a": ["nested"]}');
assert.equal(result.ok, false);
if (!result.ok) assert.match(result.error, /model-a/);
});
it("rejects object with non-string value (object)", () => {
const result = parseMappingJson('{"model-a": {"nested": true}}');
assert.equal(result.ok, false);
if (!result.ok) assert.match(result.error, /model-a/);
});
it("rejects mixed valid/invalid entries — reports the bad key", () => {
const result = parseMappingJson('{"gpt-4o": "openai-gpt-4o", "bad": 0}');
assert.equal(result.ok, false);
if (!result.ok) assert.match(result.error, /bad/);
});
});
// ── Round-trip ────────────────────────────────────────────────────────────────
describe("parseMappingJson — round-trip", () => {
it("re-serializes to identical object", () => {
const original = { a: "x", b: "y" };
const raw = JSON.stringify(original);
const result = parseMappingJson(raw);
assert.ok(result.ok);
if (result.ok) assert.deepEqual(result.value, original);
});
});