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.
364 lines
10 KiB
Rust
364 lines
10 KiB
Rust
mod common;
|
|
|
|
#[test]
|
|
fn test_single_host_config() {
|
|
let temp_dir = tempfile::TempDir::new().unwrap();
|
|
let config_path = temp_dir.path().join("config.toml");
|
|
let port = 1967 + (std::process::id() % 1000) as u16;
|
|
|
|
// Create content directory and certificates
|
|
let content_dir = temp_dir.path().join("content");
|
|
std::fs::create_dir(&content_dir).unwrap();
|
|
|
|
// Generate test certificates
|
|
use std::process::Command;
|
|
let cert_path = temp_dir.path().join("cert.pem");
|
|
let key_path = temp_dir.path().join("key.pem");
|
|
|
|
let cert_result = Command::new("openssl")
|
|
.args(&[
|
|
"req",
|
|
"-x509",
|
|
"-newkey",
|
|
"rsa:2048",
|
|
"-keyout",
|
|
&key_path.to_string_lossy(),
|
|
"-out",
|
|
&cert_path.to_string_lossy(),
|
|
"-days",
|
|
"1",
|
|
"-nodes",
|
|
"-subj",
|
|
"/CN=example.com",
|
|
])
|
|
.output();
|
|
|
|
if cert_result.is_err() {
|
|
panic!("Failed to generate test certificates for config test");
|
|
}
|
|
|
|
let config_content = format!(
|
|
r#"
|
|
bind_host = "127.0.0.1"
|
|
port = {}
|
|
|
|
["example.com"]
|
|
root = "{}"
|
|
cert = "{}"
|
|
key = "{}"
|
|
"#,
|
|
port,
|
|
content_dir.display(),
|
|
cert_path.display(),
|
|
key_path.display()
|
|
);
|
|
std::fs::write(&config_path, config_content).unwrap();
|
|
|
|
let mut server_process = std::process::Command::new(env!("CARGO_BIN_EXE_pollux"))
|
|
.arg("--config")
|
|
.arg(&config_path)
|
|
.arg("--quiet")
|
|
.spawn()
|
|
.unwrap();
|
|
|
|
std::thread::sleep(std::time::Duration::from_millis(500));
|
|
assert!(
|
|
server_process.try_wait().unwrap().is_none(),
|
|
"Server should start with valid single host config"
|
|
);
|
|
server_process.kill().unwrap();
|
|
}
|
|
|
|
#[test]
|
|
fn test_multiple_hosts_config() {
|
|
let temp_dir = common::setup_test_environment();
|
|
let config_path = temp_dir.path().join("config.toml");
|
|
let config_content = format!(
|
|
r#"
|
|
[site1.com]
|
|
root = "{}"
|
|
cert = "{}"
|
|
key = "{}"
|
|
|
|
[site2.org]
|
|
root = "{}"
|
|
cert = "{}"
|
|
key = "{}"
|
|
|
|
bind_host = "127.0.0.1"
|
|
port = 1965
|
|
"#,
|
|
temp_dir.path().join("site1").display(),
|
|
temp_dir.path().join("site1_cert.pem").display(),
|
|
temp_dir.path().join("site1_key.pem").display(),
|
|
temp_dir.path().join("site2").display(),
|
|
temp_dir.path().join("site2_cert.pem").display(),
|
|
temp_dir.path().join("site2_key.pem").display()
|
|
);
|
|
std::fs::write(&config_path, config_content).unwrap();
|
|
|
|
// Create additional directories and generate certificates
|
|
std::fs::create_dir(temp_dir.path().join("site1")).unwrap();
|
|
std::fs::create_dir(temp_dir.path().join("site2")).unwrap();
|
|
|
|
// Generate certificates for each host
|
|
use std::process::Command;
|
|
|
|
// Site 1 certificate
|
|
let cert_result1 = Command::new("openssl")
|
|
.args(&[
|
|
"req",
|
|
"-x509",
|
|
"-newkey",
|
|
"rsa:2048",
|
|
"-keyout",
|
|
&temp_dir.path().join("site1_key.pem").to_string_lossy(),
|
|
"-out",
|
|
&temp_dir.path().join("site1_cert.pem").to_string_lossy(),
|
|
"-days",
|
|
"1",
|
|
"-nodes",
|
|
"-subj",
|
|
"/CN=site1.com",
|
|
])
|
|
.output();
|
|
|
|
// Site 2 certificate
|
|
let cert_result2 = Command::new("openssl")
|
|
.args(&[
|
|
"req",
|
|
"-x509",
|
|
"-newkey",
|
|
"rsa:2048",
|
|
"-keyout",
|
|
&temp_dir.path().join("site2_key.pem").to_string_lossy(),
|
|
"-out",
|
|
&temp_dir.path().join("site2_cert.pem").to_string_lossy(),
|
|
"-days",
|
|
"1",
|
|
"-nodes",
|
|
"-subj",
|
|
"/CN=site2.org",
|
|
])
|
|
.output();
|
|
|
|
if cert_result1.is_err() || cert_result2.is_err() {
|
|
panic!("Failed to generate test certificates for multiple hosts test");
|
|
}
|
|
|
|
// Test server starts successfully with multiple host config
|
|
let port = 1968 + (std::process::id() % 1000) as u16;
|
|
let config_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("site1_cert.pem").display(),
|
|
temp_dir.path().join("site1_key.pem").display(),
|
|
temp_dir.path().join("site2").display(),
|
|
temp_dir.path().join("site2_cert.pem").display(),
|
|
temp_dir.path().join("site2_key.pem").display()
|
|
);
|
|
std::fs::write(&config_path, config_content).unwrap();
|
|
|
|
let mut server_process = std::process::Command::new(env!("CARGO_BIN_EXE_pollux"))
|
|
.arg("--config")
|
|
.arg(&config_path)
|
|
.arg("--quiet")
|
|
.spawn()
|
|
.unwrap();
|
|
|
|
std::thread::sleep(std::time::Duration::from_millis(500));
|
|
assert!(
|
|
server_process.try_wait().unwrap().is_none(),
|
|
"Server should start with valid multiple host config"
|
|
);
|
|
server_process.kill().unwrap();
|
|
}
|
|
|
|
#[test]
|
|
fn test_missing_required_fields_in_host_config() {
|
|
let temp_dir = common::setup_test_environment();
|
|
let config_path = temp_dir.path().join("config.toml");
|
|
let config_content = r#"
|
|
bind_host = "127.0.0.1"
|
|
port = 1965
|
|
|
|
["example.com"]
|
|
root = "/tmp/content"
|
|
# missing cert and key
|
|
"#;
|
|
std::fs::write(&config_path, config_content).unwrap();
|
|
|
|
let output = std::process::Command::new(env!("CARGO_BIN_EXE_pollux"))
|
|
.arg("--config")
|
|
.arg(&config_path)
|
|
.output()
|
|
.unwrap();
|
|
|
|
assert!(!output.status.success());
|
|
let stderr = String::from_utf8(output.stderr).unwrap();
|
|
assert!(stderr.contains("Missing required field") || stderr.contains("missing field"));
|
|
}
|
|
|
|
#[test]
|
|
fn test_invalid_hostname_config() {
|
|
let temp_dir = common::setup_test_environment();
|
|
let config_path = temp_dir.path().join("config.toml");
|
|
let config_content = format!(
|
|
r#"
|
|
["invalid"]
|
|
root = "{}"
|
|
cert = "{}"
|
|
key = "{}"
|
|
"#,
|
|
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, config_content).unwrap();
|
|
|
|
let output = std::process::Command::new(env!("CARGO_BIN_EXE_pollux"))
|
|
.arg("--config")
|
|
.arg(&config_path)
|
|
.arg("--quiet")
|
|
.output()
|
|
.unwrap();
|
|
|
|
assert!(!output.status.success());
|
|
let stderr = String::from_utf8(output.stderr).unwrap();
|
|
assert!(stderr.contains("Invalid hostname"));
|
|
}
|
|
|
|
#[test]
|
|
fn test_no_hosts_config() {
|
|
let temp_dir = common::setup_test_environment();
|
|
let config_path = temp_dir.path().join("config.toml");
|
|
let config_content = r#"
|
|
bind_host = "127.0.0.1"
|
|
port = 1965
|
|
# No host sections defined
|
|
"#;
|
|
std::fs::write(&config_path, config_content).unwrap();
|
|
|
|
let output = std::process::Command::new(env!("CARGO_BIN_EXE_pollux"))
|
|
.arg("--config")
|
|
.arg(&config_path)
|
|
.arg("--quiet")
|
|
.output()
|
|
.unwrap();
|
|
|
|
assert!(!output.status.success());
|
|
let stderr = String::from_utf8(output.stderr).unwrap();
|
|
assert!(stderr.contains("No host configurations found"));
|
|
}
|
|
|
|
#[test]
|
|
fn test_duplicate_hostname_config() {
|
|
let temp_dir = common::setup_test_environment();
|
|
let config_path = temp_dir.path().join("config.toml");
|
|
let config_content = format!(
|
|
r#"
|
|
[example.com]
|
|
root = "{}"
|
|
cert = "{}"
|
|
key = "{}"
|
|
|
|
[example.com]
|
|
root = "{}"
|
|
cert = "{}"
|
|
key = "{}"
|
|
"#,
|
|
temp_dir.path().join("path1").display(),
|
|
temp_dir.path().join("cert1.pem").display(),
|
|
temp_dir.path().join("key1.pem").display(),
|
|
temp_dir.path().join("path2").display(),
|
|
temp_dir.path().join("cert2.pem").display(),
|
|
temp_dir.path().join("key2.pem").display()
|
|
);
|
|
std::fs::write(&config_path, config_content).unwrap();
|
|
|
|
// Create the directories and certs
|
|
std::fs::create_dir(temp_dir.path().join("path1")).unwrap();
|
|
std::fs::create_dir(temp_dir.path().join("path2")).unwrap();
|
|
std::fs::write(temp_dir.path().join("cert1.pem"), "cert1").unwrap();
|
|
std::fs::write(temp_dir.path().join("key1.pem"), "key1").unwrap();
|
|
std::fs::write(temp_dir.path().join("cert2.pem"), "cert2").unwrap();
|
|
std::fs::write(temp_dir.path().join("key2.pem"), "key2").unwrap();
|
|
|
|
let output = std::process::Command::new(env!("CARGO_BIN_EXE_pollux"))
|
|
.arg("--config")
|
|
.arg(&config_path)
|
|
.arg("--quiet")
|
|
.output()
|
|
.unwrap();
|
|
|
|
// Duplicate table headers are not allowed in TOML, so this should fail
|
|
assert!(!output.status.success());
|
|
}
|
|
|
|
#[test]
|
|
fn test_host_with_port_override() {
|
|
let temp_dir = common::setup_test_environment();
|
|
let config_path = temp_dir.path().join("config.toml");
|
|
// Test server starts successfully
|
|
let port = 1969 + (std::process::id() % 1000) as u16;
|
|
let config_content = format!(
|
|
r#"
|
|
bind_host = "127.0.0.1"
|
|
port = {}
|
|
|
|
["example.com"]
|
|
root = "{}"
|
|
cert = "{}"
|
|
key = "{}"
|
|
port = 1970 # Override global port
|
|
"#,
|
|
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, config_content).unwrap();
|
|
|
|
let mut server_process = std::process::Command::new(env!("CARGO_BIN_EXE_pollux"))
|
|
.arg("--config")
|
|
.arg(&config_path)
|
|
.arg("--quiet")
|
|
.spawn()
|
|
.unwrap();
|
|
|
|
std::thread::sleep(std::time::Duration::from_millis(500));
|
|
assert!(
|
|
server_process.try_wait().unwrap().is_none(),
|
|
"Server should start with host port override"
|
|
);
|
|
server_process.kill().unwrap();
|
|
}
|
|
|
|
#[test]
|
|
fn test_config_file_not_found() {
|
|
let output = std::process::Command::new(env!("CARGO_BIN_EXE_pollux"))
|
|
.arg("--config")
|
|
.arg("nonexistent.toml")
|
|
.arg("--quiet")
|
|
.env("RUST_LOG", "error")
|
|
.output()
|
|
.unwrap();
|
|
|
|
assert!(!output.status.success());
|
|
let stderr = String::from_utf8(output.stderr).unwrap();
|
|
assert!(stderr.contains("Config file 'nonexistent.toml' not found"));
|
|
}
|