Task 7 completes the outbound relay: restructured the crate as a proper lib+bin, wired the full relay loop in main.rs, and added wiremock integration tests. 23 tests pass, 1 ignored, clippy clean. Commit: 8f0d776.
Cargo.toml lib/bin Split
Cargo.toml now declares both a [lib] and a [[bin]] target so integration tests can import via the crate name:
[lib]
name = "telegram_connector"
path = "src/lib.rs"
[[bin]]
name = "telegram_connector"
path = "src/main.rs"Without this split, tests/ cannot do use telegram_connector::… — rustc only exposes crate items to integration tests through the lib target.
src/lib.rs
Re-exports all 6 modules as pub mod:
configconsumeroutboundrelaytelegramtelemetry
For Agents
relay.rsusescrate::paths internally. These resolve correctly in lib crate context. Integration tests import viatelegram_connector::relay::handle_oneetc. — the lib target makes this transparent.
src/main.rs Rewrite
main.rs is now a thin orchestrator:
- Dormant exit — if
TELEGRAM_BOT_TOKENorNATS_URLis unset, log and exit cleanly (no panic). Mirrors cohort_algo’s dormant guard convention. - NATS bind — connects the JetStream pull consumer.
- Relay loop — runs inside
tokio::select!alongside a shutdown future. - Graceful shutdown — listens for Ctrl-C and (on unix) SIGTERM; exits cleanly on either signal.
Shutdown Signal: cfg(unix) Pattern
async fn shutdown_signal() {
let ctrl_c = async { signal::ctrl_c().await.expect("...") };
#[cfg(unix)]
{
let terminate = async {
signal::unix::signal(signal::unix::SignalKind::terminate())
.expect("...")
.recv()
.await;
};
tokio::select! {
_ = ctrl_c => {},
_ = terminate => {},
}
return;
}
ctrl_c.await;
}Key Insight
The
#[cfg(unix)]block ends with an explicitreturn;. This compiles cleanly on both unix and non-unix without lint noise. Without thereturn;, the compiler seesctrl_c.awaitas unreachable on unix but reachable on non-unix — the explicit return inside the cfg block resolves the ambiguity for all targets.
Integration Tests
tests/outbound_relay.rs (3 tests, all passing)
Uses wiremock to mock the Telegram Bot API.
| Test | Scenario | Expected disposition |
|---|---|---|
happy_path | Telegram returns 200 OK | Ack |
rate_limited | Telegram returns 429 with retry_after | Nak(retry_after duration) |
bad_request | Telegram returns 400 | Term (no retry) |
These tests import telegram_connector::relay::handle_one directly from the lib target.
tests/nats_smoke.rs (1 test, ignored)
Skeleton for a full NATS round-trip smoke test. Marked #[ignore] pending a real NATS server fixture. Exists to define the shape and reserve the file.
Build Status After Task 7
| Metric | Value |
|---|---|
| Tests passing | 23 |
| Tests ignored | 1 (nats_smoke) |
| Clippy | clean |
| Commit | 8f0d776 |
| Outbound relay | complete |
| Inbound commands | design-gated (no change) |