Public Access
204 lines
6.0 KiB
Rust
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");
|
|
}
|