Files
charybdis/tests/entity_crud.rs
T
2026-05-12 17:06:43 +02:00

204 lines
6.0 KiB
Rust

mod harness;
use charybdis::charybdis::core::{ComponentMetadata, ComponentSpec};
use charybdis::charybdis::entities::Entity;
use charybdis::charybdis::entities::entity::{Metadata, Spec};
use harness::TestDb;
fn test_component(name: &str) -> Entity {
Entity {
kind: "Component".to_string(),
metadata: Some(Metadata::ComponentMetadata(ComponentMetadata {
name: name.to_string(),
namespace: "default".to_string(),
description: format!("{} service", name),
..Default::default()
})),
spec: Some(Spec::ComponentSpec(ComponentSpec {
r#type: "service".to_string(),
lifecycle: "production".to_string(),
owner: "team-platform".to_string(),
..Default::default()
})),
..Default::default()
}
}
#[tokio::test]
async fn create_and_get_entity() {
let db = TestDb::new().await;
let created = db.repository.create(&test_component("payment-api")).await.unwrap();
assert!(!created.id.is_empty());
assert_eq!(created.kind, "Component");
assert!(created.created_at.is_some());
assert!(created.updated_at.is_some());
let fetched = db.repository.get_by_id(&created.id).await.unwrap().unwrap();
assert_eq!(fetched.id, created.id);
assert_eq!(fetched.kind, "Component");
}
#[tokio::test]
async fn get_nonexistent_returns_none() {
let db = TestDb::new().await;
let result = db
.repository
.get_by_id("00000000-0000-0000-0000-000000000000")
.await
.unwrap();
assert!(result.is_none());
}
#[tokio::test]
async fn update_entity() {
let db = TestDb::new().await;
let created = db.repository.create(&test_component("auth-svc")).await.unwrap();
let mut updated_data = test_component("auth-svc");
if let Some(Metadata::ComponentMetadata(ref mut m)) = updated_data.metadata {
m.description = "Updated auth service".to_string();
}
let updated = db
.repository
.update(&created.id, &updated_data)
.await
.unwrap()
.unwrap();
assert_eq!(updated.id, created.id);
assert_eq!(updated.created_at, created.created_at);
assert_ne!(updated.updated_at, created.updated_at);
if let Some(Metadata::ComponentMetadata(m)) = &updated.metadata {
assert_eq!(m.description, "Updated auth service");
} else {
panic!("Expected ComponentMetadata");
}
}
#[tokio::test]
async fn delete_entity() {
let db = TestDb::new().await;
let created = db.repository.create(&test_component("to-delete")).await.unwrap();
let deleted = db.repository.delete(&created.id).await.unwrap();
assert!(deleted);
let gone = db.repository.get_by_id(&created.id).await.unwrap();
assert!(gone.is_none());
// Double delete returns false
let deleted_again = db.repository.delete(&created.id).await.unwrap();
assert!(!deleted_again);
}
#[tokio::test]
async fn list_paginated() {
let db = TestDb::new().await;
for i in 0..5 {
db.repository
.create(&test_component(&format!("svc-{}", i)))
.await
.unwrap();
}
// Page 1: get 2 entities
let page1 = db
.repository
.list_paginated(Some("Component"), None, 2, None)
.await
.unwrap();
assert_eq!(page1.entities.len(), 2);
assert_eq!(page1.total_count, 5);
assert!(page1.next_page_token.is_some());
// Page 2: next 2
let page2 = db
.repository
.list_paginated(Some("Component"), None, 2, page1.next_page_token.as_deref())
.await
.unwrap();
assert_eq!(page2.entities.len(), 2);
assert!(page2.next_page_token.is_some());
// Page 3: last 1
let page3 = db
.repository
.list_paginated(Some("Component"), None, 2, page2.next_page_token.as_deref())
.await
.unwrap();
assert_eq!(page3.entities.len(), 1);
assert!(page3.next_page_token.is_none());
// No duplicates across pages
let all_ids: Vec<String> = page1
.entities
.iter()
.chain(page2.entities.iter())
.chain(page3.entities.iter())
.map(|e| e.id.clone())
.collect();
let unique: std::collections::HashSet<&String> = all_ids.iter().collect();
assert_eq!(all_ids.len(), unique.len());
}
#[tokio::test]
async fn get_by_kind_and_name() {
let db = TestDb::new().await;
db.repository.create(&test_component("unique-svc")).await.unwrap();
db.repository.create(&test_component("other-svc")).await.unwrap();
let found = db
.repository
.get_by_kind_and_name("Component", "unique-svc")
.await
.unwrap();
assert!(found.is_some());
if let Some(Metadata::ComponentMetadata(m)) = &found.unwrap().metadata {
assert_eq!(m.name, "unique-svc");
}
let not_found = db
.repository
.get_by_kind_and_name("Component", "nonexistent")
.await
.unwrap();
assert!(not_found.is_none());
}
#[tokio::test]
async fn atomic_annotation_update() {
let db = TestDb::new().await;
let created = db.repository.create(&test_component("annotated-svc")).await.unwrap();
// First annotation update
let mut annotations1 = std::collections::HashMap::new();
annotations1.insert("defectdojo.com/product-id".to_string(), "123".to_string());
let updated = db
.repository
.update_annotations(&created.id, annotations1)
.await
.unwrap();
assert!(updated);
// Second annotation update (should merge, not overwrite)
let mut annotations2 = std::collections::HashMap::new();
annotations2.insert("github.com/repo".to_string(), "org/repo".to_string());
db.repository
.update_annotations(&created.id, annotations2)
.await
.unwrap();
// Verify both annotations exist
let entity = db.repository.get_by_id(&created.id).await.unwrap().unwrap();
assert_eq!(entity.annotations.get("defectdojo.com/product-id").unwrap(), "123");
assert_eq!(entity.annotations.get("github.com/repo").unwrap(), "org/repo");
}