//! Integration tests for the Servme HTTP framework. //! //! These tests verify the end-to-end functionality of the server //! including middleware chains and request handling. use http_body_util::Full; use hyper::{body::Bytes, Request, Response}; use servme::{ Responder, Server, ServerBuilder, ServerConfig, ServerError, UrlExtract, middleware::{Claims, Middleware, MiddlewareFuture, MiddlewareResult}, }; use std::net::IpAddr; use tokio::time::{timeout, Duration}; // Helper to create a simple in-memory test mod helpers { use super::*; /// A simple middleware that adds a custom header pub struct TestMiddleware; impl TestMiddleware { pub fn new() -> Self { Self } } impl Middleware for TestMiddleware { fn run(&self, req: Request) -> MiddlewareFuture<'_> { Box::pin(async move { MiddlewareResult::Continue(req) }) } } use http::Request; use hyper::body::Incoming; } // ============================================================================ // Server Configuration Tests // ============================================================================ #[test] fn test_server_builder_default_config() { let builder = ServerBuilder::new(); // Verify default values assert_eq!(builder.config.ip, "127.0.0.1"); assert_eq!(builder.config.port, 8080); assert!(builder.middlewares.is_empty()); assert!(builder.data.is_none()); } #[test] fn test_server_builder_with_address() { let builder = ServerBuilder::new() .address("0.0.0.0", 3000); assert_eq!(builder.config.ip, "0.0.0.0"); assert_eq!(builder.config.port, 3000); } #[test] fn test_server_builder_chaining() { let server = ServerBuilder::new() .address("127.0.0.1", 8080) .add_api_key_middleware("test-key") .build(); assert_eq!(server.config.ip, "127.0.0.1"); assert_eq!(server.config.port, 8080); assert_eq!(server.middlewares.len(), 1); } #[test] fn test_server_config_default() { let config = ServerConfig::default(); assert_eq!(config.ip, "127.0.0.1"); assert_eq!(config.port, 8080); } // ============================================================================ // Responder Tests // ============================================================================ #[test] fn test_responder_ok() { let result = Responder::ok("Hello"); assert!(result.is_ok()); let response = result.unwrap(); assert_eq!(response.status(), http::StatusCode::OK); } #[test] fn test_responder_json() { #[derive(serde::Serialize)] struct TestData { name: String, value: i32, } let data = TestData { name: "test".to_string(), value: 42, }; let result = Responder::json(&data); assert!(result.is_ok()); let response = result.unwrap(); assert_eq!(response.status(), http::StatusCode::OK); assert_eq!( response.headers().get("content-type").unwrap(), "application/json" ); } #[test] fn test_responder_redirect() { let result = Responder::redirect("/new-location"); assert!(result.is_ok()); let response = result.unwrap(); assert_eq!(response.status(), http::StatusCode::SEE_OTHER); assert_eq!( response.headers().get("location").unwrap(), "/new-location" ); } #[test] fn test_responder_redirect_empty_fails() { let result = Responder::redirect(""); assert!(result.is_err()); } #[test] fn test_responder_status_codes() { assert!(Responder::not_found().is_ok()); assert!(Responder::unauthorized().is_ok()); assert!(Responder::forbidden().is_ok()); assert!(Responder::bad_request("bad").is_ok()); assert!(Responder::no_content().is_ok()); assert!(Responder::internal_error("error").is_ok()); let response = Responder::not_found().unwrap(); assert_eq!(response.status(), http::StatusCode::NOT_FOUND); let response = Responder::unauthorized().unwrap(); assert_eq!(response.status(), http::StatusCode::UNAUTHORIZED); } // ============================================================================ // Middleware Tests // ============================================================================ #[test] fn test_api_key_middleware_creation() { use servme::middleware::ApiKeyMiddleware; let middleware = ApiKeyMiddleware::new("test-key"); assert_eq!(middleware.api_key, "test-key"); } #[test] fn test_ip_filter_middleware_validation() { use servme::middleware::IpFilterMiddleware; // Valid IPs should work let result = IpFilterMiddleware::new(vec![], false); assert!(result.is_ok()); let result = IpFilterMiddleware::new( vec!["192.168.1.1".to_string(), "10.0.0.1".to_string()], false ); assert!(result.is_ok()); // Invalid IP should fail let result = IpFilterMiddleware::new( vec!["not-an-ip".to_string()], false ); assert!(result.is_err()); } #[test] fn test_ip_filter_authorization() { use servme::middleware::IpFilterMiddleware; // Test with unchecked for simpler testing let middleware = IpFilterMiddleware::new_unchecked( vec!["192.168.1.100".to_string()], false ); let allowed_ip: IpAddr = "192.168.1.100".parse().unwrap(); let denied_ip: IpAddr = "192.168.1.200".parse().unwrap(); assert!(middleware.is_authorized(&allowed_ip)); assert!(!middleware.is_authorized(&denied_ip)); } #[test] fn test_ip_filter_ipv6() { use servme::middleware::IpFilterMiddleware; let middleware = IpFilterMiddleware::new_unchecked( vec!["::1".to_string()], false, ); let ipv6_local: IpAddr = "::1".parse().unwrap(); let ipv6_other: IpAddr = "::2".parse().unwrap(); assert!(middleware.is_authorized(&ipv6_local)); assert!(!middleware.is_authorized(&ipv6_other)); } // ============================================================================ // URL Extract Tests // ============================================================================ #[test] fn test_url_extract_params() { use http::Uri; let uri: Uri = "/api?name=test&value=42".parse().unwrap(); let extractor = UrlExtract::new(&uri); assert_eq!(extractor.param_str("name"), Some("test".to_string())); assert_eq!(extractor.param_i64("value"), Some(42)); } #[test] fn test_url_extract_missing_param() { use http::Uri; let uri: Uri = "/api".parse().unwrap(); let extractor = UrlExtract::new(&uri); assert_eq!(extractor.param_str("missing"), None); } // ============================================================================ // Claims Tests // ============================================================================ #[test] fn test_claims_is_expired() { use servme::middleware::auth_types::Claims; let claims = Claims { sub: "user123".to_string(), exp: 1000, // Very old timestamp }; assert!(claims.is_expired(2000)); // Current time > exp assert!(!claims.is_expired(500)); // Current time < exp } #[test] fn test_claims_username() { use servme::middleware::auth_types::Claims; let claims = Claims { sub: "testuser".to_string(), exp: 9999999999, }; assert_eq!(claims.username(), "testuser"); } // ============================================================================ // Error Handling Tests // ============================================================================ #[test] fn test_server_error_display() { let error = ServerError::bind("127.0.0.1:8080", std::io::Error::new( std::io::ErrorKind::AddrInUse, "Address already in use" )); let display = format!("{}", error); assert!(display.contains("Failed to bind")); assert!(display.contains("127.0.0.1:8080")); } #[test] fn test_server_error_validation() { let error = ServerError::validation("field", "must not be empty"); let display = format!("{}", error); assert!(display.contains("Validation failed")); assert!(display.contains("field")); } // ============================================================================ // Constants Tests // ============================================================================ #[test] fn test_constants_values() { use servme::constants::*; assert_eq!(DEFAULT_HOST, "127.0.0.1"); assert_eq!(DEFAULT_PORT, 8080); assert_eq!(JWT_COOKIE_NAME, "access_token"); assert_eq!(BEARER_PREFIX, "Bearer "); assert!(FILE_EXTENSIONS.contains(&".json")); assert!(FILE_EXTENSIONS.contains(&".html")); }