use dbnexus::DbNexusKit;
use std::sync::Arc;
use async_trait::async_trait;
use dbnexus::Session;
use dbnexus::database::pool::{ConnectionPool, DatabaseSession, PoolStatus};
use dbnexus::foundation::config::DbConfig;
use dbnexus::foundation::error::DbError;
struct MockConnectionPool {
status: PoolStatus,
}
impl MockConnectionPool {
fn new() -> Self {
Self {
status: PoolStatus {
total: 1,
active: 0,
idle: 1,
wait_count: 0,
max_waiters: 10,
borrow_count: 0,
max_active: 10,
},
}
}
}
#[async_trait]
impl ConnectionPool for MockConnectionPool {
async fn get_session(&self, _role: &str) -> Result<Session, DbError> {
Err(DbError::Config("mock pool does not provide real sessions".to_string()))
}
fn status(&self) -> PoolStatus {
self.status.clone()
}
fn config(&self) -> &DbConfig {
use std::sync::OnceLock;
static CONFIG: OnceLock<DbConfig> = OnceLock::new();
CONFIG.get_or_init(DbConfig::default)
}
}
struct MockDatabaseSession {
role: String,
}
#[async_trait]
impl DatabaseSession for MockDatabaseSession {
async fn execute(&self, _sql: &str) -> Result<sea_orm::ExecResult, DbError> {
Err(DbError::Config("mock session cannot execute".to_string()))
}
async fn execute_raw(&self, _sql: &str) -> Result<sea_orm::ExecResult, DbError> {
Err(DbError::Config("mock session cannot execute".to_string()))
}
async fn execute_raw_ddl(&self, _sql: &str) -> Result<sea_orm::ExecResult, DbError> {
Err(DbError::Config("mock session cannot execute".to_string()))
}
async fn begin_transaction(&self) -> Result<(), DbError> {
Ok(())
}
async fn commit(&self) -> Result<(), DbError> {
Ok(())
}
async fn rollback(&self) -> Result<(), DbError> {
Ok(())
}
fn role(&self) -> &str {
&self.role
}
async fn is_in_transaction(&self) -> bool {
false
}
}
fn make_mock_pool() -> Arc<dyn ConnectionPool> {
Arc::new(MockConnectionPool::new())
}
fn make_mock_session(role: &str) -> Arc<dyn DatabaseSession> {
Arc::new(MockDatabaseSession { role: role.to_string() })
}
#[test]
fn test_kit_provide_and_resolve() {
let kit = DbNexusKit::new();
assert!(!kit.has_connection_pool());
let pool = make_mock_pool();
kit.provide_connection_pool(pool).expect("provide should succeed");
assert!(kit.has_connection_pool());
let resolved = kit.connection_pool();
assert!(resolved.is_ok(), "resolve should succeed: {:?}", resolved.err());
}
#[test]
fn test_kit_resolve_nonexistent() {
let kit = DbNexusKit::new();
assert!(!kit.has_connection_pool());
let result = kit.connection_pool();
assert!(result.is_err(), "resolve unregistered should fail");
}
#[test]
fn test_kit_replace() {
let kit = DbNexusKit::new();
let pool1 = make_mock_pool();
kit.provide_connection_pool(pool1).expect("provide should succeed");
let pool2 = make_mock_pool();
kit.replace_connection_pool(pool2);
assert!(kit.has_connection_pool());
let resolved = kit.connection_pool();
assert!(resolved.is_ok(), "resolve after replace should succeed");
}
#[test]
fn test_kit_replace_nonexistent() {
let kit = DbNexusKit::new();
assert!(!kit.has_database_session());
let session = make_mock_session("admin");
kit.replace_database_session(session);
assert!(kit.has_database_session());
}
#[test]
fn test_kit_multiple_capabilities() {
let kit = DbNexusKit::new();
let pool = make_mock_pool();
let session = make_mock_session("admin");
kit.provide_connection_pool(pool).expect("provide pool should succeed");
kit.provide_database_session(session)
.expect("provide session should succeed");
assert!(kit.has_connection_pool());
assert!(kit.has_database_session());
assert!(kit.connection_pool().is_ok());
assert!(kit.database_session().is_ok());
}
#[test]
fn test_kit_type_safety() {
let kit = DbNexusKit::new();
let pool = make_mock_pool();
let session = make_mock_session("reader");
kit.provide_connection_pool(pool).expect("provide pool");
kit.provide_database_session(session).expect("provide session");
let pool_resolved = kit.connection_pool().expect("resolve pool");
assert_eq!(pool_resolved.status().total, 1);
let session_resolved = kit.database_session().expect("resolve session");
assert_eq!(session_resolved.role(), "reader");
}
#[tokio::test]
async fn test_kit_concurrent_access() {
use std::sync::atomic::{AtomicUsize, Ordering};
let kit = DbNexusKit::new();
let pool = make_mock_pool();
kit.provide_connection_pool(pool).expect("provide pool");
let kit_clone = kit.clone();
let counter = Arc::new(AtomicUsize::new(0));
let mut handles = Vec::new();
for _ in 0..10 {
let kit = kit_clone.clone();
let counter = counter.clone();
handles.push(tokio::spawn(async move {
if kit.connection_pool().is_ok() {
counter.fetch_add(1, Ordering::SeqCst);
}
}));
}
for handle in handles {
handle.await.expect("task should complete");
}
assert_eq!(
counter.load(Ordering::SeqCst),
10,
"all concurrent accesses should succeed"
);
}
#[test]
fn test_kit_drop_cleanup() {
let kit = DbNexusKit::new();
let pool = make_mock_pool();
kit.provide_connection_pool(pool).expect("provide pool");
let kit_clone = kit.clone();
assert!(kit_clone.has_connection_pool());
drop(kit);
assert!(
kit_clone.has_connection_pool(),
"clone should retain capabilities after original is dropped"
);
assert!(kit_clone.connection_pool().is_ok());
}