Files
rustybeds/src/amqp.rs
gramps c1d1ff14a5 Add RabbitMQ validation, test scaffolding, and config refactor
- Extract ipl() from main() with env-aware error handling (fatal in prod, warn in dev)
- Add amqp::validate() — TCP reachability check for RabbitMQ at IPL
- Refactor config::load() into load() + load_from() for testability
- Add lib.rs to expose public API to integration test harness
- Add test fixture scaffolding: tests/fixtures/beds_test.toml, tests/common/mod.rs
- Add unit tests for amqp::validate() error paths (closed port, bad address)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-02 19:32:24 -07:00

87 lines
2.7 KiB
Rust

//! # amqp.rs — RabbitMQ Transport Layer
//!
//! Manages all AMQP interactions for the BEDS node. At IPL, validates that
//! the RabbitMQ broker is reachable before the node proceeds. Future phases
//! will add channel acquisition, queue declaration, and message dispatch.
//!
//! ## Calling Agents
//! - `ipl()` in main.rs — calls `validate()` during the IPL sequence
//!
//! ## Inputs
//! - `BrokerServicesConfig` from the loaded BEDS configuration
//!
//! ## Outputs
//! - `Ok(())` if the broker is reachable
//! - `Err(String)` with host:port and OS error if the broker cannot be reached
//!
//! **Author:** mks
//! **Version:** 1.0
//!
//! ## History
//! * `2026-04-02` - mks - original coding
use std::net::TcpStream;
use std::time::Duration;
use crate::config::BrokerServicesConfig;
/// Validates that the RabbitMQ broker is reachable.
///
/// Opens a TCP connection to the configured broker host and port. Does not
/// authenticate or open an AMQP channel — reachability only. The connection
/// is closed immediately after a successful connect.
///
/// # Arguments
///
/// * `cfg` — broker services configuration block from `BedsConfig`
///
/// # Returns
///
/// `Ok(())` if the TCP handshake succeeds.
/// `Err(String)` with a descriptive message if the broker cannot be reached
/// or if the configured address is malformed.
///
/// # History
///
/// * `2026-04-02` - mks - original coding
pub fn validate(cfg: &BrokerServicesConfig) -> Result<(), String> {
let addr_str = format!("{}:{}", cfg.app_server.host, cfg.app_server.port);
let addr: std::net::SocketAddr = addr_str
.parse()
.map_err(|e| format!("Invalid broker address {}: {}", addr_str, e))?;
TcpStream::connect_timeout(&addr, Duration::from_secs(5))
.map_err(|e| format!("RabbitMQ unreachable at {}: {}", addr_str, e))?;
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
use crate::config::load_from;
/// Loads the test fixture config. Panics if the fixture is missing or malformed.
fn test_cfg() -> crate::config::BedsConfig {
load_from("tests/fixtures/beds_test.toml", "")
.expect("test fixture beds_test.toml failed to load")
}
#[test]
fn validate_err_on_closed_port() {
// port 1 is reserved and always closed — guarantees a connection refusal
// without requiring any live service
let mut cfg = test_cfg();
cfg.broker_services.app_server.port = 1;
assert!(validate(&cfg.broker_services).is_err());
}
#[test]
fn validate_err_on_bad_address() {
let mut cfg = test_cfg();
cfg.broker_services.app_server.host = "not_a_valid_host!!!".to_string();
assert!(validate(&cfg.broker_services).is_err());
}
}