re-uploading work
This commit is contained in:
884
crates/common/tests/key_repository_tests.rs
Normal file
884
crates/common/tests/key_repository_tests.rs
Normal file
@@ -0,0 +1,884 @@
|
||||
//! Integration tests for Key repository
|
||||
//!
|
||||
//! These tests verify CRUD operations, owner validation, encryption handling,
|
||||
//! and constraints for the Key repository.
|
||||
|
||||
mod helpers;
|
||||
|
||||
use attune_common::{
|
||||
models::enums::OwnerType,
|
||||
repositories::{
|
||||
key::{CreateKeyInput, KeyRepository, UpdateKeyInput},
|
||||
Create, Delete, FindById, List, Update,
|
||||
},
|
||||
Error,
|
||||
};
|
||||
use helpers::*;
|
||||
|
||||
// ============================================================================
|
||||
// CREATE Tests - System Owner
|
||||
// ============================================================================
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_create_key_system_owner() {
|
||||
let pool = create_test_pool().await.unwrap();
|
||||
|
||||
let key = KeyFixture::new_system_unique("system_key", "test_value")
|
||||
.create(&pool)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert!(key.id > 0);
|
||||
assert_eq!(key.owner_type, OwnerType::System);
|
||||
assert_eq!(key.owner, Some("system".to_string()));
|
||||
assert_eq!(key.owner_identity, None);
|
||||
assert_eq!(key.owner_pack, None);
|
||||
assert_eq!(key.owner_action, None);
|
||||
assert_eq!(key.owner_sensor, None);
|
||||
assert_eq!(key.encrypted, false);
|
||||
assert_eq!(key.value, "test_value");
|
||||
assert!(key.created.timestamp() > 0);
|
||||
assert!(key.updated.timestamp() > 0);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_create_key_system_encrypted() {
|
||||
let pool = create_test_pool().await.unwrap();
|
||||
|
||||
let key = KeyFixture::new_system_unique("encrypted_key", "encrypted_value")
|
||||
.with_encrypted(true)
|
||||
.with_encryption_key_hash("sha256:abc123")
|
||||
.create(&pool)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(key.encrypted, true);
|
||||
assert_eq!(key.encryption_key_hash, Some("sha256:abc123".to_string()));
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// CREATE Tests - Identity Owner
|
||||
// ============================================================================
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_create_key_identity_owner() {
|
||||
let pool = create_test_pool().await.unwrap();
|
||||
|
||||
// Create an identity first
|
||||
let identity = IdentityFixture::new_unique("testuser")
|
||||
.create(&pool)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let key = KeyFixture::new_identity_unique(identity.id, "api_key", "secret_token")
|
||||
.create(&pool)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(key.owner_type, OwnerType::Identity);
|
||||
assert_eq!(key.owner, Some(identity.id.to_string()));
|
||||
assert_eq!(key.owner_identity, Some(identity.id));
|
||||
assert_eq!(key.owner_pack, None);
|
||||
assert_eq!(key.value, "secret_token");
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// CREATE Tests - Pack Owner
|
||||
// ============================================================================
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_create_key_pack_owner() {
|
||||
let pool = create_test_pool().await.unwrap();
|
||||
|
||||
let pack = PackFixture::new_unique("testpack")
|
||||
.create(&pool)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let key = KeyFixture::new_pack_unique(pack.id, &pack.r#ref, "config_key", "config_value")
|
||||
.create(&pool)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(key.owner_type, OwnerType::Pack);
|
||||
assert_eq!(key.owner, Some(pack.id.to_string()));
|
||||
assert_eq!(key.owner_pack, Some(pack.id));
|
||||
assert_eq!(key.owner_pack_ref, Some(pack.r#ref.clone()));
|
||||
assert_eq!(key.value, "config_value");
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// CREATE Tests - Constraints
|
||||
// ============================================================================
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_create_key_duplicate_ref_fails() {
|
||||
let pool = create_test_pool().await.unwrap();
|
||||
|
||||
let key_ref = format!("duplicate_key_{}", unique_test_id());
|
||||
|
||||
// Create first key
|
||||
let input = CreateKeyInput {
|
||||
r#ref: key_ref.clone(),
|
||||
owner_type: OwnerType::System,
|
||||
owner: Some("system".to_string()),
|
||||
owner_identity: None,
|
||||
owner_pack: None,
|
||||
owner_pack_ref: None,
|
||||
owner_action: None,
|
||||
owner_action_ref: None,
|
||||
owner_sensor: None,
|
||||
owner_sensor_ref: None,
|
||||
name: key_ref.clone(),
|
||||
encrypted: false,
|
||||
encryption_key_hash: None,
|
||||
value: "value1".to_string(),
|
||||
};
|
||||
|
||||
KeyRepository::create(&pool, input.clone()).await.unwrap();
|
||||
|
||||
// Try to create duplicate
|
||||
let result = KeyRepository::create(&pool, input).await;
|
||||
assert!(result.is_err());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_create_key_system_with_owner_fields_fails() {
|
||||
let pool = create_test_pool().await.unwrap();
|
||||
|
||||
// Create an identity
|
||||
let identity = IdentityFixture::new_unique("testuser")
|
||||
.create(&pool)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// Try to create system key with owner_identity set (should fail)
|
||||
let input = CreateKeyInput {
|
||||
r#ref: format!("invalid_key_{}", unique_test_id()),
|
||||
owner_type: OwnerType::System,
|
||||
owner: Some("system".to_string()),
|
||||
owner_identity: Some(identity.id), // This should cause failure
|
||||
owner_pack: None,
|
||||
owner_pack_ref: None,
|
||||
owner_action: None,
|
||||
owner_action_ref: None,
|
||||
owner_sensor: None,
|
||||
owner_sensor_ref: None,
|
||||
name: "invalid".to_string(),
|
||||
encrypted: false,
|
||||
encryption_key_hash: None,
|
||||
value: "value".to_string(),
|
||||
};
|
||||
|
||||
let result = KeyRepository::create(&pool, input).await;
|
||||
assert!(result.is_err());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_create_key_identity_without_owner_id_fails() {
|
||||
let pool = create_test_pool().await.unwrap();
|
||||
|
||||
// Try to create identity key without owner_identity set
|
||||
let input = CreateKeyInput {
|
||||
r#ref: format!("invalid_key_{}", unique_test_id()),
|
||||
owner_type: OwnerType::Identity,
|
||||
owner: None,
|
||||
owner_identity: None, // Missing required field
|
||||
owner_pack: None,
|
||||
owner_pack_ref: None,
|
||||
owner_action: None,
|
||||
owner_action_ref: None,
|
||||
owner_sensor: None,
|
||||
owner_sensor_ref: None,
|
||||
name: "invalid".to_string(),
|
||||
encrypted: false,
|
||||
encryption_key_hash: None,
|
||||
value: "value".to_string(),
|
||||
};
|
||||
|
||||
let result = KeyRepository::create(&pool, input).await;
|
||||
assert!(result.is_err());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_create_key_multiple_owners_fails() {
|
||||
let pool = create_test_pool().await.unwrap();
|
||||
|
||||
let identity = IdentityFixture::new_unique("testuser")
|
||||
.create(&pool)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let pack = PackFixture::new_unique("testpack")
|
||||
.create(&pool)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// Try to create key with both identity and pack owners (should fail)
|
||||
let input = CreateKeyInput {
|
||||
r#ref: format!("invalid_key_{}", unique_test_id()),
|
||||
owner_type: OwnerType::Identity,
|
||||
owner: None,
|
||||
owner_identity: Some(identity.id),
|
||||
owner_pack: Some(pack.id), // Can't have multiple owners
|
||||
owner_pack_ref: None,
|
||||
owner_action: None,
|
||||
owner_action_ref: None,
|
||||
owner_sensor: None,
|
||||
owner_sensor_ref: None,
|
||||
name: "invalid".to_string(),
|
||||
encrypted: false,
|
||||
encryption_key_hash: None,
|
||||
value: "value".to_string(),
|
||||
};
|
||||
|
||||
let result = KeyRepository::create(&pool, input).await;
|
||||
assert!(result.is_err());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_create_key_invalid_ref_format_fails() {
|
||||
let pool = create_test_pool().await.unwrap();
|
||||
|
||||
// Try uppercase ref (should fail CHECK constraint)
|
||||
let input = CreateKeyInput {
|
||||
r#ref: "UPPERCASE_KEY".to_string(),
|
||||
owner_type: OwnerType::System,
|
||||
owner: Some("system".to_string()),
|
||||
owner_identity: None,
|
||||
owner_pack: None,
|
||||
owner_pack_ref: None,
|
||||
owner_action: None,
|
||||
owner_action_ref: None,
|
||||
owner_sensor: None,
|
||||
owner_sensor_ref: None,
|
||||
name: "uppercase".to_string(),
|
||||
encrypted: false,
|
||||
encryption_key_hash: None,
|
||||
value: "value".to_string(),
|
||||
};
|
||||
|
||||
let result = KeyRepository::create(&pool, input).await;
|
||||
assert!(result.is_err());
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// READ Tests
|
||||
// ============================================================================
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_find_by_id_exists() {
|
||||
let pool = create_test_pool().await.unwrap();
|
||||
|
||||
let key = KeyFixture::new_system_unique("find_key", "value")
|
||||
.create(&pool)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let found = KeyRepository::find_by_id(&pool, key.id).await.unwrap();
|
||||
|
||||
assert!(found.is_some());
|
||||
let found = found.unwrap();
|
||||
assert_eq!(found.id, key.id);
|
||||
assert_eq!(found.r#ref, key.r#ref);
|
||||
assert_eq!(found.value, key.value);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_find_by_id_not_exists() {
|
||||
let pool = create_test_pool().await.unwrap();
|
||||
|
||||
let result = KeyRepository::find_by_id(&pool, 99999).await.unwrap();
|
||||
assert!(result.is_none());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_get_by_id_exists() {
|
||||
let pool = create_test_pool().await.unwrap();
|
||||
|
||||
let key = KeyFixture::new_system_unique("get_key", "value")
|
||||
.create(&pool)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let found = KeyRepository::get_by_id(&pool, key.id).await.unwrap();
|
||||
|
||||
assert_eq!(found.id, key.id);
|
||||
assert_eq!(found.r#ref, key.r#ref);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_get_by_id_not_exists_fails() {
|
||||
let pool = create_test_pool().await.unwrap();
|
||||
|
||||
let result = KeyRepository::get_by_id(&pool, 99999).await;
|
||||
assert!(result.is_err());
|
||||
assert!(matches!(result.unwrap_err(), Error::NotFound { .. }));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_find_by_ref_exists() {
|
||||
let pool = create_test_pool().await.unwrap();
|
||||
|
||||
let key = KeyFixture::new_system_unique("ref_key", "value")
|
||||
.create(&pool)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let found = KeyRepository::find_by_ref(&pool, &key.r#ref).await.unwrap();
|
||||
|
||||
assert!(found.is_some());
|
||||
let found = found.unwrap();
|
||||
assert_eq!(found.id, key.id);
|
||||
assert_eq!(found.r#ref, key.r#ref);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_find_by_ref_not_exists() {
|
||||
let pool = create_test_pool().await.unwrap();
|
||||
|
||||
let result = KeyRepository::find_by_ref(&pool, "nonexistent_key")
|
||||
.await
|
||||
.unwrap();
|
||||
assert!(result.is_none());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_list_all_keys() {
|
||||
let pool = create_test_pool().await.unwrap();
|
||||
|
||||
// Create multiple keys
|
||||
let key1 = KeyFixture::new_system_unique("list_key_a", "value1")
|
||||
.create(&pool)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let key2 = KeyFixture::new_system_unique("list_key_b", "value2")
|
||||
.create(&pool)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let keys = KeyRepository::list(&pool).await.unwrap();
|
||||
|
||||
// Should have at least our 2 keys (may have more from parallel tests)
|
||||
assert!(keys.len() >= 2);
|
||||
|
||||
// Verify our keys are in the list
|
||||
assert!(keys.iter().any(|k| k.id == key1.id));
|
||||
assert!(keys.iter().any(|k| k.id == key2.id));
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// UPDATE Tests
|
||||
// ============================================================================
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_update_value() {
|
||||
let pool = create_test_pool().await.unwrap();
|
||||
|
||||
let key = KeyFixture::new_system_unique("update_key", "original_value")
|
||||
.create(&pool)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let original_updated = key.updated;
|
||||
|
||||
// Small delay to ensure updated timestamp changes
|
||||
tokio::time::sleep(tokio::time::Duration::from_millis(10)).await;
|
||||
|
||||
let input = UpdateKeyInput {
|
||||
value: Some("new_value".to_string()),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let updated = KeyRepository::update(&pool, key.id, input).await.unwrap();
|
||||
|
||||
assert_eq!(updated.value, "new_value");
|
||||
assert!(updated.updated > original_updated);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_update_name() {
|
||||
let pool = create_test_pool().await.unwrap();
|
||||
|
||||
let key = KeyFixture::new_system_unique("update_name_key", "value")
|
||||
.create(&pool)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// Use a unique name to avoid conflicts with parallel tests
|
||||
let new_name = format!("new_name_{}", unique_test_id());
|
||||
let input = UpdateKeyInput {
|
||||
name: Some(new_name.clone()),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let updated = KeyRepository::update(&pool, key.id, input).await.unwrap();
|
||||
|
||||
assert_eq!(updated.name, new_name);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_update_encrypted_status() {
|
||||
let pool = create_test_pool().await.unwrap();
|
||||
|
||||
let key = KeyFixture::new_system_unique("encrypt_key", "plain_value")
|
||||
.create(&pool)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(key.encrypted, false);
|
||||
|
||||
let input = UpdateKeyInput {
|
||||
encrypted: Some(true),
|
||||
encryption_key_hash: Some("sha256:xyz789".to_string()),
|
||||
value: Some("encrypted_value".to_string()),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let updated = KeyRepository::update(&pool, key.id, input).await.unwrap();
|
||||
|
||||
assert_eq!(updated.encrypted, true);
|
||||
assert_eq!(
|
||||
updated.encryption_key_hash,
|
||||
Some("sha256:xyz789".to_string())
|
||||
);
|
||||
assert_eq!(updated.value, "encrypted_value");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_update_multiple_fields() {
|
||||
let pool = create_test_pool().await.unwrap();
|
||||
|
||||
let key = KeyFixture::new_system_unique("multi_update_key", "value")
|
||||
.create(&pool)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// Use a unique name to avoid conflicts with parallel tests
|
||||
let new_name = format!("updated_name_{}", unique_test_id());
|
||||
let input = UpdateKeyInput {
|
||||
name: Some(new_name.clone()),
|
||||
value: Some("updated_value".to_string()),
|
||||
encrypted: Some(true),
|
||||
encryption_key_hash: Some("hash123".to_string()),
|
||||
};
|
||||
|
||||
let updated = KeyRepository::update(&pool, key.id, input).await.unwrap();
|
||||
|
||||
assert_eq!(updated.name, new_name);
|
||||
assert_eq!(updated.value, "updated_value");
|
||||
assert_eq!(updated.encrypted, true);
|
||||
assert_eq!(updated.encryption_key_hash, Some("hash123".to_string()));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_update_no_changes() {
|
||||
let pool = create_test_pool().await.unwrap();
|
||||
|
||||
let key = KeyFixture::new_system_unique("nochange_key", "value")
|
||||
.create(&pool)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let original_updated = key.updated;
|
||||
|
||||
let input = UpdateKeyInput::default();
|
||||
|
||||
let updated = KeyRepository::update(&pool, key.id, input).await.unwrap();
|
||||
|
||||
assert_eq!(updated.id, key.id);
|
||||
assert_eq!(updated.name, key.name);
|
||||
assert_eq!(updated.value, key.value);
|
||||
// Updated timestamp should not change when no fields are updated
|
||||
assert_eq!(updated.updated, original_updated);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_update_nonexistent_key_fails() {
|
||||
let pool = create_test_pool().await.unwrap();
|
||||
|
||||
let input = UpdateKeyInput {
|
||||
value: Some("new_value".to_string()),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let result = KeyRepository::update(&pool, 99999, input).await;
|
||||
assert!(result.is_err());
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// DELETE Tests
|
||||
// ============================================================================
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_delete_existing_key() {
|
||||
let pool = create_test_pool().await.unwrap();
|
||||
|
||||
let key = KeyFixture::new_system_unique("delete_key", "value")
|
||||
.create(&pool)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let deleted = KeyRepository::delete(&pool, key.id).await.unwrap();
|
||||
assert!(deleted);
|
||||
|
||||
// Verify key is gone
|
||||
let result = KeyRepository::find_by_id(&pool, key.id).await.unwrap();
|
||||
assert!(result.is_none());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_delete_nonexistent_key() {
|
||||
let pool = create_test_pool().await.unwrap();
|
||||
|
||||
let deleted = KeyRepository::delete(&pool, 99999).await.unwrap();
|
||||
assert!(!deleted);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_delete_key_when_identity_deleted() {
|
||||
let pool = create_test_pool().await.unwrap();
|
||||
|
||||
let identity = IdentityFixture::new_unique("deleteuser")
|
||||
.create(&pool)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let key = KeyFixture::new_identity_unique(identity.id, "user_key", "value")
|
||||
.create(&pool)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// Delete the identity - this will fail because key references it
|
||||
use attune_common::repositories::{identity::IdentityRepository, Delete as _};
|
||||
let delete_result = IdentityRepository::delete(&pool, identity.id).await;
|
||||
|
||||
// Should fail due to foreign key constraint (no CASCADE on key table)
|
||||
assert!(delete_result.is_err());
|
||||
|
||||
// Key should still exist
|
||||
let result = KeyRepository::find_by_id(&pool, key.id).await.unwrap();
|
||||
assert!(result.is_some());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_delete_key_when_pack_deleted() {
|
||||
let pool = create_test_pool().await.unwrap();
|
||||
|
||||
let pack = PackFixture::new_unique("deletepack")
|
||||
.create(&pool)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let key = KeyFixture::new_pack_unique(pack.id, &pack.r#ref, "pack_key", "value")
|
||||
.create(&pool)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// Delete the pack - this will fail because key references it
|
||||
use attune_common::repositories::{pack::PackRepository, Delete as _};
|
||||
let delete_result = PackRepository::delete(&pool, pack.id).await;
|
||||
|
||||
// Should fail due to foreign key constraint (no CASCADE on key table)
|
||||
assert!(delete_result.is_err());
|
||||
|
||||
// Key should still exist
|
||||
let result = KeyRepository::find_by_id(&pool, key.id).await.unwrap();
|
||||
assert!(result.is_some());
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Specialized Query Tests
|
||||
// ============================================================================
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_find_by_owner_type_system() {
|
||||
let pool = create_test_pool().await.unwrap();
|
||||
|
||||
let _key1 = KeyFixture::new_system_unique("sys_key1", "value1")
|
||||
.create(&pool)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let _key2 = KeyFixture::new_system_unique("sys_key2", "value2")
|
||||
.create(&pool)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let keys = KeyRepository::find_by_owner_type(&pool, OwnerType::System)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// Should have at least our 2 system keys
|
||||
assert!(keys.len() >= 2);
|
||||
assert!(keys.iter().all(|k| k.owner_type == OwnerType::System));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_find_by_owner_type_identity() {
|
||||
let pool = create_test_pool().await.unwrap();
|
||||
|
||||
let identity1 = IdentityFixture::new_unique("user1")
|
||||
.create(&pool)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let identity2 = IdentityFixture::new_unique("user2")
|
||||
.create(&pool)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let key1 = KeyFixture::new_identity_unique(identity1.id, "key1", "value1")
|
||||
.create(&pool)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let key2 = KeyFixture::new_identity_unique(identity2.id, "key2", "value2")
|
||||
.create(&pool)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let keys = KeyRepository::find_by_owner_type(&pool, OwnerType::Identity)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// Should contain our identity keys
|
||||
assert!(keys.iter().any(|k| k.id == key1.id));
|
||||
assert!(keys.iter().any(|k| k.id == key2.id));
|
||||
assert!(keys.iter().all(|k| k.owner_type == OwnerType::Identity));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_find_by_owner_type_pack() {
|
||||
let pool = create_test_pool().await.unwrap();
|
||||
|
||||
let pack = PackFixture::new_unique("ownerpack")
|
||||
.create(&pool)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let key1 = KeyFixture::new_pack_unique(pack.id, &pack.r#ref, "pack_key1", "value1")
|
||||
.create(&pool)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let key2 = KeyFixture::new_pack_unique(pack.id, &pack.r#ref, "pack_key2", "value2")
|
||||
.create(&pool)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let keys = KeyRepository::find_by_owner_type(&pool, OwnerType::Pack)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// Should contain our pack keys
|
||||
assert!(keys.iter().any(|k| k.id == key1.id));
|
||||
assert!(keys.iter().any(|k| k.id == key2.id));
|
||||
assert!(keys.iter().all(|k| k.owner_type == OwnerType::Pack));
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Timestamp Tests
|
||||
// ============================================================================
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_created_timestamp_set_automatically() {
|
||||
let pool = create_test_pool().await.unwrap();
|
||||
|
||||
let before = chrono::Utc::now();
|
||||
|
||||
let key = KeyFixture::new_system_unique("timestamp_key", "value")
|
||||
.create(&pool)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let after = chrono::Utc::now();
|
||||
|
||||
assert!(key.created >= before);
|
||||
assert!(key.created <= after);
|
||||
assert_eq!(key.created, key.updated); // Should be equal on creation
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_updated_timestamp_changes_on_update() {
|
||||
let pool = create_test_pool().await.unwrap();
|
||||
|
||||
let key = KeyFixture::new_system_unique("update_time_key", "value")
|
||||
.create(&pool)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let original_updated = key.updated;
|
||||
|
||||
// Small delay to ensure timestamp changes
|
||||
tokio::time::sleep(tokio::time::Duration::from_millis(10)).await;
|
||||
|
||||
let input = UpdateKeyInput {
|
||||
value: Some("new_value".to_string()),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let updated = KeyRepository::update(&pool, key.id, input).await.unwrap();
|
||||
|
||||
assert!(updated.updated > original_updated);
|
||||
assert_eq!(updated.created, key.created); // Created should not change
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_updated_timestamp_unchanged_on_read() {
|
||||
let pool = create_test_pool().await.unwrap();
|
||||
|
||||
let key = KeyFixture::new_system_unique("read_time_key", "value")
|
||||
.create(&pool)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let original_updated = key.updated;
|
||||
|
||||
// Small delay
|
||||
tokio::time::sleep(tokio::time::Duration::from_millis(10)).await;
|
||||
|
||||
// Read the key
|
||||
let found = KeyRepository::find_by_id(&pool, key.id)
|
||||
.await
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(found.updated, original_updated); // Should not change
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Encryption Tests
|
||||
// ============================================================================
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_key_encrypted_flag() {
|
||||
let pool = create_test_pool().await.unwrap();
|
||||
|
||||
let plain_key = KeyFixture::new_system_unique("plain_key", "plain_value")
|
||||
.create(&pool)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let encrypted_key = KeyFixture::new_system_unique("encrypted_key", "cipher_text")
|
||||
.with_encrypted(true)
|
||||
.with_encryption_key_hash("sha256:abc")
|
||||
.create(&pool)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(plain_key.encrypted, false);
|
||||
assert_eq!(plain_key.encryption_key_hash, None);
|
||||
|
||||
assert_eq!(encrypted_key.encrypted, true);
|
||||
assert_eq!(
|
||||
encrypted_key.encryption_key_hash,
|
||||
Some("sha256:abc".to_string())
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_update_encryption_status() {
|
||||
let pool = create_test_pool().await.unwrap();
|
||||
|
||||
// Create plain key
|
||||
let key = KeyFixture::new_system_unique("to_encrypt", "plain_value")
|
||||
.create(&pool)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(key.encrypted, false);
|
||||
|
||||
// Encrypt it
|
||||
let input = UpdateKeyInput {
|
||||
encrypted: Some(true),
|
||||
encryption_key_hash: Some("sha256:newkey".to_string()),
|
||||
value: Some("encrypted_value".to_string()),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let encrypted = KeyRepository::update(&pool, key.id, input).await.unwrap();
|
||||
|
||||
assert_eq!(encrypted.encrypted, true);
|
||||
assert_eq!(
|
||||
encrypted.encryption_key_hash,
|
||||
Some("sha256:newkey".to_string())
|
||||
);
|
||||
assert_eq!(encrypted.value, "encrypted_value");
|
||||
|
||||
// Decrypt it
|
||||
let input = UpdateKeyInput {
|
||||
encrypted: Some(false),
|
||||
encryption_key_hash: None,
|
||||
value: Some("plain_value".to_string()),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let decrypted = KeyRepository::update(&pool, key.id, input).await.unwrap();
|
||||
|
||||
assert_eq!(decrypted.encrypted, false);
|
||||
assert_eq!(decrypted.value, "plain_value");
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Owner Validation Tests
|
||||
// ============================================================================
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_multiple_keys_same_pack_different_names() {
|
||||
let pool = create_test_pool().await.unwrap();
|
||||
|
||||
let pack = PackFixture::new_unique("multikey_pack")
|
||||
.create(&pool)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let key1 = KeyFixture::new_pack_unique(pack.id, &pack.r#ref, "key1", "value1")
|
||||
.create(&pool)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let key2 = KeyFixture::new_pack_unique(pack.id, &pack.r#ref, "key2", "value2")
|
||||
.create(&pool)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_ne!(key1.id, key2.id);
|
||||
assert_eq!(key1.owner_pack, Some(pack.id));
|
||||
assert_eq!(key2.owner_pack, Some(pack.id));
|
||||
assert_ne!(key1.name, key2.name);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_same_key_name_different_owners() {
|
||||
let pool = create_test_pool().await.unwrap();
|
||||
|
||||
let pack1 = PackFixture::new_unique("pack1")
|
||||
.create(&pool)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let pack2 = PackFixture::new_unique("pack2")
|
||||
.create(&pool)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// Same base key name, different owners - should be allowed
|
||||
// Use same base name so fixture creates keys with same logical name
|
||||
let base_name = format!("api_key_{}", unique_test_id());
|
||||
|
||||
let key1 = KeyFixture::new_pack(pack1.id, &pack1.r#ref, &base_name, "value1")
|
||||
.create(&pool)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let key2 = KeyFixture::new_pack(pack2.id, &pack2.r#ref, &base_name, "value2")
|
||||
.create(&pool)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_ne!(key1.id, key2.id);
|
||||
assert_eq!(key1.name, key2.name); // Same name
|
||||
assert_ne!(key1.owner_pack, key2.owner_pack); // Different owners
|
||||
}
|
||||
Reference in New Issue
Block a user