Some checks failed
CI / Rustfmt (push) Failing after 21s
CI / Cargo Audit & Deny (push) Failing after 33s
CI / Web Blocking Checks (push) Successful in 50s
CI / Security Blocking Checks (push) Successful in 7s
CI / Web Advisory Checks (push) Successful in 33s
CI / Security Advisory Checks (push) Successful in 34s
Publish Images And Chart / Resolve Publish Metadata (push) Successful in 1s
Publish Images And Chart / Publish init-packs (push) Failing after 11s
Publish Images And Chart / Publish init-user (push) Failing after 10s
Publish Images And Chart / Publish migrations (push) Failing after 11s
Publish Images And Chart / Publish sensor (push) Failing after 10s
Publish Images And Chart / Publish web (push) Failing after 10s
Publish Images And Chart / Publish worker (push) Failing after 10s
Publish Images And Chart / Publish api (push) Failing after 7s
Publish Images And Chart / Publish executor (push) Failing after 9s
Publish Images And Chart / Publish notifier (push) Failing after 10s
Publish Images And Chart / Publish Helm Chart (push) Has been skipped
CI / Clippy (push) Successful in 18m52s
CI / Tests (push) Has been cancelled
553 lines
15 KiB
Rust
553 lines
15 KiB
Rust
//! Integration tests for health check and authentication endpoints
|
|
|
|
use axum::http::StatusCode;
|
|
use helpers::*;
|
|
use serde_json::json;
|
|
|
|
mod helpers;
|
|
|
|
#[tokio::test]
|
|
#[ignore = "integration test — requires database"]
|
|
async fn test_register_debug() {
|
|
let ctx = TestContext::new()
|
|
.await
|
|
.expect("Failed to create test context");
|
|
|
|
let response = ctx
|
|
.post(
|
|
"/auth/register",
|
|
json!({
|
|
"login": "debuguser",
|
|
"password": "TestPassword123!",
|
|
"display_name": "Debug User"
|
|
}),
|
|
None,
|
|
)
|
|
.await
|
|
.expect("Failed to make request");
|
|
|
|
let status = response.status();
|
|
println!("Status: {}", status);
|
|
|
|
let body_text = response.text().await.expect("Failed to get body");
|
|
println!("Body: {}", body_text);
|
|
|
|
// This test is just for debugging - will fail if not 201
|
|
assert_eq!(status, StatusCode::OK);
|
|
}
|
|
|
|
#[tokio::test]
|
|
#[ignore = "integration test — requires database"]
|
|
async fn test_health_check() {
|
|
let ctx = TestContext::new()
|
|
.await
|
|
.expect("Failed to create test context");
|
|
|
|
let response = ctx
|
|
.get("/health", None)
|
|
.await
|
|
.expect("Failed to make request");
|
|
|
|
assert_eq!(response.status(), StatusCode::OK);
|
|
|
|
let body: serde_json::Value = response.json().await.expect("Failed to parse JSON");
|
|
|
|
assert_eq!(body["status"], "ok");
|
|
}
|
|
|
|
#[tokio::test]
|
|
#[ignore = "integration test — requires database"]
|
|
async fn test_health_detailed() {
|
|
let ctx = TestContext::new()
|
|
.await
|
|
.expect("Failed to create test context");
|
|
|
|
let response = ctx
|
|
.get("/health/detailed", None)
|
|
.await
|
|
.expect("Failed to make request");
|
|
|
|
assert_eq!(response.status(), StatusCode::OK);
|
|
|
|
let body: serde_json::Value = response.json().await.expect("Failed to parse JSON");
|
|
|
|
assert_eq!(body["status"], "ok");
|
|
assert_eq!(body["database"], "connected");
|
|
assert!(body["version"].is_string());
|
|
}
|
|
|
|
#[tokio::test]
|
|
#[ignore = "integration test — requires database"]
|
|
async fn test_health_ready() {
|
|
let ctx = TestContext::new()
|
|
.await
|
|
.expect("Failed to create test context");
|
|
|
|
let response = ctx
|
|
.get("/health/ready", None)
|
|
.await
|
|
.expect("Failed to make request");
|
|
|
|
assert_eq!(response.status(), StatusCode::OK);
|
|
|
|
// Readiness endpoint returns empty body with 200 status
|
|
}
|
|
|
|
#[tokio::test]
|
|
#[ignore = "integration test — requires database"]
|
|
async fn test_health_live() {
|
|
let ctx = TestContext::new()
|
|
.await
|
|
.expect("Failed to create test context");
|
|
|
|
let response = ctx
|
|
.get("/health/live", None)
|
|
.await
|
|
.expect("Failed to make request");
|
|
|
|
assert_eq!(response.status(), StatusCode::OK);
|
|
|
|
// Liveness endpoint returns empty body with 200 status
|
|
}
|
|
|
|
#[tokio::test]
|
|
#[ignore = "integration test — requires database"]
|
|
async fn test_register_user() {
|
|
let ctx = TestContext::new()
|
|
.await
|
|
.expect("Failed to create test context");
|
|
|
|
let response = ctx
|
|
.post(
|
|
"/auth/register",
|
|
json!({
|
|
"login": "newuser",
|
|
"password": "SecurePassword123!",
|
|
"display_name": "New User"
|
|
}),
|
|
None,
|
|
)
|
|
.await
|
|
.expect("Failed to make request");
|
|
|
|
assert_eq!(response.status(), StatusCode::OK);
|
|
|
|
let body: serde_json::Value = response.json().await.expect("Failed to parse JSON");
|
|
|
|
assert!(body["data"].is_object());
|
|
assert!(body["data"]["access_token"].is_string());
|
|
assert!(body["data"]["refresh_token"].is_string());
|
|
assert!(body["data"]["user"].is_object());
|
|
assert_eq!(body["data"]["user"]["login"], "newuser");
|
|
assert_eq!(body["data"]["user"]["display_name"], "New User");
|
|
}
|
|
|
|
#[tokio::test]
|
|
#[ignore = "integration test — requires database"]
|
|
async fn test_register_duplicate_user() {
|
|
let ctx = TestContext::new()
|
|
.await
|
|
.expect("Failed to create test context");
|
|
|
|
// Register first user
|
|
let _ = ctx
|
|
.post(
|
|
"/auth/register",
|
|
json!({
|
|
"login": "duplicate",
|
|
"password": "SecurePassword123!",
|
|
"display_name": "Duplicate User"
|
|
}),
|
|
None,
|
|
)
|
|
.await
|
|
.expect("Failed to make request");
|
|
|
|
// Try to register same user again
|
|
let response = ctx
|
|
.post(
|
|
"/auth/register",
|
|
json!({
|
|
"login": "duplicate",
|
|
"password": "SecurePassword123!",
|
|
"display_name": "Duplicate User"
|
|
}),
|
|
None,
|
|
)
|
|
.await
|
|
.expect("Failed to make request");
|
|
|
|
assert_eq!(response.status(), StatusCode::CONFLICT);
|
|
}
|
|
|
|
#[tokio::test]
|
|
#[ignore = "integration test — requires database"]
|
|
async fn test_register_invalid_password() {
|
|
let ctx = TestContext::new()
|
|
.await
|
|
.expect("Failed to create test context");
|
|
|
|
let response = ctx
|
|
.post(
|
|
"/auth/register",
|
|
json!({
|
|
"login": "testuser",
|
|
"password": "weak",
|
|
"display_name": "Test User"
|
|
}),
|
|
None,
|
|
)
|
|
.await
|
|
.expect("Failed to make request");
|
|
|
|
assert_eq!(response.status(), StatusCode::UNPROCESSABLE_ENTITY);
|
|
}
|
|
|
|
#[tokio::test]
|
|
#[ignore = "integration test — requires database"]
|
|
async fn test_login_success() {
|
|
let ctx = TestContext::new()
|
|
.await
|
|
.expect("Failed to create test context");
|
|
|
|
// Register a user first
|
|
let _ = ctx
|
|
.post(
|
|
"/auth/register",
|
|
json!({
|
|
"login": "loginuser",
|
|
"password": "SecurePassword123!",
|
|
"display_name": "Login User"
|
|
}),
|
|
None,
|
|
)
|
|
.await
|
|
.expect("Failed to register user");
|
|
|
|
// Now try to login
|
|
let response = ctx
|
|
.post(
|
|
"/auth/login",
|
|
json!({
|
|
"login": "loginuser",
|
|
"password": "SecurePassword123!"
|
|
}),
|
|
None,
|
|
)
|
|
.await
|
|
.expect("Failed to make request");
|
|
|
|
assert_eq!(response.status(), StatusCode::OK);
|
|
|
|
let body: serde_json::Value = response.json().await.expect("Failed to parse JSON");
|
|
|
|
assert!(body["data"]["access_token"].is_string());
|
|
assert!(body["data"]["refresh_token"].is_string());
|
|
assert_eq!(body["data"]["user"]["login"], "loginuser");
|
|
}
|
|
|
|
#[tokio::test]
|
|
#[ignore = "integration test — requires database"]
|
|
async fn test_login_wrong_password() {
|
|
let ctx = TestContext::new()
|
|
.await
|
|
.expect("Failed to create test context");
|
|
|
|
// Register a user first
|
|
let _ = ctx
|
|
.post(
|
|
"/auth/register",
|
|
json!({
|
|
"login": "wrongpassuser",
|
|
"password": "SecurePassword123!",
|
|
"display_name": "Wrong Pass User"
|
|
}),
|
|
None,
|
|
)
|
|
.await
|
|
.expect("Failed to register user");
|
|
|
|
// Try to login with wrong password
|
|
let response = ctx
|
|
.post(
|
|
"/auth/login",
|
|
json!({
|
|
"login": "wrongpassuser",
|
|
"password": "WrongPassword123!"
|
|
}),
|
|
None,
|
|
)
|
|
.await
|
|
.expect("Failed to make request");
|
|
|
|
assert_eq!(response.status(), StatusCode::UNAUTHORIZED);
|
|
}
|
|
|
|
#[tokio::test]
|
|
#[ignore = "integration test — requires database"]
|
|
async fn test_login_nonexistent_user() {
|
|
let ctx = TestContext::new()
|
|
.await
|
|
.expect("Failed to create test context");
|
|
|
|
let response = ctx
|
|
.post(
|
|
"/auth/login",
|
|
json!({
|
|
"login": "nonexistent",
|
|
"password": "SomePassword123!"
|
|
}),
|
|
None,
|
|
)
|
|
.await
|
|
.expect("Failed to make request");
|
|
|
|
assert_eq!(response.status(), StatusCode::UNAUTHORIZED);
|
|
}
|
|
|
|
// ── LDAP auth tests ──────────────────────────────────────────────────
|
|
|
|
#[tokio::test]
|
|
#[ignore = "integration test — requires database"]
|
|
async fn test_ldap_login_returns_501_when_not_configured() {
|
|
let ctx = TestContext::new()
|
|
.await
|
|
.expect("Failed to create test context");
|
|
|
|
let response = ctx
|
|
.post(
|
|
"/auth/ldap/login",
|
|
json!({
|
|
"login": "jdoe",
|
|
"password": "secret"
|
|
}),
|
|
None,
|
|
)
|
|
.await
|
|
.expect("Failed to make request");
|
|
|
|
// LDAP is not configured in config.test.yaml, so the endpoint
|
|
// should return 501 Not Implemented.
|
|
assert_eq!(response.status(), StatusCode::NOT_IMPLEMENTED);
|
|
}
|
|
|
|
#[tokio::test]
|
|
#[ignore = "integration test — requires database"]
|
|
async fn test_ldap_login_validates_empty_login() {
|
|
let ctx = TestContext::new()
|
|
.await
|
|
.expect("Failed to create test context");
|
|
|
|
let response = ctx
|
|
.post(
|
|
"/auth/ldap/login",
|
|
json!({
|
|
"login": "",
|
|
"password": "secret"
|
|
}),
|
|
None,
|
|
)
|
|
.await
|
|
.expect("Failed to make request");
|
|
|
|
// Validation should fail before we even check LDAP config
|
|
assert_eq!(response.status(), StatusCode::UNPROCESSABLE_ENTITY);
|
|
}
|
|
|
|
#[tokio::test]
|
|
#[ignore = "integration test — requires database"]
|
|
async fn test_ldap_login_validates_empty_password() {
|
|
let ctx = TestContext::new()
|
|
.await
|
|
.expect("Failed to create test context");
|
|
|
|
let response = ctx
|
|
.post(
|
|
"/auth/ldap/login",
|
|
json!({
|
|
"login": "jdoe",
|
|
"password": ""
|
|
}),
|
|
None,
|
|
)
|
|
.await
|
|
.expect("Failed to make request");
|
|
|
|
assert_eq!(response.status(), StatusCode::UNPROCESSABLE_ENTITY);
|
|
}
|
|
|
|
#[tokio::test]
|
|
#[ignore = "integration test — requires database"]
|
|
async fn test_ldap_login_validates_missing_fields() {
|
|
let ctx = TestContext::new()
|
|
.await
|
|
.expect("Failed to create test context");
|
|
|
|
let response = ctx
|
|
.post("/auth/ldap/login", json!({}), None)
|
|
.await
|
|
.expect("Failed to make request");
|
|
|
|
// Missing required fields should return 422
|
|
assert_eq!(response.status(), StatusCode::UNPROCESSABLE_ENTITY);
|
|
}
|
|
|
|
// ── auth/settings LDAP field tests ──────────────────────────────────
|
|
|
|
#[tokio::test]
|
|
#[ignore = "integration test — requires database"]
|
|
async fn test_auth_settings_includes_ldap_fields_disabled() {
|
|
let ctx = TestContext::new()
|
|
.await
|
|
.expect("Failed to create test context");
|
|
|
|
let response = ctx
|
|
.get("/auth/settings", None)
|
|
.await
|
|
.expect("Failed to make request");
|
|
|
|
assert_eq!(response.status(), StatusCode::OK);
|
|
|
|
let body: serde_json::Value = response.json().await.expect("Failed to parse JSON");
|
|
|
|
// LDAP is not configured in config.test.yaml, so these should all
|
|
// reflect the disabled state.
|
|
assert_eq!(body["data"]["ldap_enabled"], false);
|
|
assert_eq!(body["data"]["ldap_visible_by_default"], false);
|
|
assert!(body["data"]["ldap_provider_name"].is_null());
|
|
assert!(body["data"]["ldap_provider_label"].is_null());
|
|
assert!(body["data"]["ldap_provider_icon_url"].is_null());
|
|
|
|
// Existing fields should still be present
|
|
assert!(body["data"]["authentication_enabled"].is_boolean());
|
|
assert!(body["data"]["local_password_enabled"].is_boolean());
|
|
assert!(body["data"]["oidc_enabled"].is_boolean());
|
|
assert!(body["data"]["self_registration_enabled"].is_boolean());
|
|
}
|
|
|
|
#[tokio::test]
|
|
#[ignore = "integration test — requires database"]
|
|
async fn test_get_current_user() {
|
|
let ctx = TestContext::new()
|
|
.await
|
|
.expect("Failed to create test context")
|
|
.with_auth()
|
|
.await
|
|
.expect("Failed to authenticate");
|
|
|
|
let response = ctx
|
|
.get("/auth/me", ctx.token())
|
|
.await
|
|
.expect("Failed to make request");
|
|
|
|
assert_eq!(response.status(), StatusCode::OK);
|
|
|
|
let body: serde_json::Value = response.json().await.expect("Failed to parse JSON");
|
|
|
|
assert!(body["data"].is_object());
|
|
assert!(body["data"]["id"].is_number());
|
|
assert!(body["data"]["login"].is_string());
|
|
}
|
|
|
|
#[tokio::test]
|
|
#[ignore = "integration test — requires database"]
|
|
async fn test_get_current_user_unauthorized() {
|
|
let ctx = TestContext::new()
|
|
.await
|
|
.expect("Failed to create test context");
|
|
|
|
let response = ctx
|
|
.get("/auth/me", None)
|
|
.await
|
|
.expect("Failed to make request");
|
|
|
|
assert_eq!(response.status(), StatusCode::UNAUTHORIZED);
|
|
}
|
|
|
|
#[tokio::test]
|
|
#[ignore = "integration test — requires database"]
|
|
async fn test_get_current_user_invalid_token() {
|
|
let ctx = TestContext::new()
|
|
.await
|
|
.expect("Failed to create test context");
|
|
|
|
let response = ctx
|
|
.get("/auth/me", Some("invalid-token"))
|
|
.await
|
|
.expect("Failed to make request");
|
|
|
|
assert_eq!(response.status(), StatusCode::UNAUTHORIZED);
|
|
}
|
|
|
|
#[tokio::test]
|
|
#[ignore = "integration test — requires database"]
|
|
async fn test_refresh_token() {
|
|
let ctx = TestContext::new()
|
|
.await
|
|
.expect("Failed to create test context");
|
|
|
|
// Register a user first
|
|
let register_response = ctx
|
|
.post(
|
|
"/auth/register",
|
|
json!({
|
|
"login": "refreshuser",
|
|
"email": "refresh@example.com",
|
|
"password": "SecurePassword123!",
|
|
"display_name": "Refresh User"
|
|
}),
|
|
None,
|
|
)
|
|
.await
|
|
.expect("Failed to register user");
|
|
|
|
let register_body: serde_json::Value = register_response
|
|
.json()
|
|
.await
|
|
.expect("Failed to parse JSON");
|
|
|
|
let refresh_token = register_body["data"]["refresh_token"]
|
|
.as_str()
|
|
.expect("Missing refresh token");
|
|
|
|
// Use refresh token to get new access token
|
|
let response = ctx
|
|
.post(
|
|
"/auth/refresh",
|
|
json!({
|
|
"refresh_token": refresh_token
|
|
}),
|
|
None,
|
|
)
|
|
.await
|
|
.expect("Failed to make request");
|
|
|
|
assert_eq!(response.status(), StatusCode::OK);
|
|
|
|
let body: serde_json::Value = response.json().await.expect("Failed to parse JSON");
|
|
|
|
assert!(body["data"]["access_token"].is_string());
|
|
assert!(body["data"]["refresh_token"].is_string());
|
|
}
|
|
|
|
#[tokio::test]
|
|
#[ignore = "integration test — requires database"]
|
|
async fn test_refresh_with_invalid_token() {
|
|
let ctx = TestContext::new()
|
|
.await
|
|
.expect("Failed to create test context");
|
|
|
|
let response = ctx
|
|
.post(
|
|
"/auth/refresh",
|
|
json!({
|
|
"refresh_token": "invalid-refresh-token"
|
|
}),
|
|
None,
|
|
)
|
|
.await
|
|
.expect("Failed to make request");
|
|
|
|
assert_eq!(response.status(), StatusCode::UNAUTHORIZED);
|
|
}
|