#[cfg(all(test, feature = "storage"))]
mod tests {
use std::time::Duration;
use testcontainers::runners::AsyncRunner;
use testcontainers_modules::postgres::Postgres;
use vkteams_bot_cli::commands::Command;
use vkteams_bot_cli::commands::storage::*;
#[tokio::test]
#[serial_test::serial]
async fn test_storage_commands_with_real_database() {
let init_commands = StorageCommands::Database {
action: DatabaseAction::Init,
};
let init_response = init_commands.handle_database(&DatabaseAction::Init).await;
assert_eq!(init_response.command, "database-init");
assert!(!init_response.success);
let stats_commands = StorageCommands::Database {
action: DatabaseAction::Stats {
chat_id: None,
since: None,
},
};
let stats_response = stats_commands
.handle_database(&DatabaseAction::Stats {
chat_id: None,
since: None,
})
.await;
assert_eq!(stats_response.command, "database-stats");
assert!(!stats_response.success);
let search_commands = StorageCommands::Search {
action: SearchAction::Text {
query: "test search query".to_string(),
chat_id: None,
limit: 10,
},
};
let search_response = search_commands
.handle_search(&SearchAction::Text {
query: "test search query".to_string(),
chat_id: None,
limit: 10,
})
.await;
assert_eq!(search_response.command, "search-text");
assert!(!search_response.success);
let context_commands = StorageCommands::Context {
action: ContextAction::Get {
chat_id: Some("test_chat".to_string()),
context_type: ContextType::Recent,
timeframe: None,
},
};
let context_response = context_commands
.handle_context(&ContextAction::Get {
chat_id: Some("test_chat".to_string()),
context_type: ContextType::Recent,
timeframe: None,
})
.await;
assert_eq!(context_response.command, "context-get");
assert!(!context_response.success);
let cleanup_commands = StorageCommands::Database {
action: DatabaseAction::Cleanup {
older_than_days: 30,
},
};
let cleanup_response = cleanup_commands
.handle_database(&DatabaseAction::Cleanup {
older_than_days: 30,
})
.await;
assert_eq!(cleanup_response.command, "database-cleanup");
assert!(!cleanup_response.success);
}
#[tokio::test]
#[serial_test::serial]
async fn test_storage_config_with_environment_variables() {
let postgres_container = Postgres::default()
.start()
.await
.expect("Failed to start PostgreSQL container");
tokio::time::sleep(Duration::from_secs(3)).await;
let host_port = postgres_container.get_host_port_ipv4(5432).await.unwrap();
let _database_url =
format!("postgresql://postgres:postgres@localhost:{host_port}/postgres");
let storage_commands = StorageCommands::Database {
action: DatabaseAction::Init,
};
let _init_response = storage_commands
.handle_database(&DatabaseAction::Init)
.await;
}
#[tokio::test]
#[serial_test::serial]
async fn test_storage_error_handling() {
let storage_commands = StorageCommands::Database {
action: DatabaseAction::Init,
};
let _init_response = storage_commands
.handle_database(&DatabaseAction::Init)
.await;
}
#[cfg(feature = "vector-search")]
#[tokio::test]
#[serial_test::serial]
async fn test_semantic_search_feature_availability() {
let postgres_container = Postgres::default()
.start()
.await
.expect("Failed to start PostgreSQL container");
tokio::time::sleep(Duration::from_secs(3)).await;
let host_port = postgres_container.get_host_port_ipv4(5432).await.unwrap();
let _database_url =
format!("postgresql://postgres:postgres@localhost:{host_port}/postgres");
let search_commands = StorageCommands::Search {
action: SearchAction::Semantic {
query: "test semantic search".to_string(),
chat_id: None,
limit: 5,
},
};
let _search_response = search_commands
.handle_search(&SearchAction::Semantic {
query: "test semantic search".to_string(),
chat_id: None,
limit: 5,
})
.await;
}
#[cfg(not(feature = "vector-search"))]
#[tokio::test]
#[serial_test::serial]
async fn test_semantic_search_feature_disabled() {
let search_commands = StorageCommands::Search {
action: SearchAction::Semantic {
query: "test semantic search".to_string(),
chat_id: None,
limit: 5,
},
};
let _search_response = search_commands
.handle_search(&SearchAction::Semantic {
query: "test semantic search".to_string(),
chat_id: None,
limit: 5,
})
.await;
assert!(
!_search_response.success,
"Semantic search should be disabled without vector-search feature"
);
assert!(
_search_response
.error
.unwrap()
.contains("Vector search feature not enabled")
);
}
#[tokio::test]
#[serial_test::serial]
async fn test_command_validation() {
let valid_commands = vec![
StorageCommands::Database {
action: DatabaseAction::Init,
},
StorageCommands::Database {
action: DatabaseAction::Stats {
chat_id: Some("test_chat".to_string()),
since: None,
},
},
StorageCommands::Database {
action: DatabaseAction::Cleanup {
older_than_days: 30,
},
},
StorageCommands::Search {
action: SearchAction::Text {
query: "test".to_string(),
chat_id: None,
limit: 10,
},
},
StorageCommands::Context {
action: ContextAction::Get {
chat_id: Some("test".to_string()),
context_type: ContextType::Recent,
timeframe: None,
},
},
];
for command in valid_commands {
let validation_result = command.validate();
assert!(
validation_result.is_ok(),
"Command validation should pass: {command:?}"
);
}
}
#[tokio::test]
#[serial_test::serial]
async fn test_command_names() {
let commands_with_names = vec![
(
StorageCommands::Database {
action: DatabaseAction::Init,
},
"database",
),
(
StorageCommands::Search {
action: SearchAction::Text {
query: "test".to_string(),
chat_id: None,
limit: 10,
},
},
"search",
),
(
StorageCommands::Context {
action: ContextAction::Get {
chat_id: Some("test".to_string()),
context_type: ContextType::Recent,
timeframe: None,
},
},
"context",
),
];
for (command, expected_name) in commands_with_names {
assert_eq!(command.name(), expected_name);
}
}
}