1mod args;
17mod batch;
18mod build;
19mod children;
20mod clone;
21mod edit;
22mod from_template;
23mod parent;
24mod refactor;
25mod relations;
26mod schedule;
27mod show;
28mod split;
29mod start;
30mod status;
31mod template;
32
33use anyhow::Result;
34
35use crate::config;
36
37pub use args::{
39 BatchEditArgs, BatchFieldArgs, BatchMode, BatchOperation, BatchStatusArgs, TaskArgs,
40 TaskBatchArgs, TaskBlocksArgs, TaskBuildArgs, TaskBuildRefactorArgs, TaskChildrenArgs,
41 TaskCloneArgs, TaskCommand, TaskDoneArgs, TaskEditArgs, TaskEditFieldArg, TaskFieldArgs,
42 TaskFromArgs, TaskFromCommand, TaskFromTemplateArgs, TaskMarkDuplicateArgs, TaskParentArgs,
43 TaskReadyArgs, TaskRejectArgs, TaskRelateArgs, TaskRelationFormat, TaskScheduleArgs,
44 TaskShowArgs, TaskSplitArgs, TaskStartArgs, TaskStatusArg, TaskStatusArgs, TaskTemplateArgs,
45 TaskTemplateBuildArgs, TaskTemplateCommand, TaskTemplateShowArgs, TaskUpdateArgs,
46};
47
48pub fn handle_task(args: TaskArgs, force: bool) -> Result<()> {
50 let resolved = config::resolve_from_cwd()?;
51
52 match args.command {
53 Some(TaskCommand::Ready(args)) => status::handle_ready(&args, force, &resolved),
54 Some(TaskCommand::Status(args)) => status::handle_status(&args, force, &resolved),
55 Some(TaskCommand::Done(args)) => status::handle_done(&args, force, &resolved),
56 Some(TaskCommand::Reject(args)) => status::handle_reject(&args, force, &resolved),
57 Some(TaskCommand::Field(args)) => edit::handle_field(&args, force, &resolved),
58 Some(TaskCommand::Edit(args)) => edit::handle_edit(&args, force, &resolved),
59 Some(TaskCommand::Update(args)) => edit::handle_update(&args, &resolved, force),
60 Some(TaskCommand::Build(args)) => build::handle(&args, force, &resolved),
61 Some(TaskCommand::Template(template_args)) => template::handle(&resolved, &template_args),
62 Some(TaskCommand::BuildRefactor(args)) | Some(TaskCommand::Refactor(args)) => {
63 refactor::handle(&args, force, &resolved)
64 }
65 Some(TaskCommand::Show(args)) => show::handle(&args, &resolved),
66 Some(TaskCommand::Clone(args)) => clone::handle(&args, force, &resolved),
67 Some(TaskCommand::Batch(args)) => batch::handle(&args, force, &resolved),
68 Some(TaskCommand::Schedule(args)) => schedule::handle(&args, force, &resolved),
69 Some(TaskCommand::Relate(args)) => relations::handle_relate(&args, force, &resolved),
70 Some(TaskCommand::Blocks(args)) => relations::handle_blocks(&args, force, &resolved),
71 Some(TaskCommand::MarkDuplicate(args)) => {
72 relations::handle_mark_duplicate(&args, force, &resolved)
73 }
74 Some(TaskCommand::Split(args)) => split::handle(&args, force, &resolved),
75 Some(TaskCommand::Start(args)) => start::handle(&args, force, &resolved),
76 Some(TaskCommand::Children(args)) => children::handle(&args, &resolved),
77 Some(TaskCommand::Parent(args)) => parent::handle(&args, &resolved),
78 Some(TaskCommand::From(args)) => match args.command {
79 TaskFromCommand::Template(template_args) => {
80 from_template::handle(&resolved, &template_args, force)
81 }
82 },
83 None => {
84 build::handle(&args.build, force, &resolved)
86 }
87 }
88}
89
90#[cfg(test)]
91mod tests {
92 use clap::{CommandFactory, Parser};
93
94 use crate::cli::Cli;
95 use crate::cli::queue::QueueShowFormat;
96 use crate::cli::task::args::{BatchOperation, TaskEditFieldArg, TaskStatusArg};
97
98 #[test]
99 fn task_update_help_mentions_rp_examples() {
100 let mut cmd = Cli::command();
101 let task = cmd.find_subcommand_mut("task").expect("task subcommand");
102 let update = task
103 .find_subcommand_mut("update")
104 .expect("task update subcommand");
105 let help = update.render_long_help().to_string();
106
107 assert!(
108 help.contains("ralph task update --repo-prompt plan RQ-0001"),
109 "missing repo-prompt plan example: {help}"
110 );
111 assert!(
112 help.contains("ralph task update --repo-prompt off --fields scope,evidence RQ-0001"),
113 "missing repo-prompt off example: {help}"
114 );
115 assert!(
116 help.contains("ralph task update --approval-mode auto-edits --runner claude RQ-0001"),
117 "missing approval-mode example: {help}"
118 );
119 }
120
121 #[test]
122 fn task_show_help_mentions_examples() {
123 let mut cmd = Cli::command();
124 let task = cmd.find_subcommand_mut("task").expect("task subcommand");
125 let show = task
126 .find_subcommand_mut("show")
127 .expect("task show subcommand");
128 let help = show.render_long_help().to_string();
129
130 assert!(
131 help.contains("ralph task show RQ-0001"),
132 "missing show example: {help}"
133 );
134 assert!(
135 help.contains("--format compact"),
136 "missing format example: {help}"
137 );
138 }
139
140 #[test]
141 fn task_details_alias_parses() {
142 let cli =
143 Cli::try_parse_from(["ralph", "task", "details", "RQ-0001", "--format", "compact"])
144 .expect("parse");
145
146 match cli.command {
147 crate::cli::Command::Task(args) => match args.command {
148 Some(crate::cli::task::TaskCommand::Show(args)) => {
149 assert_eq!(args.task_id, "RQ-0001");
150 assert_eq!(args.format, QueueShowFormat::Compact);
151 }
152 _ => panic!("expected task show command"),
153 },
154 _ => panic!("expected task command"),
155 }
156 }
157
158 #[test]
159 fn task_build_parses_repo_prompt_and_effort_alias() {
160 let cli = Cli::try_parse_from([
161 "ralph",
162 "task",
163 "build",
164 "--repo-prompt",
165 "plan",
166 "-e",
167 "high",
168 "Add tests",
169 ])
170 .expect("parse");
171
172 match cli.command {
173 crate::cli::Command::Task(args) => match args.command {
174 Some(crate::cli::task::TaskCommand::Build(args)) => {
175 assert_eq!(args.repo_prompt, Some(crate::agent::RepoPromptMode::Plan));
176 assert_eq!(args.effort.as_deref(), Some("high"));
177 }
178 _ => panic!("expected task build command"),
179 },
180 _ => panic!("expected task command"),
181 }
182 }
183
184 #[test]
185 fn task_build_parses_runner_cli_overrides() {
186 let cli = Cli::try_parse_from([
187 "ralph",
188 "task",
189 "build",
190 "--approval-mode",
191 "yolo",
192 "--sandbox",
193 "disabled",
194 "Add tests",
195 ])
196 .expect("parse");
197
198 match cli.command {
199 crate::cli::Command::Task(args) => match args.command {
200 Some(crate::cli::task::TaskCommand::Build(args)) => {
201 assert_eq!(args.runner_cli.approval_mode.as_deref(), Some("yolo"));
202 assert_eq!(args.runner_cli.sandbox.as_deref(), Some("disabled"));
203 }
204 _ => panic!("expected task build command"),
205 },
206 _ => panic!("expected task command"),
207 }
208 }
209
210 #[test]
211 fn task_update_parses_repo_prompt_and_effort_alias() {
212 let cli = Cli::try_parse_from([
213 "ralph",
214 "task",
215 "update",
216 "--repo-prompt",
217 "off",
218 "-e",
219 "low",
220 "RQ-0001",
221 ])
222 .expect("parse");
223
224 match cli.command {
225 crate::cli::Command::Task(args) => match args.command {
226 Some(crate::cli::task::TaskCommand::Update(args)) => {
227 assert_eq!(args.repo_prompt, Some(crate::agent::RepoPromptMode::Off));
228 assert_eq!(args.effort.as_deref(), Some("low"));
229 assert_eq!(args.task_id.as_deref(), Some("RQ-0001"));
230 }
231 _ => panic!("expected task update command"),
232 },
233 _ => panic!("expected task command"),
234 }
235 }
236
237 #[test]
238 fn task_update_parses_runner_cli_overrides() {
239 let cli = Cli::try_parse_from([
240 "ralph",
241 "task",
242 "update",
243 "--approval-mode",
244 "auto-edits",
245 "--sandbox",
246 "disabled",
247 "RQ-0001",
248 ])
249 .expect("parse");
250
251 match cli.command {
252 crate::cli::Command::Task(args) => match args.command {
253 Some(crate::cli::task::TaskCommand::Update(args)) => {
254 assert_eq!(args.runner_cli.approval_mode.as_deref(), Some("auto-edits"));
255 assert_eq!(args.runner_cli.sandbox.as_deref(), Some("disabled"));
256 assert_eq!(args.task_id.as_deref(), Some("RQ-0001"));
257 }
258 _ => panic!("expected task update command"),
259 },
260 _ => panic!("expected task command"),
261 }
262 }
263
264 #[test]
265 fn task_edit_parses_dry_run_flag() {
266 let cli = Cli::try_parse_from([
267 "ralph",
268 "task",
269 "edit",
270 "--dry-run",
271 "title",
272 "New title",
273 "RQ-0001",
274 ])
275 .expect("parse");
276
277 match cli.command {
278 crate::cli::Command::Task(args) => match args.command {
279 Some(crate::cli::task::TaskCommand::Edit(args)) => {
280 assert!(args.dry_run);
281 assert_eq!(args.task_ids, vec!["RQ-0001"]);
282 assert_eq!(args.value, "New title");
283 }
284 _ => panic!("expected task edit command"),
285 },
286 _ => panic!("expected task command"),
287 }
288 }
289
290 #[test]
291 fn task_edit_without_dry_run_defaults_to_false() {
292 let cli = Cli::try_parse_from(["ralph", "task", "edit", "title", "New title", "RQ-0001"])
293 .expect("parse");
294
295 match cli.command {
296 crate::cli::Command::Task(args) => match args.command {
297 Some(crate::cli::task::TaskCommand::Edit(args)) => {
298 assert!(!args.dry_run);
299 }
300 _ => panic!("expected task edit command"),
301 },
302 _ => panic!("expected task command"),
303 }
304 }
305
306 #[test]
307 fn task_update_parses_dry_run_flag() {
308 let cli = Cli::try_parse_from(["ralph", "task", "update", "--dry-run", "RQ-0001"])
309 .expect("parse");
310
311 match cli.command {
312 crate::cli::Command::Task(args) => match args.command {
313 Some(crate::cli::task::TaskCommand::Update(args)) => {
314 assert!(args.dry_run);
315 assert_eq!(args.task_id.as_deref(), Some("RQ-0001"));
316 }
317 _ => panic!("expected task update command"),
318 },
319 _ => panic!("expected task command"),
320 }
321 }
322
323 #[test]
324 fn task_update_without_dry_run_defaults_to_false() {
325 let cli = Cli::try_parse_from(["ralph", "task", "update", "RQ-0001"]).expect("parse");
326
327 match cli.command {
328 crate::cli::Command::Task(args) => match args.command {
329 Some(crate::cli::task::TaskCommand::Update(args)) => {
330 assert!(!args.dry_run);
331 }
332 _ => panic!("expected task update command"),
333 },
334 _ => panic!("expected task command"),
335 }
336 }
337
338 #[test]
339 fn task_refactor_parses() {
340 let cli = Cli::try_parse_from(["ralph", "task", "refactor"]).expect("parse");
341 match cli.command {
342 crate::cli::Command::Task(args) => match args.command {
343 Some(crate::cli::task::TaskCommand::Refactor(_)) => {}
344 _ => panic!("expected task refactor command"),
345 },
346 _ => panic!("expected task command"),
347 }
348 }
349
350 #[test]
351 fn task_ref_alias_parses() {
352 let cli =
353 Cli::try_parse_from(["ralph", "task", "ref", "--threshold", "800"]).expect("parse");
354 match cli.command {
355 crate::cli::Command::Task(args) => match args.command {
356 Some(crate::cli::task::TaskCommand::Refactor(args)) => {
357 assert_eq!(args.threshold, 800);
358 }
359 _ => panic!("expected task refactor command via alias"),
360 },
361 _ => panic!("expected task command"),
362 }
363 }
364
365 #[test]
366 fn task_build_refactor_parses() {
367 let cli = Cli::try_parse_from(["ralph", "task", "build-refactor", "--threshold", "700"])
368 .expect("parse");
369 match cli.command {
370 crate::cli::Command::Task(args) => match args.command {
371 Some(crate::cli::task::TaskCommand::BuildRefactor(args)) => {
372 assert_eq!(args.threshold, 700);
373 }
374 _ => panic!("expected task build-refactor command"),
375 },
376 _ => panic!("expected task command"),
377 }
378 }
379
380 #[test]
381 fn task_clone_parses() {
382 let cli = Cli::try_parse_from(["ralph", "task", "clone", "RQ-0001"]).expect("parse");
383 match cli.command {
384 crate::cli::Command::Task(args) => match args.command {
385 Some(crate::cli::task::TaskCommand::Clone(args)) => {
386 assert_eq!(args.task_id, "RQ-0001");
387 assert!(!args.dry_run);
388 }
389 _ => panic!("expected task clone command"),
390 },
391 _ => panic!("expected task command"),
392 }
393 }
394
395 #[test]
396 fn task_duplicate_alias_parses() {
397 let cli = Cli::try_parse_from(["ralph", "task", "duplicate", "RQ-0001"]).expect("parse");
398 match cli.command {
399 crate::cli::Command::Task(args) => match args.command {
400 Some(crate::cli::task::TaskCommand::Clone(args)) => {
401 assert_eq!(args.task_id, "RQ-0001");
402 }
403 _ => panic!("expected task clone command via duplicate alias"),
404 },
405 _ => panic!("expected task command"),
406 }
407 }
408
409 #[test]
410 fn task_clone_parses_status_flag() {
411 let cli = Cli::try_parse_from(["ralph", "task", "clone", "--status", "todo", "RQ-0001"])
412 .expect("parse");
413 match cli.command {
414 crate::cli::Command::Task(args) => match args.command {
415 Some(crate::cli::task::TaskCommand::Clone(args)) => {
416 assert_eq!(args.task_id, "RQ-0001");
417 assert_eq!(args.status, Some(TaskStatusArg::Todo));
418 }
419 _ => panic!("expected task clone command"),
420 },
421 _ => panic!("expected task command"),
422 }
423 }
424
425 #[test]
426 fn task_clone_parses_title_prefix() {
427 let cli = Cli::try_parse_from([
428 "ralph",
429 "task",
430 "clone",
431 "--title-prefix",
432 "[Follow-up] ",
433 "RQ-0001",
434 ])
435 .expect("parse");
436 match cli.command {
437 crate::cli::Command::Task(args) => match args.command {
438 Some(crate::cli::task::TaskCommand::Clone(args)) => {
439 assert_eq!(args.task_id, "RQ-0001");
440 assert_eq!(args.title_prefix, Some("[Follow-up] ".to_string()));
441 }
442 _ => panic!("expected task clone command"),
443 },
444 _ => panic!("expected task command"),
445 }
446 }
447
448 #[test]
449 fn task_clone_parses_dry_run_flag() {
450 let cli =
451 Cli::try_parse_from(["ralph", "task", "clone", "--dry-run", "RQ-0001"]).expect("parse");
452 match cli.command {
453 crate::cli::Command::Task(args) => match args.command {
454 Some(crate::cli::task::TaskCommand::Clone(args)) => {
455 assert_eq!(args.task_id, "RQ-0001");
456 assert!(args.dry_run);
457 }
458 _ => panic!("expected task clone command"),
459 },
460 _ => panic!("expected task command"),
461 }
462 }
463
464 #[test]
465 fn task_clone_help_mentions_examples() {
466 let mut cmd = Cli::command();
467 let task = cmd.find_subcommand_mut("task").expect("task subcommand");
468 let clone = task
469 .find_subcommand_mut("clone")
470 .expect("task clone subcommand");
471 let help = clone.render_long_help().to_string();
472
473 assert!(
474 help.contains("ralph task clone RQ-0001"),
475 "missing clone example: {help}"
476 );
477 assert!(
478 help.contains("--status"),
479 "missing --status example: {help}"
480 );
481 assert!(
482 help.contains("--title-prefix"),
483 "missing --title-prefix example: {help}"
484 );
485 assert!(
486 help.contains("ralph task duplicate"),
487 "missing duplicate alias example: {help}"
488 );
489 }
490
491 #[test]
492 fn task_batch_status_parses_multiple_ids() {
493 let cli = Cli::try_parse_from([
494 "ralph", "task", "batch", "status", "doing", "RQ-0001", "RQ-0002", "RQ-0003",
495 ])
496 .expect("parse");
497 match cli.command {
498 crate::cli::Command::Task(args) => match args.command {
499 Some(crate::cli::task::TaskCommand::Batch(args)) => match args.operation {
500 BatchOperation::Status(status_args) => {
501 assert_eq!(status_args.status, TaskStatusArg::Doing);
502 assert_eq!(
503 status_args.select.task_ids,
504 vec!["RQ-0001", "RQ-0002", "RQ-0003"]
505 );
506 assert!(!args.dry_run);
507 assert!(!args.continue_on_error);
508 }
509 _ => panic!("expected batch status operation"),
510 },
511 _ => panic!("expected task batch command"),
512 },
513 _ => panic!("expected task command"),
514 }
515 }
516
517 #[test]
518 fn task_batch_status_parses_tag_filter() {
519 let cli = Cli::try_parse_from([
520 "ralph",
521 "task",
522 "batch",
523 "status",
524 "doing",
525 "--tag-filter",
526 "rust",
527 "--tag-filter",
528 "cli",
529 ])
530 .expect("parse");
531 match cli.command {
532 crate::cli::Command::Task(args) => match args.command {
533 Some(crate::cli::task::TaskCommand::Batch(args)) => match args.operation {
534 BatchOperation::Status(status_args) => {
535 assert_eq!(status_args.status, TaskStatusArg::Doing);
536 assert!(status_args.select.task_ids.is_empty());
537 assert_eq!(status_args.select.tag_filter, vec!["rust", "cli"]);
538 }
539 _ => panic!("expected batch status operation"),
540 },
541 _ => panic!("expected task batch command"),
542 },
543 _ => panic!("expected task command"),
544 }
545 }
546
547 #[test]
548 fn task_batch_field_parses_multiple_ids() {
549 let cli = Cli::try_parse_from([
550 "ralph", "task", "batch", "field", "severity", "high", "RQ-0001", "RQ-0002",
551 ])
552 .expect("parse");
553 match cli.command {
554 crate::cli::Command::Task(args) => match args.command {
555 Some(crate::cli::task::TaskCommand::Batch(args)) => match args.operation {
556 BatchOperation::Field(field_args) => {
557 assert_eq!(field_args.key, "severity");
558 assert_eq!(field_args.value, "high");
559 assert_eq!(field_args.select.task_ids, vec!["RQ-0001", "RQ-0002"]);
560 }
561 _ => panic!("expected batch field operation"),
562 },
563 _ => panic!("expected task batch command"),
564 },
565 _ => panic!("expected task command"),
566 }
567 }
568
569 #[test]
570 fn task_batch_edit_parses_dry_run() {
571 let cli = Cli::try_parse_from([
572 "ralph",
573 "task",
574 "batch",
575 "--dry-run",
576 "edit",
577 "priority",
578 "high",
579 "RQ-0001",
580 "RQ-0002",
581 ])
582 .expect("parse");
583 match cli.command {
584 crate::cli::Command::Task(args) => match args.command {
585 Some(crate::cli::task::TaskCommand::Batch(args)) => {
586 assert!(args.dry_run);
587 assert!(!args.continue_on_error);
588 match args.operation {
589 BatchOperation::Edit(edit_args) => {
590 assert_eq!(edit_args.field, TaskEditFieldArg::Priority);
591 assert_eq!(edit_args.value, "high");
592 assert_eq!(edit_args.select.task_ids, vec!["RQ-0001", "RQ-0002"]);
593 }
594 _ => panic!("expected batch edit operation"),
595 }
596 }
597 _ => panic!("expected task batch command"),
598 },
599 _ => panic!("expected task command"),
600 }
601 }
602
603 #[test]
604 fn task_batch_parses_continue_on_error() {
605 let cli = Cli::try_parse_from([
606 "ralph",
607 "task",
608 "batch",
609 "--continue-on-error",
610 "status",
611 "doing",
612 "RQ-0001",
613 "RQ-0002",
614 ])
615 .expect("parse");
616 match cli.command {
617 crate::cli::Command::Task(args) => match args.command {
618 Some(crate::cli::task::TaskCommand::Batch(args)) => {
619 assert!(!args.dry_run);
620 assert!(args.continue_on_error);
621 match args.operation {
622 BatchOperation::Status(status_args) => {
623 assert_eq!(status_args.status, TaskStatusArg::Doing);
624 }
625 _ => panic!("expected batch status operation"),
626 }
627 }
628 _ => panic!("expected task batch command"),
629 },
630 _ => panic!("expected task command"),
631 }
632 }
633
634 #[test]
635 fn task_batch_help_mentions_examples() {
636 let mut cmd = Cli::command();
637 let task = cmd.find_subcommand_mut("task").expect("task subcommand");
638 let batch = task
639 .find_subcommand_mut("batch")
640 .expect("task batch subcommand");
641 let help = batch.render_long_help().to_string();
642
643 assert!(
644 help.contains("ralph task batch status doing"),
645 "missing batch status example: {help}"
646 );
647 assert!(
648 help.contains("--tag-filter"),
649 "missing --tag-filter example: {help}"
650 );
651 assert!(
652 help.contains("--dry-run"),
653 "missing --dry-run example: {help}"
654 );
655 assert!(
656 help.contains("--continue-on-error"),
657 "missing --continue-on-error example: {help}"
658 );
659 }
660
661 #[test]
662 fn task_status_parses_multiple_ids() {
663 let cli = Cli::try_parse_from([
664 "ralph", "task", "status", "doing", "RQ-0001", "RQ-0002", "RQ-0003",
665 ])
666 .expect("parse");
667 match cli.command {
668 crate::cli::Command::Task(args) => match args.command {
669 Some(crate::cli::task::TaskCommand::Status(args)) => {
670 assert_eq!(args.status, TaskStatusArg::Doing);
671 assert_eq!(args.task_ids, vec!["RQ-0001", "RQ-0002", "RQ-0003"]);
672 }
673 _ => panic!("expected task status command"),
674 },
675 _ => panic!("expected task command"),
676 }
677 }
678
679 #[test]
680 fn task_status_parses_tag_filter() {
681 let cli = Cli::try_parse_from([
682 "ralph",
683 "task",
684 "status",
685 "doing",
686 "--tag-filter",
687 "rust",
688 "--tag-filter",
689 "cli",
690 ])
691 .expect("parse");
692 match cli.command {
693 crate::cli::Command::Task(args) => match args.command {
694 Some(crate::cli::task::TaskCommand::Status(args)) => {
695 assert_eq!(args.status, TaskStatusArg::Doing);
696 assert!(args.task_ids.is_empty());
697 assert_eq!(args.tag_filter, vec!["rust", "cli"]);
698 }
699 _ => panic!("expected task status command"),
700 },
701 _ => panic!("expected task command"),
702 }
703 }
704
705 #[test]
706 fn task_field_parses_multiple_ids() {
707 let cli = Cli::try_parse_from([
708 "ralph", "task", "field", "severity", "high", "RQ-0001", "RQ-0002",
709 ])
710 .expect("parse");
711 match cli.command {
712 crate::cli::Command::Task(args) => match args.command {
713 Some(crate::cli::task::TaskCommand::Field(args)) => {
714 assert_eq!(args.key, "severity");
715 assert_eq!(args.value, "high");
716 assert_eq!(args.task_ids, vec!["RQ-0001", "RQ-0002"]);
717 }
718 _ => panic!("expected task field command"),
719 },
720 _ => panic!("expected task command"),
721 }
722 }
723
724 #[test]
725 fn task_field_parses_dry_run_flag() {
726 let cli = Cli::try_parse_from([
727 "ralph",
728 "task",
729 "field",
730 "--dry-run",
731 "severity",
732 "high",
733 "RQ-0001",
734 ])
735 .expect("parse");
736 match cli.command {
737 crate::cli::Command::Task(args) => match args.command {
738 Some(crate::cli::task::TaskCommand::Field(args)) => {
739 assert!(args.dry_run);
740 assert_eq!(args.key, "severity");
741 assert_eq!(args.value, "high");
742 assert_eq!(args.task_ids, vec!["RQ-0001"]);
743 }
744 _ => panic!("expected task field command"),
745 },
746 _ => panic!("expected task command"),
747 }
748 }
749
750 #[test]
751 fn task_field_without_dry_run_defaults_to_false() {
752 let cli = Cli::try_parse_from(["ralph", "task", "field", "severity", "high", "RQ-0001"])
753 .expect("parse");
754 match cli.command {
755 crate::cli::Command::Task(args) => match args.command {
756 Some(crate::cli::task::TaskCommand::Field(args)) => {
757 assert!(!args.dry_run);
758 }
759 _ => panic!("expected task field command"),
760 },
761 _ => panic!("expected task command"),
762 }
763 }
764
765 #[test]
766 fn task_edit_parses_multiple_ids() {
767 let cli = Cli::try_parse_from([
768 "ralph", "task", "edit", "priority", "high", "RQ-0001", "RQ-0002",
769 ])
770 .expect("parse");
771 match cli.command {
772 crate::cli::Command::Task(args) => match args.command {
773 Some(crate::cli::task::TaskCommand::Edit(args)) => {
774 assert_eq!(args.field, TaskEditFieldArg::Priority);
775 assert_eq!(args.value, "high");
776 assert_eq!(args.task_ids, vec!["RQ-0001", "RQ-0002"]);
777 }
778 _ => panic!("expected task edit command"),
779 },
780 _ => panic!("expected task command"),
781 }
782 }
783}