From eca3ec4171775e74328d175d96ff7d6f56ea644c Mon Sep 17 00:00:00 2001 From: Quentin Gliech Date: Mon, 29 Sep 2025 18:47:57 +0200 Subject: [PATCH] Add tests for count=false and count=only --- .../src/admin/v1/compat_sessions/list.rs | 135 ++++++++++++ .../src/admin/v1/oauth2_sessions/list.rs | 56 +++++ .../src/admin/v1/upstream_oauth_links/list.rs | 150 +++++++++++++ .../admin/v1/upstream_oauth_providers/list.rs | 158 ++++++++++++++ .../handlers/src/admin/v1/user_emails/list.rs | 116 ++++++++++ .../admin/v1/user_registration_tokens/list.rs | 203 ++++++++++++++++++ .../src/admin/v1/user_sessions/list.rs | 122 +++++++++++ crates/handlers/src/admin/v1/users/list.rs | 197 +++++++++++++++++ 8 files changed, 1137 insertions(+) diff --git a/crates/handlers/src/admin/v1/compat_sessions/list.rs b/crates/handlers/src/admin/v1/compat_sessions/list.rs index 96a2708af..ff37ca912 100644 --- a/crates/handlers/src/admin/v1/compat_sessions/list.rs +++ b/crates/handlers/src/admin/v1/compat_sessions/list.rs @@ -471,5 +471,140 @@ mod tests { } } "#); + + // Test count=false + let request = Request::get("/api/admin/v1/compat-sessions?count=false") + .bearer(&token) + .empty(); + let response = state.request(request).await; + response.assert_status(StatusCode::OK); + let body: serde_json::Value = response.json(); + assert_json_snapshot!(body, @r#" + { + "data": [ + { + "type": "compat-session", + "id": "01FSHNB530AAPR7PEV8KNBZD5Y", + "attributes": { + "user_id": "01FSHN9AG0MZAA6S4AF7CTV32E", + "device_id": "LoieH5Iecx", + "user_session_id": null, + "redirect_uri": null, + "created_at": "2022-01-16T14:41:00Z", + "user_agent": null, + "last_active_at": null, + "last_active_ip": null, + "finished_at": null, + "human_name": null + }, + "links": { + "self": "/api/admin/v1/compat-sessions/01FSHNB530AAPR7PEV8KNBZD5Y" + } + }, + { + "type": "compat-session", + "id": "01FSHNCZP0PPF7X0EVMJNECPZW", + "attributes": { + "user_id": "01FSHNB530AJ6AC5HQ9X6H4RP4", + "device_id": "ZXyvelQWW9", + "user_session_id": null, + "redirect_uri": null, + "created_at": "2022-01-16T14:42:00Z", + "user_agent": null, + "last_active_at": null, + "last_active_ip": null, + "finished_at": "2022-01-16T14:43:00Z", + "human_name": null + }, + "links": { + "self": "/api/admin/v1/compat-sessions/01FSHNCZP0PPF7X0EVMJNECPZW" + } + } + ], + "links": { + "self": "/api/admin/v1/compat-sessions?count=false&page[first]=10", + "first": "/api/admin/v1/compat-sessions?count=false&page[first]=10", + "last": "/api/admin/v1/compat-sessions?count=false&page[last]=10" + } + } + "#); + + // Test count=only + let request = Request::get("/api/admin/v1/compat-sessions?count=only") + .bearer(&token) + .empty(); + let response = state.request(request).await; + response.assert_status(StatusCode::OK); + let body: serde_json::Value = response.json(); + assert_json_snapshot!(body, @r#" + { + "meta": { + "count": 2 + }, + "links": { + "self": "/api/admin/v1/compat-sessions?count=only" + } + } + "#); + + // Test count=false with filtering + let request = Request::get(format!( + "/api/admin/v1/compat-sessions?count=false&filter[user]={}", + alice.id + )) + .bearer(&token) + .empty(); + let response = state.request(request).await; + response.assert_status(StatusCode::OK); + let body: serde_json::Value = response.json(); + assert_json_snapshot!(body, @r#" + { + "data": [ + { + "type": "compat-session", + "id": "01FSHNB530AAPR7PEV8KNBZD5Y", + "attributes": { + "user_id": "01FSHN9AG0MZAA6S4AF7CTV32E", + "device_id": "LoieH5Iecx", + "user_session_id": null, + "redirect_uri": null, + "created_at": "2022-01-16T14:41:00Z", + "user_agent": null, + "last_active_at": null, + "last_active_ip": null, + "finished_at": null, + "human_name": null + }, + "links": { + "self": "/api/admin/v1/compat-sessions/01FSHNB530AAPR7PEV8KNBZD5Y" + } + } + ], + "links": { + "self": "/api/admin/v1/compat-sessions?filter[user]=01FSHN9AG0MZAA6S4AF7CTV32E&count=false&page[first]=10", + "first": "/api/admin/v1/compat-sessions?filter[user]=01FSHN9AG0MZAA6S4AF7CTV32E&count=false&page[first]=10", + "last": "/api/admin/v1/compat-sessions?filter[user]=01FSHN9AG0MZAA6S4AF7CTV32E&count=false&page[last]=10" + } + } + "#); + + // Test count=only with filtering + let request = + Request::get("/api/admin/v1/compat-sessions?count=only&filter[status]=active") + .bearer(&token) + .empty(); + let response = state.request(request).await; + response.assert_status(StatusCode::OK); + let body: serde_json::Value = response.json(); + assert_json_snapshot!(body, @r#" + { + "meta": { + "count": 1 + }, + "links": { + "self": "/api/admin/v1/compat-sessions?filter[status]=active&count=only" + } + } + "#); } } diff --git a/crates/handlers/src/admin/v1/oauth2_sessions/list.rs b/crates/handlers/src/admin/v1/oauth2_sessions/list.rs index 8b6ff6b2b..748c7bc60 100644 --- a/crates/handlers/src/admin/v1/oauth2_sessions/list.rs +++ b/crates/handlers/src/admin/v1/oauth2_sessions/list.rs @@ -381,5 +381,61 @@ mod tests { } } "#); + + // Test count=false + let request = Request::get("/api/admin/v1/oauth2-sessions?count=false") + .bearer(&token) + .empty(); + let response = state.request(request).await; + response.assert_status(StatusCode::OK); + let body: serde_json::Value = response.json(); + insta::assert_json_snapshot!(body, @r#" + { + "data": [ + { + "type": "oauth2-session", + "id": "01FSHN9AG0MKGTBNZ16RDR3PVY", + "attributes": { + "created_at": "2022-01-16T14:40:00Z", + "finished_at": null, + "user_id": null, + "user_session_id": null, + "client_id": "01FSHN9AG0FAQ50MT1E9FFRPZR", + "scope": "urn:mas:admin", + "user_agent": null, + "last_active_at": null, + "last_active_ip": null, + "human_name": null + }, + "links": { + "self": "/api/admin/v1/oauth2-sessions/01FSHN9AG0MKGTBNZ16RDR3PVY" + } + } + ], + "links": { + "self": "/api/admin/v1/oauth2-sessions?count=false&page[first]=10", + "first": "/api/admin/v1/oauth2-sessions?count=false&page[first]=10", + "last": "/api/admin/v1/oauth2-sessions?count=false&page[last]=10" + } + } + "#); + + // Test count=only + let request = Request::get("/api/admin/v1/oauth2-sessions?count=only") + .bearer(&token) + .empty(); + let response = state.request(request).await; + response.assert_status(StatusCode::OK); + let body: serde_json::Value = response.json(); + insta::assert_json_snapshot!(body, @r#" + { + "meta": { + "count": 1 + }, + "links": { + "self": "/api/admin/v1/oauth2-sessions?count=only" + } + } + "#); } } diff --git a/crates/handlers/src/admin/v1/upstream_oauth_links/list.rs b/crates/handlers/src/admin/v1/upstream_oauth_links/list.rs index 068011ab7..d5c7ee026 100644 --- a/crates/handlers/src/admin/v1/upstream_oauth_links/list.rs +++ b/crates/handlers/src/admin/v1/upstream_oauth_links/list.rs @@ -517,5 +517,155 @@ mod tests { } } "###); + + // Test count=false + let request = Request::get("/api/admin/v1/upstream-oauth-links?count=false") + .bearer(&token) + .empty(); + let response = state.request(request).await; + response.assert_status(StatusCode::OK); + let body: serde_json::Value = response.json(); + assert_json_snapshot!(body, @r###" + { + "data": [ + { + "type": "upstream-oauth-link", + "id": "01FSHN9AG0AQZQP8DX40GD59PW", + "attributes": { + "created_at": "2022-01-16T14:40:00Z", + "provider_id": "01FSHN9AG09NMZYX8MFYH578R9", + "subject": "subject1", + "user_id": "01FSHN9AG0MZAA6S4AF7CTV32E", + "human_account_name": "alice@acme" + }, + "links": { + "self": "/api/admin/v1/upstream-oauth-links/01FSHN9AG0AQZQP8DX40GD59PW" + } + }, + { + "type": "upstream-oauth-link", + "id": "01FSHN9AG0PJZ6DZNTAA1XKPT4", + "attributes": { + "created_at": "2022-01-16T14:40:00Z", + "provider_id": "01FSHN9AG09NMZYX8MFYH578R9", + "subject": "subject3", + "user_id": "01FSHN9AG0AJ6AC5HQ9X6H4RP4", + "human_account_name": "bob@acme" + }, + "links": { + "self": "/api/admin/v1/upstream-oauth-links/01FSHN9AG0PJZ6DZNTAA1XKPT4" + } + }, + { + "type": "upstream-oauth-link", + "id": "01FSHN9AG0QHEHKX2JNQ2A2D07", + "attributes": { + "created_at": "2022-01-16T14:40:00Z", + "provider_id": "01FSHN9AG0KEPHYQQXW9XPTX6Z", + "subject": "subject2", + "user_id": "01FSHN9AG0MZAA6S4AF7CTV32E", + "human_account_name": "alice@example" + }, + "links": { + "self": "/api/admin/v1/upstream-oauth-links/01FSHN9AG0QHEHKX2JNQ2A2D07" + } + } + ], + "links": { + "self": "/api/admin/v1/upstream-oauth-links?count=false&page[first]=10", + "first": "/api/admin/v1/upstream-oauth-links?count=false&page[first]=10", + "last": "/api/admin/v1/upstream-oauth-links?count=false&page[last]=10" + } + } + "###); + + // Test count=only + let request = Request::get("/api/admin/v1/upstream-oauth-links?count=only") + .bearer(&token) + .empty(); + let response = state.request(request).await; + response.assert_status(StatusCode::OK); + let body: serde_json::Value = response.json(); + assert_json_snapshot!(body, @r###" + { + "meta": { + "count": 3 + }, + "links": { + "self": "/api/admin/v1/upstream-oauth-links?count=only" + } + } + "###); + + // Test count=false with filtering + let request = Request::get(format!( + "/api/admin/v1/upstream-oauth-links?count=false&filter[user]={}", + alice.id + )) + .bearer(&token) + .empty(); + let response = state.request(request).await; + response.assert_status(StatusCode::OK); + let body: serde_json::Value = response.json(); + assert_json_snapshot!(body, @r#" + { + "data": [ + { + "type": "upstream-oauth-link", + "id": "01FSHN9AG0AQZQP8DX40GD59PW", + "attributes": { + "created_at": "2022-01-16T14:40:00Z", + "provider_id": "01FSHN9AG09NMZYX8MFYH578R9", + "subject": "subject1", + "user_id": "01FSHN9AG0MZAA6S4AF7CTV32E", + "human_account_name": "alice@acme" + }, + "links": { + "self": "/api/admin/v1/upstream-oauth-links/01FSHN9AG0AQZQP8DX40GD59PW" + } + }, + { + "type": "upstream-oauth-link", + "id": "01FSHN9AG0QHEHKX2JNQ2A2D07", + "attributes": { + "created_at": "2022-01-16T14:40:00Z", + "provider_id": "01FSHN9AG0KEPHYQQXW9XPTX6Z", + "subject": "subject2", + "user_id": "01FSHN9AG0MZAA6S4AF7CTV32E", + "human_account_name": "alice@example" + }, + "links": { + "self": "/api/admin/v1/upstream-oauth-links/01FSHN9AG0QHEHKX2JNQ2A2D07" + } + } + ], + "links": { + "self": "/api/admin/v1/upstream-oauth-links?filter[user]=01FSHN9AG0MZAA6S4AF7CTV32E&count=false&page[first]=10", + "first": "/api/admin/v1/upstream-oauth-links?filter[user]=01FSHN9AG0MZAA6S4AF7CTV32E&count=false&page[first]=10", + "last": "/api/admin/v1/upstream-oauth-links?filter[user]=01FSHN9AG0MZAA6S4AF7CTV32E&count=false&page[last]=10" + } + } + "#); + + // Test count=only with filtering + let request = Request::get(format!( + "/api/admin/v1/upstream-oauth-links?count=only&filter[provider]={}", + provider1.id + )) + .bearer(&token) + .empty(); + let response = state.request(request).await; + response.assert_status(StatusCode::OK); + let body: serde_json::Value = response.json(); + assert_json_snapshot!(body, @r#" + { + "meta": { + "count": 2 + }, + "links": { + "self": "/api/admin/v1/upstream-oauth-links?filter[provider]=01FSHN9AG09NMZYX8MFYH578R9&count=only" + } + } + "#); } } diff --git a/crates/handlers/src/admin/v1/upstream_oauth_providers/list.rs b/crates/handlers/src/admin/v1/upstream_oauth_providers/list.rs index 419c3552f..76e8c4464 100644 --- a/crates/handlers/src/admin/v1/upstream_oauth_providers/list.rs +++ b/crates/handlers/src/admin/v1/upstream_oauth_providers/list.rs @@ -565,4 +565,162 @@ mod tests { let response = state.request(request).await; response.assert_status(StatusCode::BAD_REQUEST); } + + #[sqlx::test(migrator = "mas_storage_pg::MIGRATOR")] + async fn test_count_parameter(pool: PgPool) { + setup(); + let mut state = TestState::from_pool(pool).await.unwrap(); + let admin_token = state.token_with_scope("urn:mas:admin").await; + create_test_providers(&mut state).await; + + // Test count=false + let request = Request::get("/api/admin/v1/upstream-oauth-providers?count=false") + .bearer(&admin_token) + .empty(); + let response = state.request(request).await; + response.assert_status(StatusCode::OK); + let body: serde_json::Value = response.json::(); + + insta::assert_json_snapshot!(body, @r#" + { + "data": [ + { + "type": "upstream-oauth-provider", + "id": "01FSHN9AG07HNEZXNQM2KNBNF6", + "attributes": { + "issuer": "https://appleid.apple.com", + "human_name": "Apple ID", + "brand_name": "apple", + "created_at": "2022-01-16T14:40:00Z", + "disabled_at": "2022-01-16T14:40:00Z" + }, + "links": { + "self": "/api/admin/v1/upstream-oauth-providers/01FSHN9AG07HNEZXNQM2KNBNF6" + } + }, + { + "type": "upstream-oauth-provider", + "id": "01FSHN9AG09AVTNSQFMSR34AJC", + "attributes": { + "issuer": "https://login.microsoftonline.com/common/v2.0", + "human_name": "Microsoft", + "brand_name": "microsoft", + "created_at": "2022-01-16T14:40:00Z", + "disabled_at": null + }, + "links": { + "self": "/api/admin/v1/upstream-oauth-providers/01FSHN9AG09AVTNSQFMSR34AJC" + } + }, + { + "type": "upstream-oauth-provider", + "id": "01FSHN9AG0MZAA6S4AF7CTV32E", + "attributes": { + "issuer": "https://accounts.google.com", + "human_name": "Google", + "brand_name": "google", + "created_at": "2022-01-16T14:40:00Z", + "disabled_at": null + }, + "links": { + "self": "/api/admin/v1/upstream-oauth-providers/01FSHN9AG0MZAA6S4AF7CTV32E" + } + } + ], + "links": { + "self": "/api/admin/v1/upstream-oauth-providers?count=false&page[first]=10", + "first": "/api/admin/v1/upstream-oauth-providers?count=false&page[first]=10", + "last": "/api/admin/v1/upstream-oauth-providers?count=false&page[last]=10" + } + } + "#); + + // Test count=only + let request = Request::get("/api/admin/v1/upstream-oauth-providers?count=only") + .bearer(&admin_token) + .empty(); + let response = state.request(request).await; + response.assert_status(StatusCode::OK); + let body: serde_json::Value = response.json::(); + + insta::assert_json_snapshot!(body, @r#" + { + "meta": { + "count": 3 + }, + "links": { + "self": "/api/admin/v1/upstream-oauth-providers?count=only" + } + } + "#); + + // Test count=false with filtering + let request = + Request::get("/api/admin/v1/upstream-oauth-providers?count=false&filter[enabled]=true") + .bearer(&admin_token) + .empty(); + let response = state.request(request).await; + response.assert_status(StatusCode::OK); + let body: serde_json::Value = response.json::(); + + insta::assert_json_snapshot!(body, @r#" + { + "data": [ + { + "type": "upstream-oauth-provider", + "id": "01FSHN9AG09AVTNSQFMSR34AJC", + "attributes": { + "issuer": "https://login.microsoftonline.com/common/v2.0", + "human_name": "Microsoft", + "brand_name": "microsoft", + "created_at": "2022-01-16T14:40:00Z", + "disabled_at": null + }, + "links": { + "self": "/api/admin/v1/upstream-oauth-providers/01FSHN9AG09AVTNSQFMSR34AJC" + } + }, + { + "type": "upstream-oauth-provider", + "id": "01FSHN9AG0MZAA6S4AF7CTV32E", + "attributes": { + "issuer": "https://accounts.google.com", + "human_name": "Google", + "brand_name": "google", + "created_at": "2022-01-16T14:40:00Z", + "disabled_at": null + }, + "links": { + "self": "/api/admin/v1/upstream-oauth-providers/01FSHN9AG0MZAA6S4AF7CTV32E" + } + } + ], + "links": { + "self": "/api/admin/v1/upstream-oauth-providers?filter[enabled]=true&count=false&page[first]=10", + "first": "/api/admin/v1/upstream-oauth-providers?filter[enabled]=true&count=false&page[first]=10", + "last": "/api/admin/v1/upstream-oauth-providers?filter[enabled]=true&count=false&page[last]=10" + } + } + "#); + + // Test count=only with filtering + let request = + Request::get("/api/admin/v1/upstream-oauth-providers?count=only&filter[enabled]=false") + .bearer(&admin_token) + .empty(); + let response = state.request(request).await; + response.assert_status(StatusCode::OK); + let body: serde_json::Value = response.json::(); + + insta::assert_json_snapshot!(body, @r#" + { + "meta": { + "count": 1 + }, + "links": { + "self": "/api/admin/v1/upstream-oauth-providers?count=only&filter[enabled]=false" + } + } + "#); + } } diff --git a/crates/handlers/src/admin/v1/user_emails/list.rs b/crates/handlers/src/admin/v1/user_emails/list.rs index 2363aba28..034e5214c 100644 --- a/crates/handlers/src/admin/v1/user_emails/list.rs +++ b/crates/handlers/src/admin/v1/user_emails/list.rs @@ -335,5 +335,121 @@ mod tests { } } "###); + + // Test count=false + let request = Request::get("/api/admin/v1/user-emails?count=false") + .bearer(&token) + .empty(); + let response = state.request(request).await; + response.assert_status(StatusCode::OK); + let body: serde_json::Value = response.json(); + insta::assert_json_snapshot!(body, @r###" + { + "data": [ + { + "type": "user-email", + "id": "01FSHN9AG09NMZYX8MFYH578R9", + "attributes": { + "created_at": "2022-01-16T14:40:00Z", + "user_id": "01FSHN9AG0MZAA6S4AF7CTV32E", + "email": "alice@example.com" + }, + "links": { + "self": "/api/admin/v1/user-emails/01FSHN9AG09NMZYX8MFYH578R9" + } + }, + { + "type": "user-email", + "id": "01FSHN9AG0KEPHYQQXW9XPTX6Z", + "attributes": { + "created_at": "2022-01-16T14:40:00Z", + "user_id": "01FSHN9AG0AJ6AC5HQ9X6H4RP4", + "email": "bob@example.com" + }, + "links": { + "self": "/api/admin/v1/user-emails/01FSHN9AG0KEPHYQQXW9XPTX6Z" + } + } + ], + "links": { + "self": "/api/admin/v1/user-emails?count=false&page[first]=10", + "first": "/api/admin/v1/user-emails?count=false&page[first]=10", + "last": "/api/admin/v1/user-emails?count=false&page[last]=10" + } + } + "###); + + // Test count=only + let request = Request::get("/api/admin/v1/user-emails?count=only") + .bearer(&token) + .empty(); + let response = state.request(request).await; + response.assert_status(StatusCode::OK); + let body: serde_json::Value = response.json(); + insta::assert_json_snapshot!(body, @r###" + { + "meta": { + "count": 2 + }, + "links": { + "self": "/api/admin/v1/user-emails?count=only" + } + } + "###); + + // Test count=false with filtering + let request = Request::get(format!( + "/api/admin/v1/user-emails?count=false&filter[user]={}", + alice.id + )) + .bearer(&token) + .empty(); + let response = state.request(request).await; + response.assert_status(StatusCode::OK); + let body: serde_json::Value = response.json(); + insta::assert_json_snapshot!(body, @r#" + { + "data": [ + { + "type": "user-email", + "id": "01FSHN9AG09NMZYX8MFYH578R9", + "attributes": { + "created_at": "2022-01-16T14:40:00Z", + "user_id": "01FSHN9AG0MZAA6S4AF7CTV32E", + "email": "alice@example.com" + }, + "links": { + "self": "/api/admin/v1/user-emails/01FSHN9AG09NMZYX8MFYH578R9" + } + } + ], + "links": { + "self": "/api/admin/v1/user-emails?filter[user]=01FSHN9AG0MZAA6S4AF7CTV32E&count=false&page[first]=10", + "first": "/api/admin/v1/user-emails?filter[user]=01FSHN9AG0MZAA6S4AF7CTV32E&count=false&page[first]=10", + "last": "/api/admin/v1/user-emails?filter[user]=01FSHN9AG0MZAA6S4AF7CTV32E&count=false&page[last]=10" + } + } + "#); + + // Test count=only with filtering + let request = Request::get(format!( + "/api/admin/v1/user-emails?count=only&filter[user]={}", + alice.id + )) + .bearer(&token) + .empty(); + let response = state.request(request).await; + response.assert_status(StatusCode::OK); + let body: serde_json::Value = response.json(); + insta::assert_json_snapshot!(body, @r#" + { + "meta": { + "count": 1 + }, + "links": { + "self": "/api/admin/v1/user-emails?filter[user]=01FSHN9AG0MZAA6S4AF7CTV32E&count=only" + } + } + "#); } } diff --git a/crates/handlers/src/admin/v1/user_registration_tokens/list.rs b/crates/handlers/src/admin/v1/user_registration_tokens/list.rs index f1700aaa7..c99195e5c 100644 --- a/crates/handlers/src/admin/v1/user_registration_tokens/list.rs +++ b/crates/handlers/src/admin/v1/user_registration_tokens/list.rs @@ -1186,4 +1186,207 @@ mod tests { .contains("Invalid filter parameters") ); } + + #[sqlx::test(migrator = "mas_storage_pg::MIGRATOR")] + async fn test_count_parameter(pool: PgPool) { + setup(); + let mut state = TestState::from_pool(pool).await.unwrap(); + let admin_token = state.token_with_scope("urn:mas:admin").await; + create_test_tokens(&mut state).await; + + // Test count=false + let request = Request::get("/api/admin/v1/user-registration-tokens?count=false") + .bearer(&admin_token) + .empty(); + let response = state.request(request).await; + response.assert_status(StatusCode::OK); + let body: serde_json::Value = response.json(); + insta::assert_json_snapshot!(body, @r#" + { + "data": [ + { + "type": "user-registration_token", + "id": "01FSHN9AG064K8BYZXSY5G511Z", + "attributes": { + "token": "token_expired", + "valid": false, + "usage_limit": 5, + "times_used": 0, + "created_at": "2022-01-16T14:40:00Z", + "last_used_at": null, + "expires_at": "2022-01-15T14:40:00Z", + "revoked_at": null + }, + "links": { + "self": "/api/admin/v1/user-registration-tokens/01FSHN9AG064K8BYZXSY5G511Z" + } + }, + { + "type": "user-registration_token", + "id": "01FSHN9AG07HNEZXNQM2KNBNF6", + "attributes": { + "token": "token_used", + "valid": true, + "usage_limit": 10, + "times_used": 1, + "created_at": "2022-01-16T14:40:00Z", + "last_used_at": "2022-01-16T14:40:00Z", + "expires_at": null, + "revoked_at": null + }, + "links": { + "self": "/api/admin/v1/user-registration-tokens/01FSHN9AG07HNEZXNQM2KNBNF6" + } + }, + { + "type": "user-registration_token", + "id": "01FSHN9AG09AVTNSQFMSR34AJC", + "attributes": { + "token": "token_revoked", + "valid": false, + "usage_limit": 10, + "times_used": 0, + "created_at": "2022-01-16T14:40:00Z", + "last_used_at": null, + "expires_at": null, + "revoked_at": "2022-01-16T14:40:00Z" + }, + "links": { + "self": "/api/admin/v1/user-registration-tokens/01FSHN9AG09AVTNSQFMSR34AJC" + } + }, + { + "type": "user-registration_token", + "id": "01FSHN9AG0MZAA6S4AF7CTV32E", + "attributes": { + "token": "token_unused", + "valid": true, + "usage_limit": 10, + "times_used": 0, + "created_at": "2022-01-16T14:40:00Z", + "last_used_at": null, + "expires_at": null, + "revoked_at": null + }, + "links": { + "self": "/api/admin/v1/user-registration-tokens/01FSHN9AG0MZAA6S4AF7CTV32E" + } + }, + { + "type": "user-registration_token", + "id": "01FSHN9AG0S3ZJD8CXQ7F11KXN", + "attributes": { + "token": "token_used_revoked", + "valid": false, + "usage_limit": 10, + "times_used": 1, + "created_at": "2022-01-16T14:40:00Z", + "last_used_at": "2022-01-16T14:40:00Z", + "expires_at": null, + "revoked_at": "2022-01-16T14:40:00Z" + }, + "links": { + "self": "/api/admin/v1/user-registration-tokens/01FSHN9AG0S3ZJD8CXQ7F11KXN" + } + } + ], + "links": { + "self": "/api/admin/v1/user-registration-tokens?count=false&page[first]=10", + "first": "/api/admin/v1/user-registration-tokens?count=false&page[first]=10", + "last": "/api/admin/v1/user-registration-tokens?count=false&page[last]=10" + } + } + "#); + + // Test count=only + let request = Request::get("/api/admin/v1/user-registration-tokens?count=only") + .bearer(&admin_token) + .empty(); + let response = state.request(request).await; + response.assert_status(StatusCode::OK); + let body: serde_json::Value = response.json(); + insta::assert_json_snapshot!(body, @r#" + { + "meta": { + "count": 5 + }, + "links": { + "self": "/api/admin/v1/user-registration-tokens?count=only" + } + } + "#); + + // Test count=false with filtering + let request = + Request::get("/api/admin/v1/user-registration-tokens?count=false&filter[valid]=true") + .bearer(&admin_token) + .empty(); + let response = state.request(request).await; + response.assert_status(StatusCode::OK); + let body: serde_json::Value = response.json(); + insta::assert_json_snapshot!(body, @r#" + { + "data": [ + { + "type": "user-registration_token", + "id": "01FSHN9AG07HNEZXNQM2KNBNF6", + "attributes": { + "token": "token_used", + "valid": true, + "usage_limit": 10, + "times_used": 1, + "created_at": "2022-01-16T14:40:00Z", + "last_used_at": "2022-01-16T14:40:00Z", + "expires_at": null, + "revoked_at": null + }, + "links": { + "self": "/api/admin/v1/user-registration-tokens/01FSHN9AG07HNEZXNQM2KNBNF6" + } + }, + { + "type": "user-registration_token", + "id": "01FSHN9AG0MZAA6S4AF7CTV32E", + "attributes": { + "token": "token_unused", + "valid": true, + "usage_limit": 10, + "times_used": 0, + "created_at": "2022-01-16T14:40:00Z", + "last_used_at": null, + "expires_at": null, + "revoked_at": null + }, + "links": { + "self": "/api/admin/v1/user-registration-tokens/01FSHN9AG0MZAA6S4AF7CTV32E" + } + } + ], + "links": { + "self": "/api/admin/v1/user-registration-tokens?filter[valid]=true&count=false&page[first]=10", + "first": "/api/admin/v1/user-registration-tokens?filter[valid]=true&count=false&page[first]=10", + "last": "/api/admin/v1/user-registration-tokens?filter[valid]=true&count=false&page[last]=10" + } + } + "#); + + // Test count=only with filtering + let request = + Request::get("/api/admin/v1/user-registration-tokens?count=only&filter[revoked]=true") + .bearer(&admin_token) + .empty(); + let response = state.request(request).await; + response.assert_status(StatusCode::OK); + let body: serde_json::Value = response.json(); + insta::assert_json_snapshot!(body, @r#" + { + "meta": { + "count": 2 + }, + "links": { + "self": "/api/admin/v1/user-registration-tokens?filter[revoked]=true&count=only" + } + } + "#); + } } diff --git a/crates/handlers/src/admin/v1/user_sessions/list.rs b/crates/handlers/src/admin/v1/user_sessions/list.rs index 6ba1dc5f0..c00925915 100644 --- a/crates/handlers/src/admin/v1/user_sessions/list.rs +++ b/crates/handlers/src/admin/v1/user_sessions/list.rs @@ -416,5 +416,127 @@ mod tests { } } "###); + + // Test count=false + let request = Request::get("/api/admin/v1/user-sessions?count=false") + .bearer(&token) + .empty(); + let response = state.request(request).await; + response.assert_status(StatusCode::OK); + let body: serde_json::Value = response.json(); + assert_json_snapshot!(body, @r###" + { + "data": [ + { + "type": "user-session", + "id": "01FSHNB5309NMZYX8MFYH578R9", + "attributes": { + "created_at": "2022-01-16T14:41:00Z", + "finished_at": null, + "user_id": "01FSHN9AG0MZAA6S4AF7CTV32E", + "user_agent": null, + "last_active_at": null, + "last_active_ip": null + }, + "links": { + "self": "/api/admin/v1/user-sessions/01FSHNB5309NMZYX8MFYH578R9" + } + }, + { + "type": "user-session", + "id": "01FSHNB530KEPHYQQXW9XPTX6Z", + "attributes": { + "created_at": "2022-01-16T14:41:00Z", + "finished_at": "2022-01-16T14:42:00Z", + "user_id": "01FSHNB530AJ6AC5HQ9X6H4RP4", + "user_agent": null, + "last_active_at": null, + "last_active_ip": null + }, + "links": { + "self": "/api/admin/v1/user-sessions/01FSHNB530KEPHYQQXW9XPTX6Z" + } + } + ], + "links": { + "self": "/api/admin/v1/user-sessions?count=false&page[first]=10", + "first": "/api/admin/v1/user-sessions?count=false&page[first]=10", + "last": "/api/admin/v1/user-sessions?count=false&page[last]=10" + } + } + "###); + + // Test count=only + let request = Request::get("/api/admin/v1/user-sessions?count=only") + .bearer(&token) + .empty(); + let response = state.request(request).await; + response.assert_status(StatusCode::OK); + let body: serde_json::Value = response.json(); + assert_json_snapshot!(body, @r###" + { + "meta": { + "count": 2 + }, + "links": { + "self": "/api/admin/v1/user-sessions?count=only" + } + } + "###); + + // Test count=false with filtering + let request = Request::get(format!( + "/api/admin/v1/user-sessions?count=false&filter[user]={}", + alice.id + )) + .bearer(&token) + .empty(); + let response = state.request(request).await; + response.assert_status(StatusCode::OK); + let body: serde_json::Value = response.json(); + assert_json_snapshot!(body, @r#" + { + "data": [ + { + "type": "user-session", + "id": "01FSHNB5309NMZYX8MFYH578R9", + "attributes": { + "created_at": "2022-01-16T14:41:00Z", + "finished_at": null, + "user_id": "01FSHN9AG0MZAA6S4AF7CTV32E", + "user_agent": null, + "last_active_at": null, + "last_active_ip": null + }, + "links": { + "self": "/api/admin/v1/user-sessions/01FSHNB5309NMZYX8MFYH578R9" + } + } + ], + "links": { + "self": "/api/admin/v1/user-sessions?filter[user]=01FSHN9AG0MZAA6S4AF7CTV32E&count=false&page[first]=10", + "first": "/api/admin/v1/user-sessions?filter[user]=01FSHN9AG0MZAA6S4AF7CTV32E&count=false&page[first]=10", + "last": "/api/admin/v1/user-sessions?filter[user]=01FSHN9AG0MZAA6S4AF7CTV32E&count=false&page[last]=10" + } + } + "#); + + // Test count=only with filtering + let request = Request::get("/api/admin/v1/user-sessions?count=only&filter[status]=active") + .bearer(&token) + .empty(); + let response = state.request(request).await; + response.assert_status(StatusCode::OK); + let body: serde_json::Value = response.json(); + assert_json_snapshot!(body, @r#" + { + "meta": { + "count": 1 + }, + "links": { + "self": "/api/admin/v1/user-sessions?filter[status]=active&count=only" + } + } + "#); } } diff --git a/crates/handlers/src/admin/v1/users/list.rs b/crates/handlers/src/admin/v1/users/list.rs index ddafa9984..8fd778bd2 100644 --- a/crates/handlers/src/admin/v1/users/list.rs +++ b/crates/handlers/src/admin/v1/users/list.rs @@ -204,3 +204,200 @@ pub async fn handler( Ok(Json(response)) } + +#[cfg(test)] +mod tests { + use hyper::{Request, StatusCode}; + use sqlx::PgPool; + + use crate::test_utils::{RequestBuilderExt, ResponseExt, TestState, setup}; + + #[sqlx::test(migrator = "mas_storage_pg::MIGRATOR")] + async fn test_list_users(pool: PgPool) { + setup(); + let mut state = TestState::from_pool(pool).await.unwrap(); + let token = state.token_with_scope("urn:mas:admin").await; + let mut rng = state.rng(); + + // Provision two users + let mut repo = state.repository().await.unwrap(); + repo.user() + .add(&mut rng, &state.clock, "alice".to_owned()) + .await + .unwrap(); + repo.user() + .add(&mut rng, &state.clock, "bob".to_owned()) + .await + .unwrap(); + repo.save().await.unwrap(); + + // Test default behavior (count=true) + let request = Request::get("/api/admin/v1/users").bearer(&token).empty(); + let response = state.request(request).await; + response.assert_status(StatusCode::OK); + let body: serde_json::Value = response.json(); + insta::assert_json_snapshot!(body, @r#" + { + "meta": { + "count": 2 + }, + "data": [ + { + "type": "user", + "id": "01FSHN9AG0AJ6AC5HQ9X6H4RP4", + "attributes": { + "username": "bob", + "created_at": "2022-01-16T14:40:00Z", + "locked_at": null, + "deactivated_at": null, + "admin": false, + "legacy_guest": false + }, + "links": { + "self": "/api/admin/v1/users/01FSHN9AG0AJ6AC5HQ9X6H4RP4" + } + }, + { + "type": "user", + "id": "01FSHN9AG0MZAA6S4AF7CTV32E", + "attributes": { + "username": "alice", + "created_at": "2022-01-16T14:40:00Z", + "locked_at": null, + "deactivated_at": null, + "admin": false, + "legacy_guest": false + }, + "links": { + "self": "/api/admin/v1/users/01FSHN9AG0MZAA6S4AF7CTV32E" + } + } + ], + "links": { + "self": "/api/admin/v1/users?page[first]=10", + "first": "/api/admin/v1/users?page[first]=10", + "last": "/api/admin/v1/users?page[last]=10" + } + } + "#); + + // Test count=false + let request = Request::get("/api/admin/v1/users?count=false") + .bearer(&token) + .empty(); + let response = state.request(request).await; + response.assert_status(StatusCode::OK); + let body: serde_json::Value = response.json(); + insta::assert_json_snapshot!(body, @r###" + { + "data": [ + { + "type": "user", + "id": "01FSHN9AG0AJ6AC5HQ9X6H4RP4", + "attributes": { + "username": "bob", + "created_at": "2022-01-16T14:40:00Z", + "locked_at": null, + "deactivated_at": null, + "admin": false, + "legacy_guest": false + }, + "links": { + "self": "/api/admin/v1/users/01FSHN9AG0AJ6AC5HQ9X6H4RP4" + } + }, + { + "type": "user", + "id": "01FSHN9AG0MZAA6S4AF7CTV32E", + "attributes": { + "username": "alice", + "created_at": "2022-01-16T14:40:00Z", + "locked_at": null, + "deactivated_at": null, + "admin": false, + "legacy_guest": false + }, + "links": { + "self": "/api/admin/v1/users/01FSHN9AG0MZAA6S4AF7CTV32E" + } + } + ], + "links": { + "self": "/api/admin/v1/users?count=false&page[first]=10", + "first": "/api/admin/v1/users?count=false&page[first]=10", + "last": "/api/admin/v1/users?count=false&page[last]=10" + } + } + "###); + + // Test count=only + let request = Request::get("/api/admin/v1/users?count=only") + .bearer(&token) + .empty(); + let response = state.request(request).await; + response.assert_status(StatusCode::OK); + let body: serde_json::Value = response.json(); + insta::assert_json_snapshot!(body, @r###" + { + "meta": { + "count": 2 + }, + "links": { + "self": "/api/admin/v1/users?count=only" + } + } + "###); + + // Test count=false with filtering + let request = Request::get("/api/admin/v1/users?count=false&filter[search]=alice") + .bearer(&token) + .empty(); + let response = state.request(request).await; + response.assert_status(StatusCode::OK); + let body: serde_json::Value = response.json(); + insta::assert_json_snapshot!(body, @r#" + { + "data": [ + { + "type": "user", + "id": "01FSHN9AG0MZAA6S4AF7CTV32E", + "attributes": { + "username": "alice", + "created_at": "2022-01-16T14:40:00Z", + "locked_at": null, + "deactivated_at": null, + "admin": false, + "legacy_guest": false + }, + "links": { + "self": "/api/admin/v1/users/01FSHN9AG0MZAA6S4AF7CTV32E" + } + } + ], + "links": { + "self": "/api/admin/v1/users?filter[search]=alice&count=false&page[first]=10", + "first": "/api/admin/v1/users?filter[search]=alice&count=false&page[first]=10", + "last": "/api/admin/v1/users?filter[search]=alice&count=false&page[last]=10" + } + } + "#); + + // Test count=only with filtering + let request = Request::get("/api/admin/v1/users?count=only&filter[search]=alice") + .bearer(&token) + .empty(); + let response = state.request(request).await; + response.assert_status(StatusCode::OK); + let body: serde_json::Value = response.json(); + insta::assert_json_snapshot!(body, @r#" + { + "meta": { + "count": 1 + }, + "links": { + "self": "/api/admin/v1/users?filter[search]=alice&count=only" + } + } + "#); + } +}