test(worker): assert the full external MCP tool-name inventory
Guardrail for the upcoming ExternalMcpService.cs topic split: proves the move is name-preserving by pinning the complete set of registered tool names (computed via the SDK's own McpServerTool.Create, not a reimplementation of the naming rule) before any file is moved.
This commit is contained in:
@@ -84,4 +84,57 @@ public sealed class ExternalMcpToolSchemaTests
|
||||
|
||||
Assert.True(hasHint, "No external tool description carries McpToolDocs.TaskNumberHint.");
|
||||
}
|
||||
|
||||
// Full inventory of registered external tool names, computed via the SDK's own
|
||||
// McpServerTool.Create (the same call Program.cs's WithTools<T>() makes internally) rather
|
||||
// than reimplementing the PascalCase-to-snake_case conversion — so this only breaks on an
|
||||
// actual rename/add/remove, not on a divergent reimplementation of the naming rule. Used to
|
||||
// prove that splitting ExternalMcpService.cs into topic files (MergeMcpTools, WorktreeMcpTools,
|
||||
// ...) is a byte-identical rename-preserving move: the set below must not change across that split.
|
||||
private static readonly string[] ExpectedToolNames =
|
||||
[
|
||||
"list_task_lists", "list_tasks", "get_task", "add_task", "update_task", "add_subtask",
|
||||
"update_task_status", "review_task", "run_task_now", "cancel_task", "delete_task",
|
||||
"get_task_worktree", "get_task_diff", "merge_task", "continue_merge", "verify_merges",
|
||||
"abort_merge", "get_merge_conflicts", "resolve_conflict_hunk", "preview_merge",
|
||||
"preview_merge_set", "revert_merge", "list_worktrees", "cleanup_task_worktree",
|
||||
"continue_task", "get_daily_prep_candidates", "set_my_day",
|
||||
"batch_get_tasks", "batch_add_tasks", "batch_update_task_status", "batch_cancel_tasks",
|
||||
"batch_delete_tasks", "batch_set_my_day", "batch_cleanup_task_worktrees",
|
||||
"create_list", "update_list", "delete_list",
|
||||
"get_list_config", "set_list_config", "set_task_config", "get_task_config", "get_effective_run_config",
|
||||
"list_runs", "get_run", "get_task_log",
|
||||
"list_agents",
|
||||
"reset_failed_task", "submit_task_for_review",
|
||||
"handoff_list_handler",
|
||||
"get_app_settings",
|
||||
"wait_for_task_change",
|
||||
"get_queue_state",
|
||||
"add_task_attachment", "list_task_attachments", "remove_task_attachment",
|
||||
"save_finding",
|
||||
"add_note",
|
||||
];
|
||||
|
||||
[Fact]
|
||||
public void ExternalToolNames_MatchExpectedInventory()
|
||||
{
|
||||
// A throwaway, never-constructed instance per declaring type — McpServerTool.Create needs a
|
||||
// non-null target reference to bind the method to, but naming/schema derivation never
|
||||
// touches instance state, so skipping the real constructor (and its DI dependencies) is safe.
|
||||
var targets = new Dictionary<Type, object>();
|
||||
var actual = ExternalToolMethods()
|
||||
.Select(m =>
|
||||
{
|
||||
var target = targets.TryGetValue(m.DeclaringType!, out var t)
|
||||
? t
|
||||
: targets[m.DeclaringType!] = System.Runtime.CompilerServices.RuntimeHelpers.GetUninitializedObject(m.DeclaringType!);
|
||||
return McpServerTool.Create(m, target).ProtocolTool.Name;
|
||||
})
|
||||
.OrderBy(n => n, StringComparer.Ordinal)
|
||||
.ToList();
|
||||
|
||||
var expected = ExpectedToolNames.OrderBy(n => n, StringComparer.Ordinal).ToList();
|
||||
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user