using ClaudeDo.Installer.Core; using ClaudeDo.Installer.Steps; namespace ClaudeDo.Installer.Tests; public sealed class RegisterMcpStepTests { // Progress posts to the SynchronizationContext (or the ThreadPool) asynchronously, // so asserting on captured messages right after an await needs a synchronous reporter. private sealed class SyncProgress(Action report) : IProgress { public void Report(T value) => report(value); } [Fact] public void ResolveUrl_uses_the_configured_port() { var url = RegisterMcpStep.ResolveUrl(new InstallerWorkerConfig { ExternalMcpPort = 49999 }); Assert.Equal("http://127.0.0.1:49999/mcp", url); } [Fact] public void ResolveUrl_defaults_to_47822_without_a_configured_port() { var url = RegisterMcpStep.ResolveUrl(new InstallerWorkerConfig()); Assert.Equal("http://127.0.0.1:47822/mcp", url); } [Fact] public void ResolveUrl_is_null_when_the_external_listener_is_disabled() { var url = RegisterMcpStep.ResolveUrl(new InstallerWorkerConfig { ExternalMcpPort = 0 }); Assert.Null(url); } [Fact] public async Task Not_selected_skips_without_loading_the_worker_config() { var loaded = false; var step = new RegisterMcpStep(() => { loaded = true; return new InstallerWorkerConfig(); }); var ctx = new InstallContext { RegisterMcpWithClaude = false }; var result = await step.ExecuteAsync(ctx, new Progress(_ => { }), CancellationToken.None); Assert.True(result.Success); Assert.False(loaded); } [Fact] public async Task Disabled_external_port_skips_without_running_the_claude_cli() { var step = new RegisterMcpStep(() => new InstallerWorkerConfig { ExternalMcpPort = 0 }); var ctx = new InstallContext { RegisterMcpWithClaude = true }; var messages = new List(); var result = await step.ExecuteAsync(ctx, new SyncProgress(messages.Add), CancellationToken.None); Assert.True(result.Success); Assert.Contains(messages, m => m.Contains("external_mcp_port is 0", StringComparison.Ordinal)); } }