From 90730060040f664cf5222e4733ece757a094000a Mon Sep 17 00:00:00 2001 From: perf3ct Date: Fri, 27 Jun 2025 19:33:58 +0000 Subject: [PATCH] fix(tests): move oidc tests to correct folder --- src/oidc.rs | 202 +-------------------------------------- tests/oidc_unit_tests.rs | 195 +++++++++++++++++++++++++++++++++++++ 2 files changed, 199 insertions(+), 198 deletions(-) create mode 100644 tests/oidc_unit_tests.rs diff --git a/src/oidc.rs b/src/oidc.rs index 2f0a47f..702afe6 100644 --- a/src/oidc.rs +++ b/src/oidc.rs @@ -33,6 +33,10 @@ pub struct OidcClient { } impl OidcClient { + pub fn get_discovery(&self) -> &OidcDiscovery { + &self.discovery + } + pub async fn new(config: &Config) -> Result { let client_id = config .oidc_client_id @@ -152,201 +156,3 @@ pub struct OidcAuthResponse { pub is_new_user: bool, } -#[cfg(test)] -mod tests { - use super::*; - use crate::config::Config; - use wiremock::{matchers::{method, path}, Mock, MockServer, ResponseTemplate}; - - fn create_test_config_with_oidc(issuer_url: &str) -> Config { - Config { - database_url: "postgresql://test:test@localhost/test".to_string(), - server_address: "127.0.0.1:8000".to_string(), - jwt_secret: "test-secret".to_string(), - upload_path: "./test-uploads".to_string(), - watch_folder: "./test-watch".to_string(), - allowed_file_types: vec!["pdf".to_string()], - watch_interval_seconds: Some(30), - file_stability_check_ms: Some(500), - max_file_age_hours: None, - ocr_language: "eng".to_string(), - concurrent_ocr_jobs: 2, - ocr_timeout_seconds: 60, - max_file_size_mb: 10, - memory_limit_mb: 256, - cpu_priority: "normal".to_string(), - oidc_enabled: true, - oidc_client_id: Some("test-client-id".to_string()), - oidc_client_secret: Some("test-client-secret".to_string()), - oidc_issuer_url: Some(issuer_url.to_string()), - oidc_redirect_uri: Some("http://localhost:8000/auth/oidc/callback".to_string()), - } - } - - #[tokio::test] - async fn test_oidc_discovery() { - let mock_server = MockServer::start().await; - - let discovery_response = serde_json::json!({ - "issuer": mock_server.uri(), - "authorization_endpoint": format!("{}/auth", mock_server.uri()), - "token_endpoint": format!("{}/token", mock_server.uri()), - "userinfo_endpoint": format!("{}/userinfo", mock_server.uri()) - }); - - Mock::given(method("GET")) - .and(path("/.well-known/openid-configuration")) - .respond_with(ResponseTemplate::new(200).set_body_json(discovery_response)) - .mount(&mock_server) - .await; - - let config = create_test_config_with_oidc(&mock_server.uri()); - let oidc_client = OidcClient::new(&config).await; - - assert!(oidc_client.is_ok()); - let client = oidc_client.unwrap(); - assert_eq!(client.discovery.issuer, mock_server.uri()); - assert_eq!(client.discovery.authorization_endpoint, format!("{}/auth", mock_server.uri())); - } - - #[tokio::test] - async fn test_oidc_discovery_failure() { - let mock_server = MockServer::start().await; - - Mock::given(method("GET")) - .and(path("/.well-known/openid-configuration")) - .respond_with(ResponseTemplate::new(404)) - .mount(&mock_server) - .await; - - let config = create_test_config_with_oidc(&mock_server.uri()); - let oidc_client = OidcClient::new(&config).await; - - assert!(oidc_client.is_err()); - } - - #[tokio::test] - async fn test_get_authorization_url() { - let mock_server = MockServer::start().await; - - let discovery_response = serde_json::json!({ - "issuer": mock_server.uri(), - "authorization_endpoint": format!("{}/auth", mock_server.uri()), - "token_endpoint": format!("{}/token", mock_server.uri()), - "userinfo_endpoint": format!("{}/userinfo", mock_server.uri()) - }); - - Mock::given(method("GET")) - .and(path("/.well-known/openid-configuration")) - .respond_with(ResponseTemplate::new(200).set_body_json(discovery_response)) - .mount(&mock_server) - .await; - - let config = create_test_config_with_oidc(&mock_server.uri()); - let oidc_client = OidcClient::new(&config).await.unwrap(); - - let (auth_url, csrf_token) = oidc_client.get_authorization_url(); - - assert!(auth_url.to_string().contains("/auth")); - assert!(auth_url.to_string().contains("client_id=test-client-id")); - assert!(auth_url.to_string().contains("scope=openid+email+profile")); - assert!(!csrf_token.secret().is_empty()); - } - - #[tokio::test] - async fn test_get_user_info() { - let mock_server = MockServer::start().await; - - let discovery_response = serde_json::json!({ - "issuer": mock_server.uri(), - "authorization_endpoint": format!("{}/auth", mock_server.uri()), - "token_endpoint": format!("{}/token", mock_server.uri()), - "userinfo_endpoint": format!("{}/userinfo", mock_server.uri()) - }); - - Mock::given(method("GET")) - .and(path("/.well-known/openid-configuration")) - .respond_with(ResponseTemplate::new(200).set_body_json(discovery_response)) - .mount(&mock_server) - .await; - - let user_info_response = serde_json::json!({ - "sub": "test-user-123", - "email": "test@example.com", - "name": "Test User", - "preferred_username": "testuser" - }); - - Mock::given(method("GET")) - .and(path("/userinfo")) - .respond_with(ResponseTemplate::new(200).set_body_json(user_info_response)) - .mount(&mock_server) - .await; - - let config = create_test_config_with_oidc(&mock_server.uri()); - let oidc_client = OidcClient::new(&config).await.unwrap(); - - let user_info = oidc_client.get_user_info("test-access-token").await; - - assert!(user_info.is_ok()); - let info = user_info.unwrap(); - assert_eq!(info.sub, "test-user-123"); - assert_eq!(info.email, Some("test@example.com".to_string())); - assert_eq!(info.preferred_username, Some("testuser".to_string())); - } - - #[tokio::test] - async fn test_get_user_info_unauthorized() { - let mock_server = MockServer::start().await; - - let discovery_response = serde_json::json!({ - "issuer": mock_server.uri(), - "authorization_endpoint": format!("{}/auth", mock_server.uri()), - "token_endpoint": format!("{}/token", mock_server.uri()), - "userinfo_endpoint": format!("{}/userinfo", mock_server.uri()) - }); - - Mock::given(method("GET")) - .and(path("/.well-known/openid-configuration")) - .respond_with(ResponseTemplate::new(200).set_body_json(discovery_response)) - .mount(&mock_server) - .await; - - Mock::given(method("GET")) - .and(path("/userinfo")) - .respond_with(ResponseTemplate::new(401)) - .mount(&mock_server) - .await; - - let config = create_test_config_with_oidc(&mock_server.uri()); - let oidc_client = OidcClient::new(&config).await.unwrap(); - - let user_info = oidc_client.get_user_info("invalid-access-token").await; - - assert!(user_info.is_err()); - } - - #[test] - fn test_oidc_config_validation() { - let mut config = create_test_config_with_oidc("https://test.example.com"); - - // Test missing client ID - config.oidc_client_id = None; - assert!(tokio_test::block_on(OidcClient::new(&config)).is_err()); - - // Test missing client secret - config.oidc_client_id = Some("test-client-id".to_string()); - config.oidc_client_secret = None; - assert!(tokio_test::block_on(OidcClient::new(&config)).is_err()); - - // Test missing issuer URL - config.oidc_client_secret = Some("test-client-secret".to_string()); - config.oidc_issuer_url = None; - assert!(tokio_test::block_on(OidcClient::new(&config)).is_err()); - - // Test missing redirect URI - config.oidc_issuer_url = Some("https://test.example.com".to_string()); - config.oidc_redirect_uri = None; - assert!(tokio_test::block_on(OidcClient::new(&config)).is_err()); - } -} \ No newline at end of file diff --git a/tests/oidc_unit_tests.rs b/tests/oidc_unit_tests.rs new file mode 100644 index 0000000..c176570 --- /dev/null +++ b/tests/oidc_unit_tests.rs @@ -0,0 +1,195 @@ +use readur::config::Config; +use readur::oidc::OidcClient; +use wiremock::{matchers::{method, path}, Mock, MockServer, ResponseTemplate}; + +fn create_test_config_with_oidc(issuer_url: &str) -> Config { + Config { + database_url: "postgresql://test:test@localhost/test".to_string(), + server_address: "127.0.0.1:8000".to_string(), + jwt_secret: "test-secret".to_string(), + upload_path: "./test-uploads".to_string(), + watch_folder: "./test-watch".to_string(), + allowed_file_types: vec!["pdf".to_string()], + watch_interval_seconds: Some(30), + file_stability_check_ms: Some(500), + max_file_age_hours: None, + ocr_language: "eng".to_string(), + concurrent_ocr_jobs: 2, + ocr_timeout_seconds: 60, + max_file_size_mb: 10, + memory_limit_mb: 256, + cpu_priority: "normal".to_string(), + oidc_enabled: true, + oidc_client_id: Some("test-client-id".to_string()), + oidc_client_secret: Some("test-client-secret".to_string()), + oidc_issuer_url: Some(issuer_url.to_string()), + oidc_redirect_uri: Some("http://localhost:8000/auth/oidc/callback".to_string()), + } +} + +#[tokio::test] +async fn test_oidc_discovery() { + let mock_server = MockServer::start().await; + + let discovery_response = serde_json::json!({ + "issuer": mock_server.uri(), + "authorization_endpoint": format!("{}/auth", mock_server.uri()), + "token_endpoint": format!("{}/token", mock_server.uri()), + "userinfo_endpoint": format!("{}/userinfo", mock_server.uri()) + }); + + Mock::given(method("GET")) + .and(path("/.well-known/openid-configuration")) + .respond_with(ResponseTemplate::new(200).set_body_json(discovery_response)) + .mount(&mock_server) + .await; + + let config = create_test_config_with_oidc(&mock_server.uri()); + let oidc_client = OidcClient::new(&config).await; + + assert!(oidc_client.is_ok()); + let client = oidc_client.unwrap(); + assert_eq!(client.get_discovery().issuer, mock_server.uri()); + assert_eq!(client.get_discovery().authorization_endpoint, format!("{}/auth", mock_server.uri())); +} + +#[tokio::test] +async fn test_oidc_discovery_failure() { + let mock_server = MockServer::start().await; + + Mock::given(method("GET")) + .and(path("/.well-known/openid-configuration")) + .respond_with(ResponseTemplate::new(404)) + .mount(&mock_server) + .await; + + let config = create_test_config_with_oidc(&mock_server.uri()); + let oidc_client = OidcClient::new(&config).await; + + assert!(oidc_client.is_err()); +} + +#[tokio::test] +async fn test_get_authorization_url() { + let mock_server = MockServer::start().await; + + let discovery_response = serde_json::json!({ + "issuer": mock_server.uri(), + "authorization_endpoint": format!("{}/auth", mock_server.uri()), + "token_endpoint": format!("{}/token", mock_server.uri()), + "userinfo_endpoint": format!("{}/userinfo", mock_server.uri()) + }); + + Mock::given(method("GET")) + .and(path("/.well-known/openid-configuration")) + .respond_with(ResponseTemplate::new(200).set_body_json(discovery_response)) + .mount(&mock_server) + .await; + + let config = create_test_config_with_oidc(&mock_server.uri()); + let oidc_client = OidcClient::new(&config).await.unwrap(); + + let (auth_url, csrf_token) = oidc_client.get_authorization_url(); + + assert!(auth_url.to_string().contains("/auth")); + assert!(auth_url.to_string().contains("client_id=test-client-id")); + assert!(auth_url.to_string().contains("scope=openid+email+profile")); + assert!(!csrf_token.secret().is_empty()); +} + +#[tokio::test] +async fn test_get_user_info() { + let mock_server = MockServer::start().await; + + let discovery_response = serde_json::json!({ + "issuer": mock_server.uri(), + "authorization_endpoint": format!("{}/auth", mock_server.uri()), + "token_endpoint": format!("{}/token", mock_server.uri()), + "userinfo_endpoint": format!("{}/userinfo", mock_server.uri()) + }); + + Mock::given(method("GET")) + .and(path("/.well-known/openid-configuration")) + .respond_with(ResponseTemplate::new(200).set_body_json(discovery_response)) + .mount(&mock_server) + .await; + + let user_info_response = serde_json::json!({ + "sub": "test-user-123", + "email": "test@example.com", + "name": "Test User", + "preferred_username": "testuser" + }); + + Mock::given(method("GET")) + .and(path("/userinfo")) + .respond_with(ResponseTemplate::new(200).set_body_json(user_info_response)) + .mount(&mock_server) + .await; + + let config = create_test_config_with_oidc(&mock_server.uri()); + let oidc_client = OidcClient::new(&config).await.unwrap(); + + let user_info = oidc_client.get_user_info("test-access-token").await; + + assert!(user_info.is_ok()); + let info = user_info.unwrap(); + assert_eq!(info.sub, "test-user-123"); + assert_eq!(info.email, Some("test@example.com".to_string())); + assert_eq!(info.preferred_username, Some("testuser".to_string())); +} + +#[tokio::test] +async fn test_get_user_info_unauthorized() { + let mock_server = MockServer::start().await; + + let discovery_response = serde_json::json!({ + "issuer": mock_server.uri(), + "authorization_endpoint": format!("{}/auth", mock_server.uri()), + "token_endpoint": format!("{}/token", mock_server.uri()), + "userinfo_endpoint": format!("{}/userinfo", mock_server.uri()) + }); + + Mock::given(method("GET")) + .and(path("/.well-known/openid-configuration")) + .respond_with(ResponseTemplate::new(200).set_body_json(discovery_response)) + .mount(&mock_server) + .await; + + Mock::given(method("GET")) + .and(path("/userinfo")) + .respond_with(ResponseTemplate::new(401)) + .mount(&mock_server) + .await; + + let config = create_test_config_with_oidc(&mock_server.uri()); + let oidc_client = OidcClient::new(&config).await.unwrap(); + + let user_info = oidc_client.get_user_info("invalid-access-token").await; + + assert!(user_info.is_err()); +} + +#[test] +fn test_oidc_config_validation() { + let mut config = create_test_config_with_oidc("https://test.example.com"); + + // Test missing client ID + config.oidc_client_id = None; + assert!(tokio_test::block_on(OidcClient::new(&config)).is_err()); + + // Test missing client secret + config.oidc_client_id = Some("test-client-id".to_string()); + config.oidc_client_secret = None; + assert!(tokio_test::block_on(OidcClient::new(&config)).is_err()); + + // Test missing issuer URL + config.oidc_client_secret = Some("test-client-secret".to_string()); + config.oidc_issuer_url = None; + assert!(tokio_test::block_on(OidcClient::new(&config)).is_err()); + + // Test missing redirect URI + config.oidc_issuer_url = Some("https://test.example.com".to_string()); + config.oidc_redirect_uri = None; + assert!(tokio_test::block_on(OidcClient::new(&config)).is_err()); +} \ No newline at end of file