


























@@ -2,9 +2,89 @@ import { describe, expect, it } from "vitest";
22import { SENSITIVE_URL_HINT_TAG } from "../shared/net/redact-sensitive-url.js";
33import { computeBaseConfigSchemaResponse } from "./schema-base.js";
445+type TestJsonSchema = {
6+additionalProperties?: TestJsonSchema | boolean;
7+allOf?: TestJsonSchema[];
8+anyOf?: TestJsonSchema[];
9+const?: unknown;
10+enum?: unknown[];
11+items?: TestJsonSchema | TestJsonSchema[];
12+oneOf?: TestJsonSchema[];
13+properties?: Record<string, TestJsonSchema>;
14+type?: unknown;
15+};
16+517const BASE_CONFIG_SCHEMA = computeBaseConfigSchemaResponse({
618generatedAt: "2026-05-05T00:00:00.000Z",
719});
20+const BASE_SCHEMA = BASE_CONFIG_SCHEMA.schema as TestJsonSchema;
21+22+const METADATA_KEYS = new Set(["default", "description", "nullable", "tags", "title", "x-tags"]);
23+24+function schemaAt(schema: TestJsonSchema, path: string[]): TestJsonSchema | undefined {
25+let node: TestJsonSchema | undefined = schema;
26+for (const segment of path) {
27+if (!node) {
28+return undefined;
29+}
30+if (segment === "[]") {
31+node = Array.isArray(node.items) ? node.items[0] : node.items;
32+} else {
33+node = node.properties?.[segment];
34+}
35+}
36+return node;
37+}
38+39+function sortedAnyOfTypes(node: TestJsonSchema | undefined): string[] {
40+return (node?.anyOf ?? [])
41+.map((branch) => String(branch.type))
42+.toSorted((left, right) => left.localeCompare(right));
43+}
44+45+function itemSchema(node: TestJsonSchema | undefined): TestJsonSchema | undefined {
46+return Array.isArray(node?.items) ? node.items[0] : node?.items;
47+}
48+49+function expectAnyOfTypes(path: string[], expectedTypes: string[]): TestJsonSchema[] {
50+const node = schemaAt(BASE_SCHEMA, path);
51+expect(node, path.join(".")).toBeDefined();
52+expect(sortedAnyOfTypes(node), path.join(".")).toEqual(expectedTypes);
53+return node?.anyOf ?? [];
54+}
55+56+function hasOnlyMetadataKeys(schema: TestJsonSchema): boolean {
57+return Object.keys(schema).every((key) => METADATA_KEYS.has(key));
58+}
59+60+function collectMetadataOnlyCompositionBranches(
61+schema: TestJsonSchema,
62+path: string[] = [],
63+hits: string[] = [],
64+): string[] {
65+for (const keyword of ["allOf", "anyOf", "oneOf"] as const) {
66+for (const [index, branch] of (schema[keyword] ?? []).entries()) {
67+const branchPath = `${path.join(".") || "<root>"}.${keyword}[${index}]`;
68+if (hasOnlyMetadataKeys(branch)) {
69+hits.push(branchPath);
70+}
71+collectMetadataOnlyCompositionBranches(branch, [branchPath], hits);
72+}
73+}
74+75+for (const [key, child] of Object.entries(schema.properties ?? {})) {
76+collectMetadataOnlyCompositionBranches(child, [...path, key], hits);
77+}
78+if (schema.additionalProperties && typeof schema.additionalProperties === "object") {
79+collectMetadataOnlyCompositionBranches(schema.additionalProperties, [...path, "*"], hits);
80+}
81+const items = Array.isArray(schema.items) ? schema.items : schema.items ? [schema.items] : [];
82+for (const [index, child] of items.entries()) {
83+collectMetadataOnlyCompositionBranches(child, [...path, `items[${index}]`], hits);
84+}
85+86+return hits;
87+}
888989describe("base config schema", () => {
1090it("is deterministic for a fixed generatedAt timestamp", () => {
@@ -70,4 +150,48 @@ describe("base config schema", () => {
70150expect(uiHints).toHaveProperty("agents.defaults.videoGenerationModel.fallbacks");
71151expect(uiHints).toHaveProperty("agents.defaults.mediaGenerationAutoProviderFallback");
72152});
153+154+it("publishes accepted input shapes for transform-backed config fields", () => {
155+const lastTouchedAtBranches = expectAnyOfTypes(["meta", "lastTouchedAt"], ["number", "string"]);
156+expect(lastTouchedAtBranches.every((branch) => Object.keys(branch).length > 0)).toBe(true);
157+158+for (const path of [
159+["agents", "defaults", "sandbox", "docker", "setupCommand"],
160+["agents", "list", "[]", "sandbox", "docker", "setupCommand"],
161+]) {
162+const branches = expectAnyOfTypes(path, ["array", "string"]);
163+expect(itemSchema(branches.find((branch) => branch.type === "array"))?.type).toBe("string");
164+}
165+166+const codexAllowedDomains = schemaAt(BASE_SCHEMA, [
167+"tools",
168+"web",
169+"search",
170+"openaiCodex",
171+"allowedDomains",
172+]);
173+expect(codexAllowedDomains?.type).toBe("array");
174+expect(itemSchema(codexAllowedDomains)?.type).toBe("string");
175+176+const codexUserLocation = schemaAt(BASE_SCHEMA, [
177+"tools",
178+"web",
179+"search",
180+"openaiCodex",
181+"userLocation",
182+]);
183+expect(codexUserLocation?.type).toBe("object");
184+expect(codexUserLocation?.properties?.country?.type).toBe("string");
185+expect(codexUserLocation?.properties?.region?.type).toBe("string");
186+expect(codexUserLocation?.properties?.city?.type).toBe("string");
187+expect(codexUserLocation?.properties?.timezone?.type).toBe("string");
188+189+expect(schemaAt(BASE_SCHEMA, ["gateway", "controlUi", "chatMessageMaxWidth"])?.type).toBe(
190+"string",
191+);
192+});
193+194+it("does not publish metadata-only composition branches", () => {
195+expect(collectMetadataOnlyCompositionBranches(BASE_SCHEMA)).toEqual([]);
196+});
73197});
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。