mod common;
use common::{ContextBuilder, StepBuilder, setup_test_memory};
use do_memory_core::{ExecutionResult, ExecutionStep, TaskContext, TaskOutcome, TaskType};
use uuid::Uuid;
#[derive(Debug)]
struct LargeInputTest {
name: &'static str,
description_size: usize,
metadata_count: usize,
step_count: usize,
json_params_count: usize,
tags_count: usize,
}
#[allow(clippy::excessive_nesting)]
#[tokio::test]
#[ignore = "slow integration test - run with --ignored or in release CI"]
async fn should_handle_large_inputs_without_data_loss() {
let test_cases = vec![
LargeInputTest {
name: "Large description (1MB)",
description_size: 1_000_000,
metadata_count: 0,
step_count: 1,
json_params_count: 0,
tags_count: 0,
},
LargeInputTest {
name: "Excessive metadata (1000 fields)",
description_size: 100,
metadata_count: 1000,
step_count: 1,
json_params_count: 0,
tags_count: 0,
},
LargeInputTest {
name: "Many steps (100)",
description_size: 100,
metadata_count: 0,
step_count: 100,
json_params_count: 0,
tags_count: 0,
},
LargeInputTest {
name: "Very long sequence (500 steps)",
description_size: 100,
metadata_count: 0,
step_count: 500,
json_params_count: 0,
tags_count: 0,
},
LargeInputTest {
name: "Large JSON parameters (1000 fields)",
description_size: 100,
metadata_count: 0,
step_count: 1,
json_params_count: 1000,
tags_count: 0,
},
LargeInputTest {
name: "Many tags (100)",
description_size: 100,
metadata_count: 0,
step_count: 1,
json_params_count: 0,
tags_count: 100,
},
];
for test_case in test_cases {
println!("Testing: {}", test_case.name);
let memory = setup_test_memory();
let description = if test_case.description_size > 100 {
"x".repeat(test_case.description_size)
} else {
"Test".to_string()
};
let mut context_builder = ContextBuilder::new("test-domain").language("rust");
if test_case.tags_count > 0 {
for i in 0..test_case.tags_count {
context_builder = context_builder.tag(format!("tag_{i}"));
}
}
let context = context_builder.build();
let id = memory
.start_episode(description.clone(), context.clone(), TaskType::Other)
.await;
if test_case.description_size > 100 {
let episode = memory.get_episode(id).await.unwrap();
assert_eq!(
episode.task_description.len(),
test_case.description_size,
"Should store full description"
);
}
if test_case.metadata_count > 0 {
let mut episode = memory.get_episode(id).await.unwrap();
for i in 0..test_case.metadata_count {
episode
.metadata
.insert(format!("field_{i}"), format!("value_{i}"));
}
let json_result = serde_json::to_string(&episode);
assert!(
json_result.is_ok(),
"Should serialize with {} metadata fields",
test_case.metadata_count
);
}
for i in 0..test_case.step_count {
let mut step_builder =
StepBuilder::new(i + 1, format!("tool_{i}"), format!("action_{i}"));
if test_case.json_params_count > 0 && i == 0 {
let mut large_params = serde_json::Map::new();
for j in 0..test_case.json_params_count {
large_params.insert(
format!("param_{j}"),
serde_json::json!({
"index": j,
"data": format!("value_{}", j),
"nested": {"level1": {"level2": {"value": j * 2}}}
}),
);
}
step_builder = step_builder.parameters(serde_json::Value::Object(large_params));
}
let step = step_builder.success("OK").build();
memory.log_step(id, step).await;
}
memory.flush_steps(id).await.unwrap();
let episode = memory.get_episode(id).await.unwrap();
assert_eq!(
episode.steps.len(),
test_case.step_count,
"Should store all {} steps for test: {}",
test_case.step_count,
test_case.name
);
if test_case.tags_count > 0 {
assert_eq!(
episode.context.tags.len(),
test_case.tags_count,
"Should store all {} tags",
test_case.tags_count
);
}
if test_case.step_count <= 100 {
memory
.complete_episode(
id,
TaskOutcome::Success {
verdict: "Success".to_string(),
artifacts: vec![],
},
)
.await
.unwrap();
}
}
}
#[derive(Debug)]
struct SpecialCharTest {
name: &'static str,
description: String,
tool: String,
action: String,
should_test_in_step: bool,
}
#[tokio::test]
#[ignore = "slow integration test - run with --ignored or in release CI"]
async fn should_handle_special_characters_and_edge_cases_gracefully() {
let test_cases = vec![
SpecialCharTest {
name: "Empty description",
description: String::new(),
tool: "tool".to_string(),
action: "action".to_string(),
should_test_in_step: false,
},
SpecialCharTest {
name: "Unicode and emojis",
description: "Task with 䏿–‡ émojis 🎉 and symbols @#$%^&*()".to_string(),
tool: "tool_🔧".to_string(),
action: "Action with 特殊文å—".to_string(),
should_test_in_step: true,
},
SpecialCharTest {
name: "Null bytes",
description: format!("Task{}\0with null", '\0'),
tool: "tool".to_string(),
action: "action".to_string(),
should_test_in_step: true,
},
SpecialCharTest {
name: "Whitespace only",
description: " \n\t\r\n ".to_string(),
tool: " \t ".to_string(),
action: " \n ".to_string(),
should_test_in_step: true,
},
];
for test_case in test_cases {
println!("Testing: {}", test_case.name);
let memory = setup_test_memory();
let id = memory
.start_episode(
test_case.description.clone(),
TaskContext::default(),
TaskType::Other,
)
.await;
let episode = memory.get_episode(id).await.unwrap();
assert_eq!(
episode.task_description, test_case.description,
"Should preserve description for: {}",
test_case.name
);
if test_case.should_test_in_step {
let mut step = ExecutionStep::new(1, test_case.tool.clone(), test_case.action.clone());
step.result = Some(ExecutionResult::Success {
output: format!("Output for {}", test_case.name),
});
if test_case.name == "Null bytes" {
step.metadata
.insert("key\0with_null".to_string(), "value\0too".to_string());
}
memory.log_step(id, step).await;
memory.flush_steps(id).await.unwrap();
let updated = memory.get_episode(id).await.unwrap();
assert_eq!(updated.steps[0].tool, test_case.tool);
assert_eq!(updated.steps[0].action, test_case.action);
if test_case.name == "Null bytes" {
assert!(updated.steps[0].metadata.contains_key("key\0with_null"));
}
}
memory
.complete_episode(
id,
TaskOutcome::Success {
verdict: "Success".to_string(),
artifacts: vec![],
},
)
.await
.unwrap();
let completed = memory.get_episode(id).await.unwrap();
assert!(completed.is_complete());
}
}
#[tokio::test]
async fn should_handle_deeply_nested_json_structures() {
let memory = setup_test_memory();
let mut nested = serde_json::json!("deepest");
for _ in 0..50 {
nested = serde_json::json!({"nested": nested});
}
let id = memory
.start_episode("Test".to_string(), TaskContext::default(), TaskType::Other)
.await;
let step = StepBuilder::new(1, "tool", "action")
.parameters(nested.clone())
.success("OK")
.build();
memory.log_step(id, step).await;
memory.flush_steps(id).await.unwrap();
let episode = memory.get_episode(id).await.unwrap();
assert_eq!(
episode.steps[0].parameters, nested,
"Should preserve deeply nested JSON"
);
let json_result = serde_json::to_string(&episode);
assert!(json_result.is_ok(), "Should serialize nested JSON");
}
#[tokio::test]
async fn should_provide_type_safe_uuid_handling() {
let memory = setup_test_memory();
let valid_id = Uuid::new_v4();
let result = memory.get_episode(valid_id).await;
assert!(
result.is_err(),
"Non-existent UUID should return NotFound error"
);
match result {
Err(do_memory_core::Error::NotFound(id)) => {
assert_eq!(id, valid_id, "Error should contain the UUID we queried");
}
_ => panic!("Expected NotFound error"),
}
let real_id = memory
.start_episode(
"Real task".to_string(),
TaskContext::default(),
TaskType::Other,
)
.await;
let episode = memory.get_episode(real_id).await;
assert!(
episode.is_ok(),
"Valid UUID for existing episode should work"
);
}