use hyperinfer_core::Database;
use hyperinfer_server::SqlxDb;
use sqlx::postgres::PgPoolOptions;
use testcontainers::ImageExt;
use testcontainers::{runners::AsyncRunner, ContainerAsync};
use testcontainers_modules::postgres::Postgres;
async fn setup_test_db() -> (SqlxDb, ContainerAsync<Postgres>) {
let postgres = Postgres::default()
.with_tag("13-alpine")
.start()
.await
.expect("Failed to start PostgreSQL container");
let port = postgres.get_host_port_ipv4(5432).await.unwrap();
let connection_string = format!("postgres://postgres:postgres@127.0.0.1:{}/postgres", port);
let pool = PgPoolOptions::new()
.max_connections(5)
.connect(&connection_string)
.await
.expect("Failed to connect to PostgreSQL");
sqlx::query("CREATE EXTENSION IF NOT EXISTS pgcrypto")
.execute(&pool)
.await
.expect("Failed to enable pgcrypto extension");
sqlx::raw_sql(include_str!("../migrations/001_initial_schema.sql"))
.execute(&pool)
.await
.expect("Failed to run migration 001");
sqlx::raw_sql(include_str!("../migrations/002_usage_logs.sql"))
.execute(&pool)
.await
.expect("Failed to run migration 002");
sqlx::raw_sql(include_str!(
"../migrations/003_usage_logs_composite_index.sql"
))
.execute(&pool)
.await
.expect("Failed to run migration 003");
sqlx::raw_sql(include_str!("../migrations/004_add_password_hash.sql"))
.execute(&pool)
.await
.expect("Failed to run migration 004");
sqlx::raw_sql(include_str!("../migrations/005_deployments.sql"))
.execute(&pool)
.await
.expect("Failed to run migration 005");
sqlx::raw_sql(include_str!("../migrations/006_routing_config.sql"))
.execute(&pool)
.await
.expect("Failed to run migration 006");
sqlx::raw_sql(include_str!("../migrations/007_fix_deployments.sql"))
.execute(&pool)
.await
.expect("Failed to run migration 007");
(SqlxDb::new(pool), postgres)
}
#[tokio::test]
async fn test_database_create_and_get_team() {
let (db, _container) = setup_test_db().await;
let team = db
.create_team("Test Team", 10000)
.await
.expect("Failed to create team");
assert_eq!(team.name, "Test Team");
assert_eq!(team.budget_cents, 10000);
let fetched = db
.get_team(&team.id)
.await
.expect("Failed to get team")
.expect("Team not found");
assert_eq!(fetched.id, team.id);
assert_eq!(fetched.name, "Test Team");
}
#[tokio::test]
async fn test_database_create_and_get_user() {
let (db, _container) = setup_test_db().await;
let team = db
.create_team("Test Team", 10000)
.await
.expect("Failed to create team");
let user = db
.create_user(&team.id, "test@example.com", "admin", None)
.await
.expect("Failed to create user");
assert_eq!(user.email, "test@example.com");
assert_eq!(user.role, "admin");
let fetched = db
.get_user(&user.id)
.await
.expect("Failed to get user")
.expect("User not found");
assert_eq!(fetched.id, user.id);
assert_eq!(fetched.email, "test@example.com");
}
#[tokio::test]
async fn test_database_create_and_get_api_key() {
let (db, _container) = setup_test_db().await;
let team = db
.create_team("Test Team", 10000)
.await
.expect("Failed to create team");
let user = db
.create_user(&team.id, "test@example.com", "admin", None)
.await
.expect("Failed to create user");
let api_key = db
.create_api_key(
"hashed_key_123",
&user.id,
&team.id,
Some("My API Key".to_string()),
)
.await
.expect("Failed to create API key");
assert_eq!(api_key.key_hash, "hashed_key_123");
assert_eq!(api_key.name, Some("My API Key".to_string()));
assert!(api_key.is_active);
let fetched = db
.get_api_key(&api_key.id)
.await
.expect("Failed to get API key")
.expect("API key not found");
assert_eq!(fetched.id, api_key.id);
assert_eq!(fetched.key_hash, "hashed_key_123");
}
#[tokio::test]
async fn test_database_create_and_get_model_alias() {
let (db, _container) = setup_test_db().await;
let team = db
.create_team("Test Team", 10000)
.await
.expect("Failed to create team");
let alias = db
.create_model_alias(&team.id, "gpt-4-fast", "gpt-4-turbo", "openai")
.await
.expect("Failed to create model alias");
assert_eq!(alias.alias, "gpt-4-fast");
assert_eq!(alias.target_model, "gpt-4-turbo");
assert_eq!(alias.provider, "openai");
let fetched = db
.get_model_alias(&alias.id)
.await
.expect("Failed to get model alias")
.expect("Model alias not found");
assert_eq!(fetched.id, alias.id);
assert_eq!(fetched.alias, "gpt-4-fast");
}
#[tokio::test]
async fn test_database_create_and_get_quota() {
let (db, _container) = setup_test_db().await;
let team = db
.create_team("Test Team", 10000)
.await
.expect("Failed to create team");
let quota = db
.create_quota(&team.id, 100, 10000)
.await
.expect("Failed to create quota");
assert_eq!(quota.rpm_limit, 100);
assert_eq!(quota.tpm_limit, 10000);
let fetched = db
.get_quota(&team.id)
.await
.expect("Failed to get quota")
.expect("Quota not found");
assert_eq!(fetched.team_id, team.id);
assert_eq!(fetched.rpm_limit, 100);
}
#[tokio::test]
async fn test_get_nonexistent_team() {
let (db, _container) = setup_test_db().await;
let result = db
.get_team("00000000-0000-0000-0000-000000000000")
.await
.expect("Query failed");
assert!(result.is_none(), "Should return None for non-existent team");
}
#[tokio::test]
async fn test_get_nonexistent_user() {
let (db, _container) = setup_test_db().await;
let result = db
.get_user("00000000-0000-0000-0000-000000000000")
.await
.expect("Query failed");
assert!(result.is_none(), "Should return None for non-existent user");
}
#[tokio::test]
async fn test_get_nonexistent_api_key() {
let (db, _container) = setup_test_db().await;
let result = db
.get_api_key("00000000-0000-0000-0000-000000000000")
.await
.expect("Query failed");
assert!(
result.is_none(),
"Should return None for non-existent API key"
);
}
#[tokio::test]
async fn test_get_nonexistent_model_alias() {
let (db, _container) = setup_test_db().await;
let result = db
.get_model_alias("00000000-0000-0000-0000-000000000000")
.await
.expect("Query failed");
assert!(
result.is_none(),
"Should return None for non-existent model alias"
);
}
#[tokio::test]
async fn test_get_nonexistent_quota() {
let (db, _container) = setup_test_db().await;
let result = db
.get_quota("00000000-0000-0000-0000-000000000000")
.await
.expect("Query failed");
assert!(
result.is_none(),
"Should return None for non-existent quota"
);
}
#[tokio::test]
async fn test_duplicate_team_name() {
let (db, _container) = setup_test_db().await;
db.create_team("Unique Team", 10000)
.await
.expect("Failed to create first team");
let result = db.create_team("Unique Team", 20000).await;
assert!(result.is_err(), "Should fail on duplicate team name");
}
#[tokio::test]
async fn test_duplicate_user_email() {
let (db, _container) = setup_test_db().await;
let team = db
.create_team("Test Team", 10000)
.await
.expect("Failed to create team");
db.create_user(&team.id, "unique@example.com", "admin", None)
.await
.expect("Failed to create first user");
let result = db
.create_user(&team.id, "unique@example.com", "member", None)
.await;
assert!(result.is_err(), "Should fail on duplicate user email");
}
#[tokio::test]
async fn test_duplicate_api_key_hash() {
let (db, _container) = setup_test_db().await;
let team = db
.create_team("Test Team", 10000)
.await
.expect("Failed to create team");
let user = db
.create_user(&team.id, "test@example.com", "admin", None)
.await
.expect("Failed to create user");
db.create_api_key("unique_hash", &user.id, &team.id, None)
.await
.expect("Failed to create first API key");
let result = db
.create_api_key("unique_hash", &user.id, &team.id, None)
.await;
assert!(result.is_err(), "Should fail on duplicate API key hash");
}
#[tokio::test]
async fn test_duplicate_model_alias_per_team() {
let (db, _container) = setup_test_db().await;
let team = db
.create_team("Test Team", 10000)
.await
.expect("Failed to create team");
db.create_model_alias(&team.id, "gpt-4", "gpt-4-turbo", "openai")
.await
.expect("Failed to create first alias");
let result = db
.create_model_alias(&team.id, "gpt-4", "gpt-4o", "openai")
.await;
assert!(
result.is_err(),
"Should fail on duplicate model alias per team"
);
}
#[tokio::test]
async fn test_duplicate_quota_per_team() {
let (db, _container) = setup_test_db().await;
let team = db
.create_team("Test Team", 10000)
.await
.expect("Failed to create team");
db.create_quota(&team.id, 100, 10000)
.await
.expect("Failed to create first quota");
let result = db.create_quota(&team.id, 200, 20000).await;
assert!(result.is_err(), "Should fail on duplicate quota per team");
}
#[tokio::test]
async fn test_invalid_uuid_format() {
let (db, _container) = setup_test_db().await;
let result = db.get_team("not-a-uuid").await;
assert!(result.is_err(), "Should fail on invalid UUID format");
}
#[tokio::test]
async fn test_create_user_invalid_team_fk() {
let (db, _container) = setup_test_db().await;
let result = db
.create_user(
"00000000-0000-0000-0000-000000000000",
"test@example.com",
"admin",
None,
)
.await;
assert!(result.is_err(), "Should fail on invalid team foreign key");
}
#[tokio::test]
async fn test_create_api_key_invalid_user_fk() {
let (db, _container) = setup_test_db().await;
let team = db
.create_team("Test Team", 10000)
.await
.expect("Failed to create team");
let result = db
.create_api_key(
"hash123",
"00000000-0000-0000-0000-000000000000",
&team.id,
None,
)
.await;
assert!(result.is_err(), "Should fail on invalid user foreign key");
}
#[tokio::test]
async fn test_create_api_key_invalid_team_fk() {
let (db, _container) = setup_test_db().await;
let team = db
.create_team("Test Team", 10000)
.await
.expect("Failed to create team");
let user = db
.create_user(&team.id, "test@example.com", "admin", None)
.await
.expect("Failed to create user");
let result = db
.create_api_key(
"hash123",
&user.id,
"00000000-0000-0000-0000-000000000000",
None,
)
.await;
assert!(result.is_err(), "Should fail on invalid team foreign key");
}
#[tokio::test]
async fn test_create_model_alias_invalid_team_fk() {
let (db, _container) = setup_test_db().await;
let result = db
.create_model_alias(
"00000000-0000-0000-0000-000000000000",
"gpt-4",
"gpt-4-turbo",
"openai",
)
.await;
assert!(result.is_err(), "Should fail on invalid team foreign key");
}
#[tokio::test]
async fn test_create_quota_invalid_team_fk() {
let (db, _container) = setup_test_db().await;
let result = db
.create_quota("00000000-0000-0000-0000-000000000000", 100, 10000)
.await;
assert!(result.is_err(), "Should fail on invalid team foreign key");
}
#[tokio::test]
async fn test_record_usage_success() {
let (db, _container) = setup_test_db().await;
let team = db
.create_team("Test Team", 10000)
.await
.expect("Failed to create team");
let user = db
.create_user(&team.id, "test@example.com", "admin", None)
.await
.expect("Failed to create user");
let api_key = db
.create_api_key("test_hash", &user.id, &team.id, None)
.await
.expect("Failed to create API key");
let usage_log = db
.record_usage(&team.id, &api_key.id, "gpt-4", 100, 50, 250)
.await
.expect("Failed to record usage");
assert_eq!(usage_log.team_id, team.id);
assert_eq!(usage_log.api_key_id, api_key.id);
assert_eq!(usage_log.model, "gpt-4");
assert_eq!(usage_log.input_tokens, 100);
assert_eq!(usage_log.output_tokens, 50);
assert_eq!(usage_log.response_time_ms, 250);
}
#[tokio::test]
async fn test_record_usage_zero_tokens() {
let (db, _container) = setup_test_db().await;
let team = db
.create_team("Test Team", 10000)
.await
.expect("Failed to create team");
let user = db
.create_user(&team.id, "test@example.com", "admin", None)
.await
.expect("Failed to create user");
let api_key = db
.create_api_key("test_hash", &user.id, &team.id, None)
.await
.expect("Failed to create API key");
let usage_log = db
.record_usage(&team.id, &api_key.id, "gpt-4", 0, 0, 0)
.await
.expect("Failed to record usage");
assert_eq!(usage_log.input_tokens, 0);
assert_eq!(usage_log.output_tokens, 0);
assert_eq!(usage_log.response_time_ms, 0);
}
#[tokio::test]
async fn test_record_usage_large_values() {
let (db, _container) = setup_test_db().await;
let team = db
.create_team("Test Team", 10000)
.await
.expect("Failed to create team");
let user = db
.create_user(&team.id, "test@example.com", "admin", None)
.await
.expect("Failed to create user");
let api_key = db
.create_api_key("test_hash", &user.id, &team.id, None)
.await
.expect("Failed to create API key");
let usage_log = db
.record_usage(&team.id, &api_key.id, "gpt-4", i32::MAX, i32::MAX, i64::MAX)
.await
.expect("Failed to record usage");
assert_eq!(usage_log.input_tokens, i32::MAX);
assert_eq!(usage_log.output_tokens, i32::MAX);
assert_eq!(usage_log.response_time_ms, i64::MAX);
}
#[tokio::test]
async fn test_record_usage_multiple_logs() {
let (db, _container) = setup_test_db().await;
let team = db
.create_team("Test Team", 10000)
.await
.expect("Failed to create team");
let user = db
.create_user(&team.id, "test@example.com", "admin", None)
.await
.expect("Failed to create user");
let api_key = db
.create_api_key("test_hash", &user.id, &team.id, None)
.await
.expect("Failed to create API key");
let log1 = db
.record_usage(&team.id, &api_key.id, "gpt-4", 100, 50, 250)
.await
.expect("Failed to record first usage");
let log2 = db
.record_usage(&team.id, &api_key.id, "gpt-3.5-turbo", 200, 100, 150)
.await
.expect("Failed to record second usage");
assert_ne!(log1.id, log2.id);
assert_eq!(log1.team_id, team.id);
assert_eq!(log2.team_id, team.id);
assert_eq!(log1.model, "gpt-4");
assert_eq!(log2.model, "gpt-3.5-turbo");
}
#[tokio::test]
async fn test_record_usage_invalid_team_id() {
let (db, _container) = setup_test_db().await;
let team = db
.create_team("Test Team", 10000)
.await
.expect("Failed to create team");
let user = db
.create_user(&team.id, "test@example.com", "admin", None)
.await
.expect("Failed to create user");
let api_key = db
.create_api_key("test_hash", &user.id, &team.id, None)
.await
.expect("Failed to create API key");
let result = db
.record_usage(
"00000000-0000-0000-0000-000000000000",
&api_key.id,
"gpt-4",
100,
50,
250,
)
.await;
assert!(result.is_err(), "Should fail with invalid team foreign key");
}
#[tokio::test]
async fn test_record_usage_invalid_api_key_id() {
let (db, _container) = setup_test_db().await;
let team = db
.create_team("Test Team", 10000)
.await
.expect("Failed to create team");
let result = db
.record_usage(
&team.id,
"00000000-0000-0000-0000-000000000000",
"gpt-4",
100,
50,
250,
)
.await;
assert!(
result.is_err(),
"Should fail with invalid api_key foreign key"
);
}
#[tokio::test]
async fn test_record_usage_invalid_uuid_format() {
let (db, _container) = setup_test_db().await;
let result = db
.record_usage("not-a-uuid", "also-not-a-uuid", "gpt-4", 100, 50, 250)
.await;
assert!(result.is_err(), "Should fail with invalid UUID format");
}
#[tokio::test]
async fn test_record_usage_different_models() {
let (db, _container) = setup_test_db().await;
let team = db
.create_team("Test Team", 10000)
.await
.expect("Failed to create team");
let user = db
.create_user(&team.id, "test@example.com", "admin", None)
.await
.expect("Failed to create user");
let api_key = db
.create_api_key("test_hash", &user.id, &team.id, None)
.await
.expect("Failed to create API key");
let models = vec!["gpt-4", "gpt-3.5-turbo", "claude-3-opus", "claude-3-sonnet"];
for model in models {
let log = db
.record_usage(&team.id, &api_key.id, model, 100, 50, 200)
.await
.expect("Failed to record usage");
assert_eq!(log.model, model);
}
}
#[tokio::test]
async fn test_get_api_key_by_hash() {
let (db, _container) = setup_test_db().await;
let team = db
.create_team("Test Team", 10000)
.await
.expect("Failed to create team");
let user = db
.create_user(&team.id, "test@example.com", "admin", None)
.await
.expect("Failed to create user");
let api_key = db
.create_api_key("unique_hash_123", &user.id, &team.id, None)
.await
.expect("Failed to create API key");
let fetched = db
.get_api_key_by_hash("unique_hash_123")
.await
.expect("Failed to get API key by hash")
.expect("API key not found");
assert_eq!(fetched.id, api_key.id);
assert_eq!(fetched.key_hash, "unique_hash_123");
assert!(fetched.is_active);
}
#[tokio::test]
async fn test_get_api_key_by_hash_inactive() {
let (db, _container) = setup_test_db().await;
let team = db
.create_team("Test Team", 10000)
.await
.expect("Failed to create team");
let user = db
.create_user(&team.id, "test@example.com", "admin", None)
.await
.expect("Failed to create user");
let _api_key = db
.create_api_key("test_hash", &user.id, &team.id, None)
.await
.expect("Failed to create API key");
let fetched = db
.get_api_key_by_hash("test_hash")
.await
.expect("Failed to get API key by hash");
assert!(fetched.is_some(), "Should find active API key");
}
#[tokio::test]
async fn test_get_api_key_by_hash_not_found() {
let (db, _container) = setup_test_db().await;
let result = db
.get_api_key_by_hash("nonexistent_hash")
.await
.expect("Query failed");
assert!(result.is_none(), "Should return None for nonexistent hash");
}
use hyperinfer_server::db::hash_password;
#[tokio::test]
async fn test_count_users_by_role() {
let (db, _container) = setup_test_db().await;
let team = db
.create_team("Test Team", 10000)
.await
.expect("Failed to create team");
let admin_count = db
.count_users_by_role("admin")
.await
.expect("Failed to count admins");
assert_eq!(admin_count, 0);
db.create_user(&team.id, "admin@example.com", "admin", None)
.await
.expect("Failed to create admin user");
let admin_count = db
.count_users_by_role("admin")
.await
.expect("Failed to count admins");
assert_eq!(admin_count, 1);
db.create_user(&team.id, "member@example.com", "member", None)
.await
.expect("Failed to create member user");
let member_count = db
.count_users_by_role("member")
.await
.expect("Failed to count members");
assert_eq!(member_count, 1);
}
#[tokio::test]
async fn test_create_user_with_password_hash() {
let (db, _container) = setup_test_db().await;
let team = db
.create_team("Test Team", 10000)
.await
.expect("Failed to create team");
let password_hash = hash_password("secure_password_123").expect("Failed to hash password");
let user = db
.create_user(&team.id, "user@example.com", "admin", Some(password_hash))
.await
.expect("Failed to create user with password");
assert_eq!(user.email, "user@example.com");
assert!(user.password_hash.is_some());
assert_ne!(user.password_hash.unwrap(), "secure_password_123"); }
#[tokio::test]
async fn test_get_user_by_email() {
let (db, _container) = setup_test_db().await;
let team = db
.create_team("Test Team", 10000)
.await
.expect("Failed to create team");
let user = db
.create_user(&team.id, "test@example.com", "admin", None)
.await
.expect("Failed to create user");
let fetched = db
.get_user_by_email("test@example.com")
.await
.expect("Failed to get user by email")
.expect("User not found");
assert_eq!(fetched.id, user.id);
assert_eq!(fetched.email, "test@example.com");
let not_found = db
.get_user_by_email("nonexistent@example.com")
.await
.expect("Query failed");
assert!(not_found.is_none());
}
#[tokio::test]
async fn test_password_hashing() {
let hash1 = hash_password("password123").expect("Failed to hash password");
let hash2 = hash_password("password123").expect("Failed to hash password");
assert_ne!(hash1, hash2);
use hyperinfer_server::db::verify_password;
assert!(verify_password("password123", &hash1));
assert!(verify_password("password123", &hash2));
assert!(!verify_password("wrongpassword", &hash1));
assert!(!verify_password("wrongpassword", &hash2));
}
#[tokio::test]
async fn test_deployment_crud() {
let (db, _container) = setup_test_db().await;
let req = hyperinfer_core::CreateDeploymentRequest {
name: "test-deployment".to_string(),
provider: "openai".to_string(),
model: "gpt-4".to_string(),
api_key_ref: Some("sk-test123".to_string()),
base_url: "https://api.openai.com/v1".to_string(),
is_active: true,
weight: 5,
priority: 1,
max_tpm: Some(100000),
max_rpm: Some(1000),
cost_per_1k_input_tokens: Some(0.03),
cost_per_1k_output_tokens: Some(0.06),
metadata: Some(serde_json::json!({"region": "us-east-1"})),
sort_order: Some(10),
};
let created = db
.create_deployment(req)
.await
.expect("Failed to create deployment");
assert_eq!(created.name, "test-deployment");
assert_eq!(created.provider, "openai");
assert_eq!(created.model, "gpt-4");
assert_eq!(created.weight, 5);
assert_eq!(created.sort_order, 10);
let fetched = db
.get_deployment(&created.id)
.await
.expect("Failed to get deployment")
.expect("Deployment not found");
assert_eq!(fetched.id, created.id);
assert_eq!(fetched.name, "test-deployment");
let list = db
.list_deployments("gpt-4", None)
.await
.expect("Failed to list deployments");
assert_eq!(list.len(), 1);
assert_eq!(list[0].id, created.id);
let update_req = hyperinfer_core::CreateDeploymentRequest {
name: "test-deployment-updated".to_string(),
provider: "openai".to_string(),
model: "gpt-4".to_string(),
api_key_ref: None,
base_url: "https://api.openai.com/v1".to_string(),
is_active: false,
weight: 10,
priority: 2,
max_tpm: None,
max_rpm: None,
cost_per_1k_input_tokens: None,
cost_per_1k_output_tokens: None,
metadata: None,
sort_order: None,
};
let updated = db
.update_deployment(&created.id, update_req)
.await
.expect("Failed to update deployment");
assert_eq!(updated.name, "test-deployment-updated");
assert_eq!(updated.weight, 10);
assert!(!updated.is_active);
db.delete_deployment(&created.id)
.await
.expect("Failed to delete deployment");
let deleted = db
.get_deployment(&created.id)
.await
.expect("Failed to get deployment");
assert!(deleted.is_none());
}
#[tokio::test]
async fn test_deployment_list_filter_by_active() {
let (db, _container) = setup_test_db().await;
let req1 = hyperinfer_core::CreateDeploymentRequest {
name: "active-deployment".to_string(),
provider: "openai".to_string(),
model: "gpt-4".to_string(),
api_key_ref: None,
base_url: "https://api.openai.com/v1".to_string(),
is_active: true,
weight: 1,
priority: 0,
max_tpm: None,
max_rpm: None,
cost_per_1k_input_tokens: None,
cost_per_1k_output_tokens: None,
metadata: None,
sort_order: None,
};
db.create_deployment(req1).await.expect("Failed to create");
let req2 = hyperinfer_core::CreateDeploymentRequest {
name: "inactive-deployment".to_string(),
provider: "openai".to_string(),
model: "gpt-4".to_string(),
api_key_ref: None,
base_url: "https://api.openai.com/v1".to_string(),
is_active: false,
weight: 1,
priority: 0,
max_tpm: None,
max_rpm: None,
cost_per_1k_input_tokens: None,
cost_per_1k_output_tokens: None,
metadata: None,
sort_order: None,
};
db.create_deployment(req2).await.expect("Failed to create");
let all = db
.list_deployments("gpt-4", None)
.await
.expect("Failed to list");
assert_eq!(all.len(), 2);
let active = db
.list_deployments("gpt-4", Some(true))
.await
.expect("Failed to list active");
assert_eq!(active.len(), 1);
assert_eq!(active[0].name, "active-deployment");
let inactive = db
.list_deployments("gpt-4", Some(false))
.await
.expect("Failed to list inactive");
assert_eq!(inactive.len(), 1);
assert_eq!(inactive[0].name, "inactive-deployment");
}
#[tokio::test]
async fn test_deployment_delete_not_found() {
let (db, _container) = setup_test_db().await;
let fake_id = uuid::Uuid::new_v4().to_string();
let result = db.delete_deployment(&fake_id).await;
assert!(matches!(result, Err(hyperinfer_core::DbError::NotFound)));
}
#[tokio::test]
async fn test_deployment_unique_name_constraint() {
let (db, _container) = setup_test_db().await;
let req = hyperinfer_core::CreateDeploymentRequest {
name: "duplicate-name".to_string(),
provider: "openai".to_string(),
model: "gpt-4".to_string(),
api_key_ref: None,
base_url: "https://api.openai.com/v1".to_string(),
is_active: true,
weight: 1,
priority: 0,
max_tpm: None,
max_rpm: None,
cost_per_1k_input_tokens: None,
cost_per_1k_output_tokens: None,
metadata: None,
sort_order: None,
};
db.create_deployment(req.clone())
.await
.expect("First create should succeed");
let result = db.create_deployment(req).await;
assert!(matches!(
result,
Err(hyperinfer_core::DbError::UniqueViolation(_))
));
}