use crate::common::TestDatabaseManager;
use anyhow::Result;
use codex_memory::{mcp_server::MCPHandlers, Storage};
use serde_json::{json, Value};
use std::sync::Arc;
#[tokio::test]
async fn test_malformed_json_requests() -> Result<()> {
let mut manager = TestDatabaseManager::new()?;
let pool = manager.setup_test_database().await?;
let storage = Arc::new(Storage::new(pool));
let handlers = MCPHandlers::new(storage);
let malformed_cases = vec![
json!({"content": null}), json!({"content": 123}), json!({"content": true}), json!({"content": []}), json!({"content": {}}), json!({"context": 123, "content": "test"}), json!({"summary": [], "content": "test"}), json!({"tags": "not_array", "content": "test"}), json!({"tags": [123, 456], "content": "test"}), json!({"tags": true, "content": "test"}), ];
for (i, params) in malformed_cases.iter().enumerate() {
println!("Testing malformed case #{}: {}", i, params);
let result = handlers
.handle_tool_call("store_memory", params.clone())
.await;
match result {
Ok(_) => {
println!(" Case #{} unexpectedly succeeded", i);
}
Err(e) => {
println!(" Case #{} failed as expected: {}", i, e);
let error_msg = e.to_string().to_lowercase();
assert!(
error_msg.contains("missing")
|| error_msg.contains("invalid")
|| error_msg.contains("type")
|| error_msg.contains("parameter"),
"Error should be descriptive: {}",
e
);
}
}
}
manager.cleanup().await?;
Ok(())
}
#[tokio::test]
async fn test_oversized_mcp_payload() -> Result<()> {
let mut manager = TestDatabaseManager::new()?;
let pool = manager.setup_test_database().await?;
let storage = Arc::new(Storage::new(pool));
let handlers = MCPHandlers::new(storage);
let large_content = "Large content ".repeat(100000); let large_context = "Large context ".repeat(50000); let large_summary = "Large summary ".repeat(10000);
let large_tags: Vec<Value> = (0..10000)
.map(|i| json!(format!("very_long_tag_name_that_takes_up_space_{:06}", i)))
.collect();
let oversized_payload = json!({
"content": large_content,
"context": large_context,
"summary": large_summary,
"tags": large_tags
});
println!("Testing oversized payload (~2.5MB)");
let result = handlers
.handle_tool_call("store_memory", oversized_payload)
.await;
match result {
Ok(response) => {
println!("Oversized payload succeeded: {}", response);
if let Some(id) = response["id"].as_str() {
let get_params = json!({"id": id});
let retrieved = handlers.handle_tool_call("get_memory", get_params).await?;
let retrieved_content = retrieved["content"].as_str().unwrap();
assert_eq!(retrieved_content.len(), large_content.len());
}
}
Err(e) => {
println!("Oversized payload failed (may be expected): {}", e);
}
}
manager.cleanup().await?;
Ok(())
}
#[tokio::test]
async fn test_invalid_tier_values() -> Result<()> {
let mut manager = TestDatabaseManager::new()?;
let pool = manager.setup_test_database().await?;
let storage = Arc::new(Storage::new(pool));
let handlers = MCPHandlers::new(storage);
let invalid_tiers = vec![
"invalid_tier",
"hot", "archive", "WORKING", "Cold", "", "working123", "work-ing", ];
for tier in invalid_tiers {
println!("Testing invalid tier: '{}'", tier);
let params = json!({
"content": format!("Content with invalid tier: {}", tier),
"context": format!("Context for invalid tier test: {}", tier),
"summary": format!("Summary for invalid tier test: {}", tier),
"tags": []
});
let result = handlers.handle_tool_call("store_memory", params).await;
match result {
Ok(_) => {
println!(
" Content with tier reference '{}' stored successfully",
tier
);
}
Err(e) => {
panic!("Unexpected failure for tier reference '{}': {}", tier, e);
}
}
}
manager.cleanup().await?;
Ok(())
}
#[tokio::test]
async fn test_concurrent_mcp_requests() -> Result<()> {
let mut manager = TestDatabaseManager::new()?;
let pool = manager.setup_test_database().await?;
let storage = Arc::new(Storage::new(pool));
let handlers = Arc::new(MCPHandlers::new(storage));
let mut handles = vec![];
for i in 0..50 {
let handlers_clone = handlers.clone();
let handle = tokio::spawn(async move {
let params = json!({
"content": format!("Concurrent MCP request #{}", i),
"context": format!("Context for request #{}", i),
"summary": format!("Summary for request #{}", i),
"tags": [format!("concurrent-{}", i), "stress-test"]
});
handlers_clone
.handle_tool_call("store_memory", params)
.await
});
handles.push(handle);
}
let mut successes = 0;
let mut failures = 0;
for handle in handles {
match handle.await {
Ok(Ok(_)) => successes += 1,
Ok(Err(e)) => {
println!("MCP request failed: {}", e);
failures += 1;
}
Err(e) => {
println!("Task failed: {}", e);
failures += 1;
}
}
}
println!(
"Concurrent MCP requests: {} succeeded, {} failed",
successes, failures
);
assert!(
successes > 40,
"Most concurrent MCP requests should succeed"
);
manager.cleanup().await?;
Ok(())
}
#[tokio::test]
async fn test_missing_required_parameters() -> Result<()> {
let mut manager = TestDatabaseManager::new()?;
let pool = manager.setup_test_database().await?;
let storage = Arc::new(Storage::new(pool));
let handlers = MCPHandlers::new(storage);
let result = handlers.handle_tool_call("store_memory", json!({})).await;
assert!(result.is_err());
assert!(result.unwrap_err().to_string().contains("Missing content"));
let result = handlers
.handle_tool_call(
"store_memory",
json!({
"context": "Context without content",
"summary": "Summary without content",
"tags": ["tag1", "tag2"]
}),
)
.await;
assert!(result.is_err());
assert!(result.unwrap_err().to_string().contains("Missing content"));
let result = handlers.handle_tool_call("get_memory", json!({})).await;
assert!(result.is_err());
assert!(result.unwrap_err().to_string().contains("Missing id"));
let result = handlers.handle_tool_call("delete_memory", json!({})).await;
assert!(result.is_err());
assert!(result.unwrap_err().to_string().contains("Missing id"));
manager.cleanup().await?;
Ok(())
}
#[tokio::test]
async fn test_invalid_uuid_formats() -> Result<()> {
let mut manager = TestDatabaseManager::new()?;
let pool = manager.setup_test_database().await?;
let storage = Arc::new(Storage::new(pool));
let handlers = MCPHandlers::new(storage);
let invalid_uuids = vec![
"not-a-uuid",
"12345678-1234-1234-1234", "12345678-1234-1234-1234-12345678901234", "gggggggg-1234-1234-1234-123456789012", "12345678_1234_1234_1234_123456789012", "", "null",
"undefined",
"00000000-0000-0000-0000-000000000000", "123e4567-e89b-12d3-a456-426614174000", ];
for uuid in invalid_uuids {
println!("Testing invalid UUID: '{}'", uuid);
let params = json!({"id": uuid});
let result = handlers
.handle_tool_call("get_memory", params.clone())
.await;
match result {
Ok(_) => {
println!(" UUID '{}' was accepted but should return not found", uuid);
}
Err(e) => {
let error_msg = e.to_string().to_lowercase();
if error_msg.contains("invalid uuid") {
println!(" Invalid UUID '{}' rejected correctly", uuid);
} else if error_msg.contains("not found") {
println!(" Valid UUID '{}' not found (expected)", uuid);
} else {
println!(" UUID '{}' failed: {}", uuid, e);
}
}
}
let result = handlers.handle_tool_call("delete_memory", params).await;
match result {
Ok(response) => {
let deleted = response["deleted"].as_bool().unwrap_or(false);
if uuid == "00000000-0000-0000-0000-000000000000"
|| uuid == "123e4567-e89b-12d3-a456-426614174000"
{
assert!(
!deleted,
"Delete of non-existent memory should show deleted=false"
);
println!(
" Valid UUID '{}' delete returned success with deleted=false (expected)",
uuid
);
} else {
panic!("Delete with invalid UUID '{}' should fail", uuid);
}
}
Err(e) => {
let error_msg = e.to_string().to_lowercase();
assert!(
error_msg.contains("invalid uuid"),
"Delete with invalid UUID '{}' failed for wrong reason: {}",
uuid,
e
);
println!(" Invalid UUID '{}' delete rejected correctly", uuid);
}
}
}
manager.cleanup().await?;
Ok(())
}
#[tokio::test]
async fn test_extremely_nested_json() -> Result<()> {
let mut manager = TestDatabaseManager::new()?;
let pool = manager.setup_test_database().await?;
let storage = Arc::new(Storage::new(pool));
let handlers = MCPHandlers::new(storage);
let mut nested_json = json!("deep");
for _ in 0..100 {
nested_json = json!({"level": nested_json});
}
let params = json!({
"content": "Content with deeply nested context",
"context": nested_json.to_string(), "summary": "Summary for deeply nested context test",
"tags": ["nested", "json", "stress"]
});
println!("Testing deeply nested JSON context");
let result = handlers.handle_tool_call("store_memory", params).await;
match result {
Ok(response) => {
println!("Nested JSON succeeded");
if let Some(id) = response["id"].as_str() {
let get_params = json!({"id": id});
let retrieved = handlers.handle_tool_call("get_memory", get_params).await?;
let retrieved_context = retrieved["context"].as_str().unwrap();
assert!(
retrieved_context.contains("level"),
"Nested JSON should be preserved"
);
}
}
Err(e) => {
println!("Nested JSON failed: {}", e);
}
}
manager.cleanup().await?;
Ok(())
}
#[tokio::test]
async fn test_tool_call_with_extra_parameters() -> Result<()> {
let mut manager = TestDatabaseManager::new()?;
let pool = manager.setup_test_database().await?;
let storage = Arc::new(Storage::new(pool));
let handlers = MCPHandlers::new(storage);
let params_with_extra = json!({
"content": "Test content with extra params",
"context": "Test context",
"summary": "Test summary",
"tags": ["test"],
"extra_param": "should be ignored",
"another_extra": 123,
"nested_extra": {"key": "value"},
"unknown_tier": "super_hot",
"random_field": [1, 2, 3]
});
let result = handlers
.handle_tool_call("store_memory", params_with_extra)
.await;
match result {
Ok(response) => {
println!("Extra parameters were ignored successfully");
if let Some(id) = response["id"].as_str() {
let get_params = json!({"id": id});
let retrieved = handlers.handle_tool_call("get_memory", get_params).await?;
assert_eq!(retrieved["content"], "Test content with extra params");
assert_eq!(retrieved["context"], "Test context");
assert_eq!(retrieved["summary"], "Test summary");
}
}
Err(e) => {
println!("Extra parameters caused failure: {}", e);
}
}
manager.cleanup().await?;
Ok(())
}