#![cfg(feature = "executor_tokio")]
use product_os_server::{ProductOSServer, Body};
use product_os_async_executor::TokioExecutor;
use product_os_server::{ServerConfig, Certificate, CertificateFilesKind};
use product_os_security::Security;
#[tokio::test]
async fn test_certificate_none_generates_self_signed() {
let mut config = ServerConfig::new();
config.certificate = None;
let _server: ProductOSServer<(), TokioExecutor, _> = ProductOSServer::new_with_config(config);
}
#[tokio::test]
async fn test_certificate_self_signed_explicit() {
let mut config = ServerConfig::new();
config.certificate = Some(Certificate {
file_kind: Some(CertificateFilesKind::SelfSigned),
files: None,
entries: None,
names: Some(vec!["localhost".to_string(), "127.0.0.1".to_string()]),
serial: None,
valid_for: None,
});
let _server: ProductOSServer<(), TokioExecutor, _> = ProductOSServer::new_with_config(config);
}
#[tokio::test]
async fn test_certificate_with_entries() {
use serde_json::json;
let mut config = ServerConfig::new();
let entries = vec![
serde_json::from_value(json!({"CN": "Test Server"})).unwrap(),
serde_json::from_value(json!({"O": "Test Organization"})).unwrap(),
];
config.certificate = Some(Certificate {
file_kind: Some(CertificateFilesKind::SelfSigned),
files: None,
entries: Some(entries),
names: Some(vec!["test.local".to_string()]),
serial: Some(12345),
valid_for: Some(365),
});
let _server: ProductOSServer<(), TokioExecutor, _> = ProductOSServer::new_with_config(config);
}
#[cfg(feature = "security_certificates")]
#[tokio::test]
async fn test_certificate_update_after_creation() {
let config = ServerConfig::new();
let mut server: ProductOSServer<(), TokioExecutor, _> = ProductOSServer::new_with_config(config);
let new_cert = Certificate {
file_kind: Some(CertificateFilesKind::SelfSigned),
files: None,
entries: None,
names: Some(vec!["updated.local".to_string()]),
serial: None,
valid_for: Some(30),
};
server.set_certificate(&Some(new_cert));
}
#[tokio::test]
async fn test_certificate_ca_without_files_fallback() {
let mut config = ServerConfig::new();
config.certificate = Some(Certificate {
file_kind: Some(CertificateFilesKind::CA),
files: None, entries: None,
names: Some(vec!["ca-fallback.local".to_string()]),
serial: None,
valid_for: None,
});
let _server: ProductOSServer<(), TokioExecutor, _> = ProductOSServer::new_with_config(config);
}
#[tokio::test]
async fn test_security_enabled() {
let mut config = ServerConfig::new();
config.security = Some(serde_json::to_value(Security {
enable: true,
csrf: false,
csp: None,
}).unwrap());
let _server: ProductOSServer<(), TokioExecutor, _> = ProductOSServer::new_with_config(config);
}
#[tokio::test]
async fn test_security_disabled() {
let mut config = ServerConfig::new();
config.security = Some(serde_json::to_value(Security {
enable: false,
csrf: false,
csp: None,
}).unwrap());
let _server: ProductOSServer<(), TokioExecutor, _> = ProductOSServer::new_with_config(config);
}
#[tokio::test]
async fn test_security_set_after_creation() {
let config = ServerConfig::new();
let mut server: ProductOSServer<(), TokioExecutor, _> = ProductOSServer::new_with_config(config);
server.set_security(Some(Security {
enable: true,
csrf: false,
csp: None,
}));
server.set_security(None);
}
#[cfg(feature = "csrf")]
mod csrf_tests {
use super::*;
#[tokio::test]
async fn test_csrf_enabled() {
let mut config = ServerConfig::new();
config.security = Some(serde_json::to_value(Security {
enable: true,
csrf: true,
csp: None,
}).unwrap());
let _server: ProductOSServer<(), TokioExecutor, _> = ProductOSServer::new_with_config(config);
}
#[tokio::test]
async fn test_csrf_toggle() {
let config = ServerConfig::new();
let mut server: ProductOSServer<(), TokioExecutor, _> = ProductOSServer::new_with_config(config);
server.set_security(Some(Security {
enable: true,
csrf: true,
csp: None,
}));
server.set_security(Some(Security {
enable: true,
csrf: false,
csp: None,
}));
}
}
#[cfg(feature = "cspolicy")]
mod csp_tests {
use super::*;
use product_os_security::CSPConfig;
#[tokio::test]
async fn test_csp_configuration() {
let mut config = ServerConfig::new();
config.security = Some(serde_json::to_value(Security {
enable: true,
csrf: false,
csp: None,
}).unwrap());
let _server: ProductOSServer<(), TokioExecutor, _> = ProductOSServer::new_with_config(config);
}
#[tokio::test]
async fn test_csp_with_custom_directives() {
let mut config = ServerConfig::new();
let mut csp = CSPConfig::default();
csp.default_src = Some(vec!["self".to_string()]);
csp.script_src = Some(vec!["self".to_string(), "https://trusted.com".to_string()]);
csp.style_src = Some(vec!["self".to_string()]);
csp.img_src = Some(vec!["self".to_string(), "data:".to_string()]);
config.security = Some(serde_json::to_value(Security {
enable: true,
csrf: false,
csp: Some(csp),
}).unwrap());
let _server: ProductOSServer<(), TokioExecutor, _> = ProductOSServer::new_with_config(config);
}
}
#[cfg(feature = "compression")]
mod compression_tests {
use super::*;
use product_os_server::Compression;
#[tokio::test]
async fn test_compression_gzip_only() {
let config = ServerConfig::new();
let mut server: ProductOSServer<(), TokioExecutor, _> = ProductOSServer::new_with_config(config);
server.set_compression(Some(Compression {
enable: true,
gzip: true,
deflate: false,
brotli: false,
}));
}
#[tokio::test]
async fn test_compression_deflate_only() {
let config = ServerConfig::new();
let mut server: ProductOSServer<(), TokioExecutor, _> = ProductOSServer::new_with_config(config);
server.set_compression(Some(Compression {
enable: true,
gzip: false,
deflate: true,
brotli: false,
}));
}
#[tokio::test]
async fn test_compression_brotli_only() {
let config = ServerConfig::new();
let mut server: ProductOSServer<(), TokioExecutor, _> = ProductOSServer::new_with_config(config);
server.set_compression(Some(Compression {
enable: true,
gzip: false,
deflate: false,
brotli: true,
}));
}
#[tokio::test]
async fn test_compression_disabled_with_flag() {
let config = ServerConfig::new();
let mut server: ProductOSServer<(), TokioExecutor, _> = ProductOSServer::new_with_config(config);
server.set_compression(Some(Compression {
enable: false,
gzip: true,
deflate: true,
brotli: true,
}));
}
}
#[cfg(feature = "tls")]
mod tls_tests {
use super::*;
#[tokio::test]
async fn test_tls_server_creation() {
let mut config = ServerConfig::new();
config.network.port = 8443;
config.network.secure = true;
let _server: ProductOSServer<(), TokioExecutor, _> = ProductOSServer::new_with_config(config);
}
#[tokio::test]
async fn test_tls_custom_certificate_names() {
let mut config = ServerConfig::new();
config.certificate = Some(Certificate {
file_kind: Some(CertificateFilesKind::SelfSigned),
files: None,
entries: None,
names: Some(vec![
"localhost".to_string(),
"127.0.0.1".to_string(),
"::1".to_string(),
"my-app.local".to_string(),
]),
serial: None,
valid_for: Some(90),
});
let _server: ProductOSServer<(), TokioExecutor, _> = ProductOSServer::new_with_config(config);
}
}
#[cfg(feature = "dual_server")]
mod dual_server_tests {
use super::*;
#[tokio::test]
async fn test_dual_server_feature_available() {
let config = ServerConfig::new();
let _server: ProductOSServer<(), TokioExecutor, _> = ProductOSServer::new_with_config(config);
}
}
#[tokio::test]
async fn test_network_insecure_port_config() {
let mut config = ServerConfig::new();
config.network.allow_insecure = true;
config.network.insecure_port = 8080;
config.network.insecure_use_different_port = true;
config.network.insecure_force_secure = false;
let _server: ProductOSServer<(), TokioExecutor, _> = ProductOSServer::new_with_config(config);
}
#[tokio::test]
async fn test_network_force_secure_config() {
let mut config = ServerConfig::new();
config.network.allow_insecure = true;
config.network.insecure_force_secure = true;
let _server: ProductOSServer<(), TokioExecutor, _> = ProductOSServer::new_with_config(config);
}
#[tokio::test]
async fn test_network_listen_all_interfaces() {
let mut config = ServerConfig::new();
config.network.listen_all_interfaces = true;
config.network.host = "0.0.0.0".to_string();
let _server: ProductOSServer<(), TokioExecutor, _> = ProductOSServer::new_with_config(config);
}
#[tokio::test]
async fn test_network_custom_host_port() {
let mut config = ServerConfig::new();
config.network.host = "localhost".to_string();
config.network.port = 9000;
config.network.protocol = "http".to_string();
let server: ProductOSServer<(), TokioExecutor, _> = ProductOSServer::new_with_config(config.clone());
let retrieved_config = server.get_config();
assert_eq!(retrieved_config.network.host, "localhost");
assert_eq!(retrieved_config.network.port, 9000);
}
#[tokio::test]
async fn test_router_multiple_paths() {
use product_os_server::{StatusCode, Response};
let config = ServerConfig::new();
let mut server: ProductOSServer<(), TokioExecutor, _> = ProductOSServer::new_with_config(config);
async fn handler() -> Result<Response<Body>, StatusCode> {
Ok(Response::new(Body::empty()))
}
server.add_get("/", handler);
server.add_get("/api", handler);
server.add_get("/api/v1", handler);
server.add_get("/api/v1/users", handler);
server.add_get("/api/v1/users/:id", handler);
}
#[tokio::test]
async fn test_router_all_methods() {
use product_os_server::{StatusCode, Response, Method};
let config = ServerConfig::new();
let mut server: ProductOSServer<(), TokioExecutor, _> = ProductOSServer::new_with_config(config);
async fn handler() -> Result<Response<Body>, StatusCode> {
Ok(Response::new(Body::empty()))
}
server.add_handler("/resource/get", Method::GET, handler);
server.add_handler("/resource/post", Method::POST, handler);
server.add_handler("/resource/put", Method::PUT, handler);
server.add_handler("/resource/patch", Method::PATCH, handler);
server.add_handler("/resource/delete", Method::DELETE, handler);
server.add_handler("/resource/head", Method::HEAD, handler);
server.add_handler("/resource/options", Method::OPTIONS, handler);
server.add_handler("/resource/trace", Method::TRACE, handler);
}
#[tokio::test]
async fn test_router_replace() {
use product_os_server::ProductOSRouter;
let config = ServerConfig::new();
let mut server: ProductOSServer<(), TokioExecutor, _> = ProductOSServer::new_with_config(config);
let new_router = ProductOSRouter::new();
server.set_router(new_router);
}
#[tokio::test]
async fn test_stateful_server_complex_state() {
use std::sync::Arc;
use parking_lot::Mutex;
use std::collections::HashMap;
#[derive(Clone)]
#[allow(dead_code)]
struct ComplexState {
counter: Arc<Mutex<i32>>,
cache: Arc<Mutex<HashMap<String, String>>>,
name: String,
}
let state = ComplexState {
counter: Arc::new(Mutex::new(0)),
cache: Arc::new(Mutex::new(HashMap::new())),
name: "TestServer".to_string(),
};
let config = ServerConfig::new();
let _server: ProductOSServer<ComplexState, TokioExecutor, _> =
ProductOSServer::new_with_state_with_config(config, state.clone());
*state.counter.lock() += 10;
state.cache.lock().insert("key".to_string(), "value".to_string());
assert_eq!(*state.counter.lock(), 10);
assert_eq!(state.cache.lock().get("key"), Some(&"value".to_string()));
}
#[tokio::test]
async fn test_stateful_server_simple_constructor() {
#[derive(Clone)]
#[allow(dead_code)]
struct SimpleState {
value: i32,
}
let state = SimpleState { value: 42 };
let _server: ProductOSServer<SimpleState, TokioExecutor, _> =
ProductOSServer::new_with_state(state);
}
#[test]
fn test_error_types() {
use product_os_server::ProductOSServerError;
let generic = ProductOSServerError::GenericError("test".to_string());
let init = ProductOSServerError::InitializationError {
reason: "test reason".to_string()
};
let config = ProductOSServerError::ConfigurationError {
component: "network".to_string(),
message: "invalid port".to_string()
};
let cert = ProductOSServerError::CertificateError {
message: "invalid cert".to_string()
};
let exec = ProductOSServerError::ExecutorError {
message: "spawn failed".to_string()
};
let timeout = ProductOSServerError::Timeout {
operation: "lock acquisition".to_string()
};
let lock = ProductOSServerError::LockError {
resource: "controller".to_string()
};
assert!(!format!("{}", generic).is_empty());
assert!(!format!("{}", init).is_empty());
assert!(!format!("{}", config).is_empty());
assert!(!format!("{}", cert).is_empty());
assert!(!format!("{}", exec).is_empty());
assert!(!format!("{}", timeout).is_empty());
assert!(!format!("{}", lock).is_empty());
}
#[cfg(feature = "controller")]
mod controller_tests {
use super::*;
use product_os_server::ProductOSServerError;
#[tokio::test]
async fn test_controller_error_type() {
let controller_err = ProductOSServerError::ControllerError {
operation: "add_feature".to_string(),
message: "feature not found".to_string(),
};
assert!(!format!("{}", controller_err).is_empty());
}
#[tokio::test]
async fn test_store_error_type() {
let store_err = ProductOSServerError::StoreError {
store_type: "key-value".to_string(),
message: "connection failed".to_string(),
};
assert!(!format!("{}", store_err).is_empty());
}
#[tokio::test]
async fn test_controller_set_none() {
let config = ServerConfig::new();
let mut server: ProductOSServer<(), TokioExecutor, _> = ProductOSServer::new_with_config(config);
server.set_controller(None);
}
}
#[tokio::test]
async fn test_logging_level_changes_multiple() {
let config = ServerConfig::new();
let mut server: ProductOSServer<(), TokioExecutor, _> = ProductOSServer::new_with_config(config);
server.set_logging(tracing::Level::TRACE);
server.set_logging(tracing::Level::DEBUG);
server.set_logging(tracing::Level::INFO);
server.set_logging(tracing::Level::WARN);
server.set_logging(tracing::Level::ERROR);
}
#[cfg(feature = "ws")]
mod websocket_tests {
use super::*;
use product_os_server::{StatusCode, Response};
#[tokio::test]
async fn test_ws_multiple_endpoints() {
let config = ServerConfig::new();
let mut server: ProductOSServer<(), TokioExecutor, _> = ProductOSServer::new_with_config(config);
async fn ws_handler() -> Result<Response<Body>, StatusCode> {
Ok(Response::new(Body::empty()))
}
server.add_ws_handler("/ws", ws_handler);
server.add_ws_handler("/ws/chat", ws_handler);
server.add_ws_handler("/ws/notifications", ws_handler);
}
}
#[cfg(feature = "sse")]
mod sse_tests {
use super::*;
use product_os_server::{StatusCode, Response};
#[tokio::test]
async fn test_sse_multiple_endpoints() {
let config = ServerConfig::new();
let mut server: ProductOSServer<(), TokioExecutor, _> = ProductOSServer::new_with_config(config);
async fn sse_handler() -> Result<Response<Body>, StatusCode> {
Ok(Response::new(Body::empty()))
}
server.add_sse_handler("/events", sse_handler);
server.add_sse_handler("/events/updates", sse_handler);
server.add_sse_handler("/events/notifications", sse_handler);
}
}
#[cfg(feature = "cors")]
mod cors_tests {
use super::*;
use product_os_server::{StatusCode, Response, Method};
#[tokio::test]
async fn test_cors_multiple_methods() {
let config = ServerConfig::new();
let mut server: ProductOSServer<(), TokioExecutor, _> = ProductOSServer::new_with_config(config);
async fn cors_handler() -> Result<Response<Body>, StatusCode> {
Ok(Response::new(Body::empty()))
}
server.add_cors_handler("/api/resource", Method::GET, cors_handler);
server.add_cors_handler("/api/resource", Method::POST, cors_handler);
server.add_cors_handler("/api/resource", Method::PUT, cors_handler);
server.add_cors_handler("/api/resource", Method::DELETE, cors_handler);
}
}