operationstestingtools

The mock server is the place to rehearse weird days before they happen in production. It stands in for Render and Twilio, gives you dropdowns for common stories, and then sends Strike the same kind of updates the real outside systems would send.

This is not a developer reference for every endpoint. It is a cookbook: “I want to see this thing happen; what do I pick?” Each recipe assumes the local stack from the Osprey Strike README is running.

Start Here

Run the normal local stack:

just services up
just mock dev
just api dev
just web dev

Open Strike at http://localhost:4700. Open the mock dashboard at http://localhost:4740/control/.

The dashboard has scenario pickers for Render, Pager, SMS, Workflow, Speed, and Faults. Set the scenario before you do the thing in Strike. For example, choose a pager scenario before sending a page, or choose a Render/workflow scenario before creating an ECO that will create the Render investigation task.

Key Takeaway

Think of each dropdown as “what should happen next time?” The mock does not rewrite the past. It applies the selected story to the next matching action.

Two practical habits keep test runs readable:

  • Start with Speed on normal unless you are deliberately trying to outrun polling or catch a brief state.
  • Reset Render mock state between demos when old tasks would make the current story hard to explain.
curl -X DELETE http://localhost:4740/control/render/reset

Render And Workflow Recipes

Render is the outside field-work system. In the mock, there are two related controls:

  • Render scenario: what happens to the investigation task Strike creates in Render.
  • Workflow scenario: what happens after that, including follow-on tasks, status changes, deletes, and reopens.

For most testing, set both. A simple happy-path demo uses Render succeeds and Workflow full-repair.

If you want to see… Set Render to… Set Workflow to… What to watch in Strike
A normal ECO that finishes succeeds full-repair Investigation moves through assigned work, a follow-on appears, and labeled work completes.
A more involved repair succeeds complex-repair Two follow-on tasks appear: aerial fiber work and pole replacement.
An ECO with no extra field task succeeds investigation-only The investigation completes without a follow-on task.
A failure in the mock dashboard fails manual The dashboard reports a failure, but the stored task stays blueprinted; Strike’s poller sees the same task status as manual. This does not simulate an API-visible failed integration.
Render is reachable but returns no tasks render-returns-empty Forced to manual The task is removed before the first poll, and Render returns a successful empty result, not a failed fetch or an observed task going missing.
Render accepts the task but never updates it manual manual or stalled The ECO sits in a stuck-looking state without a provider error.
OSP accepts but never assigns the work succeeds stalled The investigation gets allocated, then stops moving.
A task hits jeopardy, then recovers succeeds jeopardy The follow-on task enters jeopardy, returns to released, then completes.
A completed ECO gets more work later succeeds late-clone The investigation completes, then a late follow-on reopens the work.
A completed task is reopened succeeds reopened The task completes, moves back to released, then completes again.
The Strike-created investigation task disappears missing-investigation-task Any non-confusing workflow This is the corruption case: Strike’s own investigation task vanishes while Render is reachable.
A follow-on task is deleted by the field crew deletes-followup-task Auto-selects the follow-on deletion workflow The follow-on appears, disappears, and should not be treated as corruption.
A field-created follow-on is missing the ECO label succeeds unlabelled-follow-on The follow-on exists in mock Render but stays invisible to Strike’s label-filtered task panel.

The difference between the two delete cases matters. If the investigation task Strike created disappears, that is suspicious. If a follow-on task disappears, that can be normal field behavior.

Pager Recipes

Pager scenarios test the phone-call side of a callout. Use at least two contacts for second-contact recipes, or three for accepts-third. The mock fits the script to the ECO’s actual roster: extra scripted contacts are dropped, and a short script repeats its last outcome to cover a longer list. accepts-third on two contacts therefore cannot produce a third-person acceptance.

The table describes the ordinary region-list story. A run’s business-hours lead is called before that list, repeat passes follow the run’s policy, and an escalation chain follows only after the region list is spent without acceptance. A lead also present on the region list is skipped there. Current mock chain calls model unanswered attempts, not an escalation-person acceptance.

If you want to see… Set Pager to… What happens
The first person accepts accepts-first One contact is called and accepts after a short delay.
The first person declines, second accepts accepts-second First contact declines, the run moves to the second contact, and the second accepts.
Two declines before an accept accepts-third First two contacts decline, third accepts.
No one accepts exhausted Every contact on the actual roster declines, across the configured passes. A chain, if supplied, is tried without an answer; exhaustion distinguishes no chain from a tried chain.
The first call gets no result timeout The mock reports the call start, then marks its own scenario timed out. This older preset is not the same as a provider-silent run left open in the dashboard.
The provider goes silent partway through provider-silent First contact declines; second reports calling and nothing further. The mock stays open and does not call a third person; Strike’s inactivity handling determines the final outcome. Requires at least two contacts.
A person connects but gives no usable answer no-response First contact gives no response, then second accepts. The first outcome is neither a decline nor a provider failure.
A plain failed call, then a recovery call-fails First call fails without special detail, then second contact accepts.
A busy line line-busy First attempt reports a busy line, then second contact accepts.
A carrier or device rejection carrier-rejected First attempt is rejected before ringing, then second contact accepts.
Historical voicemail detail voicemail First attempt fails with a machine-beep verdict and no rescue result; second contact accepts. Use the machine-rescue presets below for the newer prompt outcomes.
A machine-classified call is rescued machine-rescued First contact presses 1 at the rescue prompt and accepts; the result keeps the original machine-detection verdict and rescue evidence.
No key at the rescue prompt machine-no-keypress First call fails with “No key pressed at the prompt”; second accepts.
A different key at the rescue prompt machine-other-keypress First call fails with “Callout not opened”; second accepts. It is not a decline.
Hangup during the rescue prompt machine-hangup First call follows the no-response path; second accepts.
Twilio/provider system error provider-error The provider reports an error, then the next contact accepts.
No automatic pager updates manual The call is acknowledged, but no pager webhooks are sent.

Use call-fails, line-busy, carrier-rejected, and voicemail when you are checking the wording in the pager event timeline. They all continue to the second contact, but the first failed attempt should read differently.

Tip

For a non-dev test script, write down the expected final contact before starting. It is easier to judge the page history when you already know “second accepts” or “third accepts” is the intended result.

SMS Recipes

SMS scenarios test text-message delivery callbacks after Strike sends a message through the Twilio mock.

If you want to see… Set SMS to… What happens
Normal delivery delivered The message moves from sent to delivered.
Carrier rejection undelivered The message is sent, then marked undelivered with a representative carrier error code.
Twilio cannot process it failed The message is sent, then marked failed with a representative Twilio error code.
No delivery callback manual The send is acknowledged, but no delivery status follows.

The mock also has an inbound control endpoint for contact replies such as STOP, HELP, YES, and START:

curl -X POST http://localhost:4740/control/twilio/inbound \
  -H 'Content-Type: application/json' \
  -d '{"from":"+15551234567","body":"STOP"}'

Use inbound replies when checking opt-out and opt-in behavior. Use delivery scenarios when checking whether the outbound message itself reached the phone network.

Faults, Speed, And Cleanup

Faults apply to the Render mock. They are useful when the thing you want to test is not the field-work story, but Strike’s behavior when Render itself is unreliable.

If you want to see… Set Fault to… What it means
No injected provider trouble none Normal behavior.
Short traffic burst throttling rate-limited 429 after a few requests, with a short retry window.
Daily quota lockout with retry hint quota-exhausted 429 from the first request, with a five-minute Retry-After.
Daily quota lockout without retry hint quota-exhausted-no-header 429 from the first request, with no Retry-After header.
Occasional provider errors flaky-10 About one in ten requests returns 500.
Very noisy provider errors flaky-50 About half of requests return 500.
Render outage outage Every Render request returns 503.

A fixed pager interval override is capped, including after speed scaling, so a slowed demo cannot exceed the API’s post-result inactivity allowance. The Render override is not subject to that pager cap.

Speed changes timing, not the meaning of the scenario. Faster speeds are helpful for quick demos. Normal or slower speeds are better when you are capturing screenshots or explaining state changes out loud.

Screenshot Checklist

Screenshots still need to be captured from a crabbox or local checkout running the current Go toolchain. These are the views worth adding:

  1. Mock dashboard with the Scenario Configuration drawer open, showing Render, Pager, Workflow, Speed, and Fault controls.
  2. Happy path: Strike ECO task panel after succeeds + full-repair has created the follow-on task.
  3. Jeopardy: task panel or timeline while the follow-on task is in jeopardy.
  4. Pager escalation: page history after accepts-second or accepts-third.
  5. Failed-call detail: pager history after line-busy, carrier-rejected, or voicemail.
  6. SMS delivery: invitation/consent area after delivered and after one failure-shaped SMS scenario.
  7. Render fault: app state while Fault is outage or quota-exhausted.

Summary

  1. Use the mock dashboard at http://localhost:4740/control/ before starting the action you want to test.
  2. Render scenarios cover the investigation task; workflow scenarios cover the field-work story around it.
  3. Pager scenarios are best read as contact outcomes: who accepts, who declines, what failed, and whether the provider called back.
  4. SMS scenarios cover delivery callbacks, while inbound replies cover opt-in and opt-out behavior.
  5. Faults are for Render reliability testing: rate limits, quota lockout, flaky errors, and outage.

Discussion Prompts

  • Which three scenarios should every release demo include before showing Strike to a customer?
  • Where should the app make the difference between "normal field behavior" and "corruption" obvious to a non-dev tester?
  • Which screenshots belong in this cairn permanently once a crabbox run can capture them?

References

  1. osprey-strike/README.md for local startup commands and service ports.
  2. osprey-strike/packages/mock/internal/dashboard/handler.go and scenario_handlers.go for dashboard routes and selected-scenario behavior.
  3. osprey-strike/packages/mock/internal/orchestrator/scenarios*.go for Render, pager, SMS, and machine-rescue presets; runner.go, runner_policy.go, and walk.go for roster fitting, silence, and call order.
  4. osprey-strike/packages/mock/internal/workflow/scenario_defs.go for workflow recipes and task progressions.
  5. osprey-strike/packages/mock/internal/errorinjection/presets.go for Render fault presets.