fix: correct nullability, SSE, base URL, and dependency defects found by live API testing#41
Merged
Merged
Conversation
Five defects found by live-testing a generated client against the RunPod v2 API. The published spec was accurate in every case; the generator was not. - 3.1 `type: ["X", "null"]` on a required property generated a non-Option field, so clients compiled and then failed on the first response carrying null. Consolidates all three nullability spellings behind is_nullable_any so a fourth call site cannot drift. (openapi-generator-dsu) - SSE-only operations returned () and called .text() on an open stream, which never returns; the caller's task hung rather than erroring. They now return a futures_util::Stream of bytes. Operations declaring both JSON and SSE keep their JSON contract. (openapi-generator-x9v) - Generated clients ignored the configured base_url entirely and started empty. Now seeded from config, which the CLI resolves from servers[0].url when the TOML is silent. (openapi-generator-igg) - The Default(..) error variant was declared but never constructed, so a typed default-response body surfaced as typed: None. (openapi-generator-nu7) - Multipart specs enabled reqwest's multipart feature on reqwest-middleware but not reqwest, so file-upload clients did not compile. (openapi-generator-upz) Verified against the live API: list_gpu_types and list_pods both failed before and now succeed, with pool: None on 24 GPU types and template: None on every pod confirming the null path is genuinely exercised. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Five generator defects, all found by generating a client from RunPod's published v2 OpenAPI document and exercising it against the live API.
Worth stating up front: the published spec was accurate in every case. All 31 collected live response bodies validated clean against it. Every failure was ours. Spec-diffing alone would have reported "all green" indefinitely.
Fixed
OpenAPI 3.1 nullability on required properties (
openapi-generator-dsu, P0)A property both listed in
requiredand declared nullable viatype: ["X", "null"]generated a bareT. Such a client compiles, then fails to deserialize the first real response containingnull.GET /v2/catalog/gpusandGET /v2/podsfailed outright:GpuType.poolandPod.templateare null in production.Pod.startedAtwas the worst latent case — null for any pod that hasn't started, taking out the wholelistPodscall.The
Optiondecision (!is_required || prop.nullable) was already correct;prop.nullablewas simply never set for this form. Two call sites checked onlynullable: trueand theanyOf-with-null shape. This bug has now recurred once per nullability spelling added, each time by missing a site, so all three route through a singleis_nullable_any()helper. CoversallOf-composed schemas too — RunPod'sPodkeeps itsrequiredlist inside anallOfbranch.SSE operations returned
()and hung forever (openapi-generator-x9v, P1)Operations whose success content is
text/event-streamgenerated-> Result<(), _>and calledresponse.text().awaiton an open stream, which never returns. With no default timeout on the generated client, this deadlocks the caller's task rather than erroring. Measured hung past 600s.The streaming signal was already detected in analysis and honored by the server generator; only the client ignored it. These now return
impl futures_util::Stream<Item = Result<bytes::Bytes, reqwest::Error>>. The error path still buffers, since error responses are finite.Scoped deliberately: operations declaring both a JSON body and SSE keep their JSON contract, since those are the
stream: trueendpoints covered by explicit[streaming]config and changing their return type would break existing callers.Configured
base_urlwas ignored (openapi-generator-igg)Worse than originally filed: generated clients started with
String::new()regardless, so even users who set[http_client] base_urlin TOML got a client that sent every request to a relative path. Now seeded from config, and the CLI resolvesservers[0].urlinto config when the TOML is silent. Explicit config still wins; relative (/v1) and templated ({region}) server URLs are ignored as unusable.Default(..)error variant was never constructed (openapi-generator-oug)Declared on every per-operation error enum, constructed zero times — a live 422 whose body parsed cleanly still surfaced as
typed: None, so the typed-errors feature silently didn't work fordefaultresponses.Multipart clients didn't compile (
openapi-generator-upz)REQUIRED_DEPS.tomlenabled reqwest'smultipartfeature onreqwest-middlewarebut not onreqwest, which is pinneddefault-features = false. Any spec with a file upload — OpenAI's included — produced non-compiling output.Verification
362 tests pass. One intermediate failure was
every_generation_mode_compiles_from_its_exact_dependency_fragmentcorrectly catching that SSE output now requiresfutures-util; its passing confirms the streaming client compiles from its own declared fragment.Against the live RunPod API,
list_gpu_typesandlist_podsboth failed before and now succeed. These are not trivial passes — the responses containpool: Noneon 24 GPU types (alongside realSome("AMPERE_80")values) andtemplate: Noneon all 8 pods, so the previously-broken path is genuinely exercised.Not addressed here
Three findings from the same investigation remain open:
format: floatmaps tof32and loses money precision (live:0.03deserializes as0.029999999329447746). RunPod's billing endpoints usedouble; catalog prices don't.problem_details()can never succeed against this API's error shape.x-enum-varnamesis ignored for parameter enums.🤖 Generated with Claude Code