1
use axum::{
2
    Router,
3
    body::Body,
4
    http::{Request, StatusCode},
5
    routing::get,
6
};
7
use tower::ServiceExt;
8

            
9
use crate::common::{create_mock_jwt_auth, create_mock_user, create_test_app_state};
10

            
11
// SIMPLE HANDLERS TESTS (no external dependencies)
12

            
13
#[supp_macro::shared_runtime_test]
14
1
async fn test_get_version_handler() {
15
1
    let app = Router::new().route("/version", get(web::handler::get_version));
16

            
17
1
    let response = app
18
1
        .oneshot(
19
1
            Request::builder()
20
1
                .uri("/version")
21
1
                .body(Body::empty())
22
1
                .unwrap(),
23
1
        )
24
1
        .await
25
1
        .unwrap();
26

            
27
1
    assert_eq!(response.status(), StatusCode::OK);
28

            
29
1
    let body = axum::body::to_bytes(response.into_body(), usize::MAX)
30
1
        .await
31
1
        .unwrap();
32
1
    let body_str = String::from_utf8(body.to_vec()).unwrap();
33

            
34
    // Should contain some version info (git hash)
35
1
    assert!(!body_str.is_empty());
36
}
37

            
38
#[supp_macro::shared_runtime_test]
39
1
async fn test_get_logout_link_handler() {
40
1
    let app_state = create_test_app_state().await;
41
    // The fragment now varies by role, so it reads the auth extension the real
42
    // router supplies; without it the handler cannot run at all.
43
1
    let app = Router::new()
44
1
        .route("/logout", get(web::handler::get_logout_link))
45
1
        .layer(axum::Extension(create_mock_jwt_auth(create_mock_user())))
46
1
        .with_state(app_state);
47

            
48
1
    let response = app
49
1
        .oneshot(
50
1
            Request::builder()
51
1
                .uri("/logout")
52
1
                .body(Body::empty())
53
1
                .unwrap(),
54
1
        )
55
1
        .await
56
1
        .unwrap();
57

            
58
1
    assert_eq!(response.status(), StatusCode::OK);
59

            
60
1
    let body = axum::body::to_bytes(response.into_body(), usize::MAX)
61
1
        .await
62
1
        .unwrap();
63
1
    let body_str = String::from_utf8(body.to_vec()).unwrap();
64

            
65
    // Should contain the logout link
66
1
    assert!(body_str.contains("/api/auth/logout"));
67
}
68

            
69
#[supp_macro::shared_runtime_test]
70
1
async fn test_handlers_return_proper_json_errors() {
71
1
    let app_state = create_test_app_state().await;
72
1
    let app = Router::new()
73
1
        .route(
74
1
            "/auth/login",
75
1
            axum::routing::post(web::handler::login_user_handler),
76
        )
77
1
        .route(
78
1
            "/auth/register",
79
1
            axum::routing::post(web::handler::register_user_handler),
80
        )
81
1
        .route(
82
1
            "/auth/refresh",
83
1
            axum::routing::get(web::handler::refresh_access_token_handler),
84
        )
85
        // `/auth/refresh` sits behind `auth` in production, which inserts this.
86
1
        .layer(axum::Extension(create_mock_jwt_auth(create_mock_user())))
87
1
        .with_state(app_state);
88

            
89
    // Test login with invalid JSON
90
1
    let response = app
91
1
        .clone()
92
1
        .oneshot(
93
1
            Request::builder()
94
1
                .method("POST")
95
1
                .uri("/auth/login")
96
1
                .header("content-type", "application/json")
97
1
                .body(Body::from("invalid json"))
98
1
                .unwrap(),
99
1
        )
100
1
        .await
101
1
        .unwrap();
102

            
103
1
    assert_eq!(response.status(), StatusCode::BAD_REQUEST);
104

            
105
    // Test register with invalid JSON
106
1
    let response = app
107
1
        .clone()
108
1
        .oneshot(
109
1
            Request::builder()
110
1
                .method("POST")
111
1
                .uri("/auth/register")
112
1
                .header("content-type", "application/json")
113
1
                .body(Body::from("invalid json"))
114
1
                .unwrap(),
115
1
        )
116
1
        .await
117
1
        .unwrap();
118

            
119
1
    assert_eq!(response.status(), StatusCode::BAD_REQUEST);
120

            
121
    // Test refresh without cookie
122
1
    let response = app
123
1
        .oneshot(
124
1
            Request::builder()
125
1
                .uri("/auth/refresh")
126
1
                .body(Body::empty())
127
1
                .unwrap(),
128
1
        )
129
1
        .await
130
1
        .unwrap();
131

            
132
    // May return 401 (Unauthorized) or 403 (Forbidden) depending on auth middleware
133
1
    assert!(
134
1
        response.status() == StatusCode::UNAUTHORIZED || response.status() == StatusCode::FORBIDDEN,
135
        "Expected 401 or 403, got: {}",
136
        response.status()
137
    );
138

            
139
    // Verify it returns JSON
140
1
    let body = axum::body::to_bytes(response.into_body(), usize::MAX)
141
1
        .await
142
1
        .unwrap();
143
1
    let body_str = String::from_utf8(body.to_vec()).unwrap();
144
1
    let _: serde_json::Value =
145
1
        serde_json::from_str(&body_str).expect("Response should be valid JSON");
146
}