Skip to main content
Version: 1.8.0

Mock Provider

The Mock provider returns configurable fake completions without calling an upstream model API. Use it for integration tests, CI pipelines, and local development. Requests still enter through the authenticated DVARA LLM Gateway and run the controls enabled for that request path.

Model prefix: mock/

1. Activate​

Set one environment variable:

export MOCK_PROVIDER_ENABLED=true

With only this setting, a request for a mock/* model returns the configured default after 100 ms. The provider is off by default.

Dev / CI only — Mock bundles Groovy

The Mock provider ships Groovy for scenario scripting, which means anyone who flips MOCK_PROVIDER_ENABLED=true on a production profile can execute arbitrary code inside the gateway JVM. The gateway fires a startup WARN whenever Mock is enabled outside the dev / test / ci / local / default Spring profile allow-list. Treat that warning as an alert, not a footnote — never run Mock on production traffic.

curl -s -X POST http://localhost:8080/v1/chat/completions \
-H "Authorization: Bearer $DVARA_API_KEY" \
-H "Content-Type: application/json" \
-d '{"model": "mock/any", "messages": [{"role": "user", "content": "hi"}]}'
{
"id": "mock-5654b316969d4c54b1549ac39b54b322",
"object": "chat.completion",
"created": 1787971119,
"model": "mock/any",
"choices": [
{
"index": 0,
"message": {"role": "assistant", "content": "This is a mock response"},
"finish_reason": "stop"
}
],
"usage": {"prompt_tokens": 4, "completion_tokens": 5, "total_tokens": 9}
}

Any name after mock/ works because the prefix selects the provider. The response includes an estimated synthetic token count. Usage and cost views populate only when their storage is configured; a cost record also needs matching pricing data.

The Authorization header is not optional here. Mock changes which provider answers, not whether the request is authenticated. A call without a key returns 401 api_key_required; a key that is not in the store returns 401 invalid_api_key.

2. Simple tuning via env vars​

Every scalar field on the Mock provider can be set from an environment variable — no config file required. Useful in CI, where the whole point is "don't ship a YAML file with the pipeline":

Env varDefaultWhat it does
MOCK_PROVIDER_ENABLEDfalseActivates the Mock provider. Required.
DVARA_LLM_GATEWAY_PROVIDERS_MOCK_RESPONSE"This is a mock response"Static text returned for every mock/* request that has no per-model override.
DVARA_LLM_GATEWAY_PROVIDERS_MOCK_LATENCY_MS100Simulated end-to-end latency per call, in milliseconds. Set to 0 for CI.
DVARA_LLM_GATEWAY_PROVIDERS_MOCK_STREAM_TOKEN_DELAY_MS20Inter-token delay when the client requests streaming. Set to 0 for CI.
DVARA_LLM_GATEWAY_PROVIDERS_MOCK_ERROR_RATE0.0Probability in [0.0, 1.0] that a given request returns PROVIDER_ERROR. Used for testing client retry logic.

Example — a tight CI profile with zero latency and 10% injected failures:

export MOCK_PROVIDER_ENABLED=true
export DVARA_LLM_GATEWAY_PROVIDERS_MOCK_RESPONSE="CI mock response"
export DVARA_LLM_GATEWAY_PROVIDERS_MOCK_LATENCY_MS=0
export DVARA_LLM_GATEWAY_PROVIDERS_MOCK_STREAM_TOKEN_DELAY_MS=0
export DVARA_LLM_GATEWAY_PROVIDERS_MOCK_ERROR_RATE=0.1

Error injection — error-rate: 0.1 means roughly 10% of requests fail with the DVARA code provider_error. The Mock provider is excluded from circuit-breaker wrapping, so injected failures do not open its circuit.

3. Wiremock-style conditional matchers​

Real test scenarios rarely depend only on the model name. A "billing question" mock should respond when the user's message contains the word "billing"; a streaming smoke test should match when stream=true; an error-injection scenario should fire only on a specific workspace. Matching on the model alone cannot express any of this.

DVARA's Mock provider supports an ordered list of wiremock-style matchers, where each matcher has a Groovy predicate (when) evaluated against the incoming request and a response (response) that's either static text or a dynamic Groovy expression. Matchers run in declaration order; the first match wins; if nothing matches, the gateway falls through to the default response.

Mount a small config file with SPRING_CONFIG_ADDITIONAL_LOCATION and point the gateway at it. Create mock-override.yml next to your compose file:

dvara:
llm-gateway:
providers:
mock:
enabled: true
response: "Default fake response" # final fall-through

matchers:
# 1. Match on the user message text
- name: billing-question
when: "request.messages.last().textContent().toLowerCase().contains('billing')"
response: "Your current balance is $42.50. Anything else?"

# 2. Match on streaming flag
- name: streaming-smoke-test
when: "request.stream"
response: "This response will be streamed token by token"

# 3. Match on temperature with a Groovy-computed response
- name: high-temperature-creative
when: "request.temperature != null && request.temperature > 0.9"
response: "groovy: ['cats', 'dogs', 'birds'].shuffle().first()"

# 4. Inject errors for one specific model — handy for testing retry logic
- name: quota-exceeded-simulation
when: "request.model == 'mock/quota-exceeded'"
response: "groovy: throw new RuntimeException('quota exceeded')"

The request binding exposes every field of the incoming chat request:

  • request.model — the model string the client sent (e.g. mock/billing)
  • request.messages — the list of messages; request.messages.last() is the most recent
  • request.messages.last().textContent() — concatenated text content of every block in a message, joining text blocks with a space and skipping image blocks. Use this instead of walking the content list by hand.
  • request.temperature — the temperature the caller sent (may be null)
  • request.maxTokens — the max_tokens the caller sent (may be null)
  • request.stream — boolean, true if the caller requested SSE streaming
  • request.responseFormat — the response_format object, if any
  • request.metadata — the request metadata map (may be null). Commonly carries workspace_id for workspace-keyed matchers — e.g. when: "request.metadata?.tenant_id == 'workspace-a'".

Matcher precedence is file-backed scenarios (step 4) → YAML matchers (this step) → response (default). The first matcher whose when returns true wins; once a matcher fires, lower-precedence matchers are skipped.

Failure modes:

  • A matcher whose when predicate has invalid Groovy syntax aborts gateway startup with a clear error pointing at the offending matcher's name and the parse error.
  • A matcher whose when predicate throws at runtime (NPE, division by zero, an explicit throw inside the script) returns PROVIDER_ERROR to the caller with the exception message — testers see the failure rather than silently falling through.
  • A response: "groovy: ..." script that throws follows the same pattern.

4. File-backed scenarios with hot reload​

Inline YAML matchers are great for one-liners but painful for anything serious — multi-line Groovy is awkward in YAML strings, scenarios can't be unit-tested in isolation, and there's no IDE syntax highlighting. For that, mount a directory of .groovy scenario files alongside your config and point DVARA at it.

Step 1 — lay out the directory. Create a mock-scenarios/ folder next to your compose file with one file per scenario:

mock-scenarios/
├── 01-billing-question.groovy
├── 02-streaming-test.groovy
└── 03-error-injection.groovy

Files are loaded in lexicographic filename order, so prefix them with 01-, 02-, 03- to control which matcher wins when multiple predicates would match the same request.

Step 2 — write each scenario as a plain Groovy script that sets three top-level variables:

// mock-scenarios/01-billing-question.groovy

name = 'billing-question'

when = { request ->
request.messages.last().textContent().toLowerCase().contains('billing')
}

respond = { request ->
'Your current balance is $42.50. Anything else?'
}

name is a human-readable label used in logs, audit events, and Prometheus metrics. when is a closure that takes the request and returns true to match. respond is a closure that takes the request and returns the response body as a string. Both closures see the same request binding documented in step 3.

If name is missing, DVARA falls back to the filename without the .groovy extension (so 01-billing-question.groovy becomes 01-billing-question). Missing when or respond aborts startup with a clear error pointing at the offending file.

Step 3 — point DVARA at the directory. Add scenarios-dir to your mock-override.yml:

dvara:
llm-gateway:
providers:
mock:
enabled: true
scenarios-dir: /app/config/mock-scenarios
response: "No scenario matched"

Mount both the override file and the scenarios directory in your docker-compose.yml:

services:
dvara-gateway:
image: ghcr.io/dvarahq/dvara-gateway-oss:1.8.5
environment:
MOCK_PROVIDER_ENABLED: "true"
SPRING_CONFIG_ADDITIONAL_LOCATION: /app/config/mock-override.yml
volumes:
- ./mock-override.yml:/app/config/mock-override.yml:ro
- ./mock-scenarios:/app/config/mock-scenarios:ro

Hot reload. DVARA watches the configured directory and reloads the complete scenario set after a file is created, changed, or deleted. New requests see the new set after a successful reload; in-flight requests continue with the set they started with. If any file is invalid, DVARA keeps the previous complete set and logs the failed reload. When audit storage is configured, a successful reload writes MOCK_SCENARIO_RELOADED.

5. Interactive editing in DVARA Flightdeck​

Enterprise only

Not included in DVARA Open Source.

The Mock provider remains unsuitable for production traffic.

For interactive scenario development — browser-based editor, compile-check on save, synchronous reload, a Run test panel — DVARA Flightdeck ships the Mock Scenario Editor. It reads and writes the same scenarios directory the filesystem watcher observes, so scenarios authored in the editor are immediately available for git-commit and promotion through your normal deploy pipeline.

The editor is a triple opt-in by design (mock.enabled + scenarios-dir + console-authoring=true, plus the owner role on every write endpoint). See Mock Scenario Editor for the enabling flags, the editor walk-through, and the security model.

Observability​

When metrics are enabled, the gateway exposes three counters on the authenticated /actuator/prometheus endpoint. See Observability for the bearer-token requirement.

CounterLabelsMeaning
gateway_mock_matcher_fires_totalscenario, source (file or yaml)Incremented every time a matcher's predicate returns true. Tells you which scenarios are firing and how often.
gateway_mock_matcher_fallthrough_total(none)Incremented every time no matcher fires and the configured default response is returned.
gateway_mock_scenarios_reloaded_total—Incremented on every successful reload of the scenarios directory.

Compute the matcher hit rate on a Grafana dashboard with:

rate(gateway_mock_matcher_fires_total[5m])
/
(rate(gateway_mock_matcher_fires_total[5m]) + rate(gateway_mock_matcher_fallthrough_total[5m]))

When audit storage is configured, the same activity can produce these events:

EventTrigger
MOCK_MATCHER_FIREDA matcher's predicate returned true (sampled at audit-sample-rate, default 1.0)
MOCK_SCENARIO_RELOADEDScenarios directory was successfully reloaded
MOCK_SCENARIO_CREATED / MOCK_SCENARIO_UPDATED / MOCK_SCENARIO_DELETEDDVARA Flightdeck wrote, edited, or deleted a scenario file

For high-throughput load tests where per-fire audit writes become a bottleneck, set dvara.llm-gateway.providers.mock.audit-sample-rate to a value below 1.0 (e.g. 0.1 for 10% sampling). The Prometheus counter still increments on every fire regardless of the sample rate, so the cumulative rate stays accurate.