mod common;
#[test]
fn test_task_create_and_list() {
let (dir, bin) = common::setup_test_project("task_test");
std::process::Command::new(&bin)
.args(["init", "--force"])
.current_dir(&dir)
.output()
.unwrap();
std::process::Command::new(&bin)
.args([
"agent",
"register",
"--name",
"tester",
"--provider",
"test",
])
.env("CARRYCTX_AGENT", "tester")
.current_dir(&dir)
.output()
.unwrap();
let create = std::process::Command::new(&bin)
.args(["task", "create", "--title", "Integration test task"])
.env("CARRYCTX_AGENT", "tester")
.current_dir(&dir)
.output()
.unwrap();
assert!(create.status.success(), "task create should succeed");
let list = std::process::Command::new(&bin)
.args(["task", "list", "--json"])
.env("CARRYCTX_AGENT", "tester")
.current_dir(&dir)
.output()
.unwrap();
assert!(list.status.success(), "task list should succeed");
let stdout = String::from_utf8_lossy(&list.stdout);
assert!(
stdout.contains("Integration test task"),
"task list should contain the created task"
);
}
#[test]
fn test_task_priority_aliases_and_validation() {
let (dir, bin) = common::setup_test_project("task_priority_test");
std::process::Command::new(&bin)
.args(["init", "--force"])
.current_dir(&dir)
.output()
.unwrap();
std::process::Command::new(&bin)
.args([
"agent",
"register",
"--name",
"tester",
"--provider",
"test",
])
.env("CARRYCTX_AGENT", "tester")
.current_dir(&dir)
.output()
.unwrap();
let p_crit = std::process::Command::new(&bin)
.args([
"task",
"create",
"--title",
"Crit Task",
"--priority",
"critical",
"--json",
])
.env("CARRYCTX_AGENT", "tester")
.current_dir(&dir)
.output()
.unwrap();
assert!(
p_crit.status.success(),
"priority critical alias should succeed"
);
let stdout_crit = String::from_utf8_lossy(&p_crit.stdout);
assert!(
stdout_crit.contains(r#""priority":"urgent""#),
"critical must map to urgent priority"
);
let p_med = std::process::Command::new(&bin)
.args([
"task",
"create",
"--title",
"Med Task",
"--priority",
"medium",
"--json",
])
.env("CARRYCTX_AGENT", "tester")
.current_dir(&dir)
.output()
.unwrap();
assert!(
p_med.status.success(),
"priority medium alias should succeed"
);
let stdout_med = String::from_utf8_lossy(&p_med.stdout);
assert!(
stdout_med.contains(r#""priority":"normal""#),
"medium must map to normal priority"
);
let p_backlog = std::process::Command::new(&bin)
.args([
"task",
"create",
"--title",
"Backlog Task",
"--priority",
"backlog",
"--json",
])
.env("CARRYCTX_AGENT", "tester")
.current_dir(&dir)
.output()
.unwrap();
assert!(
p_backlog.status.success(),
"priority backlog alias should succeed"
);
let stdout_backlog = String::from_utf8_lossy(&p_backlog.stdout);
assert!(
stdout_backlog.contains(r#""priority":"low""#),
"backlog must map to low priority"
);
let p_inv = std::process::Command::new(&bin)
.args([
"task",
"create",
"--title",
"Bad Task",
"--priority",
"bogus",
])
.env("CARRYCTX_AGENT", "tester")
.current_dir(&dir)
.output()
.unwrap();
assert!(!p_inv.status.success(), "invalid priority must be rejected");
let p_dry_inv = std::process::Command::new(&bin)
.args([
"task",
"create",
"--title",
"Bad Task",
"--priority",
"bogus",
"--dry-run",
])
.env("CARRYCTX_AGENT", "tester")
.current_dir(&dir)
.output()
.unwrap();
assert!(
!p_dry_inv.status.success(),
"--dry-run must not bypass invalid priority validation"
);
let edit_crit = std::process::Command::new(&bin)
.args([
"task",
"edit",
"CTX-0001",
"--priority",
"critical",
"--json",
])
.env("CARRYCTX_AGENT", "tester")
.current_dir(&dir)
.output()
.unwrap();
assert!(
edit_crit.status.success(),
"task edit with critical alias should succeed"
);
let edit_inv_dry = std::process::Command::new(&bin)
.args([
"task",
"edit",
"CTX-0001",
"--priority",
"bogus",
"--dry-run",
])
.env("CARRYCTX_AGENT", "tester")
.current_dir(&dir)
.output()
.unwrap();
assert!(
!edit_inv_dry.status.success(),
"task edit --dry-run must reject invalid priority"
);
}
#[test]
fn test_task_help_shows_priority_possible_values() {
let (_, bin) = common::setup_test_project("task_help_test");
let help_create = std::process::Command::new(&bin)
.args(["task", "create", "--help"])
.output()
.unwrap();
assert!(help_create.status.success());
let help_create_txt = String::from_utf8_lossy(&help_create.stdout);
assert!(
help_create_txt.contains("possible values:")
&& help_create_txt.contains("low")
&& help_create_txt.contains("normal")
&& help_create_txt.contains("high")
&& help_create_txt.contains("urgent"),
"task create --help must list possible values for priority: {help_create_txt}"
);
let help_edit = std::process::Command::new(&bin)
.args(["task", "edit", "--help"])
.output()
.unwrap();
assert!(help_edit.status.success());
let help_edit_txt = String::from_utf8_lossy(&help_edit.stdout);
assert!(
help_edit_txt.contains("possible values:")
&& help_edit_txt.contains("low")
&& help_edit_txt.contains("normal")
&& help_edit_txt.contains("high")
&& help_edit_txt.contains("urgent"),
"task edit --help must list possible values for priority: {help_edit_txt}"
);
}
#[test]
fn test_task_list_mine_with_default_cap() {
let (dir, bin) = common::setup_test_project("task_mine_cap");
common::init_and_agent(&dir, &bin);
for title in ["mine cap one", "mine cap two"] {
let out = common::run_cmd(
&dir,
&bin,
&[
"task",
"create",
"--title",
title,
"--assignee",
"tester",
"--json",
],
);
assert!(out.status.success(), "create '{title}' failed");
}
let out = common::run_cmd(&dir, &bin, &["task", "list", "--mine", "--json"]);
assert!(
out.status.success(),
"task list --mine must succeed under the default cap: {}",
String::from_utf8_lossy(&out.stderr)
);
let value: serde_json::Value =
serde_json::from_str(&String::from_utf8_lossy(&out.stdout)).expect("valid json");
assert_eq!(value["success"], serde_json::Value::Bool(true));
assert_eq!(value["error"], serde_json::Value::Null);
let data = value["data"].as_array().expect("data is a task array");
assert_eq!(data.len(), 2, "--mine returns exactly the caller's tasks");
let titles: Vec<&str> = data.iter().filter_map(|t| t["title"].as_str()).collect();
assert!(titles.contains(&"mine cap one"));
assert!(titles.contains(&"mine cap two"));
let out = common::run_cmd(
&dir,
&bin,
&["task", "list", "--mine", "--limit", "1", "--json"],
);
assert!(out.status.success(), "task list --mine --limit failed");
}
#[test]
fn test_task_list_respects_config_and_flag_limits() {
let (dir, bin) = common::setup_test_project("task_list_limit");
common::init_and_agent(&dir, &bin);
for i in 0..5 {
let out = common::run_cmd(
&dir,
&bin,
&[
"task",
"create",
"--title",
&format!("limited {i}"),
"--json",
],
);
assert!(out.status.success(), "create {i} failed");
}
let out = common::run_cmd(&dir, &bin, &["task", "list", "--limit", "2", "--json"]);
assert!(out.status.success(), "task list failed");
let value: serde_json::Value =
serde_json::from_str(&String::from_utf8_lossy(&out.stdout)).expect("valid json");
assert_eq!(value["data"].as_array().unwrap().len(), 2);
let cfg = dir.join(".carryctx/config.toml");
let content = std::fs::read_to_string(&cfg).unwrap();
std::fs::write(&cfg, content.replace("list_limit = 200", "list_limit = 3")).unwrap();
let out = common::run_cmd(&dir, &bin, &["task", "list", "--json"]);
assert!(out.status.success(), "task list failed");
let value: serde_json::Value =
serde_json::from_str(&String::from_utf8_lossy(&out.stdout)).expect("valid json");
assert_eq!(value["data"].as_array().unwrap().len(), 3);
let out = common::run_cmd(&dir, &bin, &["task", "list", "--limit", "4", "--json"]);
assert!(out.status.success());
let value: serde_json::Value =
serde_json::from_str(&String::from_utf8_lossy(&out.stdout)).expect("valid json");
assert_eq!(value["data"].as_array().unwrap().len(), 4);
}