From ca2f54a68daa6030f269154a73a62f759d929293 Mon Sep 17 00:00:00 2001 From: Kit Langton Date: Tue, 28 Apr 2026 22:20:10 -0400 Subject: [PATCH] test(httpapi): compare request body schema kind --- .../server/routes/instance/httpapi/public.ts | 2 +- .../test/server/httpapi-bridge.test.ts | 36 ++++++++++++++++--- 2 files changed, 32 insertions(+), 6 deletions(-) diff --git a/packages/opencode/src/server/routes/instance/httpapi/public.ts b/packages/opencode/src/server/routes/instance/httpapi/public.ts index 33839d65e0..64d5f16a62 100644 --- a/packages/opencode/src/server/routes/instance/httpapi/public.ts +++ b/packages/opencode/src/server/routes/instance/httpapi/public.ts @@ -379,7 +379,7 @@ function normalizeLegacyErrorResponses(operation: OpenApiOperation) { } function applyLegacyErrorResponses(operation: OpenApiOperation, route: string) { - const responses = LegacyErrorResponses[route as keyof typeof LegacyErrorResponses] + const responses: ReadonlyArray<400 | 404> | undefined = LegacyErrorResponses[route as keyof typeof LegacyErrorResponses] if (!responses) return operation.responses ??= {} if (responses.includes(400)) operation.responses["400"] = legacyErrorResponse("Bad request", "BadRequestError") diff --git a/packages/opencode/test/server/httpapi-bridge.test.ts b/packages/opencode/test/server/httpapi-bridge.test.ts index d0432ec2c6..a0324cce39 100644 --- a/packages/opencode/test/server/httpapi-bridge.test.ts +++ b/packages/opencode/test/server/httpapi-bridge.test.ts @@ -57,16 +57,32 @@ function openApiParameters(spec: { paths: Record>> }) { +function openApiRequestBodies(spec: OpenApiSpec) { return Object.fromEntries( Object.entries(spec.paths).flatMap(([path, item]) => methods .filter((method) => item[method]) - .map((method) => [`${method.toUpperCase()} ${path}`, requestBodyKey(item[method]?.requestBody)]), + .map((method) => [`${method.toUpperCase()} ${path}`, requestBodyKey(spec, item[method]?.requestBody)]), ), ) } +type OpenApiSpec = { + components?: { + schemas?: Record + } + paths: Record>> +} + +type OpenApiSchema = { + $ref?: string + allOf?: unknown[] + anyOf?: unknown[] + oneOf?: unknown[] + properties?: Record + type?: string | string[] +} + type Operation = { parameters?: unknown[] responses?: unknown @@ -74,7 +90,7 @@ type Operation = { } type RequestBody = { - content?: Record + content?: Record required?: boolean } @@ -97,17 +113,27 @@ function parameterSchema(input: { return param.schema } -function requestBodyKey(body: unknown) { +function requestBodyKey(spec: OpenApiSpec, body: unknown) { if (!body || typeof body !== "object" || !("content" in body)) return "" const requestBody = body as RequestBody return JSON.stringify({ required: requestBody.required === true, content: Object.entries(requestBody.content ?? {}) - .map(([type, value]) => [type, value.schema?.$ref ?? value.schema?.type ?? "inline"]) + .map(([type, value]) => [type, requestBodySchemaKind(spec, value.schema)]) .sort(), }) } +function requestBodySchemaKind(spec: OpenApiSpec, schema: OpenApiSchema | undefined) { + if (!schema) return "" + const resolved = (schema.$ref ? spec.components?.schemas?.[schema.$ref.replace("#/components/schemas/", "")] : schema) as + | OpenApiSchema + | undefined + if (resolved?.properties) return "object" + if (resolved?.anyOf ?? resolved?.oneOf ?? resolved?.allOf) return "object" + return resolved?.type ?? schema.type ?? "inline" +} + function responseContentTypes(input: { spec: { paths: Record>> } path: string