mod common; /// Make a Gemini request over TLS and return the response fn make_gemini_request(host: &str, port: u16, request: &str) -> String { // Use the Python client for TLS requests use std::process::Command; let url = request.to_string(); let output = Command::new("python3") .arg("tests/gemini_test_client.py") .arg(url) .env("GEMINI_PORT", &port.to_string()) .env("GEMINI_CONNECT_HOST", host) .output() .expect("Failed to run test client"); String::from_utf8(output.stdout).unwrap().trim().to_string() } // Unit tests for hostname extraction - temporarily disabled due to import issues // TODO: Fix import path for server functions /* #[test] fn test_extract_hostname_and_path_valid_urls() { // Test various valid Gemini URLs let test_cases = vec![ ("gemini://example.com/", ("example.com", "/")), ("gemini://example.com/page.gmi", ("example.com", "/page.gmi")), ("gemini://sub.example.com/path/to/file.txt", ("sub.example.com", "/path/to/file.txt")), ("gemini://localhost:1965/", ("localhost", "/")), ("gemini://test.com", ("test.com", "/")), ]; for (url, expected) in test_cases { let result = pollux::server::extract_hostname_and_path(url); assert!(result.is_ok(), "Failed to parse: {}", url); let (hostname, path) = result.unwrap(); assert_eq!(hostname, expected.0, "Hostname mismatch for: {}", url); assert_eq!(path, expected.1, "Path mismatch for: {}", url); } } #[test] fn test_extract_hostname_and_path_invalid_urls() { // Test invalid URLs let invalid_urls = vec![ "", // empty "http://example.com/", // wrong scheme "gemini://", // no hostname "//example.com/", // no scheme "gemini://example.com:99999/", // port is handled by path "gemini://example.com?query", // query params not supported ]; for url in invalid_urls { let result = pollux::server::extract_hostname_and_path(url); assert!(result.is_err(), "Should fail for invalid URL: {}", url); } } */ #[test] fn test_virtual_host_routing_multiple_hosts() { let temp_dir = common::setup_test_environment(); let port = 2000 + (std::process::id() % 1000) as u16; // Create directories for hosts (content already exists from setup_test_environment) std::fs::create_dir(temp_dir.path().join("site1")).unwrap(); std::fs::create_dir(temp_dir.path().join("site2")).unwrap(); // Create config with two hosts let config_path = temp_dir.path().join("config.toml"); let content = format!(r#" bind_host = "127.0.0.1" port = {} ["site1.com"] root = "{}" cert = "{}" key = "{}" ["site2.org"] root = "{}" cert = "{}" key = "{}" "#, port, temp_dir.path().join("site1").display(), temp_dir.path().join("cert.pem").display(), temp_dir.path().join("key.pem").display(), temp_dir.path().join("site2").display(), temp_dir.path().join("cert.pem").display(), temp_dir.path().join("key.pem").display()); std::fs::write(&config_path, content).unwrap(); // Create host-specific content std::fs::create_dir_all(temp_dir.path().join("site1")).unwrap(); std::fs::create_dir_all(temp_dir.path().join("site2")).unwrap(); std::fs::write(temp_dir.path().join("site1").join("index.gmi"), "# Site 1 Content\n").unwrap(); std::fs::write(temp_dir.path().join("site2").join("index.gmi"), "# Site 2 Content\n").unwrap(); // Use the same certs for both hosts (server uses first cert anyway) // Start server with TLS let mut server_process = std::process::Command::new(env!("CARGO_BIN_EXE_pollux")) .arg("--config") .arg(&config_path) .spawn() .unwrap(); // Wait for server to start std::thread::sleep(std::time::Duration::from_millis(500)); // Test request to site1.com with TLS let response1 = make_gemini_request("127.0.0.1", port, "gemini://site1.com/index.gmi"); assert!(response1.starts_with("20"), "Expected success response for site1.com, got: {}", response1); // Test request to site2.org let response2 = make_gemini_request("127.0.0.1", port, "gemini://site2.org/index.gmi"); assert!(response2.starts_with("20"), "Expected success response for site2.org, got: {}", response2); server_process.kill().unwrap(); } #[test] fn test_virtual_host_routing_known_hostname() { let temp_dir = common::setup_test_environment(); let port = 2100 + (std::process::id() % 1000) as u16; // Content directory already created by setup_test_environment // Config with only one host let config_path = temp_dir.path().join("config.toml"); let content = format!(r#" bind_host = "127.0.0.1" port = {} ["example.com"] root = "{}" cert = "{}" key = "{}" "#, port, temp_dir.path().join("content").display(), temp_dir.path().join("cert.pem").display(), temp_dir.path().join("key.pem").display()); std::fs::write(&config_path, content).unwrap(); // Start server with TLS let mut server_process = std::process::Command::new(env!("CARGO_BIN_EXE_pollux")) .arg("--config") .arg(&config_path) .spawn() .unwrap(); // Wait for server to start std::thread::sleep(std::time::Duration::from_millis(500)); // Test request to unknown hostname let response = make_gemini_request("127.0.0.1", port, "gemini://unknown.com/index.gmi"); assert!(response.starts_with("53"), "Should return status 53 for unknown hostname, got: {}", response); server_process.kill().unwrap(); } #[test] fn test_virtual_host_routing_malformed_url() { let temp_dir = common::setup_test_environment(); let port = 2200 + (std::process::id() % 1000) as u16; // Content directory already created by setup_test_environment // Config with one host let config_path = temp_dir.path().join("config.toml"); let content = format!(r#" bind_host = "127.0.0.1" port = {} ["example.com"] root = "{}" cert = "{}" key = "{}" "#, port, temp_dir.path().join("content").display(), temp_dir.path().join("cert.pem").display(), temp_dir.path().join("key.pem").display()); std::fs::write(&config_path, content).unwrap(); // Start server with TLS let mut server_process = std::process::Command::new(env!("CARGO_BIN_EXE_pollux")) .arg("--config") .arg(&config_path) .spawn() .unwrap(); // Wait for server to start std::thread::sleep(std::time::Duration::from_millis(500)); // Test malformed URL (wrong protocol) let response = make_gemini_request("127.0.0.1", port, "http://example.com/index.gmi"); assert!(response.starts_with("59"), "Should return status 59 for malformed URL, got: {}", response); server_process.kill().unwrap(); }