commit eb9f58ce4af8cb2ba25265d698cb68ecd8eaff8e
parent d9edb3f48a3e48244654baf0dfbe100fb65f531b
Author: Henrique Chan Carvalho Machado <henriqueccmachado@tecnico.ulisboa.pt>
Date: Sun, 19 Oct 2025 19:43:16 +0200
oauth2_gateway: implemented tests for basic functionality
Diffstat:
4 files changed, 136 insertions(+), 13 deletions(-)
diff --git a/oauth2_gateway/Cargo.toml b/oauth2_gateway/Cargo.toml
@@ -3,9 +3,18 @@ name = "oauth2-gateway"
version = "0.0.1"
edition = "2024"
+[lib] # For tests
+name = "oauth2_gateway"
+path = "src/lib.rs"
+
+[[bin]]
+name = "oauth2-gateway"
+path = "src/main.rs"
+
[dependencies]
# Web framework
axum = "0.8.6"
+axum-test = "18.1.0"
tokio = { version = "1.48.0", features = ["full"] }
tower = "0.5"
tower-http = { version = "0.6.6", features = ["trace"] }
@@ -31,3 +40,4 @@ tracing-subscriber = { version = "0.3.20", features = ["env-filter"] }
# Error handling
anyhow = "1.0.100"
+
diff --git a/oauth2_gateway/src/lib.rs b/oauth2_gateway/src/lib.rs
@@ -0,0 +1,4 @@
+pub mod config;
+pub mod handlers;
+pub mod models;
+pub mod state;
+\ No newline at end of file
diff --git a/oauth2_gateway/src/main.rs b/oauth2_gateway/src/main.rs
@@ -1,19 +1,12 @@
+use oauth2_gateway::{config::Config, handlers, state::AppState};
+use clap::Parser;
+use tower_http::trace::TraceLayer;
+use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};
use anyhow::Result;
use axum::{
routing::{get, post},
Router,
};
-use clap::Parser;
-use tower_http::trace::TraceLayer;
-use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};
-
-mod config;
-mod handlers;
-mod models;
-mod state;
-
-use config::Config;
-use state::AppState;
#[derive(Parser, Debug)]
#[command(version)]
@@ -40,7 +33,6 @@ async fn main() -> Result<()> {
let config = Config::from_file(&args.config)?;
- // Share config between modules
let state = AppState::new(config.clone());
let app = Router::new()
@@ -49,7 +41,7 @@ async fn main() -> Result<()> {
.route("/authorize/{nonce}", get(handlers::authorize))
.route("/token", post(handlers::token))
.route("/info", get(handlers::info))
- .route("/notify/{client_id}", post(handlers::notification_webhook))
+ .route("/notification/{client_id}", post(handlers::notification_webhook))
.layer(TraceLayer::new_for_http())
.with_state(state);
diff --git a/oauth2_gateway/tests/api_tests.rs b/oauth2_gateway/tests/api_tests.rs
@@ -0,0 +1,116 @@
+use axum::{routing::*, Router};
+use axum_test::TestServer;
+use oauth2_gateway::{config::*, handlers, models::*, state::AppState};
+use serde_json::json;
+
+fn create_test_app() -> Router {
+ let config = Config {
+ server: ServerConfig {
+ host: "127.0.0.1".to_string(),
+ port: 8080,
+ },
+ exchange: ExchangeConfig {
+ base_url: "http://test-exchange.com".to_string(),
+ notification_endpoint: "/notify".to_string(),
+ },
+ verifier: VerifierConfig {
+ base_url: "http://test-verifier.com".to_string(),
+ management_api_path: "/api".to_string(),
+ },
+ };
+
+ let state = AppState::new(config);
+
+ Router::new()
+ .route("/health", get(handlers::health_check))
+ .route("/setup/{client_id}", post(handlers::setup))
+ .route("/authorize/{nonce}", get(handlers::authorize))
+ .route("/token", post(handlers::token))
+ .route("/info", get(handlers::info))
+ .route("/notification/{client_id}", post(handlers::notification_webhook))
+ .with_state(state)
+}
+
+#[tokio::test]
+async fn test_health_check() {
+ let app = create_test_app();
+ let server = TestServer::new(app).unwrap();
+
+ let response = server.get("/health").await;
+
+ response.assert_status_ok();
+ response.assert_json(&json!({
+ "status": "healthy",
+ "service": "oauth2-gateway",
+ // "version": env!("CARGO_PKG_VERSION")
+ }));
+}
+
+#[tokio::test]
+async fn test_setup_endpoint() {
+ let app = create_test_app();
+ let server = TestServer::new(app).unwrap();
+
+ let response = server
+ .post("/setup/test-client")
+ .json(&json!({
+ "scope": "first_name last_name age_over_18"
+ }))
+ .await;
+
+ response.assert_status_ok();
+
+ // Check response has a nonce
+ let body: SetupResponse = response.json();
+ assert!(!body.nonce.is_empty());
+ println!("Generated nonce: {}", body.nonce);
+}
+
+#[tokio::test]
+async fn test_setup_different_clients() {
+ let app = create_test_app();
+ let server = TestServer::new(app).unwrap();
+
+ let response1 = server
+ .post("/setup/client-1")
+ .json(&json!({"scope": "first_name"}))
+ .await;
+
+ let response2 = server
+ .post("/setup/client-2")
+ .json(&json!({"scope": "last_name"}))
+ .await;
+
+ response1.assert_status_ok();
+ response2.assert_status_ok();
+
+ let nonce1: SetupResponse = response1.json();
+ let nonce2: SetupResponse = response2.json();
+
+ assert_ne!(nonce1.nonce, nonce2.nonce);
+}
+
+#[tokio::test]
+async fn test_authorize_endpoint() {
+ let app = create_test_app();
+ let server = TestServer::new(app).unwrap();
+
+ // Get a nonce from setup
+ let setup_response = server
+ .post("/setup/test-client")
+ .json(&json!({"scope": "test"}))
+ .await;
+
+ let setup: SetupResponse = setup_response.json();
+
+ // Authorize with that nonce
+ let response = server
+ .get(&format!("/authorize/{}", setup.nonce))
+ .await;
+
+ response.assert_status_ok();
+
+ let body: AuthorizeResponse = response.json();
+ assert!(!body.verification_url.is_empty());
+ println!("Verification URL: {}", body.verification_url);
+}