using ClaudeDo.Data; namespace ClaudeDo.Data.Tests; public class PromptFilesTests { [Fact] public void RenderTemplate_replaces_known_tokens() { var outp = PromptFiles.RenderTemplate( "Plan for {date}, cap {maxTasks}.", new Dictionary { ["date"] = "2026-06-04", ["maxTasks"] = "5" }); Assert.Equal("Plan for 2026-06-04, cap 5.", outp); } [Fact] public void RenderTemplate_leaves_unknown_braces_intact() { var outp = PromptFiles.RenderTemplate( "## {Wochentag}, {dd.MM.yyyy} — {start}", new Dictionary { ["start"] = "01.06.2026" }); Assert.Equal("## {Wochentag}, {dd.MM.yyyy} — 01.06.2026", outp); } [Fact] public void DefaultFor_system_mentions_blocked_marker_and_scope() { var d = PromptFiles.DefaultFor(PromptKind.System); Assert.Contains("CLAUDEDO_BLOCKED:", d); Assert.Contains("unattended", d, StringComparison.OrdinalIgnoreCase); } [Fact] public void DefaultFor_planning_initial_has_title_and_description_tokens() { var d = PromptFiles.DefaultFor(PromptKind.PlanningInitial); Assert.Contains("{title}", d); Assert.Contains("{description}", d); } [Fact] public void PathFor_planning_is_planning_system_file() { Assert.EndsWith("planning-system.md", PromptFiles.PathFor(PromptKind.Planning)); } [Fact] public void PathFor_merge_helper_kinds_map_to_their_files() { Assert.EndsWith("merge-helper-system.md", PromptFiles.PathFor(PromptKind.MergeHelper)); Assert.EndsWith("merge-helper-initial.md", PromptFiles.PathFor(PromptKind.MergeHelperInitial)); } [Fact] public void DefaultFor_merge_helper_covers_all_five_phases() { var d = PromptFiles.DefaultFor(PromptKind.MergeHelper); Assert.False(string.IsNullOrWhiteSpace(d)); Assert.Contains("Phase 0", d); Assert.Contains("Phase 1", d); Assert.Contains("Phase 2", d); Assert.Contains("Phase 3", d); Assert.Contains("Phase 4", d); Assert.Contains("Phase 5", d); } [Fact] public void DefaultFor_merge_helper_names_the_tools_each_phase_needs() { var d = PromptFiles.DefaultFor(PromptKind.MergeHelper); Assert.Contains("batch_get_tasks", d); // phase 0 Assert.Contains("update_task", d); // phase 1 + 2 Assert.Contains("get_app_settings", d); // phase 3 Assert.Contains("update_task_status", d); // phase 3 Assert.Contains("review_task", d); // phase 4 Assert.Contains("continue_merge", d); // phase 4 Assert.DoesNotContain("run_task_now(", d); // single override slot — must not batch-start } [Fact] public void DefaultFor_merge_helper_only_asks_dedupe_questions_when_a_candidate_exists() { var d = PromptFiles.DefaultFor(PromptKind.MergeHelper); Assert.Contains("move straight to Phase 2", d); Assert.Contains("do not ask the user to confirm the absence of duplicates", d); Assert.Contains("Cancel nothing without an explicit answer", d); Assert.DoesNotContain("whenever you are unsure", d); } [Fact] public void DefaultFor_merge_helper_phase0_treats_brief_as_primary_source() { var d = PromptFiles.DefaultFor(PromptKind.MergeHelper); Assert.Contains("primary source", d, StringComparison.OrdinalIgnoreCase); Assert.Contains("batch_get_tasks", d); Assert.Contains("Do not act on any single task before you have read", d); } [Fact] public void DefaultFor_merge_helper_allows_splitting_bundled_tasks_in_phase_2() { var d = PromptFiles.DefaultFor(PromptKind.MergeHelper); Assert.Contains("Propose splitting it to the user", d); Assert.Contains("add_task/add_subtask", d); Assert.Contains("do not invent requirements", d); } [Fact] public void DefaultFor_merge_helper_checks_effective_max_turns_before_queuing() { var d = PromptFiles.DefaultFor(PromptKind.MergeHelper); Assert.Contains("effective max-turns", d); Assert.Contains("get_list_config", d); Assert.Contains("set_task_config", d); Assert.Contains("get_task_config", d); } [Fact] public void DefaultFor_merge_helper_checks_file_collisions_before_merge_order() { var d = PromptFiles.DefaultFor(PromptKind.MergeHelper); Assert.Contains("Default to the order the brief lists them", d); Assert.Contains("tell the user which tasks collide", d); Assert.Contains("NORMAL case, not a failure", d); } [Fact] public void DefaultFor_merge_helper_initial_has_repo_token() { var d = PromptFiles.DefaultFor(PromptKind.MergeHelperInitial); Assert.Contains("{repo}", d); } [Fact] public void DefaultFor_merge_helper_initial_has_scope_and_tasks_tokens() { var d = PromptFiles.DefaultFor(PromptKind.MergeHelperInitial); Assert.False(string.IsNullOrWhiteSpace(d)); Assert.Contains("{scope}", d); Assert.Contains("{tasks}", d); } [Fact] public void RenderTemplate_merge_helper_initial_substitutes_scope_and_tasks() { var outp = PromptFiles.RenderTemplate( PromptFiles.DefaultFor(PromptKind.MergeHelperInitial), new Dictionary { ["scope"] = "All lists", ["tasks"] = "- [Idle] T1" }); Assert.Contains("Scope: All lists", outp); Assert.Contains("- [Idle] T1", outp); Assert.DoesNotContain("{scope}", outp); Assert.DoesNotContain("{tasks}", outp); } }