Skip to content

feat(ringo-flow): HTTP mock server for webhook scenarios#26

Merged
davidborzek merged 4 commits into
mainfrom
feat/ringo-flow-http-mock-server
Jun 22, 2026
Merged

feat(ringo-flow): HTTP mock server for webhook scenarios#26
davidborzek merged 4 commits into
mainfrom
feat/ringo-flow-http-mock-server

Conversation

@davidborzek

@davidborzek davidborzek commented Jun 22, 2026

Copy link
Copy Markdown
Owner

What

A scriptable HTTP mock server for ringo-flow, for testing webhook-driven
telephony APIs: the system under test calls a webhook for a call and the
scenario answers with the actions to perform.

let hooks = mock_server();

hooks.on("POST", "/voice", |req| {
    if req.json("event") == "incoming_call" {
        json_response(#{ actions: [ #{ type: "answer" } ] })
    } else {
        json_response(#{ actions: [ #{ type: "hangup" } ] })
    }
});

http("PUT", env("API_URL") + "/config?webhook=" + hooks.url + "/voice");
a.dial(env("API_NUMBER"));

await_until(|| assert(hooks.request_count("/voice")).equals(1), "10s");
assert(hooks.last_request("/voice").json("event")).equals("incoming_call");

Details

  • mock_server([#{port}]) — free port by default; runs on the shared tokio
    runtime, torn down at reset_sessions (per-scenario/-file isolation), and the
    serving task is awaited so the port is released before the next bind.
  • Routes: respond (static) and on (Rhai-closure) responders. Match by exact
    path or regex("/calls/.*"), and by a method or any ("*" / omit it).
    Precedence: exact path > regex, exact method > any; re-registering a route
    replaces it in place (response staging).
  • Inspection: request_count / last_request / requests, polled via the
    existing await_until — no second waiting mechanism. MockRequest exposes
    method / path / body / header / query / json.
  • Helpers: json_response (map or array) and text_response.
  • Responder failures are logged (and answered with a bare 500); the error text
    is never exposed over HTTP.

Testing

  • Unit/roundtrip tests: static + dynamic responders, 404, request recording,
    regex + any-method routing, error-not-leaked, explicit-port release on teardown.
  • cargo fmt --all --check, cargo clippy --all-targets, and
    cargo test -p ringo-flow -p ringo-core pass. Example
    examples/webhook-mock.rhai, README and generated docs updated.

🤖 Generated with Claude Code

davidborzek added a commit that referenced this pull request Jun 22, 2026
Addresses review of #26.

- reset_sessions now awaits each mock server's serving task after signalling
  shutdown, so an explicit reused port is provably released before the next
  scenario rebinds it (no "address in use" race). Test added.
- Note in docs/comments that the on(...) responder runs on a runtime worker
  (don't block it), an oversized body reads as empty, and an invalid response
  header collapses to an empty reply.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
davidborzek and others added 4 commits June 22, 2026 18:36
Mock the webhook a telephony API calls and answer with the call actions
to perform.

- mock_server([#{port}]) with respond/on routes and request_count/
  last_request/requests, polled via await_until.
- Static and Rhai-closure responders; responder errors are logged and
  answered with a bare 500, not exposed over HTTP.
- Runs on the shared runtime; torn down at reset_sessions.

Includes json_response/text_response helpers, an example and docs.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- regex("/calls/.*") as a path matcher for respond/on/request_count/...
- Any-method routes via "*" or by omitting the method argument.
- Precedence: exact path > regex, exact method > any; ties go to the
  last registered route. Re-registering still replaces in place.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Addresses review of #26.

- reset_sessions now awaits each mock server's serving task after signalling
  shutdown, so an explicit reused port is provably released before the next
  scenario rebinds it (no "address in use" race). Test added.
- Note in docs/comments that the on(...) responder runs on a runtime worker
  (don't block it), an oversized body reads as empty, and an invalid response
  header collapses to an empty reply.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@davidborzek davidborzek force-pushed the feat/ringo-flow-http-mock-server branch from 5291939 to 86c45d7 Compare June 22, 2026 16:37
@davidborzek davidborzek merged commit 6637bec into main Jun 22, 2026
1 check passed
This was referenced Jun 22, 2026
@davidborzek davidborzek deleted the feat/ringo-flow-http-mock-server branch June 25, 2026 07:09
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