dotCMS / dotCMS/core

Make provider test stubs reject what a real provider rejects

Open
#37,615 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Java
Stars
970
Forks
486
Avg merge
3d 33m
Merged PRs (30d)
170

Description

The problem

Every WireMock stub in the dotAI and /api/inference/v1 test suites is permissive: it matches on a path, sometimes on a fragment of the body, and returns a canned answer regardless of what else the request contained. A real provider is not permissive — it rejects fields it does not recognise.

That gap is not theoretical. It shipped a broken endpoint past a green suite.

What it cost, concretely

/api/inference/v1/images/generations sent response_format: b64_json on every request. Per OpenAI's own OpenAPI spec, that parameter belongs to dall-e-2 / dall-e-3 and "isn't supported for the GPT image models, which always return base64-encoded images."

So every request to gpt-image-1, gpt-image-2 and their variants — the models customers actually configure — failed at the provider with:

400 {"error":{"message":"Unknown parameter: 'response_format'.","code":"unknown_parameter"}}

Twelve integration tests covered that endpoint and all twelve passed, because the stub happily accepted a parameter the real provider rejects. It was found by a human running one curl against a live account, and only after two wrong diagnoses. Fixed in #37561.

The same blind spot is why a second defect hid underneath it: the provider's 400 was being translated into a retryable 502, so a permanently malformed request looked temporary.

Proposal

Make the provider stubs reject what a real provider would reject, so "we sent something the provider doesn't accept" fails in CI rather than in someone's terminal.

Rough shape, in increasing order of strictness:

  1. Assert on the outbound request in tests that care — already done for the images case (InferenceImagesTest.test_generations_sendsNoOutputFormatParameter asserts the field is absent). Cheap, targeted, but only covers fields somebody thought of.
  2. A shared strict stub helper that fails the request when the body carries a field outside an allowlist per operation, mirroring the real API surface. Catches the whole class rather than one instance.
  3. Generate the allowlist from OpenAI's published OpenAPI spec rather than hand-maintaining it, so it tracks the format instead of drifting from it.

(2) is probably the right target; (3) is worth considering only if (2) proves annoying to keep current.

Scope

Eleven test classes stub a provider today:

ChatCompletionsTest, ChatCompletionsStreamingTest, InferenceEmbeddingsTest, InferenceImagesTest, InferenceModelValidationTest, InferenceFallbackTest, InferenceAuthorizationTest, InferenceSiteResolutionTest, InferenceSiteIsolationTest, InferenceLoggingTest, InferenceClientConformanceTest

plus the shared helper AiTest.prepareWireMock() and the checked-in mappings under dotcms-integration/src/test/resources/mappings/, which the older dotAI tests share.

The risk, stated plainly

A strict stub is a stub that fails for reasons unrelated to what a test is asserting. Done carelessly this makes the suite brittle — every harmless new field breaks a dozen tests, people start loosening the allowlist reflexively, and the strictness becomes decorative.

That is the reason this was not bolted onto #37561: the fix belongs with someone who can think about where strictness earns its keep and where it just adds noise. Starting with the chat and images operations, where the parameter surface is well defined and the payoff is highest, is likely wiser than converting all eleven at once.

Why it's worth doing

Of the nine defects found while validating this feature against a live provider, none was caught by the test suite. This one is the only one whose root cause is a property of the tests themselves rather than of the code — so it is the only one where fixing the tests prevents a class of future bugs rather than one recurrence.

Found while validating #37431 (PRs #37559 and #37561).

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with AiTest.prepareWireMock(), the eleven named provider test classes, and the checked-in mappings under dotcms-integration/src/test/resources/mappings/. Focus first on the chat and images operations, then run their integration tests while checking that unsupported request fields are rejected without breaking unrelated assertions. Done means the stubs catch provider-incompatible fields while remaining specific enough to avoid broad suite brittleness.

Written by the indexing model from the issue text.

Assessment

Tech stack
java, openapi
Domain
api, testing
Issue type
Refactor
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.