#![allow(clippy::unwrap_used)]
use std::collections::HashMap;
use async_trait::async_trait;
use indexmap::IndexMap;
use crate::{
db::{
SupportsMutations,
traits::DatabaseAdapter,
types::{DatabaseType, JsonbValue, PoolMetrics, sql_hints::OrderByClause},
where_clause::WhereClause,
},
error::Result,
schema::{AutoParams, CompiledSchema, CursorType, QueryDefinition},
};
pub struct CapturingMockAdapter {
pub mock_results: Vec<JsonbValue>,
pub captured_where: std::sync::Mutex<Option<WhereClause>>,
pub captured_limit: std::sync::Mutex<Option<u32>>,
pub captured_offset: std::sync::Mutex<Option<u32>>,
pub captured_aggregate_sql: std::sync::Mutex<Option<String>>,
pub captured_aggregate_params: std::sync::Mutex<Option<Vec<serde_json::Value>>>,
}
impl CapturingMockAdapter {
pub fn new(mock_results: Vec<JsonbValue>) -> Self {
Self {
mock_results,
captured_where: std::sync::Mutex::new(None),
captured_limit: std::sync::Mutex::new(None),
captured_offset: std::sync::Mutex::new(None),
captured_aggregate_sql: std::sync::Mutex::new(None),
captured_aggregate_params: std::sync::Mutex::new(None),
}
}
pub fn captured_where(&self) -> Option<WhereClause> {
self.captured_where.lock().unwrap().clone()
}
pub fn captured_limit(&self) -> Option<u32> {
*self.captured_limit.lock().unwrap()
}
pub fn captured_offset(&self) -> Option<u32> {
*self.captured_offset.lock().unwrap()
}
pub fn captured_aggregate_sql(&self) -> Option<String> {
self.captured_aggregate_sql.lock().unwrap().clone()
}
#[allow(dead_code)] pub fn captured_aggregate_params(&self) -> Option<Vec<serde_json::Value>> {
self.captured_aggregate_params.lock().unwrap().clone()
}
}
#[async_trait]
impl DatabaseAdapter for CapturingMockAdapter {
async fn execute_with_projection(
&self,
view: &str,
_projection: Option<&crate::schema::SqlProjectionHint>,
where_clause: Option<&WhereClause>,
limit: Option<u32>,
offset: Option<u32>,
_order_by: Option<&[OrderByClause]>,
) -> Result<Vec<JsonbValue>> {
self.execute_where_query(view, where_clause, limit, offset, None).await
}
async fn execute_where_query(
&self,
_view: &str,
where_clause: Option<&WhereClause>,
limit: Option<u32>,
offset: Option<u32>,
_order_by: Option<&[OrderByClause]>,
) -> Result<Vec<JsonbValue>> {
*self.captured_where.lock().unwrap() = where_clause.cloned();
*self.captured_limit.lock().unwrap() = limit;
*self.captured_offset.lock().unwrap() = offset;
Ok(self.mock_results.clone())
}
async fn health_check(&self) -> Result<()> {
Ok(())
}
fn database_type(&self) -> DatabaseType {
DatabaseType::PostgreSQL
}
fn pool_metrics(&self) -> PoolMetrics {
PoolMetrics {
total_connections: 1,
active_connections: 0,
idle_connections: 1,
waiting_requests: 0,
}
}
async fn execute_raw_query(
&self,
_sql: &str,
) -> Result<Vec<std::collections::HashMap<String, serde_json::Value>>> {
Ok(vec![])
}
async fn execute_parameterized_aggregate(
&self,
sql: &str,
params: &[serde_json::Value],
) -> Result<Vec<std::collections::HashMap<String, serde_json::Value>>> {
*self.captured_aggregate_sql.lock().unwrap() = Some(sql.to_string());
*self.captured_aggregate_params.lock().unwrap() = Some(params.to_vec());
Ok(vec![])
}
async fn execute_function_call(
&self,
_function_name: &str,
_args: &[serde_json::Value],
) -> Result<Vec<std::collections::HashMap<String, serde_json::Value>>> {
Ok(vec![])
}
}
impl SupportsMutations for CapturingMockAdapter {}
pub struct MockAdapter {
pub mock_results: Vec<JsonbValue>,
pub view_responses: std::collections::HashMap<String, Vec<JsonbValue>>,
}
impl MockAdapter {
pub fn new(mock_results: Vec<JsonbValue>) -> Self {
Self {
mock_results,
view_responses: std::collections::HashMap::new(),
}
}
pub fn with_view(mut self, view: &str, results: Vec<JsonbValue>) -> Self {
self.view_responses.insert(view.to_string(), results);
self
}
}
#[async_trait]
impl DatabaseAdapter for MockAdapter {
async fn execute_with_projection(
&self,
view: &str,
_projection: Option<&crate::schema::SqlProjectionHint>,
where_clause: Option<&WhereClause>,
limit: Option<u32>,
_offset: Option<u32>,
_order_by: Option<&[OrderByClause]>,
) -> Result<Vec<JsonbValue>> {
self.execute_where_query(view, where_clause, limit, None, None).await
}
async fn execute_where_query(
&self,
view: &str,
_where_clause: Option<&WhereClause>,
_limit: Option<u32>,
_offset: Option<u32>,
_order_by: Option<&[OrderByClause]>,
) -> Result<Vec<JsonbValue>> {
if let Some(results) = self.view_responses.get(view) {
return Ok(results.clone());
}
Ok(self.mock_results.clone())
}
async fn health_check(&self) -> Result<()> {
Ok(())
}
fn database_type(&self) -> DatabaseType {
DatabaseType::PostgreSQL
}
fn pool_metrics(&self) -> PoolMetrics {
PoolMetrics {
total_connections: 1,
active_connections: 0,
idle_connections: 1,
waiting_requests: 0,
}
}
async fn execute_raw_query(
&self,
_sql: &str,
) -> Result<Vec<std::collections::HashMap<String, serde_json::Value>>> {
Ok(vec![])
}
async fn execute_parameterized_aggregate(
&self,
_sql: &str,
_params: &[serde_json::Value],
) -> Result<Vec<std::collections::HashMap<String, serde_json::Value>>> {
Ok(vec![])
}
async fn execute_function_call(
&self,
_function_name: &str,
_args: &[serde_json::Value],
) -> Result<Vec<std::collections::HashMap<String, serde_json::Value>>> {
Ok(vec![])
}
}
impl SupportsMutations for MockAdapter {}
pub struct ReadOnlyMockAdapter;
#[async_trait]
impl DatabaseAdapter for ReadOnlyMockAdapter {
async fn execute_with_projection(
&self,
view: &str,
_projection: Option<&crate::schema::SqlProjectionHint>,
where_clause: Option<&WhereClause>,
limit: Option<u32>,
_offset: Option<u32>,
_order_by: Option<&[OrderByClause]>,
) -> Result<Vec<JsonbValue>> {
self.execute_where_query(view, where_clause, limit, None, None).await
}
async fn execute_where_query(
&self,
_view: &str,
_where_clause: Option<&WhereClause>,
_limit: Option<u32>,
_offset: Option<u32>,
_order_by: Option<&[OrderByClause]>,
) -> Result<Vec<JsonbValue>> {
Ok(vec![])
}
async fn health_check(&self) -> Result<()> {
Ok(())
}
fn database_type(&self) -> DatabaseType {
DatabaseType::SQLite
}
fn pool_metrics(&self) -> PoolMetrics {
PoolMetrics {
total_connections: 1,
active_connections: 0,
idle_connections: 1,
waiting_requests: 0,
}
}
async fn execute_raw_query(
&self,
_sql: &str,
) -> Result<Vec<std::collections::HashMap<String, serde_json::Value>>> {
Ok(vec![])
}
async fn execute_parameterized_aggregate(
&self,
_sql: &str,
_params: &[serde_json::Value],
) -> Result<Vec<std::collections::HashMap<String, serde_json::Value>>> {
Ok(vec![])
}
fn supports_mutations(&self) -> bool {
false
}
}
pub fn test_schema() -> CompiledSchema {
let mut schema = CompiledSchema::new();
schema.queries.push(QueryDefinition {
name: "users".to_string(),
return_type: "User".to_string(),
returns_list: true,
nullable: false,
arguments: Vec::new(),
sql_source: Some("v_user".to_string()),
description: None,
auto_params: AutoParams::default(),
deprecation: None,
jsonb_column: "data".to_string(),
relay: false,
relay_cursor_column: None,
relay_cursor_type: CursorType::default(),
inject_params: IndexMap::default(),
cache_ttl_seconds: None,
additional_views: vec![],
requires_role: None,
rest_path: None,
rest_method: None,
native_columns: HashMap::new(),
});
schema
}
pub fn mock_user_results() -> Vec<JsonbValue> {
vec![
JsonbValue::new(serde_json::json!({"id": "1", "name": "Alice"})),
JsonbValue::new(serde_json::json!({"id": "2", "name": "Bob"})),
]
}