Test that the server correctly reports missing certificate errors, rejects invalid hostnames, fails gracefully on port conflicts, and starts successfully with multiple virtual hosts configured.
239 lines
7 KiB
Rust
239 lines
7 KiB
Rust
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)
|
|
.arg("--quiet")
|
|
.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)
|
|
.arg("--quiet")
|
|
.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)
|
|
.arg("--quiet")
|
|
.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();
|
|
}
|