176 lines
6.4 KiB
TypeScript
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);
|
|
});
|
|
});
|