Files
MyAiDesk/nomifun-tauri/crates/backend/nomifun-db/tests/provider_repository.rs
T
freedak f7a720204a Update: 将子项目从 submodule 转为完整内容
- 移除 GovAI, nomifun-tauri, 算力盒子 的 submodule 引用
- 添加所有子项目的完整源代码
- 保留原始 .git 为 .git.bak 备份
2026-07-04 19:20:46 +08:00

271 lines
7.1 KiB
Rust

//! Black-box integration tests for IProviderRepository.
//!
//! Tests exercise the public trait interface against an in-memory SQLite database.
use std::sync::Arc;
use nomifun_db::{
CreateProviderParams, DbError, IProviderRepository, SqliteProviderRepository, UpdateProviderParams,
init_database_memory,
};
async fn repo() -> Arc<dyn IProviderRepository> {
let db = init_database_memory().await.unwrap();
Arc::new(SqliteProviderRepository::new(db.pool().clone()))
}
fn sample_params() -> CreateProviderParams<'static> {
CreateProviderParams {
id: None,
platform: "anthropic",
name: "Anthropic",
base_url: "https://api.anthropic.com",
api_key_encrypted: "enc_key_data",
models: r#"["claude-sonnet-4-20250514"]"#,
enabled: true,
capabilities: r#"[{"type":"text"}]"#,
context_limit: Some(200000),
model_protocols: None,
model_enabled: None,
model_health: None,
bedrock_config: None,
is_full_url: false,
}
}
// -- Empty state --
#[tokio::test]
async fn list_returns_empty_when_no_providers() {
let r = repo().await;
assert!(r.list().await.unwrap().is_empty());
}
// -- Create --
#[tokio::test]
async fn create_returns_provider_with_generated_id() {
let r = repo().await;
let p = r.create(sample_params()).await.unwrap();
assert!(!p.id.is_empty());
assert_eq!(p.platform, "anthropic");
assert_eq!(p.name, "Anthropic");
assert_eq!(p.base_url, "https://api.anthropic.com");
assert!(p.enabled);
assert_eq!(p.context_limit, Some(200000));
assert!(p.created_at > 0);
}
#[tokio::test]
async fn create_stores_json_fields_as_strings() {
let r = repo().await;
let p = r.create(sample_params()).await.unwrap();
assert_eq!(p.models, r#"["claude-sonnet-4-20250514"]"#);
assert_eq!(p.capabilities, r#"[{"type":"text"}]"#);
}
#[tokio::test]
async fn create_with_all_optional_fields() {
let r = repo().await;
let p = r
.create(CreateProviderParams {
model_protocols: Some(r#"{"m1":"openai"}"#),
model_enabled: Some(r#"{"m1":true}"#),
model_health: Some(r#"{"m1":{"status":"healthy"}}"#),
bedrock_config: Some(r#"{"region":"us-east-1"}"#),
..sample_params()
})
.await
.unwrap();
assert_eq!(p.model_protocols.as_deref(), Some(r#"{"m1":"openai"}"#));
assert_eq!(p.model_enabled.as_deref(), Some(r#"{"m1":true}"#));
assert!(p.model_health.is_some());
assert!(p.bedrock_config.is_some());
}
// -- Find by ID --
#[tokio::test]
async fn find_by_id_existing_returns_provider() {
let r = repo().await;
let created = r.create(sample_params()).await.unwrap();
let found = r.find_by_id(&created.id).await.unwrap().unwrap();
assert_eq!(found.id, created.id);
assert_eq!(found.name, "Anthropic");
}
#[tokio::test]
async fn find_by_id_nonexistent_returns_none() {
let r = repo().await;
assert!(r.find_by_id("no_such_id").await.unwrap().is_none());
}
// -- List --
#[tokio::test]
async fn list_returns_all_providers_in_creation_order() {
let r = repo().await;
let first = r.create(sample_params()).await.unwrap();
let second = r
.create(CreateProviderParams {
platform: "openai",
name: "OpenAI",
..sample_params()
})
.await
.unwrap();
let all = r.list().await.unwrap();
assert_eq!(all.len(), 2);
assert_eq!(all[0].id, first.id);
assert_eq!(all[1].id, second.id);
}
// -- Update --
#[tokio::test]
async fn update_partial_fields_preserves_others() {
let r = repo().await;
let created = r.create(sample_params()).await.unwrap();
let updated = r
.update(
&created.id,
UpdateProviderParams {
name: Some("New Name"),
enabled: Some(false),
..Default::default()
},
)
.await
.unwrap();
assert_eq!(updated.name, "New Name");
assert!(!updated.enabled);
assert_eq!(updated.platform, "anthropic");
assert_eq!(updated.base_url, "https://api.anthropic.com");
}
#[tokio::test]
async fn update_api_key_changes_encrypted_value() {
let r = repo().await;
let created = r.create(sample_params()).await.unwrap();
let updated = r
.update(
&created.id,
UpdateProviderParams {
api_key_encrypted: Some("new_encrypted"),
..Default::default()
},
)
.await
.unwrap();
assert_eq!(updated.api_key_encrypted, "new_encrypted");
}
#[tokio::test]
async fn update_optional_fields_can_be_set_and_cleared() {
let r = repo().await;
let created = r.create(sample_params()).await.unwrap();
assert!(created.bedrock_config.is_none());
// Set
let with_config = r
.update(
&created.id,
UpdateProviderParams {
bedrock_config: Some(Some(r#"{"region":"eu-west-1"}"#)),
..Default::default()
},
)
.await
.unwrap();
assert!(with_config.bedrock_config.is_some());
// Clear
let cleared = r
.update(
&created.id,
UpdateProviderParams {
bedrock_config: Some(None),
..Default::default()
},
)
.await
.unwrap();
assert!(cleared.bedrock_config.is_none());
}
#[tokio::test]
async fn update_nonexistent_returns_not_found() {
let r = repo().await;
let err = r
.update("nonexistent", UpdateProviderParams::default())
.await
.unwrap_err();
assert!(matches!(err, DbError::NotFound(_)), "expected NotFound, got: {err:?}");
}
#[tokio::test]
async fn update_advances_updated_at() {
let r = repo().await;
let created = r.create(sample_params()).await.unwrap();
let updated = r
.update(
&created.id,
UpdateProviderParams {
name: Some("Changed"),
..Default::default()
},
)
.await
.unwrap();
assert!(updated.updated_at >= created.updated_at);
assert_eq!(updated.created_at, created.created_at);
}
// -- Delete --
#[tokio::test]
async fn delete_removes_provider() {
let r = repo().await;
let created = r.create(sample_params()).await.unwrap();
r.delete(&created.id).await.unwrap();
assert!(r.find_by_id(&created.id).await.unwrap().is_none());
}
#[tokio::test]
async fn delete_nonexistent_returns_not_found() {
let r = repo().await;
let err = r.delete("nonexistent").await.unwrap_err();
assert!(matches!(err, DbError::NotFound(_)), "expected NotFound, got: {err:?}");
}
#[tokio::test]
async fn delete_does_not_affect_other_providers() {
let r = repo().await;
let p1 = r.create(sample_params()).await.unwrap();
let p2 = r
.create(CreateProviderParams {
name: "Other",
..sample_params()
})
.await
.unwrap();
r.delete(&p1.id).await.unwrap();
let all = r.list().await.unwrap();
assert_eq!(all.len(), 1);
assert_eq!(all[0].id, p2.id);
}