mod test_helpers;
use jjj::models::{Problem, ProjectConfig, Solution};
use std::process::Command;
use tempfile::TempDir;
use test_helpers::jj_available;
#[allow(dead_code)]
fn create_test_repo() -> Option<TempDir> {
if !jj_available() {
return None;
}
let temp_dir = TempDir::new().ok()?;
let repo_path = temp_dir.path();
let status = Command::new("jj")
.args(&["git", "init", "--colocate"])
.current_dir(repo_path)
.status()
.ok()?;
if !status.success() {
return None;
}
Command::new("jj")
.args(&["config", "set", "--repo", "user.name", "Test User"])
.current_dir(repo_path)
.status()
.ok()?;
Command::new("jj")
.args(&["config", "set", "--repo", "user.email", "test@example.com"])
.current_dir(repo_path)
.status()
.ok()?;
Some(temp_dir)
}
#[test]
fn test_storage_requires_jj() {
if !jj_available() {
println!("jj not available - integration tests skipped");
return;
}
println!("jj is available for integration tests");
}
#[test]
fn test_project_config_roundtrip() {
let mut config = ProjectConfig::default();
config.name = Some("Test Project".to_string());
let toml_str = toml::to_string(&config).expect("Failed to serialize");
let loaded: ProjectConfig = toml::from_str(&toml_str).expect("Failed to deserialize");
assert_eq!(loaded.name, config.name);
}
#[test]
fn test_problem_creation() {
let problem = Problem::new("p1".to_string(), "Test problem".to_string());
assert_eq!(problem.id, "p1");
assert_eq!(problem.title, "Test problem");
assert!(problem.is_open());
assert!(problem.solution_ids.is_empty());
assert!(problem.child_ids.is_empty());
assert!(problem.parent_id.is_none());
}
#[test]
fn test_solution_creation() {
let solution = Solution::new(
"s1".to_string(),
"Test solution".to_string(),
"p1".to_string(),
);
assert_eq!(solution.id, "s1");
assert_eq!(solution.title, "Test solution");
assert_eq!(solution.problem_id, "p1");
assert!(solution.is_proposed());
}
#[test]
fn test_solution_status_transitions() {
let mut solution = Solution::new(
"s1".to_string(),
"Test solution".to_string(),
"p1".to_string(),
);
assert!(solution.is_proposed());
solution.submit().unwrap();
assert!(solution.is_submitted());
solution.approve().unwrap();
assert!(solution.is_approved());
}
#[test]
fn test_solution_refute() {
let mut solution = Solution::new(
"s1".to_string(),
"Test solution".to_string(),
"p1".to_string(),
);
solution.submit().unwrap();
solution.withdraw().unwrap();
assert!(solution.is_withdrawn());
}
#[test]
fn test_solution_attach_change() {
let mut solution = Solution::new(
"s1".to_string(),
"Test solution".to_string(),
"p1".to_string(),
);
solution.attach_change("abc123".to_string());
assert_eq!(solution.change_ids.len(), 1);
assert!(solution.change_ids.contains(&"abc123".to_string()));
}
#[test]
fn test_problem_status_transitions() {
let mut problem = Problem::new("p1".to_string(), "Test problem".to_string());
assert!(problem.is_open());
problem
.try_set_status(jjj::models::ProblemStatus::InProgress)
.unwrap();
assert!(problem.is_in_progress());
problem
.try_set_status(jjj::models::ProblemStatus::Solved)
.unwrap();
assert!(problem.is_resolved());
}
#[test]
fn test_problem_dissolve() {
let mut problem = Problem::new("p1".to_string(), "Test problem".to_string());
problem
.try_set_status(jjj::models::ProblemStatus::Dissolved)
.unwrap();
assert!(problem.is_resolved());
}
#[test]
fn test_problem_dag_structure() {
let mut parent = Problem::new("p1".to_string(), "Parent problem".to_string());
let mut child = Problem::new("p2".to_string(), "Child problem".to_string());
child.set_parent(Some("p1".to_string()));
parent.add_child("p2".to_string());
assert!(child.parent_id.as_deref() == Some("p1"));
assert!(parent.child_ids.contains(&"p2".to_string()));
}
#[test]
fn test_problem_milestone_assignment() {
let mut problem = Problem::new("p1".to_string(), "Test problem".to_string());
assert!(problem.milestone_id.is_none());
problem.set_milestone(Some("m1".to_string()));
assert_eq!(problem.milestone_id.as_deref(), Some("m1"));
problem.set_milestone(None);
assert!(problem.milestone_id.is_none());
}
#[test]
fn test_entity_file_naming() {
let problem_ids = vec!["p1", "p100", "p9999"];
let solution_ids = vec!["s1", "s100", "s9999"];
for id in problem_ids {
let filename = format!("{}.md", id);
assert!(filename.ends_with(".md"));
assert!(filename.starts_with("p"));
}
for id in solution_ids {
let filename = format!("{}.md", id);
assert!(filename.ends_with(".md"));
assert!(filename.starts_with("s"));
}
}
#[test]
fn test_config_is_human_readable() {
let mut config = ProjectConfig::default();
config.name = Some("My Project".to_string());
let toml_str = toml::to_string(&config).expect("Failed");
assert!(toml_str.contains("My Project"));
let modified = toml_str.replace("My Project", "Edited Project");
let loaded: ProjectConfig = toml::from_str(&modified).expect("Failed");
assert_eq!(loaded.name.as_deref(), Some("Edited Project"));
}
#[test]
fn test_timestamps_preserved() {
let problem = Problem::new("p1".to_string(), "Test".to_string());
let created = problem.created_at;
assert!(problem.updated_at >= created);
}