Skip to content

feat: E2E test harness with testcontainers + wiremock#576

Merged
nolik merged 4 commits into
mainfrom
feat/e2e-test-harness
Jun 3, 2026
Merged

feat: E2E test harness with testcontainers + wiremock#576
nolik merged 4 commits into
mainfrom
feat/e2e-test-harness

Conversation

@nolik
Copy link
Copy Markdown
Contributor

@nolik nolik commented May 27, 2026

Summary

Introduce an end-to-end test harness for rust-bot so handler regressions are actually caught.

  • lib/bin splitsrc/lib.rs re-exports modules and the boot logic, src/main.rs shrinks to env-loading. Lets tests/ import the dispatcher.
  • Minimal DI seamsGptParameters now carries a reqwest::Client + OpenAI base URL; handlers take &GptParameters instead of &mut. No error-handling rewrite (separate follow-up per RECOMMENDATIONS.md §1).
  • Harness (tests/common/mod.rs) — testcontainers-managed Postgres + Redis, wiremock-backed Telegram Bot API (Bot::set_api_url points at it), wiremock-backed OpenAI completions. Helper that dispatches a single Update through the real handler tree and asserts it was routed.
  • Three E2E tests — rust-mention (Postgres side-effects), chat-gpt (OpenAI + Redis context), url-summary (article fetch + OpenAI summary + Telegram reply).
  • CI — new e2e job in .github/workflows/build.yml; existing build job (unit tests only) keeps fast feedback.

Why this shape

Direct Update injection (via dispatching to the dptree::Handler directly) instead of mocking the full Telegram HTTP API. Reasons: it exercises business logic without testing teloxide internals, doesn't require Telegram JSON-shape fixtures to match teloxide's serde across version bumps, and is faster.

Test plan

  • CI build job: cargo build + cargo test --lib (8 existing unit tests) passes
  • CI e2e job: cargo test --tests passes — runs unit + 3 E2E integration tests with Docker (testcontainers Postgres + Redis + wiremock)
  • Locally: cargo fmt --check, cargo check --all-targets clean (verified)
  • Smoke check after merge: temporarily change rust-mention reply text and confirm rust_mention_e2e fails

Out of scope (follow-up PRs)

Per RECOMMENDATIONS.md (kept local, gitignored):

  • thiserror/anyhow error-handling rewrite
  • ChatStore trait + mockall for handler-only unit tests
  • tracing migration
  • clippy gate in CI

🤖 Generated with Claude Code

nolik and others added 2 commits May 27, 2026 23:27
Split the binary into a lib + bin so integration tests in tests/ can
import the dispatcher. Add minimal DI seams (inject reqwest::Client and
OpenAI base URL into GptParameters; accept &GptParameters in handlers
instead of &mut) without rewriting error handling.

Add tests/common/mod.rs with:
- testcontainers-backed Postgres and Redis spawners
- wiremock-backed Telegram Bot API (Bot::set_api_url points at it)
- wiremock-backed OpenAI completions endpoint
- helper that dispatches a single Update through the real handler tree

Add three E2E tests covering rust-mention, chat-gpt and url-summary
flows end-to-end. Add a separate `e2e` job to .github/workflows/build.yml
so the slow Docker-backed suite doesn't block unit-test feedback.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
cargo does not expand globs in --test selectors, so `cargo test --test '*'`
fails with "no test target named *". Use `cargo test --tests` to run unit
and integration tests in one shot.

Also assert in dispatch_one that the handler tree resolved to Break(Ok(()))
so failures distinguish "no routing" from "routed but missed wiremock".

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Comment thread .github/workflows/build.yml Fixed
nolik and others added 2 commits May 27, 2026 23:41
Two runtime bugs surfaced once the suite ran against a real container
runtime (podman, exposing /var/run/docker.sock so testcontainers works
transparently):

1. teloxide-core 0.10's UpdateKind::Deserialize tries next_key::<&str>
   first and falls back to next_key::<String>, but serde_json::from_value
   produces owned String keys for which the &str attempt advances the
   cursor before failing. The fallback then sees an empty map and the
   variant degrades silently to UpdateKind::Error(Object {}). Workaround:
   round-trip through a JSON string so the borrowed-str path succeeds.

2. teloxide builds outbound URLs as `/bot{token}/{MethodName}` with
   capitalized method names (`/SendMessage`, `/SendSticker`,
   `/RestrictChatMember`), not the lowercased forms used by the Telegram
   Bot API docs. wiremock path matchers updated to match.

Verified locally: 8 unit + 3 E2E integration tests pass.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ntain permissions'

Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
@nolik nolik merged commit ed8cea3 into main Jun 3, 2026
5 checks passed
@nolik nolik deleted the feat/e2e-test-harness branch June 3, 2026 20:01
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.

2 participants