use super::*;
use crate::storage::GENERIC_CACHE_STORE;
#[tokio::test]
async fn test_store_aaguid_in_cache_success() {
use crate::test_utils::init_test_environment;
init_test_environment().await;
let json = r#"
{
"00000000-0000-0000-0000-000000000000": {
"name": "Test Authenticator",
"icon_dark": "https://example.com/icon-dark.png",
"icon_light": "https://example.com/icon-light.png"
},
"11111111-1111-1111-1111-111111111111": {
"name": "Another Authenticator",
"icon_dark": null,
"icon_light": null
}
}
"#;
let result = store_aaguid_in_cache(json.to_string()).await;
assert!(result.is_ok(), "Failed to store valid JSON: {result:?}");
let info1 = get_authenticator_info("00000000-0000-0000-0000-000000000000").await;
assert!(info1.is_ok(), "Failed to get first AAGUID from cache");
let info1 = info1.unwrap();
assert!(info1.is_some(), "First AAGUID should exist in cache");
let info1 = info1.unwrap();
assert_eq!(info1.name, "Test Authenticator");
assert_eq!(
info1.icon_dark,
Some("https://example.com/icon-dark.png".to_string())
);
let info2 = get_authenticator_info("11111111-1111-1111-1111-111111111111").await;
assert!(info2.is_ok(), "Failed to get second AAGUID from cache");
let info2 = info2.unwrap();
assert!(info2.is_some(), "Second AAGUID should exist in cache");
let info2 = info2.unwrap();
assert_eq!(info2.name, "Another Authenticator");
assert_eq!(info2.icon_dark, None);
}
#[tokio::test]
async fn test_store_aaguid_in_cache_invalid_json() {
use crate::test_utils::init_test_environment;
init_test_environment().await;
let invalid_json = r#"
{
"00000000-0000-0000-0000-000000000000": {
"name": "Test Authenticator",
"icon_dark": "https://example.com/icon-dark.png",
"icon_light": "https://example.com/icon-light.png",
}
}
"#;
let result = store_aaguid_in_cache(invalid_json.to_string()).await;
assert!(result.is_err(), "Expected error for invalid JSON");
if let Err(PasskeyError::Storage(msg)) = result {
assert!(
msg.contains("expected") || msg.contains("trailing comma"),
"Error message should indicate JSON parsing issue: {msg}"
);
} else {
panic!("Expected PasskeyError::Storage");
}
}
#[test]
fn test_authenticator_info_parsing() {
let json = r#"
{
"name": "Test Authenticator",
"icon_dark": "https://example.com/icon-dark.png",
"icon_light": "https://example.com/icon-light.png"
}
"#;
let info: Result<AuthenticatorInfo, _> = serde_json::from_str(json);
assert!(info.is_ok());
let info = info.unwrap();
assert_eq!(info.name, "Test Authenticator");
assert_eq!(
info.icon_dark,
Some("https://example.com/icon-dark.png".to_string())
);
assert_eq!(
info.icon_light,
Some("https://example.com/icon-light.png".to_string())
);
}
#[test]
fn test_authenticator_info_parsing_null_icons() {
let json = r#"
{
"name": "Test Authenticator",
"icon_dark": null,
"icon_light": null
}
"#;
let info: Result<AuthenticatorInfo, _> = serde_json::from_str(json);
assert!(info.is_ok());
let info = info.unwrap();
assert_eq!(info.name, "Test Authenticator");
assert_eq!(info.icon_dark, None);
assert_eq!(info.icon_light, None);
}
#[test]
fn test_authenticator_info_parsing_missing_fields() {
let json = r#"
{
"icon_dark": "https://example.com/icon-dark.png",
"icon_light": "https://example.com/icon-light.png"
}
"#;
let info: Result<AuthenticatorInfo, _> = serde_json::from_str(json);
assert!(
info.is_err(),
"Should fail when required 'name' field is missing"
);
}
#[test]
fn test_aaguid_format_validation() {
let valid_aaguid = "00000000-0000-0000-0000-000000000000";
assert_eq!(valid_aaguid.len(), 36);
assert!(valid_aaguid.chars().filter(|&c| c == '-').count() == 4);
let invalid_aaguid = "invalid-aaguid-format";
assert_ne!(invalid_aaguid.len(), 36);
}
#[tokio::test]
async fn test_get_authenticator_info_not_found() {
use crate::test_utils::init_test_environment;
init_test_environment().await;
let non_existent_aaguid = "99999999-9999-9999-9999-999999999999";
let result = get_authenticator_info(non_existent_aaguid).await;
assert!(
result.is_ok(),
"Should handle non-existent AAGUID gracefully"
);
assert!(
result.unwrap().is_none(),
"Should return None for non-existent AAGUID"
);
}
#[tokio::test]
async fn test_get_authenticator_info_batch_empty() {
use crate::test_utils::init_test_environment;
init_test_environment().await;
let empty_aaguids: Vec<String> = vec![];
let result = get_authenticator_info_batch(&empty_aaguids).await;
assert!(result.is_ok());
let info_map = result.unwrap();
assert!(info_map.is_empty());
}
#[tokio::test]
async fn test_get_authenticator_info_success() {
use crate::test_utils::init_test_environment;
init_test_environment().await;
let json = r#"
{
"12345678-1234-1234-1234-123456789abc": {
"name": "YubiKey 5",
"icon_dark": "https://example.com/yubikey-dark.png",
"icon_light": "https://example.com/yubikey-light.png"
}
}
"#;
let store_result = store_aaguid_in_cache(json.to_string()).await;
assert!(store_result.is_ok(), "Failed to store test data");
let aaguid = "12345678-1234-1234-1234-123456789abc";
let result = get_authenticator_info(aaguid).await;
assert!(result.is_ok(), "Failed to retrieve stored AAGUID");
let info = result.unwrap();
assert!(info.is_some(), "Should find the stored AAGUID");
let info = info.unwrap();
assert_eq!(info.name, "YubiKey 5");
assert_eq!(
info.icon_dark,
Some("https://example.com/yubikey-dark.png".to_string())
);
assert_eq!(
info.icon_light,
Some("https://example.com/yubikey-light.png".to_string())
);
}
#[tokio::test]
async fn test_get_authenticator_info_batch_with_data() {
use crate::test_utils::init_test_environment;
init_test_environment().await;
let json = r#"
{
"aaaa0000-bbbb-cccc-dddd-eeeeeeeeeeee": {
"name": "Authenticator A",
"icon_dark": "https://example.com/a-dark.png",
"icon_light": null
},
"bbbb1111-cccc-dddd-eeee-ffffffffffff": {
"name": "Authenticator B",
"icon_dark": null,
"icon_light": "https://example.com/b-light.png"
},
"cccc2222-dddd-eeee-ffff-000000000000": {
"name": "Authenticator C",
"icon_dark": null,
"icon_light": null
}
}
"#;
let store_result = store_aaguid_in_cache(json.to_string()).await;
assert!(store_result.is_ok(), "Failed to store test data");
let aaguids = vec![
"aaaa0000-bbbb-cccc-dddd-eeeeeeeeeeee".to_string(),
"bbbb1111-cccc-dddd-eeee-ffffffffffff".to_string(),
"nonexistent-aaguid-here".to_string(), "cccc2222-dddd-eeee-ffff-000000000000".to_string(),
];
let result = get_authenticator_info_batch(&aaguids).await;
assert!(result.is_ok(), "Batch retrieval should succeed");
let info_map = result.unwrap();
assert_eq!(
info_map.len(),
3,
"Should return 3 existing AAGUIDs, ignore non-existent"
);
let info_a = info_map
.get("aaaa0000-bbbb-cccc-dddd-eeeeeeeeeeee")
.unwrap();
assert_eq!(info_a.name, "Authenticator A");
assert_eq!(
info_a.icon_dark,
Some("https://example.com/a-dark.png".to_string())
);
assert_eq!(info_a.icon_light, None);
let info_b = info_map
.get("bbbb1111-cccc-dddd-eeee-ffffffffffff")
.unwrap();
assert_eq!(info_b.name, "Authenticator B");
assert_eq!(info_b.icon_dark, None);
assert_eq!(
info_b.icon_light,
Some("https://example.com/b-light.png".to_string())
);
let info_c = info_map
.get("cccc2222-dddd-eeee-ffff-000000000000")
.unwrap();
assert_eq!(info_c.name, "Authenticator C");
assert_eq!(info_c.icon_dark, None);
assert_eq!(info_c.icon_light, None);
assert!(!info_map.contains_key("nonexistent-aaguid-here"));
}
#[tokio::test]
async fn test_get_authenticator_info_corrupted_cache() {
use crate::storage::CacheData;
use crate::test_utils::init_test_environment;
init_test_environment().await;
let aaguid = "corrupt-data-test-aaguid";
let corrupted_data = CacheData {
value: "invalid json data".to_string(),
};
let mut cache = GENERIC_CACHE_STORE.lock().await;
let cache_prefix = CachePrefix::new("aaguid".to_string()).unwrap();
let cache_key = CacheKey::new(aaguid.to_string()).unwrap();
let put_result = cache.put(cache_prefix, cache_key, corrupted_data).await;
assert!(put_result.is_ok(), "Should be able to put corrupted data");
drop(cache);
let result = get_authenticator_info(aaguid).await;
assert!(
result.is_err(),
"Should return error for corrupted cache data"
);
if let Err(PasskeyError::Storage(msg)) = result {
assert!(
msg.contains("expected") || msg.contains("EOF") || msg.contains("invalid"),
"Error should indicate JSON parsing issue: {msg}"
);
} else {
panic!("Expected PasskeyError::Storage for corrupted data");
}
}
#[tokio::test]
async fn test_store_aaguid_in_cache_empty_object() {
use crate::test_utils::init_test_environment;
init_test_environment().await;
let empty_json = "{}";
let result = store_aaguid_in_cache(empty_json.to_string()).await;
assert!(result.is_ok(), "Empty JSON object should be valid");
let non_existent = get_authenticator_info("any-aaguid").await;
assert!(non_existent.is_ok());
assert!(
non_existent.unwrap().is_none(),
"No AAGUIDs should be stored from empty object"
);
}
#[tokio::test]
async fn test_get_authenticator_info_batch_duplicates() {
use crate::test_utils::init_test_environment;
init_test_environment().await;
let json = r#"
{
"duplicate-test-aaguid": {
"name": "Duplicate Test",
"icon_dark": null,
"icon_light": null
}
}
"#;
let store_result = store_aaguid_in_cache(json.to_string()).await;
assert!(store_result.is_ok(), "Failed to store test data");
let aaguids = vec![
"duplicate-test-aaguid".to_string(),
"duplicate-test-aaguid".to_string(), "nonexistent".to_string(),
"duplicate-test-aaguid".to_string(), ];
let result = get_authenticator_info_batch(&aaguids).await;
assert!(
result.is_ok(),
"Batch retrieval with duplicates should succeed"
);
let info_map = result.unwrap();
assert_eq!(
info_map.len(),
1,
"Should have only one unique result despite duplicates"
);
let info = info_map.get("duplicate-test-aaguid").unwrap();
assert_eq!(info.name, "Duplicate Test");
}