#![allow(clippy::cast_precision_loss)]
use do_memory_core::{Episode, ExecutionResult, ExecutionStep, TaskContext, TaskOutcome, TaskType};
use proptest::prelude::*;
fn arb_task_description() -> impl Strategy<Value = String> {
"[a-zA-Z ]{5,50}"
}
fn arb_domain() -> impl Strategy<Value = String> {
prop_oneof![
Just("testing".to_string()),
Just("development".to_string()),
Just("security".to_string()),
Just("documentation".to_string()),
Just("refactoring".to_string()),
]
}
fn arb_step_count() -> impl Strategy<Value = usize> {
0..20usize
}
proptest! {
#[test]
fn episode_ids_are_unique(
desc1 in arb_task_description(),
desc2 in arb_task_description(),
) {
let context = TaskContext::default();
let ep1 = Episode::new(desc1, context.clone(), TaskType::Testing);
let ep2 = Episode::new(desc2, context, TaskType::Testing);
assert_ne!(ep1.episode_id, ep2.episode_id);
}
#[test]
fn step_order_preserved(num_steps in arb_step_count()) {
let context = TaskContext::default();
let mut episode = Episode::new("Test task".to_string(), context, TaskType::Testing);
for i in 0..num_steps {
let step = ExecutionStep::new(i + 1, format!("tool_{i}"), format!("action_{i}"));
episode.add_step(step);
}
assert_eq!(episode.steps.len(), num_steps);
for (i, step) in episode.steps.iter().enumerate() {
assert_eq!(step.step_number, i + 1);
}
}
#[test]
fn episode_maintains_task_type(
desc in arb_task_description(),
task_type_idx in 0..5usize,
) {
let context = TaskContext::default();
let task_type = match task_type_idx {
0 => TaskType::CodeGeneration,
1 => TaskType::Testing,
2 => TaskType::Refactoring,
3 => TaskType::Documentation,
_ => TaskType::Debugging,
};
let episode = Episode::new(desc, context, task_type);
assert_eq!(episode.task_type, task_type);
}
#[test]
fn episode_domain_preserved(domain in arb_domain()) {
let context = TaskContext {
domain: domain.clone(),
..TaskContext::default()
};
let episode = Episode::new("Test task".to_string(), context, TaskType::Testing);
assert_eq!(episode.context.domain, domain);
}
#[test]
fn outcome_verdict_bounded(verdict_len in 0..200usize) {
let context = TaskContext::default();
let mut episode = Episode::new("Test task".to_string(), context, TaskType::Testing);
let verdict: String = "x".repeat(verdict_len);
let outcome = TaskOutcome::Success {
verdict: verdict.clone(),
artifacts: vec![],
};
episode.complete(outcome);
assert!(episode.end_time.is_some());
}
#[test]
fn duplicate_step_numbers_handled(
tool in "[a-z_]{3,10}",
action in "[a-zA-Z ]{5,20}",
) {
let context = TaskContext::default();
let mut episode = Episode::new("Test task".to_string(), context, TaskType::Testing);
let step1 = ExecutionStep::new(1, tool.clone(), action.clone());
let step2 = ExecutionStep::new(1, tool, action);
episode.add_step(step1);
episode.add_step(step2);
assert_eq!(episode.steps.len(), 2);
}
}
#[test]
fn test_step_results_dont_affect_validity() {
let context = TaskContext::default();
let mut episode = Episode::new("Test".to_string(), context, TaskType::Testing);
for i in 0..5 {
let mut step = ExecutionStep::new(i + 1, format!("tool_{i}"), format!("action_{i}"));
step.result = if i % 2 == 0 {
Some(ExecutionResult::Success {
output: "OK".to_string(),
})
} else {
Some(ExecutionResult::Error {
message: "Failed".to_string(),
})
};
episode.add_step(step);
}
episode.complete(TaskOutcome::Success {
verdict: "Done".to_string(),
artifacts: vec![],
});
assert!(episode.is_complete());
}