Skip to content

Commit 014f589

Browse files
gpgkd906claude
andcommitted
补完单元测试覆盖率:81 个新测试覆盖 7 个关键文件
domain/action.rs: 38.6% → 100% (19 tests) service/action.rs: 69.5% → 96.3% (26 tests) email/ses.rs: 37.2% → 80.6% (6 tests) middleware/rate_limit.rs: 62.1% → 77.4% (16 tests) cache/mod.rs: 61.5% → 63.8% (8 tests) api/webauthn.rs: 53.5% → 61.0% (5 tests) api/password.rs: 65.9% → 70.9% (5 tests) Overall: 90.12% → 91.05% Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 78dd74f commit 014f589

7 files changed

Lines changed: 1568 additions & 5 deletions

File tree

auth9-core/src/api/password.rs

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,4 +213,43 @@ mod tests {
213213
let response = MessageResponse::new("Test message");
214214
assert_eq!(response.message, "Test message");
215215
}
216+
217+
#[test]
218+
fn test_forgot_password_input_deserialization() {
219+
let json = r#"{"email": "test@example.com"}"#;
220+
let input: ForgotPasswordInput = serde_json::from_str(json).unwrap();
221+
assert_eq!(input.email, "test@example.com");
222+
}
223+
224+
#[test]
225+
fn test_reset_password_input_deserialization() {
226+
let json = r#"{"token": "abc123", "new_password": "NewPass123!"}"#;
227+
let input: ResetPasswordInput = serde_json::from_str(json).unwrap();
228+
assert_eq!(input.token, "abc123");
229+
assert_eq!(input.new_password, "NewPass123!");
230+
}
231+
232+
#[test]
233+
fn test_change_password_input_deserialization() {
234+
let json = r#"{"current_password": "OldPass", "new_password": "NewPass123!"}"#;
235+
let input: ChangePasswordInput = serde_json::from_str(json).unwrap();
236+
assert_eq!(input.current_password, "OldPass");
237+
assert_eq!(input.new_password, "NewPass123!");
238+
}
239+
240+
#[test]
241+
fn test_admin_set_password_input_deserialization() {
242+
let json = r#"{"password": "TempPass123!", "temporary": true}"#;
243+
let input: AdminSetPasswordInput = serde_json::from_str(json).unwrap();
244+
assert_eq!(input.password, "TempPass123!");
245+
assert!(input.temporary);
246+
}
247+
248+
#[test]
249+
fn test_admin_set_password_input_temporary_default() {
250+
let json = r#"{"password": "TempPass123!"}"#;
251+
let input: AdminSetPasswordInput = serde_json::from_str(json).unwrap();
252+
assert_eq!(input.password, "TempPass123!");
253+
assert!(!input.temporary);
254+
}
216255
}

auth9-core/src/api/webauthn.rs

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -302,4 +302,52 @@ mod tests {
302302
let ip = extract_client_ip(&headers);
303303
assert!(ip.is_none());
304304
}
305+
306+
#[test]
307+
fn test_extract_client_ip_xff_with_spaces() {
308+
let mut headers = HeaderMap::new();
309+
headers.insert("x-forwarded-for", " 192.168.1.1 , 10.0.0.1 ".parse().unwrap());
310+
let ip = extract_client_ip(&headers);
311+
assert_eq!(ip, Some("192.168.1.1".to_string()));
312+
}
313+
314+
#[test]
315+
fn test_extract_client_ip_xff_single() {
316+
let mut headers = HeaderMap::new();
317+
headers.insert("x-forwarded-for", "10.0.0.1".parse().unwrap());
318+
let ip = extract_client_ip(&headers);
319+
assert_eq!(ip, Some("10.0.0.1".to_string()));
320+
}
321+
322+
#[test]
323+
fn test_extract_client_ip_prefers_xff_over_real_ip() {
324+
let mut headers = HeaderMap::new();
325+
headers.insert("x-forwarded-for", "1.1.1.1".parse().unwrap());
326+
headers.insert("x-real-ip", "2.2.2.2".parse().unwrap());
327+
let ip = extract_client_ip(&headers);
328+
assert_eq!(ip, Some("1.1.1.1".to_string()));
329+
}
330+
331+
#[test]
332+
fn test_authentication_start_response_fields() {
333+
let response = AuthenticationStartResponse {
334+
challenge_id: "ch-123".to_string(),
335+
public_key: serde_json::json!({"rpId": "localhost"}),
336+
};
337+
assert_eq!(response.challenge_id, "ch-123");
338+
let json = serde_json::to_string(&response).unwrap();
339+
assert!(json.contains("rpId"));
340+
}
341+
342+
#[test]
343+
fn test_authentication_token_response_fields() {
344+
let response = AuthenticationTokenResponse {
345+
access_token: "tok-123".to_string(),
346+
token_type: "Bearer".to_string(),
347+
expires_in: 7200,
348+
};
349+
assert_eq!(response.access_token, "tok-123");
350+
assert_eq!(response.token_type, "Bearer");
351+
assert_eq!(response.expires_in, 7200);
352+
}
305353
}

auth9-core/src/cache/mod.rs

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1336,4 +1336,67 @@ mod tests {
13361336
let result = cache.invalidate_user_roles_for_tenant(user_id, tenant_id).await;
13371337
assert!(result.is_ok());
13381338
}
1339+
1340+
// ========================================================================
1341+
// CacheOperations trait dispatch tests - OIDC and Refresh Token
1342+
// ========================================================================
1343+
1344+
#[tokio::test]
1345+
async fn test_noop_cache_operations_trait_oidc_state() {
1346+
let cache: &dyn CacheOperations = &NoOpCacheManager::new();
1347+
assert!(cache.store_oidc_state("nonce-1", "payload", 300).await.is_ok());
1348+
// NoOp impl stores in-memory, so consuming works
1349+
let result = cache.consume_oidc_state("nonce-1").await.unwrap();
1350+
assert!(result.is_some());
1351+
let second = cache.consume_oidc_state("nonce-1").await.unwrap();
1352+
assert!(second.is_none());
1353+
}
1354+
1355+
#[tokio::test]
1356+
async fn test_noop_cache_operations_trait_refresh_session() {
1357+
let cache: &dyn CacheOperations = &NoOpCacheManager::new();
1358+
assert!(cache.bind_refresh_token_session("rt-1", "sid-1", 300).await.is_ok());
1359+
let result = cache.get_refresh_token_session("rt-1").await.unwrap();
1360+
assert_eq!(result.as_deref(), Some("sid-1"));
1361+
assert!(cache.remove_refresh_token_session("rt-1").await.is_ok());
1362+
let missing = cache.get_refresh_token_session("rt-1").await.unwrap();
1363+
assert!(missing.is_none());
1364+
}
1365+
1366+
#[test]
1367+
fn test_refresh_token_hash_deterministic() {
1368+
let hash1 = NoOpCacheManager::refresh_token_hash("test-token");
1369+
let hash2 = NoOpCacheManager::refresh_token_hash("test-token");
1370+
assert_eq!(hash1, hash2);
1371+
}
1372+
1373+
#[test]
1374+
fn test_refresh_token_hash_different_inputs() {
1375+
let hash1 = NoOpCacheManager::refresh_token_hash("token-a");
1376+
let hash2 = NoOpCacheManager::refresh_token_hash("token-b");
1377+
assert_ne!(hash1, hash2);
1378+
}
1379+
1380+
#[test]
1381+
fn test_cache_manager_refresh_token_hash_deterministic() {
1382+
let hash1 = CacheManager::refresh_token_hash("test-token");
1383+
let hash2 = CacheManager::refresh_token_hash("test-token");
1384+
assert_eq!(hash1, hash2);
1385+
// Both managers should produce same hash
1386+
let noop_hash = NoOpCacheManager::refresh_token_hash("test-token");
1387+
assert_eq!(hash1, noop_hash);
1388+
}
1389+
1390+
#[test]
1391+
fn test_oidc_state_key_format() {
1392+
let key = format!("{}:{}", keys::OIDC_STATE, "nonce-abc");
1393+
assert_eq!(key, "auth9:oidc_state:nonce-abc");
1394+
}
1395+
1396+
#[test]
1397+
fn test_refresh_token_session_key_format() {
1398+
let key = format!("{}:{}", keys::REFRESH_TOKEN_SESSION, "hash-abc");
1399+
assert_eq!(key, "auth9:refresh_session:hash-abc");
1400+
}
1401+
13391402
}

0 commit comments

Comments
 (0)