Skip to content

feat: simulate network failures, deterministic and probabilistic - #128

Open
MaxMichel2 wants to merge 1 commit into
feat/networkmock-response-headersfrom
feat/networkmock-failure-simulation
Open

MaxMichel2 wants to merge 1 commit into
feat/networkmock-response-headersfrom
feat/networkmock-failure-simulation

Conversation

@MaxMichel2

Copy link
Copy Markdown
Collaborator

Summary

Closes #88, #95.

Stacked on #127 (PR chain: #123#124#127 → this) — merge in order.

Adds the ability to make an operation fail instead of returning a response — the class of bugs (timeouts, dropped connections) that's hardest to reproduce on demand during manual testing, and most valuable to have mockable.

What changed

  • OperationMockState.Failure(kind: FailureKind): a new sealed-interface variant alongside Network/Mock. Every request to the operation fails the same way until the selection changes — the deterministic case (feat: simulate network failures #88). FailureKind has TIMEOUT and CONNECTION_REFUSED, each mapped in the Ktor plugin to the real exception a Ktor engine throws for the equivalent condition (HttpRequestTimeoutException / a connection-level kotlinx.io.IOException), so an app's existing error handling exercises the same code path against the simulated failure.
  • Operation.failureRate (from a new x-devview.failureRate extension field) — the probabilistic case (feat: probabilistic failure-rate injection #95). Deliberately operation-level only, with no spec-wide default (unlike delayMs) — "some percentage of everything fails" is a much blunter tool than "this specific flaky endpoint fails sometimes." Each request to an otherwise-mocked operation independently rolls against the configured rate before serving its response; an operation left on Network passthrough is never affected.
  • NetworkMockConfig.random: Random: injectable, defaults to Random.Default, so tests pin the probabilistic roll deterministically instead of relying on real randomness.
  • UI: a "Simulate Failure" section in the operation picker page, one row per FailureKind, plus a read-only note showing the configured failure rate when the spec declares one.

Public API (breaking, per your call on 0.2.0 alphas)

  • OperationMockState gains a new sealed subtype (Failure) — any exhaustive when an integrator has over it needs a new branch. Updated internally: EndpointCard, EndpointStateChip, NetworkMockOperationSheet's selected-response lookup, ModelUtils' icon/color extensions, and NetworkMockScreen's Mocked/Network list filter (folds Failure into "Mocked", since it's a deliberately-configured non-default state like Mock).
  • MockColorScheme gains a new required constructor parameter (failure: StatusColors) — matches Operation.method's precedent in alpha03.
  • api.txt regenerated for all three affected modules (devview-networkmock-core, devview-networkmock-ktor, devview-networkmock) via metalavaGenerateSignature.

Tests

  • MockConfigRepositoryTest: x-devview.failureRate parses as operation-level with no spec-wide default.
  • MockStateRepositoryTest: Failure state round-trips through the real DataStore JSON encode/decode path.
  • NetworkMockPluginTest: deterministic Failure(TIMEOUT)/Failure(CONNECTION_REFUSED) throw the right exception type; a fake Random pinned to "always hits"/"always misses" proves the probabilistic roll; the roll is confirmed to not apply when the endpoint state is Network.

Docs updated: networkmock-core.md (new "Simulating failures" section), networkmock-ui.md, networkmock-workflows.md (new workflow), theming.md, and all three module CLAUDE.md files.

Verification

.\gradlew.bat detektFull -Pandroidx.baselineprofile.skipgeneration
.\gradlew.bat cleanTestAndroidHostTest testAndroidHostTest -Pandroidx.baselineprofile.skipgeneration
.\gradlew.bat :konsist:test -Pandroidx.baselineprofile.skipgeneration
.\gradlew.bat :sample:androidApp:assembleDebug -Pandroidx.baselineprofile.skipgeneration
.\gradlew.bat :devview-networkmock:compileAndroidDeviceTest -Pandroidx.baselineprofile.skipgeneration

All green, including a full repo-wide testAndroidHostTest run to confirm no fallout outside the networkmock modules.

🤖 Generated with Claude Code

Adds the ability to make an operation fail instead of returning a
response - the network-mocking equivalent of a chaos-testing knob for
the class of bugs (timeouts, dropped connections) that's hardest to
reproduce on demand during manual testing.

- OperationMockState.Failure(kind: FailureKind): a new sealed-interface
  variant alongside Network/Mock. Every request to the operation fails
  the same way until the selection changes. FailureKind has two values,
  TIMEOUT and CONNECTION_REFUSED, each mapped in the Ktor plugin to the
  real exception a Ktor engine throws for the equivalent condition
  (HttpRequestTimeoutException / kotlinx.io.IOException) so existing
  app error handling exercises the same code path.
- Operation.failureRate (from a new x-devview.failureRate extension
  field, operation-level only - no spec-wide default, unlike delayMs):
  each request to an otherwise-mocked operation independently rolls
  against the configured rate before serving its response. Applies
  only to requests that would otherwise be mocked; Network passthrough
  is never affected.
- NetworkMockConfig.random: Random, injectable so tests can pin the
  probabilistic roll deterministically instead of relying on real
  randomness.
- UI: a "Simulate Failure" section in the operation picker page with
  one row per FailureKind, plus a read-only note showing the
  configured failure rate when the spec declares one. MockColorScheme
  gains a failure StatusColors slot; ModelUtils' icon/color
  extensions and every exhaustive when over OperationMockState
  (EndpointCard, EndpointStateChip, the operation sheet, the
  Mocked/Network list filter) updated for the new variant.

Breaking (fine during 0.2.0 alphas, matches Operation.method's
precedent in alpha03): OperationMockState gains a new sealed subtype,
and MockColorScheme gains a new required constructor parameter.

Closes #88, #95.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@MaxMichel2 MaxMichel2 added this to the 0.2.0 milestone Sep 22, 2026
@MaxMichel2 MaxMichel2 self-assigned this Sep 22, 2026
@MaxMichel2
MaxMichel2 added this pull request to stack #130 September 22, 2026 12:07

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant