diff --git a/src/Nitro/CommandLine/src/CommandLine/Commands/Clients/PublishClientCommand.cs b/src/Nitro/CommandLine/src/CommandLine/Commands/Clients/PublishClientCommand.cs index 28424c57306..976e2c543c7 100644 --- a/src/Nitro/CommandLine/src/CommandLine/Commands/Clients/PublishClientCommand.cs +++ b/src/Nitro/CommandLine/src/CommandLine/Commands/Clients/PublishClientCommand.cs @@ -142,7 +142,7 @@ private static async Task ExecuteAsync( } } - activity.Fail(errorTree); + await activity.FailAllAsync(errorTree); throw new ExitException("Client publish failed."); diff --git a/src/Nitro/CommandLine/src/CommandLine/Commands/Clients/ValidateClientCommand.cs b/src/Nitro/CommandLine/src/CommandLine/Commands/Clients/ValidateClientCommand.cs index 24a6d6a0de6..502bfe1f42e 100644 --- a/src/Nitro/CommandLine/src/CommandLine/Commands/Clients/ValidateClientCommand.cs +++ b/src/Nitro/CommandLine/src/CommandLine/Commands/Clients/ValidateClientCommand.cs @@ -124,7 +124,7 @@ private static async Task ExecuteAsync( } } - activity.Fail(errorTree); + await activity.FailAllAsync(errorTree); throw new ExitException("Client validation failed."); diff --git a/src/Nitro/CommandLine/src/CommandLine/Commands/Fusion/FusionPublishCommand.cs b/src/Nitro/CommandLine/src/CommandLine/Commands/Fusion/FusionPublishCommand.cs index 42253011296..9b9b00d4ac6 100644 --- a/src/Nitro/CommandLine/src/CommandLine/Commands/Fusion/FusionPublishCommand.cs +++ b/src/Nitro/CommandLine/src/CommandLine/Commands/Fusion/FusionPublishCommand.cs @@ -144,7 +144,7 @@ async Task PublishFusionConfigurationWithArchiveAsync() throw new ExitException(Messages.ArchiveFileDoesNotExist(archiveFile)); } - await using var activity = StartPublishActivity(console, stageName, apiId, force); + await using var activity = StartPublishActivity(console, stageName, apiId, tag, force); await using var archiveStream = fileSystem.OpenReadStream(archiveFile); return await ExecutePublishAsync( @@ -170,7 +170,7 @@ async Task PublishFusionConfigurationWithSourceSchemaFilesAsync() } } - await using var activity = StartPublishActivity(console, stageName, apiId, force); + await using var activity = StartPublishActivity(console, stageName, apiId, tag, force); var newSourceSchemas = await FusionComposeCommand.ReadSourceSchemasAsync( fileSystem, @@ -189,7 +189,7 @@ async Task PublishFusionConfigurationWithSourceSchemasAsync() .Select(i => ParseSourceSchemaVersion(i, tag)) .ToArray(); - await using var activity = StartPublishActivity(console, stageName, apiId, force); + await using var activity = StartPublishActivity(console, stageName, apiId, tag, force); var newSourceSchemas = new Dictionary(); @@ -371,7 +371,11 @@ await FusionPublishHelpers.ClaimDeploymentSlotAsync( } else if (!force) { - throw new ExitException("Failed to validate configuration."); + // Write directly instead of throwing so the release-slot fallback + // in the outer catch is not triggered — the publish hasn't actually + // reserved any remote state that needs tearing down here. + console.Error.WriteErrorLine("Fusion configuration validation failed."); + return ExitCodes.Error; } } @@ -477,11 +481,12 @@ private static INitroConsoleActivity StartPublishActivity( INitroConsole console, string stageName, string apiId, + string tag, bool force) { var activity = console.StartActivity( - $"Publishing Fusion configuration to stage '{stageName}' of API '{apiId.EscapeMarkup()}'", - "Failed to publish Fusion configuration."); + $"Publishing new Fusion configuration version '{tag.EscapeMarkup()}' of API '{apiId.EscapeMarkup()}' to stage '{stageName.EscapeMarkup()}'", + "Failed to publish a new Fusion configuration version."); if (force) { diff --git a/src/Nitro/CommandLine/src/CommandLine/Commands/Fusion/FusionPublishHelpers.cs b/src/Nitro/CommandLine/src/CommandLine/Commands/Fusion/FusionPublishHelpers.cs index b817ebc7577..51f7ad82807 100644 --- a/src/Nitro/CommandLine/src/CommandLine/Commands/Fusion/FusionPublishHelpers.cs +++ b/src/Nitro/CommandLine/src/CommandLine/Commands/Fusion/FusionPublishHelpers.cs @@ -1,6 +1,7 @@ using System.Text.Json; using ChilliCream.Nitro.Client; using ChilliCream.Nitro.Client.FusionConfiguration; +using ChilliCream.Nitro.CommandLine.Helpers; using HotChocolate.Fusion; using HotChocolate.Fusion.Logging; using HotChocolate.Fusion.Packaging; @@ -68,7 +69,7 @@ public static async Task RequestDeploymentSlotAsync( throw MutationReturnedNoData(); } - // activity.Update($"Request ID: {requestId.EscapeMarkup()}"); + activity.Update($"Publication request created. {$"(ID: {requestId.EscapeMarkup()})".Dim()}"); using var subscriptionCancellation = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken); @@ -103,8 +104,9 @@ public static async Task RequestDeploymentSlotAsync( } } - activity.Fail(errorTree); - throw Exit("Your request has failed."); + await activity.FailAllAsync(errorTree); + + throw new ExitException("Your request has failed."); case IFusionConfigurationPublishingSuccess: await subscriptionCancellation.CancelAsync(); @@ -225,7 +227,7 @@ public static async Task UploadFusionConfigurationAsync( } } - activity.Fail(publishErrorTree); + await activity.FailAllAsync(publishErrorTree); throw new ExitException("Failed to publish the new configuration."); case IFusionConfigurationPublishingSuccess: @@ -336,17 +338,17 @@ public static async Task ValidateFusionConfigurationAsync( { case IProcessingTaskIsQueued: throw Exit( - "Your request is in the queued state. Try to run `fusion-configuration publish start` once the request is ready "); + "Your request is in the queued state. Try to run `fusion-configuration publish start` once the request is ready."); case IFusionConfigurationPublishingFailed: - throw Exit("Your request has already failed"); + throw Exit("Your request has already failed."); case IFusionConfigurationPublishingSuccess: - throw Exit("You request is already published"); + throw Exit("Your request is already published."); case IProcessingTaskIsReady: throw Exit( - "Your request is ready for the composition. Run `fusion-configuration publish start`"); + "Your request is ready for the composition. Run `fusion-configuration publish start`."); case IFusionConfigurationValidationFailed { Errors: var errors }: var errorTree = new Tree(""); @@ -377,6 +379,7 @@ public static async Task ValidateFusionConfigurationAsync( } activity.Fail(errorTree); + return false; case IFusionConfigurationValidationSuccess: @@ -386,7 +389,7 @@ public static async Task ValidateFusionConfigurationAsync( case IValidationInProgress: case IWaitForApproval: case IProcessingTaskApproved: - // activity.Update(Messages.Validating); + activity.Update(Messages.Validating); break; default: diff --git a/src/Nitro/CommandLine/src/CommandLine/Commands/Fusion/FusionValidateCommand.cs b/src/Nitro/CommandLine/src/CommandLine/Commands/Fusion/FusionValidateCommand.cs index 8d03e6aef95..6641b8c618d 100644 --- a/src/Nitro/CommandLine/src/CommandLine/Commands/Fusion/FusionValidateCommand.cs +++ b/src/Nitro/CommandLine/src/CommandLine/Commands/Fusion/FusionValidateCommand.cs @@ -228,7 +228,12 @@ async Task ValidateAsync(INitroConsoleActivity activity, Stream archiveStre source: null, ct); - return isValid ? ExitCodes.Success : ExitCodes.Error; + if (!isValid) + { + throw new ExitException("Schema validation failed."); + } + + return ExitCodes.Success; } finally { @@ -239,7 +244,7 @@ async Task ValidateAsync(INitroConsoleActivity activity, Stream archiveStre INitroConsoleActivity StartActivity() { return console.StartActivity( - $"Validating Fusion configuration against stage '{stageName.EscapeMarkup()}' of API '{apiId.EscapeMarkup()}'", + $"Validating Fusion configuration of API '{apiId.EscapeMarkup()}' against stage '{stageName.EscapeMarkup()}'", "Failed to validate the Fusion configuration."); } } diff --git a/src/Nitro/CommandLine/src/CommandLine/Commands/Fusion/PublishCommand/FusionConfigurationPublishBeginCommand.cs b/src/Nitro/CommandLine/src/CommandLine/Commands/Fusion/PublishCommand/FusionConfigurationPublishBeginCommand.cs index 5ba4dfe13da..e80624d4f09 100644 --- a/src/Nitro/CommandLine/src/CommandLine/Commands/Fusion/PublishCommand/FusionConfigurationPublishBeginCommand.cs +++ b/src/Nitro/CommandLine/src/CommandLine/Commands/Fusion/PublishCommand/FusionConfigurationPublishBeginCommand.cs @@ -82,6 +82,8 @@ async Task RequestDeploymentSlotAsync(INitroConsoleActivity activity) fusionConfigurationClient, cancellationToken); + activity.Success("Deployment slot ready."); + resultHolder.SetResult(new ObjectResult(new FusionConfigurationPublishBeginCommandResult { RequestId = requestId })); await FusionConfigurationPublishingState.SetRequestId( diff --git a/src/Nitro/CommandLine/src/CommandLine/Commands/Fusion/PublishCommand/FusionConfigurationPublishValidateCommand.cs b/src/Nitro/CommandLine/src/CommandLine/Commands/Fusion/PublishCommand/FusionConfigurationPublishValidateCommand.cs index 3435bb39a0f..daf5870d761 100644 --- a/src/Nitro/CommandLine/src/CommandLine/Commands/Fusion/PublishCommand/FusionConfigurationPublishValidateCommand.cs +++ b/src/Nitro/CommandLine/src/CommandLine/Commands/Fusion/PublishCommand/FusionConfigurationPublishValidateCommand.cs @@ -61,7 +61,14 @@ await FusionConfigurationPublishingState.GetRequestId(fileSystem, cancellationTo fusionConfigurationClient, cancellationToken); - return isValidArchive ? ExitCodes.Success : ExitCodes.Error; + if (!isValidArchive) + { + throw new ExitException("Fusion configuration validation failed."); + } + + activity.Success("Validated configuration."); + + return ExitCodes.Success; } } } diff --git a/src/Nitro/CommandLine/src/CommandLine/Commands/Mcp/PublishMcpFeatureCollectionCommand.cs b/src/Nitro/CommandLine/src/CommandLine/Commands/Mcp/PublishMcpFeatureCollectionCommand.cs index eb7c755225c..e066924d425 100644 --- a/src/Nitro/CommandLine/src/CommandLine/Commands/Mcp/PublishMcpFeatureCollectionCommand.cs +++ b/src/Nitro/CommandLine/src/CommandLine/Commands/Mcp/PublishMcpFeatureCollectionCommand.cs @@ -143,7 +143,7 @@ private static async Task ExecuteAsync( } } - activity.Fail(errorTree); + await activity.FailAllAsync(errorTree); throw new ExitException("MCP feature collection publish failed."); diff --git a/src/Nitro/CommandLine/src/CommandLine/Commands/Mcp/ValidateMcpFeatureCollectionCommand.cs b/src/Nitro/CommandLine/src/CommandLine/Commands/Mcp/ValidateMcpFeatureCollectionCommand.cs index 78dbca51855..ebfe5359b86 100644 --- a/src/Nitro/CommandLine/src/CommandLine/Commands/Mcp/ValidateMcpFeatureCollectionCommand.cs +++ b/src/Nitro/CommandLine/src/CommandLine/Commands/Mcp/ValidateMcpFeatureCollectionCommand.cs @@ -139,7 +139,7 @@ await McpFeatureCollectionHelpers.BuildMcpFeatureCollectionArchive( } } - activity.Fail(errorTree); + await activity.FailAllAsync(errorTree); throw new ExitException("MCP feature collection validation failed."); diff --git a/src/Nitro/CommandLine/src/CommandLine/Commands/OpenApi/PublishOpenApiCollectionCommand.cs b/src/Nitro/CommandLine/src/CommandLine/Commands/OpenApi/PublishOpenApiCollectionCommand.cs index 3ab619b4a2a..2244166759c 100644 --- a/src/Nitro/CommandLine/src/CommandLine/Commands/OpenApi/PublishOpenApiCollectionCommand.cs +++ b/src/Nitro/CommandLine/src/CommandLine/Commands/OpenApi/PublishOpenApiCollectionCommand.cs @@ -142,7 +142,7 @@ private static async Task ExecuteAsync( } } - activity.Fail(errorTree); + await activity.FailAllAsync(errorTree); throw new ExitException("OpenAPI collection publish failed."); diff --git a/src/Nitro/CommandLine/src/CommandLine/Commands/OpenApi/ValidateOpenApiCollectionCommand.cs b/src/Nitro/CommandLine/src/CommandLine/Commands/OpenApi/ValidateOpenApiCollectionCommand.cs index ae04900a450..4e5f9d09fc8 100644 --- a/src/Nitro/CommandLine/src/CommandLine/Commands/OpenApi/ValidateOpenApiCollectionCommand.cs +++ b/src/Nitro/CommandLine/src/CommandLine/Commands/OpenApi/ValidateOpenApiCollectionCommand.cs @@ -131,7 +131,7 @@ await OpenApiCollectionHelpers.BuildOpenApiCollectionArchive( } } - activity.Fail(errorTree); + await activity.FailAllAsync(errorTree); throw new ExitException("OpenAPI collection validation failed."); diff --git a/src/Nitro/CommandLine/src/CommandLine/Commands/Schemas/PublishSchemaCommand.cs b/src/Nitro/CommandLine/src/CommandLine/Commands/Schemas/PublishSchemaCommand.cs index 13396317ee7..2784472f88c 100644 --- a/src/Nitro/CommandLine/src/CommandLine/Commands/Schemas/PublishSchemaCommand.cs +++ b/src/Nitro/CommandLine/src/CommandLine/Commands/Schemas/PublishSchemaCommand.cs @@ -164,7 +164,7 @@ private static async Task ExecuteAsync( } } - activity.Fail(errorTree); + await activity.FailAllAsync(errorTree); throw new ExitException("Schema publish failed."); diff --git a/src/Nitro/CommandLine/src/CommandLine/Commands/Schemas/SchemaHelpers.cs b/src/Nitro/CommandLine/src/CommandLine/Commands/Schemas/SchemaHelpers.cs index e97debc2483..59a132f0875 100644 --- a/src/Nitro/CommandLine/src/CommandLine/Commands/Schemas/SchemaHelpers.cs +++ b/src/Nitro/CommandLine/src/CommandLine/Commands/Schemas/SchemaHelpers.cs @@ -96,11 +96,7 @@ public static async Task ValidateSchemaAsync( } } - activity.Fail(errorTree); - - await activity.FailAllAsync(); - - console.Error.WriteErrorLine("Schema validation failed."); + await activity.FailAllAsync(errorTree); return false; diff --git a/src/Nitro/CommandLine/src/CommandLine/Commands/Schemas/ValidateSchemaCommand.cs b/src/Nitro/CommandLine/src/CommandLine/Commands/Schemas/ValidateSchemaCommand.cs index 28cd84fb2d9..0a9732183b7 100644 --- a/src/Nitro/CommandLine/src/CommandLine/Commands/Schemas/ValidateSchemaCommand.cs +++ b/src/Nitro/CommandLine/src/CommandLine/Commands/Schemas/ValidateSchemaCommand.cs @@ -74,6 +74,11 @@ private static async Task ExecuteAsync( source, ct); - return isValid ? ExitCodes.Success : ExitCodes.Error; + if (!isValid) + { + throw new ExitException("Schema validation failed."); + } + + return ExitCodes.Success; } } diff --git a/src/Nitro/CommandLine/src/CommandLine/Commands/Stages/EditStagesCommand.cs b/src/Nitro/CommandLine/src/CommandLine/Commands/Stages/EditStagesCommand.cs index 0c1debfe5af..e9dff895029 100644 --- a/src/Nitro/CommandLine/src/CommandLine/Commands/Stages/EditStagesCommand.cs +++ b/src/Nitro/CommandLine/src/CommandLine/Commands/Stages/EditStagesCommand.cs @@ -219,7 +219,7 @@ public static async Task UpdateStagesAsync( } } - activity.Fail(errorTree); + await activity.FailAllAsync(errorTree); throw new ExitException("Stage update failed."); } diff --git a/src/Nitro/CommandLine/src/CommandLine/Services/Console/ActivityEntry.cs b/src/Nitro/CommandLine/src/CommandLine/Services/Console/ActivityEntry.cs index 08b97b04e66..9030b97e8c5 100644 --- a/src/Nitro/CommandLine/src/CommandLine/Services/Console/ActivityEntry.cs +++ b/src/Nitro/CommandLine/src/CommandLine/Services/Console/ActivityEntry.cs @@ -22,11 +22,13 @@ public ActivityEntry(string text, ActivityState state = ActivityState.Active) public IRenderable? Details { get; set; } + public bool IsTerminator { get; init; } + public TimeSpan Elapsed => _stopwatch.Elapsed; - public ActivityEntry AddChild(string text, ActivityState state) + public ActivityEntry AddChild(string text, ActivityState state, bool isTerminator = false) { - var child = new ActivityEntry(text, state); + var child = new ActivityEntry(text, state) { IsTerminator = isTerminator }; _children.Add(child); return child; } diff --git a/src/Nitro/CommandLine/src/CommandLine/Services/Console/ActivityTree.cs b/src/Nitro/CommandLine/src/CommandLine/Services/Console/ActivityTree.cs index 559575a82e4..952cf03782a 100644 --- a/src/Nitro/CommandLine/src/CommandLine/Services/Console/ActivityTree.cs +++ b/src/Nitro/CommandLine/src/CommandLine/Services/Console/ActivityTree.cs @@ -27,11 +27,11 @@ public ActivityEntry AddRoot(string text) } } - public ActivityEntry AddChild(ActivityEntry parent, string text, ActivityState state) + public ActivityEntry AddChild(ActivityEntry parent, string text, ActivityState state, bool isTerminator = false) { lock (_lock) { - return parent.AddChild(text, state); + return parent.AddChild(text, state, isTerminator); } } @@ -117,7 +117,21 @@ private void RenderEntry( var icon = IconFor(entry); var textStyle = TextStyleFor(entry.State); - var hasChildren = entry.Children.Count > 0; + // When a failed entry's own terminator child duplicates a preceding failed + // child, suppress the terminator — the preceding child already conveys the + // failure. Only the explicitly marked terminator can be hidden; real children + // are never dropped. + var visibleChildCount = entry.Children.Count; + if (entry.State == ActivityState.Failed + && visibleChildCount > 1 + && entry.Children[visibleChildCount - 1].IsTerminator + && entry.Children[visibleChildCount - 1].State == ActivityState.Failed + && entry.Children[visibleChildCount - 2].State == ActivityState.Failed) + { + visibleChildCount--; + } + + var hasChildren = visibleChildCount > 0; var hasDetails = entry.Details is not null; var continuationPrefix = BuildContinuationPrefix( parentPrefix, @@ -139,9 +153,9 @@ private void RenderEntry( segments.Add(Segment.LineBreak); var childPrefix = parentPrefix + lane; - for (var i = 0; i < entry.Children.Count; i++) + for (var i = 0; i < visibleChildCount; i++) { - var isLastChild = i == entry.Children.Count - 1 && !hasDetails; + var isLastChild = i == visibleChildCount - 1 && !hasDetails; var childPosition = isLastChild ? NodePosition.Last : NodePosition.Middle; RenderEntry(segments, entry.Children[i], childPrefix, childPosition, options, maxWidth); } diff --git a/src/Nitro/CommandLine/src/CommandLine/Services/Console/IActivitySink.cs b/src/Nitro/CommandLine/src/CommandLine/Services/Console/IActivitySink.cs index 631adab882c..973f8bce90b 100644 --- a/src/Nitro/CommandLine/src/CommandLine/Services/Console/IActivitySink.cs +++ b/src/Nitro/CommandLine/src/CommandLine/Services/Console/IActivitySink.cs @@ -20,7 +20,5 @@ internal interface IActivitySink void FailActiveDescendants(ActivityEntry entry); - void FailSilent(ActivityEntry entry, string failureMessage); - void Stop(); } diff --git a/src/Nitro/CommandLine/src/CommandLine/Services/Console/INitroConsoleActivity.cs b/src/Nitro/CommandLine/src/CommandLine/Services/Console/INitroConsoleActivity.cs index 0b92d38129f..e5e0d909c81 100644 --- a/src/Nitro/CommandLine/src/CommandLine/Services/Console/INitroConsoleActivity.cs +++ b/src/Nitro/CommandLine/src/CommandLine/Services/Console/INitroConsoleActivity.cs @@ -16,7 +16,7 @@ internal interface INitroConsoleActivity : IAsyncDisposable void Fail(IRenderable details); - ValueTask FailAllAsync(); + ValueTask FailAllAsync(IRenderable? details = null); INitroConsoleActivity StartChildActivity(string title, string failureMessage); } diff --git a/src/Nitro/CommandLine/src/CommandLine/Services/Console/LiveActivitySink.cs b/src/Nitro/CommandLine/src/CommandLine/Services/Console/LiveActivitySink.cs index 948286a18fb..02b8712e85e 100644 --- a/src/Nitro/CommandLine/src/CommandLine/Services/Console/LiveActivitySink.cs +++ b/src/Nitro/CommandLine/src/CommandLine/Services/Console/LiveActivitySink.cs @@ -29,7 +29,7 @@ public ActivityEntry AddChild(ActivityEntry parent, string text, ActivityState s public ActivityEntry CompleteChild(ActivityEntry parent, string text, ActivityState state) { - return _tree.AddChild(parent, text, state); + return _tree.AddChild(parent, text, state, isTerminator: true); } public void SetState(ActivityEntry entry, ActivityState state) @@ -52,12 +52,6 @@ public void FailActiveDescendants(ActivityEntry entry) _tree.FailActiveDescendants(entry); } - public void FailSilent(ActivityEntry entry, string failureMessage) - { - _tree.SetEntryState(entry, ActivityState.Failed); - _tree.FailActiveDescendants(entry); - } - public void Stop() { _timer.Dispose(); diff --git a/src/Nitro/CommandLine/src/CommandLine/Services/Console/NitroConsoleActivity.cs b/src/Nitro/CommandLine/src/CommandLine/Services/Console/NitroConsoleActivity.cs index c5ac34c7f0d..90e1b6dab9c 100644 --- a/src/Nitro/CommandLine/src/CommandLine/Services/Console/NitroConsoleActivity.cs +++ b/src/Nitro/CommandLine/src/CommandLine/Services/Console/NitroConsoleActivity.cs @@ -22,7 +22,14 @@ private NitroConsoleActivity( _parent = parent; } - private bool IsRoot => _parent is null; + public static INitroConsoleActivity Start( + IActivitySink sink, + string title, + string failureMessage) + { + var root = sink.AddRoot(title); + return new NitroConsoleActivity(sink, root, failureMessage, parent: null); + } public void Update( string message, @@ -58,19 +65,19 @@ public void Fail(string message) Complete(message, ActivityState.Failed, details: null); } - public void Fail(IRenderable details) + public void Fail() { - Complete(_failureMessage, ActivityState.Failed, details); + Fail(_failureMessage); } - public void Fail() + public void Fail(IRenderable details) { - Fail(_failureMessage); + Complete(_failureMessage, ActivityState.Failed, details); } - public async ValueTask FailAllAsync() + public async ValueTask FailAllAsync(IRenderable? details = null) { - FailSilent(); + Complete(_failureMessage, ActivityState.Failed, details); if (_parent is not null) { @@ -109,14 +116,7 @@ public async ValueTask DisposeAsync() } } - public static INitroConsoleActivity Start( - IActivitySink sink, - string title, - string failureMessage) - { - var root = sink.AddRoot(title); - return new NitroConsoleActivity(sink, root, failureMessage, parent: null); - } + private bool IsRoot => _parent is null; private void Complete(string message, ActivityState state, IRenderable? details) { @@ -155,22 +155,6 @@ private void Complete(string message, ActivityState state, IRenderable? details) } } - private void FailSilent() - { - if (_completed) - { - return; - } - - _sink.FailSilent(_entry, _failureMessage); - _completed = true; - - if (IsRoot) - { - _sink.Stop(); - } - } - private static ActivityState MapState(ActivityUpdateKind kind) => kind switch { ActivityUpdateKind.Warning => ActivityState.Warning, diff --git a/src/Nitro/CommandLine/src/CommandLine/Services/Console/StreamingActivitySink.cs b/src/Nitro/CommandLine/src/CommandLine/Services/Console/StreamingActivitySink.cs index 5c699f600ab..5e9d17e2e08 100644 --- a/src/Nitro/CommandLine/src/CommandLine/Services/Console/StreamingActivitySink.cs +++ b/src/Nitro/CommandLine/src/CommandLine/Services/Console/StreamingActivitySink.cs @@ -48,7 +48,7 @@ public ActivityEntry CompleteChild(ActivityEntry parent, string text, ActivitySt TreeLineWriter.WriteWrapped(_console, linePrefix, continuationPrefix, markupText); - var entry = parent.AddChild(text, state); + var entry = parent.AddChild(text, state, isTerminator: true); _meta[entry] = new EntryMeta( Prefix: parentMeta.Prefix + " ", DetailsPrefix: parentMeta.Prefix + " "); @@ -86,18 +86,6 @@ public void FailActiveDescendants(ActivityEntry entry) // Streaming is append-only; descendant state does not affect already-written output. } - public void FailSilent(ActivityEntry entry, string failureMessage) - { - var entryMeta = _meta[entry]; - var markupText = FormatTerminator(failureMessage, ActivityState.Failed); - var linePrefix = entryMeta.Prefix + "└── "; - var continuationPrefix = entryMeta.Prefix + " " + " "; - - TreeLineWriter.WriteWrapped(_console, linePrefix, continuationPrefix, markupText); - - entry.State = ActivityState.Failed; - } - public void Stop() { } diff --git a/src/Nitro/CommandLine/test/CommandLine.Tests/Commands/Fusion/FusionConfigurationPublishBeginCommandTests.cs b/src/Nitro/CommandLine/test/CommandLine.Tests/Commands/Fusion/FusionConfigurationPublishBeginCommandTests.cs index f5f3d23d803..a4475c34cd7 100644 --- a/src/Nitro/CommandLine/test/CommandLine.Tests/Commands/Fusion/FusionConfigurationPublishBeginCommandTests.cs +++ b/src/Nitro/CommandLine/test/CommandLine.Tests/Commands/Fusion/FusionConfigurationPublishBeginCommandTests.cs @@ -178,7 +178,7 @@ public async Task MutationReturnsNullRequestId_ReturnsError() } [Fact] - public async Task Success_DeploymentSlotReady() + public async Task Success_DeploymentSlotReady_ReturnsSuccess() { // arrange SetupRequestDeploymentSlotMutation(); @@ -200,7 +200,8 @@ public async Task Success_DeploymentSlotReady() result.AssertSuccess( """ Requesting deployment slot for stage 'dev' of API 'api-1' - └── ✕ Failed to request a deployment slot. + ├── Publication request created. (ID: request-id) + └── ✓ Deployment slot ready. { "requestId": "request-id" @@ -209,7 +210,7 @@ public async Task Success_DeploymentSlotReady() } [Fact] - public async Task Success_DeploymentSlotReady_JsonOutput() + public async Task Success_DeploymentSlotReady_ReturnsSuccess_JsonOutput() { // arrange SetupInteractionMode(InteractionMode.JsonOutput); @@ -238,7 +239,7 @@ public async Task Success_DeploymentSlotReady_JsonOutput() } [Fact] - public async Task Begin_Should_HandleQueuePosition_When_ProcessingTaskIsQueued() + public async Task Begin_Should_HandleQueuePosition_When_ProcessingTaskIsQueued_ReturnsSuccess() { // arrange SetupRequestDeploymentSlotMutation(); @@ -262,8 +263,9 @@ public async Task Begin_Should_HandleQueuePosition_When_ProcessingTaskIsQueued() result.AssertSuccess( """ Requesting deployment slot for stage 'dev' of API 'api-1' + ├── Publication request created. (ID: request-id) ├── ⏳ Your request is queued at position 3. - └── ✕ Failed to request a deployment slot. + └── ✓ Deployment slot ready. { "requestId": "request-id" @@ -272,7 +274,7 @@ public async Task Begin_Should_HandleQueuePosition_When_ProcessingTaskIsQueued() } [Fact] - public async Task Begin_Should_PassSubgraphId_When_Provided() + public async Task Begin_Should_PassSubgraphId_When_Provided_ReturnsSuccess() { // arrange SetupRequestDeploymentSlotMutation(subgraphId: "subgraph-1"); @@ -297,7 +299,7 @@ public async Task Begin_Should_PassSubgraphId_When_Provided() } [Fact] - public async Task Begin_Should_PassSubgraphName_When_Provided() + public async Task Begin_Should_PassSubgraphName_When_Provided_ReturnsSuccess() { // arrange SetupRequestDeploymentSlotMutation(subgraphName: "subgraph-1"); @@ -322,7 +324,7 @@ public async Task Begin_Should_PassSubgraphName_When_Provided() } [Fact] - public async Task Begin_Should_PassWaitForApproval_When_Provided() + public async Task Begin_Should_PassWaitForApproval_When_Provided_ReturnsSuccess() { // arrange SetupRequestDeploymentSlotMutation(waitForApproval: true); diff --git a/src/Nitro/CommandLine/test/CommandLine.Tests/Commands/Fusion/FusionConfigurationPublishCancelCommandTests.cs b/src/Nitro/CommandLine/test/CommandLine.Tests/Commands/Fusion/FusionConfigurationPublishCancelCommandTests.cs index c332a52d22c..e45c72c10d9 100644 --- a/src/Nitro/CommandLine/test/CommandLine.Tests/Commands/Fusion/FusionConfigurationPublishCancelCommandTests.cs +++ b/src/Nitro/CommandLine/test/CommandLine.Tests/Commands/Fusion/FusionConfigurationPublishCancelCommandTests.cs @@ -127,7 +127,7 @@ Canceling publication } [Fact] - public async Task RequestIdFromArg_Success() + public async Task RequestIdFromArg_Success_ReturnsSuccess() { // arrange SetupFusionPublishingStateCache(RequestId); @@ -145,7 +145,7 @@ Canceling publication } [Fact] - public async Task RequestIdFromStateFile_Success() + public async Task RequestIdFromStateFile_Success_ReturnsSuccess() { // arrange SetupFusionPublishingStateCache(RequestId); diff --git a/src/Nitro/CommandLine/test/CommandLine.Tests/Commands/Fusion/FusionConfigurationPublishCommitCommandTests.cs b/src/Nitro/CommandLine/test/CommandLine.Tests/Commands/Fusion/FusionConfigurationPublishCommitCommandTests.cs index 431fc0fe114..c6372d43f17 100644 --- a/src/Nitro/CommandLine/test/CommandLine.Tests/Commands/Fusion/FusionConfigurationPublishCommitCommandTests.cs +++ b/src/Nitro/CommandLine/test/CommandLine.Tests/Commands/Fusion/FusionConfigurationPublishCommitCommandTests.cs @@ -165,7 +165,7 @@ Publishing Fusion configuration } [Fact] - public async Task Success_CommitsArchive_NonInteractive() + public async Task Success_CommitsArchive_ReturnsSuccess() { // arrange SetupArchiveFile(); @@ -193,33 +193,7 @@ Publishing Fusion configuration } [Fact] - public async Task Success_CommitsArchive_Interactive() - { - // arrange - SetupArchiveFile(); - var capturedStream = SetupFusionConfigurationUploadMutation(); - SetupFusionConfigurationUploadSubscription(); - SetupInteractionMode(InteractionMode.Interactive); - - // act - var result = await ExecuteCommandAsync( - "fusion", - "publish", - "commit", - "--request-id", - RequestId, - "--archive", - ArchiveFile); - - // assert - Assert.Empty(result.StdErr); - Assert.Equal(0, result.ExitCode); - var schema = await GetFusionSchemaAsync(capturedStream); - AssertFusionSchema(schema); - } - - [Fact] - public async Task Success_CommitsArchive_JsonOutput() + public async Task Success_CommitsArchive_ReturnsSuccess_JsonOutput() { // arrange SetupArchiveFile(); @@ -248,7 +222,7 @@ public async Task Success_CommitsArchive_JsonOutput() [InlineData(InteractionMode.Interactive)] [InlineData(InteractionMode.NonInteractive)] [InlineData(InteractionMode.JsonOutput)] - public async Task RequestIdFromStateFile_Success(InteractionMode mode) + public async Task RequestIdFromStateFile_Success_ReturnsSuccess(InteractionMode mode) { // arrange SetupFusionPublishingStateCache(RequestId); @@ -273,7 +247,7 @@ public async Task RequestIdFromStateFile_Success(InteractionMode mode) } [Fact] - public async Task Commit_Should_ReturnError_When_CommitFails() + public async Task Commit_Should_ReturnError_When_CommitFails_ReturnsError() { // arrange SetupArchiveFile(); @@ -309,7 +283,7 @@ The commit has failed. } [Fact] - public async Task Commit_Should_HandleSubscriptionEvents_When_PublishFails() + public async Task Commit_Should_HandleSubscriptionEvents_When_PublishFails_ReturnsError() { // arrange SetupArchiveFile(); @@ -342,7 +316,7 @@ Publishing Fusion configuration } [Fact] - public async Task Commit_Should_HandleSubscriptionEvents_When_Queued() + public async Task Commit_Should_HandleSubscriptionEvents_When_Queued_ReturnsSuccess() { // arrange SetupArchiveFile(); diff --git a/src/Nitro/CommandLine/test/CommandLine.Tests/Commands/Fusion/FusionConfigurationPublishStartCommandTests.cs b/src/Nitro/CommandLine/test/CommandLine.Tests/Commands/Fusion/FusionConfigurationPublishStartCommandTests.cs index bcb8d9bc44f..9e914ba312a 100644 --- a/src/Nitro/CommandLine/test/CommandLine.Tests/Commands/Fusion/FusionConfigurationPublishStartCommandTests.cs +++ b/src/Nitro/CommandLine/test/CommandLine.Tests/Commands/Fusion/FusionConfigurationPublishStartCommandTests.cs @@ -79,7 +79,7 @@ No request ID was provided and no request ID was found in the cache. Please prov [InlineData(InteractionMode.Interactive)] [InlineData(InteractionMode.NonInteractive)] [InlineData(InteractionMode.JsonOutput)] - public async Task RequestIdFromStateFile_Success(InteractionMode mode) + public async Task RequestIdFromStateFile_Success_ReturnsSuccess(InteractionMode mode) { // arrange SetupFusionPublishingStateCache(RequestId); @@ -118,7 +118,7 @@ Starting composition } [Fact] - public async Task Success_WithRequestIdOption() + public async Task Success_WithRequestIdOption_ReturnsSuccess() { // arrange SetupClaimDeploymentSlotMutation(); diff --git a/src/Nitro/CommandLine/test/CommandLine.Tests/Commands/Fusion/FusionConfigurationPublishValidateCommandTests.cs b/src/Nitro/CommandLine/test/CommandLine.Tests/Commands/Fusion/FusionConfigurationPublishValidateCommandTests.cs index b052f9c4fb4..6872f9c17f7 100644 --- a/src/Nitro/CommandLine/test/CommandLine.Tests/Commands/Fusion/FusionConfigurationPublishValidateCommandTests.cs +++ b/src/Nitro/CommandLine/test/CommandLine.Tests/Commands/Fusion/FusionConfigurationPublishValidateCommandTests.cs @@ -109,7 +109,7 @@ Validating Fusion configuration } [Fact] - public async Task Subscription_ValidationSuccess_ReturnsSuccess_NonInteractive() + public async Task Subscription_ValidationSuccess_ReturnsSuccess() { // arrange SetupArchiveFile(); @@ -131,36 +131,12 @@ public async Task Subscription_ValidationSuccess_ReturnsSuccess_NonInteractive() result.AssertSuccess( """ Validating Fusion configuration - └── ✕ Failed to validate the Fusion configuration. + └── ✓ Validated configuration. """); } [Fact] - public async Task Subscription_ValidationSuccess_ReturnsSuccess_Interactive() - { - // arrange - SetupInteractionMode(InteractionMode.Interactive); - SetupArchiveFile(); - SetupFusionConfigurationValidationMutation(); - SetupFusionConfigurationValidationSubscription( - CreateValidationSuccessEvent()); - - // act - var result = await ExecuteCommandAsync( - "fusion", - "publish", - "validate", - "--archive", - ArchiveFile, - "--request-id", - RequestId); - - // assert - result.AssertSuccess(); - } - - [Fact] - public async Task Subscription_ValidationFailed_ReturnsError_NonInteractive() + public async Task Subscription_ValidationFailed_ReturnsError() { // arrange var errorMock = new Mock(MockBehavior.Strict); @@ -196,12 +172,15 @@ Validating Fusion configuration └── ✕ Failed to validate the Fusion configuration. └── Something went wrong. """); - result.StdErr.MatchInlineSnapshot(""); + result.StdErr.MatchInlineSnapshot( + """ + Fusion configuration validation failed. + """); Assert.Equal(1, result.ExitCode); } [Fact] - public async Task Subscription_Queued_ThrowsExitException() + public async Task Subscription_Queued_ReturnsError() { // arrange SetupArchiveFile(); @@ -226,13 +205,13 @@ Validating Fusion configuration """); result.StdErr.MatchInlineSnapshot( """ - Your request is in the queued state. Try to run `fusion-configuration publish start` once the request is ready + Your request is in the queued state. Try to run `fusion-configuration publish start` once the request is ready. """); Assert.Equal(1, result.ExitCode); } [Fact] - public async Task Subscription_AlreadyFailed_ThrowsExitException() + public async Task Subscription_AlreadyFailed_ReturnsError() { // arrange SetupArchiveFile(); @@ -257,13 +236,13 @@ Validating Fusion configuration """); result.StdErr.MatchInlineSnapshot( """ - Your request has already failed + Your request has already failed. """); Assert.Equal(1, result.ExitCode); } [Fact] - public async Task Subscription_AlreadyPublished_ThrowsExitException() + public async Task Subscription_AlreadyPublished_ReturnsError() { // arrange SetupArchiveFile(); @@ -288,13 +267,13 @@ Validating Fusion configuration """); result.StdErr.MatchInlineSnapshot( """ - You request is already published + Your request is already published. """); Assert.Equal(1, result.ExitCode); } [Fact] - public async Task Subscription_Ready_ThrowsExitException() + public async Task Subscription_Ready_ReturnsError() { // arrange SetupArchiveFile(); @@ -319,13 +298,13 @@ Validating Fusion configuration """); result.StdErr.MatchInlineSnapshot( """ - Your request is ready for the composition. Run `fusion-configuration publish start` + Your request is ready for the composition. Run `fusion-configuration publish start`. """); Assert.Equal(1, result.ExitCode); } [Fact] - public async Task Subscription_InProgressThenSuccess_ReturnsSuccess_NonInteractive() + public async Task Subscription_InProgressThenSuccess_ReturnsSuccess() { // arrange SetupArchiveFile(); @@ -349,12 +328,14 @@ public async Task Subscription_InProgressThenSuccess_ReturnsSuccess_NonInteracti result.AssertSuccess( """ Validating Fusion configuration - └── ✕ Failed to validate the Fusion configuration. + ├── Validating... + ├── Validating... + └── ✓ Validated configuration. """); } [Fact] - public async Task Subscription_UnknownEvent_ThrowsExitException() + public async Task Subscription_UnknownEvent_ReturnsError() { // arrange var unknownEvent = new Mock( @@ -382,37 +363,11 @@ Validating Fusion configuration ├── ! Unknown server response. Consider updating the CLI. └── ✕ Failed to validate the Fusion configuration. """); - Assert.Empty(result.StdErr); - Assert.Equal(1, result.ExitCode); - } - - [Fact] - public async Task Validate_Should_HandleApprovalEvents_When_WaitForApproval() - { - // arrange - SetupArchiveFile(); - SetupFusionConfigurationValidationMutation(); - SetupFusionConfigurationValidationSubscription( - CreateWaitForApprovalEvent(), - CreateProcessingTaskApprovedEvent(), - CreateValidationSuccessEvent()); - - // act - var result = await ExecuteCommandAsync( - "fusion", - "publish", - "validate", - "--archive", - ArchiveFile, - "--request-id", - RequestId); - - // assert - result.AssertSuccess( + result.StdErr.MatchInlineSnapshot( """ - Validating Fusion configuration - └── ✕ Failed to validate the Fusion configuration. + Fusion configuration validation failed. """); + Assert.Equal(1, result.ExitCode); } #region Theory Data diff --git a/src/Nitro/CommandLine/test/CommandLine.Tests/Commands/Fusion/FusionPublishCommandTests.cs b/src/Nitro/CommandLine/test/CommandLine.Tests/Commands/Fusion/FusionPublishCommandTests.cs index a48e319f748..4b7f5564981 100644 --- a/src/Nitro/CommandLine/test/CommandLine.Tests/Commands/Fusion/FusionPublishCommandTests.cs +++ b/src/Nitro/CommandLine/test/CommandLine.Tests/Commands/Fusion/FusionPublishCommandTests.cs @@ -267,12 +267,14 @@ public async Task WithArchive_ReturnsSuccess() // assert result.AssertSuccess( """ - Publishing Fusion configuration to stage 'dev' of API 'api-1' + Publishing new Fusion configuration version 'v1' of API 'api-1' to stage 'dev' ├── Requesting deployment slot + │ ├── Publication request created. (ID: request-id) │ └── ✓ Deployment slot ready. ├── Claiming deployment slot │ └── ✓ Claimed deployment slot. ├── Validating configuration against 'dev' + │ ├── Validating... │ └── ✓ Validated configuration. ├── Uploading configuration to 'dev' │ └── ✓ Uploaded configuration. @@ -306,12 +308,14 @@ public async Task WithArchive_WithEnvVars_ReturnsSuccess() // assert result.AssertSuccess( """ - Publishing Fusion configuration to stage 'dev' of API 'api-1' + Publishing new Fusion configuration version 'v1' of API 'api-1' to stage 'dev' ├── Requesting deployment slot + │ ├── Publication request created. (ID: request-id) │ └── ✓ Deployment slot ready. ├── Claiming deployment slot │ └── ✓ Claimed deployment slot. ├── Validating configuration against 'dev' + │ ├── Validating... │ └── ✓ Validated configuration. ├── Uploading configuration to 'dev' │ └── ✓ Uploaded configuration. @@ -351,10 +355,10 @@ public async Task WithArchive_RequestDeploymentSlotHasErrors_ReturnsError( """); result.StdOut.MatchInlineSnapshot( """ - Publishing Fusion configuration to stage 'dev' of API 'api-1' + Publishing new Fusion configuration version 'v1' of API 'api-1' to stage 'dev' ├── Requesting deployment slot │ └── ✕ Failed to request a deployment slot. - └── ✕ Failed to publish Fusion configuration. + └── ✕ Failed to publish a new Fusion configuration version. """); Assert.Equal(1, result.ExitCode); } @@ -386,10 +390,10 @@ public async Task WithArchive_RequestDeploymentSlotThrows_ReturnsError() """); result.StdOut.MatchInlineSnapshot( """ - Publishing Fusion configuration to stage 'dev' of API 'api-1' + Publishing new Fusion configuration version 'v1' of API 'api-1' to stage 'dev' ├── Requesting deployment slot │ └── ✕ Failed to request a deployment slot. - └── ✕ Failed to publish Fusion configuration. + └── ✕ Failed to publish a new Fusion configuration version. """); Assert.Equal(1, result.ExitCode); } @@ -427,12 +431,13 @@ public async Task WithArchive_ClaimDeploymentSlotHasError_ReturnsError( """); result.StdOut.MatchInlineSnapshot( """ - Publishing Fusion configuration to stage 'dev' of API 'api-1' + Publishing new Fusion configuration version 'v1' of API 'api-1' to stage 'dev' ├── Requesting deployment slot + │ ├── Publication request created. (ID: request-id) │ └── ✓ Deployment slot ready. ├── Claiming deployment slot │ └── ✕ Failed to claim the deployment slot. - └── ✕ Failed to publish Fusion configuration. + └── ✕ Failed to publish a new Fusion configuration version. """); Assert.Equal(1, result.ExitCode); } @@ -467,12 +472,13 @@ public async Task WithArchive_ClaimDeploymentSlotThrows_ReturnsError() """); result.StdOut.MatchInlineSnapshot( """ - Publishing Fusion configuration to stage 'dev' of API 'api-1' + Publishing new Fusion configuration version 'v1' of API 'api-1' to stage 'dev' ├── Requesting deployment slot + │ ├── Publication request created. (ID: request-id) │ └── ✓ Deployment slot ready. ├── Claiming deployment slot │ └── ✕ Failed to claim the deployment slot. - └── ✕ Failed to publish Fusion configuration. + └── ✕ Failed to publish a new Fusion configuration version. """); Assert.Equal(1, result.ExitCode); } @@ -511,14 +517,15 @@ public async Task WithArchive_ValidationHasErrors_ReturnsError( """); result.StdOut.MatchInlineSnapshot( """ - Publishing Fusion configuration to stage 'dev' of API 'api-1' + Publishing new Fusion configuration version 'v1' of API 'api-1' to stage 'dev' ├── Requesting deployment slot + │ ├── Publication request created. (ID: request-id) │ └── ✓ Deployment slot ready. ├── Claiming deployment slot │ └── ✓ Claimed deployment slot. ├── Validating configuration against 'dev' │ └── ✕ Failed to validate the new configuration. - └── ✕ Failed to publish Fusion configuration. + └── ✕ Failed to publish a new Fusion configuration version. """); Assert.Equal(1, result.ExitCode); } @@ -554,14 +561,15 @@ public async Task WithArchive_ValidationThrows_ReturnsError() """); result.StdOut.MatchInlineSnapshot( """ - Publishing Fusion configuration to stage 'dev' of API 'api-1' + Publishing new Fusion configuration version 'v1' of API 'api-1' to stage 'dev' ├── Requesting deployment slot + │ ├── Publication request created. (ID: request-id) │ └── ✓ Deployment slot ready. ├── Claiming deployment slot │ └── ✓ Claimed deployment slot. ├── Validating configuration against 'dev' │ └── ✕ Failed to validate the new configuration. - └── ✕ Failed to publish Fusion configuration. + └── ✕ Failed to publish a new Fusion configuration version. """); Assert.Equal(1, result.ExitCode); } @@ -595,8 +603,9 @@ public async Task WithArchive_WaitForApproval_NoBreakingChanges_ReturnsSuccess() // assert result.AssertSuccess( """ - Publishing Fusion configuration to stage 'dev' of API 'api-1' + Publishing new Fusion configuration version 'v1' of API 'api-1' to stage 'dev' ├── Requesting deployment slot + │ ├── Publication request created. (ID: request-id) │ └── ✓ Deployment slot ready. ├── Claiming deployment slot │ └── ✓ Claimed deployment slot. @@ -638,16 +647,18 @@ public async Task WithArchive_BreakingChanges_ReturnsError() // assert result.StdErr.MatchInlineSnapshot( """ - Failed to validate configuration. + Fusion configuration validation failed. """); result.StdOut.MatchInlineSnapshot( """ - Publishing Fusion configuration to stage 'dev' of API 'api-1' + Publishing new Fusion configuration version 'v1' of API 'api-1' to stage 'dev' ├── Requesting deployment slot + │ ├── Publication request created. (ID: request-id) │ └── ✓ Deployment slot ready. ├── Claiming deployment slot │ └── ✓ Claimed deployment slot. ├── Validating configuration against 'dev' + │ ├── Validating... │ └── ✕ Failed to validate the new configuration. │ ├── GraphQL schema changes │ │ ├── ✕ Directive foo was modified @@ -673,7 +684,7 @@ Publishing Fusion configuration to stage 'dev' of API 'api-1' │ │ └── Tool 'Fail' │ │ └── The field `person` does not exist on the type `Query`. (1:14) │ └── An unexpected error occurred. - └── ✕ Failed to publish Fusion configuration. + └── ✕ Failed to publish a new Fusion configuration version. """); Assert.Equal(1, result.ExitCode); } @@ -710,13 +721,15 @@ public async Task WithArchive_BreakingChanges_Force_ReturnsSuccess() // assert result.AssertSuccess( """ - Publishing Fusion configuration to stage 'dev' of API 'api-1' + Publishing new Fusion configuration version 'v1' of API 'api-1' to stage 'dev' ├── ! Force push is enabled. ├── Requesting deployment slot + │ ├── Publication request created. (ID: request-id) │ └── ✓ Deployment slot ready. ├── Claiming deployment slot │ └── ✓ Claimed deployment slot. ├── Validating configuration against 'dev' + │ ├── Validating... │ └── ✕ Failed to validate the new configuration. │ ├── GraphQL schema changes │ │ ├── ✕ Directive foo was modified @@ -781,8 +794,9 @@ public async Task WithArchive_WaitForApproval_BreakingChanges_Approved_ReturnsSu // assert result.AssertSuccess( """ - Publishing Fusion configuration to stage 'dev' of API 'api-1' + Publishing new Fusion configuration version 'v1' of API 'api-1' to stage 'dev' ├── Requesting deployment slot + │ ├── Publication request created. (ID: request-id) │ └── ✓ Deployment slot ready. ├── Claiming deployment slot │ └── ✓ Claimed deployment slot. @@ -843,8 +857,9 @@ public async Task WithArchive_WaitForApproval_BreakingChanges_NotApproved_Return """); result.StdOut.MatchInlineSnapshot( """ - Publishing Fusion configuration to stage 'dev' of API 'api-1' + Publishing new Fusion configuration version 'v1' of API 'api-1' to stage 'dev' ├── Requesting deployment slot + │ ├── Publication request created. (ID: request-id) │ └── ✓ Deployment slot ready. ├── Claiming deployment slot │ └── ✓ Claimed deployment slot. @@ -863,7 +878,7 @@ Publishing Fusion configuration to stage 'dev' of API 'api-1' │ │ └── The field `person` does not exist on the type `Query`. (1:14) │ ├── ⏳ Waiting for approval. Approve in Nitro to continue. │ └── ✕ Failed to upload the new configuration. - └── ✕ Failed to publish Fusion configuration. + └── ✕ Failed to publish a new Fusion configuration version. """); Assert.Equal(1, result.ExitCode); } @@ -904,16 +919,18 @@ public async Task WithArchive_UploadHasErrors_ReturnsError( """); result.StdOut.MatchInlineSnapshot( """ - Publishing Fusion configuration to stage 'dev' of API 'api-1' + Publishing new Fusion configuration version 'v1' of API 'api-1' to stage 'dev' ├── Requesting deployment slot + │ ├── Publication request created. (ID: request-id) │ └── ✓ Deployment slot ready. ├── Claiming deployment slot │ └── ✓ Claimed deployment slot. ├── Validating configuration against 'dev' + │ ├── Validating... │ └── ✓ Validated configuration. ├── Uploading configuration to 'dev' │ └── ✕ Failed to upload the new configuration. - └── ✕ Failed to publish Fusion configuration. + └── ✕ Failed to publish a new Fusion configuration version. """); Assert.Equal(1, result.ExitCode); } @@ -951,16 +968,18 @@ public async Task WithArchive_UploadThrows_ReturnsError() """); result.StdOut.MatchInlineSnapshot( """ - Publishing Fusion configuration to stage 'dev' of API 'api-1' + Publishing new Fusion configuration version 'v1' of API 'api-1' to stage 'dev' ├── Requesting deployment slot + │ ├── Publication request created. (ID: request-id) │ └── ✓ Deployment slot ready. ├── Claiming deployment slot │ └── ✓ Claimed deployment slot. ├── Validating configuration against 'dev' + │ ├── Validating... │ └── ✓ Validated configuration. ├── Uploading configuration to 'dev' │ └── ✕ Failed to upload the new configuration. - └── ✕ Failed to publish Fusion configuration. + └── ✕ Failed to publish a new Fusion configuration version. """); Assert.Equal(1, result.ExitCode); } @@ -999,19 +1018,21 @@ public async Task WithArchive_PublishingFailedWithErrors_ReturnsError() """); result.StdOut.MatchInlineSnapshot( """ - Publishing Fusion configuration to stage 'dev' of API 'api-1' + Publishing new Fusion configuration version 'v1' of API 'api-1' to stage 'dev' ├── Requesting deployment slot + │ ├── Publication request created. (ID: request-id) │ └── ✓ Deployment slot ready. ├── Claiming deployment slot │ └── ✓ Claimed deployment slot. ├── Validating configuration against 'dev' + │ ├── Validating... │ └── ✓ Validated configuration. ├── Uploading configuration to 'dev' │ └── ✕ Failed to upload the new configuration. │ ├── Invalid GraphQL schema │ │ └── Field 'Query.foo' has no type. (SCHEMA_ERROR) │ └── An error occurred. - └── ✕ Failed to publish Fusion configuration. + └── ✕ Failed to publish a new Fusion configuration version. """); Assert.Equal(1, result.ExitCode); } @@ -1050,12 +1071,13 @@ Something unexpected happened. """); result.StdOut.MatchInlineSnapshot( """ - Publishing Fusion configuration to stage 'dev' of API 'api-1' + Publishing new Fusion configuration version 'v1' of API 'api-1' to stage 'dev' ├── Requesting deployment slot + │ ├── Publication request created. (ID: request-id) │ └── ✓ Deployment slot ready. ├── Claiming deployment slot │ └── ✕ Failed to claim the deployment slot. - └── ✕ Failed to publish Fusion configuration. + └── ✕ Failed to publish a new Fusion configuration version. """); Assert.Equal(1, result.ExitCode); } @@ -1097,12 +1119,13 @@ public async Task WithArchive_ReleaseDeploymentSlotHasErrors_ReturnsError( """); result.StdOut.MatchInlineSnapshot( """ - Publishing Fusion configuration to stage 'dev' of API 'api-1' + Publishing new Fusion configuration version 'v1' of API 'api-1' to stage 'dev' ├── Requesting deployment slot + │ ├── Publication request created. (ID: request-id) │ └── ✓ Deployment slot ready. ├── Claiming deployment slot │ └── ✕ Failed to claim the deployment slot. - └── ✕ Failed to publish Fusion configuration. + └── ✕ Failed to publish a new Fusion configuration version. """); Assert.Equal(1, result.ExitCode); } @@ -1172,8 +1195,9 @@ public async Task WithSourceSchemaFile_ReturnsSuccess() // assert result.AssertSuccess( """ - Publishing Fusion configuration to stage 'dev' of API 'api-1' + Publishing new Fusion configuration version 'v1' of API 'api-1' to stage 'dev' ├── Requesting deployment slot + │ ├── Publication request created. (ID: request-id) │ └── ✓ Deployment slot ready. ├── Claiming deployment slot │ └── ✓ Claimed deployment slot. @@ -1182,6 +1206,7 @@ Publishing Fusion configuration to stage 'dev' of API 'api-1' ├── Composing new configuration │ └── ✓ Composed new configuration. ├── Validating configuration against 'dev' + │ ├── Validating... │ └── ✓ Validated configuration. ├── Uploading configuration to 'dev' │ └── ✓ Uploaded configuration. @@ -1219,8 +1244,9 @@ public async Task WithSourceSchemaFile_WithEnvVars_ReturnsSuccess() // assert result.AssertSuccess( """ - Publishing Fusion configuration to stage 'dev' of API 'api-1' + Publishing new Fusion configuration version 'v1' of API 'api-1' to stage 'dev' ├── Requesting deployment slot + │ ├── Publication request created. (ID: request-id) │ └── ✓ Deployment slot ready. ├── Claiming deployment slot │ └── ✓ Claimed deployment slot. @@ -1229,6 +1255,7 @@ Publishing Fusion configuration to stage 'dev' of API 'api-1' ├── Composing new configuration │ └── ✓ Composed new configuration. ├── Validating configuration against 'dev' + │ ├── Validating... │ └── ✓ Validated configuration. ├── Uploading configuration to 'dev' │ └── ✓ Uploaded configuration. @@ -1268,10 +1295,10 @@ public async Task WithSourceSchemaFile_RequestDeploymentSlotHasErrors_ReturnsErr """); result.StdOut.MatchInlineSnapshot( """ - Publishing Fusion configuration to stage 'dev' of API 'api-1' + Publishing new Fusion configuration version 'v1' of API 'api-1' to stage 'dev' ├── Requesting deployment slot │ └── ✕ Failed to request a deployment slot. - └── ✕ Failed to publish Fusion configuration. + └── ✕ Failed to publish a new Fusion configuration version. """); Assert.Equal(1, result.ExitCode); } @@ -1303,10 +1330,10 @@ public async Task WithSourceSchemaFile_RequestDeploymentSlotThrows_ReturnsError( """); result.StdOut.MatchInlineSnapshot( """ - Publishing Fusion configuration to stage 'dev' of API 'api-1' + Publishing new Fusion configuration version 'v1' of API 'api-1' to stage 'dev' ├── Requesting deployment slot │ └── ✕ Failed to request a deployment slot. - └── ✕ Failed to publish Fusion configuration. + └── ✕ Failed to publish a new Fusion configuration version. """); Assert.Equal(1, result.ExitCode); } @@ -1344,12 +1371,13 @@ public async Task WithSourceSchemaFile_ClaimDeploymentSlotHasError_ReturnsError( """); result.StdOut.MatchInlineSnapshot( """ - Publishing Fusion configuration to stage 'dev' of API 'api-1' + Publishing new Fusion configuration version 'v1' of API 'api-1' to stage 'dev' ├── Requesting deployment slot + │ ├── Publication request created. (ID: request-id) │ └── ✓ Deployment slot ready. ├── Claiming deployment slot │ └── ✕ Failed to claim the deployment slot. - └── ✕ Failed to publish Fusion configuration. + └── ✕ Failed to publish a new Fusion configuration version. """); Assert.Equal(1, result.ExitCode); } @@ -1384,12 +1412,13 @@ public async Task WithSourceSchemaFile_ClaimDeploymentSlotThrows_ReturnsError() """); result.StdOut.MatchInlineSnapshot( """ - Publishing Fusion configuration to stage 'dev' of API 'api-1' + Publishing new Fusion configuration version 'v1' of API 'api-1' to stage 'dev' ├── Requesting deployment slot + │ ├── Publication request created. (ID: request-id) │ └── ✓ Deployment slot ready. ├── Claiming deployment slot │ └── ✕ Failed to claim the deployment slot. - └── ✕ Failed to publish Fusion configuration. + └── ✕ Failed to publish a new Fusion configuration version. """); Assert.Equal(1, result.ExitCode); } @@ -1425,14 +1454,15 @@ public async Task WithSourceSchemaFile_ConfigurationDownloadThrows_ReturnsError( """); result.StdOut.MatchInlineSnapshot( """ - Publishing Fusion configuration to stage 'dev' of API 'api-1' + Publishing new Fusion configuration version 'v1' of API 'api-1' to stage 'dev' ├── Requesting deployment slot + │ ├── Publication request created. (ID: request-id) │ └── ✓ Deployment slot ready. ├── Claiming deployment slot │ └── ✓ Claimed deployment slot. ├── Downloading existing configuration from 'dev' │ └── ✕ Failed to download the existing Fusion configuration. - └── ✕ Failed to publish Fusion configuration. + └── ✕ Failed to publish a new Fusion configuration version. """); Assert.Equal(1, result.ExitCode); } @@ -1468,8 +1498,9 @@ Source schema validation failed. """); result.StdOut.MatchInlineSnapshot( """ - Publishing Fusion configuration to stage 'dev' of API 'api-1' + Publishing new Fusion configuration version 'v1' of API 'api-1' to stage 'dev' ├── Requesting deployment slot + │ ├── Publication request created. (ID: request-id) │ └── ✓ Deployment slot ready. ├── Claiming deployment slot │ └── ✓ Claimed deployment slot. @@ -1477,7 +1508,7 @@ Publishing Fusion configuration to stage 'dev' of API 'api-1' │ └── ✓ Downloaded existing configuration from 'dev'. ├── Composing new configuration │ └── ✕ Failed to compose new configuration. - └── ✕ Failed to publish Fusion configuration. + └── ✕ Failed to publish a new Fusion configuration version. ## Composition log @@ -1521,8 +1552,9 @@ public async Task WithSourceSchemaFile_ValidationHasErrors_ReturnsError( """); result.StdOut.MatchInlineSnapshot( """ - Publishing Fusion configuration to stage 'dev' of API 'api-1' + Publishing new Fusion configuration version 'v1' of API 'api-1' to stage 'dev' ├── Requesting deployment slot + │ ├── Publication request created. (ID: request-id) │ └── ✓ Deployment slot ready. ├── Claiming deployment slot │ └── ✓ Claimed deployment slot. @@ -1532,7 +1564,7 @@ Publishing Fusion configuration to stage 'dev' of API 'api-1' │ └── ✓ Composed new configuration. ├── Validating configuration against 'dev' │ └── ✕ Failed to validate the new configuration. - └── ✕ Failed to publish Fusion configuration. + └── ✕ Failed to publish a new Fusion configuration version. """); Assert.Equal(1, result.ExitCode); } @@ -1569,8 +1601,9 @@ public async Task WithSourceSchemaFile_ValidationThrows_ReturnsError() """); result.StdOut.MatchInlineSnapshot( """ - Publishing Fusion configuration to stage 'dev' of API 'api-1' + Publishing new Fusion configuration version 'v1' of API 'api-1' to stage 'dev' ├── Requesting deployment slot + │ ├── Publication request created. (ID: request-id) │ └── ✓ Deployment slot ready. ├── Claiming deployment slot │ └── ✓ Claimed deployment slot. @@ -1580,7 +1613,7 @@ Publishing Fusion configuration to stage 'dev' of API 'api-1' │ └── ✓ Composed new configuration. ├── Validating configuration against 'dev' │ └── ✕ Failed to validate the new configuration. - └── ✕ Failed to publish Fusion configuration. + └── ✕ Failed to publish a new Fusion configuration version. """); Assert.Equal(1, result.ExitCode); } @@ -1617,12 +1650,13 @@ public async Task WithSourceSchemaFile_BreakingChanges_ReturnsError() // assert result.StdErr.MatchInlineSnapshot( """ - Failed to validate configuration. + Fusion configuration validation failed. """); result.StdOut.MatchInlineSnapshot( """ - Publishing Fusion configuration to stage 'dev' of API 'api-1' + Publishing new Fusion configuration version 'v1' of API 'api-1' to stage 'dev' ├── Requesting deployment slot + │ ├── Publication request created. (ID: request-id) │ └── ✓ Deployment slot ready. ├── Claiming deployment slot │ └── ✓ Claimed deployment slot. @@ -1631,6 +1665,7 @@ Publishing Fusion configuration to stage 'dev' of API 'api-1' ├── Composing new configuration │ └── ✓ Composed new configuration. ├── Validating configuration against 'dev' + │ ├── Validating... │ └── ✕ Failed to validate the new configuration. │ ├── GraphQL schema changes │ │ ├── ✕ Directive foo was modified @@ -1656,7 +1691,7 @@ Publishing Fusion configuration to stage 'dev' of API 'api-1' │ │ └── Tool 'Fail' │ │ └── The field `person` does not exist on the type `Query`. (1:14) │ └── An unexpected error occurred. - └── ✕ Failed to publish Fusion configuration. + └── ✕ Failed to publish a new Fusion configuration version. """); Assert.Equal(1, result.ExitCode); } @@ -1694,9 +1729,10 @@ public async Task WithSourceSchemaFile_BreakingChanges_Force_ReturnsSuccess() // assert result.AssertSuccess( """ - Publishing Fusion configuration to stage 'dev' of API 'api-1' + Publishing new Fusion configuration version 'v1' of API 'api-1' to stage 'dev' ├── ! Force push is enabled. ├── Requesting deployment slot + │ ├── Publication request created. (ID: request-id) │ └── ✓ Deployment slot ready. ├── Claiming deployment slot │ └── ✓ Claimed deployment slot. @@ -1705,6 +1741,7 @@ Publishing Fusion configuration to stage 'dev' of API 'api-1' ├── Composing new configuration │ └── ✓ Composed new configuration. ├── Validating configuration against 'dev' + │ ├── Validating... │ └── ✕ Failed to validate the new configuration. │ ├── GraphQL schema changes │ │ ├── ✕ Directive foo was modified @@ -1768,8 +1805,9 @@ public async Task WithSourceSchemaFile_WaitForApproval_NoBreakingChanges_Returns // assert result.AssertSuccess( """ - Publishing Fusion configuration to stage 'dev' of API 'api-1' + Publishing new Fusion configuration version 'v1' of API 'api-1' to stage 'dev' ├── Requesting deployment slot + │ ├── Publication request created. (ID: request-id) │ └── ✓ Deployment slot ready. ├── Claiming deployment slot │ └── ✓ Claimed deployment slot. @@ -1817,8 +1855,9 @@ public async Task WithSourceSchemaFile_WaitForApproval_BreakingChanges_Approved_ // assert result.AssertSuccess( """ - Publishing Fusion configuration to stage 'dev' of API 'api-1' + Publishing new Fusion configuration version 'v1' of API 'api-1' to stage 'dev' ├── Requesting deployment slot + │ ├── Publication request created. (ID: request-id) │ └── ✓ Deployment slot ready. ├── Claiming deployment slot │ └── ✓ Claimed deployment slot. @@ -1884,8 +1923,9 @@ public async Task WithSourceSchemaFile_WaitForApproval_BreakingChanges_NotApprov """); result.StdOut.MatchInlineSnapshot( """ - Publishing Fusion configuration to stage 'dev' of API 'api-1' + Publishing new Fusion configuration version 'v1' of API 'api-1' to stage 'dev' ├── Requesting deployment slot + │ ├── Publication request created. (ID: request-id) │ └── ✓ Deployment slot ready. ├── Claiming deployment slot │ └── ✓ Claimed deployment slot. @@ -1908,7 +1948,7 @@ Publishing Fusion configuration to stage 'dev' of API 'api-1' │ │ └── The field `person` does not exist on the type `Query`. (1:14) │ ├── ⏳ Waiting for approval. Approve in Nitro to continue. │ └── ✕ Failed to upload the new configuration. - └── ✕ Failed to publish Fusion configuration. + └── ✕ Failed to publish a new Fusion configuration version. """); Assert.Equal(1, result.ExitCode); } @@ -1950,8 +1990,9 @@ public async Task WithSourceSchemaFile_UploadHasErrors_ReturnsError( """); result.StdOut.MatchInlineSnapshot( """ - Publishing Fusion configuration to stage 'dev' of API 'api-1' + Publishing new Fusion configuration version 'v1' of API 'api-1' to stage 'dev' ├── Requesting deployment slot + │ ├── Publication request created. (ID: request-id) │ └── ✓ Deployment slot ready. ├── Claiming deployment slot │ └── ✓ Claimed deployment slot. @@ -1960,10 +2001,11 @@ Publishing Fusion configuration to stage 'dev' of API 'api-1' ├── Composing new configuration │ └── ✓ Composed new configuration. ├── Validating configuration against 'dev' + │ ├── Validating... │ └── ✓ Validated configuration. ├── Uploading configuration to 'dev' │ └── ✕ Failed to upload the new configuration. - └── ✕ Failed to publish Fusion configuration. + └── ✕ Failed to publish a new Fusion configuration version. """); Assert.Equal(1, result.ExitCode); } @@ -2002,8 +2044,9 @@ public async Task WithSourceSchemaFile_UploadThrows_ReturnsError() """); result.StdOut.MatchInlineSnapshot( """ - Publishing Fusion configuration to stage 'dev' of API 'api-1' + Publishing new Fusion configuration version 'v1' of API 'api-1' to stage 'dev' ├── Requesting deployment slot + │ ├── Publication request created. (ID: request-id) │ └── ✓ Deployment slot ready. ├── Claiming deployment slot │ └── ✓ Claimed deployment slot. @@ -2012,10 +2055,11 @@ Publishing Fusion configuration to stage 'dev' of API 'api-1' ├── Composing new configuration │ └── ✓ Composed new configuration. ├── Validating configuration against 'dev' + │ ├── Validating... │ └── ✓ Validated configuration. ├── Uploading configuration to 'dev' │ └── ✕ Failed to upload the new configuration. - └── ✕ Failed to publish Fusion configuration. + └── ✕ Failed to publish a new Fusion configuration version. """); Assert.Equal(1, result.ExitCode); } @@ -2055,8 +2099,9 @@ public async Task WithSourceSchemaFile_PublishingFailedWithErrors_ReturnsError() """); result.StdOut.MatchInlineSnapshot( """ - Publishing Fusion configuration to stage 'dev' of API 'api-1' + Publishing new Fusion configuration version 'v1' of API 'api-1' to stage 'dev' ├── Requesting deployment slot + │ ├── Publication request created. (ID: request-id) │ └── ✓ Deployment slot ready. ├── Claiming deployment slot │ └── ✓ Claimed deployment slot. @@ -2065,13 +2110,14 @@ Publishing Fusion configuration to stage 'dev' of API 'api-1' ├── Composing new configuration │ └── ✓ Composed new configuration. ├── Validating configuration against 'dev' + │ ├── Validating... │ └── ✓ Validated configuration. ├── Uploading configuration to 'dev' │ └── ✕ Failed to upload the new configuration. │ ├── Invalid GraphQL schema │ │ └── Field 'Query.foo' has no type. (SCHEMA_ERROR) │ └── An error occurred. - └── ✕ Failed to publish Fusion configuration. + └── ✕ Failed to publish a new Fusion configuration version. """); Assert.Equal(1, result.ExitCode); } @@ -2110,12 +2156,13 @@ Something unexpected happened. """); result.StdOut.MatchInlineSnapshot( """ - Publishing Fusion configuration to stage 'dev' of API 'api-1' + Publishing new Fusion configuration version 'v1' of API 'api-1' to stage 'dev' ├── Requesting deployment slot + │ ├── Publication request created. (ID: request-id) │ └── ✓ Deployment slot ready. ├── Claiming deployment slot │ └── ✕ Failed to claim the deployment slot. - └── ✕ Failed to publish Fusion configuration. + └── ✕ Failed to publish a new Fusion configuration version. """); Assert.Equal(1, result.ExitCode); } @@ -2157,12 +2204,13 @@ public async Task WithSourceSchemaFile_ReleaseDeploymentSlotHasErrors_ReturnsErr """); result.StdOut.MatchInlineSnapshot( """ - Publishing Fusion configuration to stage 'dev' of API 'api-1' + Publishing new Fusion configuration version 'v1' of API 'api-1' to stage 'dev' ├── Requesting deployment slot + │ ├── Publication request created. (ID: request-id) │ └── ✓ Deployment slot ready. ├── Claiming deployment slot │ └── ✕ Failed to claim the deployment slot. - └── ✕ Failed to publish Fusion configuration. + └── ✕ Failed to publish a new Fusion configuration version. """); Assert.Equal(1, result.ExitCode); } @@ -2197,10 +2245,10 @@ Could not find source schema 'products' with version 'v1'. """); result.StdOut.MatchInlineSnapshot( """ - Publishing Fusion configuration to stage 'dev' of API 'api-1' + Publishing new Fusion configuration version 'v1' of API 'api-1' to stage 'dev' ├── Downloading 1 source schema(s) │ └── ✕ Could not find source schema 'products' with version 'v1'. - └── ✕ Failed to publish Fusion configuration. + └── ✕ Failed to publish a new Fusion configuration version. """); Assert.Equal(1, result.ExitCode); } @@ -2231,10 +2279,10 @@ public async Task WithSourceSchema_SourceSchemaDownloadThrows_ReturnsError() """); result.StdOut.MatchInlineSnapshot( """ - Publishing Fusion configuration to stage 'dev' of API 'api-1' + Publishing new Fusion configuration version 'v1' of API 'api-1' to stage 'dev' ├── Downloading 1 source schema(s) │ └── ✕ Failed to download source schemas. - └── ✕ Failed to publish Fusion configuration. + └── ✕ Failed to publish a new Fusion configuration version. """); Assert.Equal(1, result.ExitCode); } @@ -2269,10 +2317,11 @@ public async Task WithSourceSchema_ReturnsSuccess() // assert result.AssertSuccess( """ - Publishing Fusion configuration to stage 'dev' of API 'api-1' + Publishing new Fusion configuration version 'v1' of API 'api-1' to stage 'dev' ├── Downloading 1 source schema(s) │ └── ✓ Downloaded 1 source schema(s). ├── Requesting deployment slot + │ ├── Publication request created. (ID: request-id) │ └── ✓ Deployment slot ready. ├── Claiming deployment slot │ └── ✓ Claimed deployment slot. @@ -2281,6 +2330,7 @@ Publishing Fusion configuration to stage 'dev' of API 'api-1' ├── Composing new configuration │ └── ✓ Composed new configuration. ├── Validating configuration against 'dev' + │ ├── Validating... │ └── ✓ Validated configuration. ├── Uploading configuration to 'dev' │ └── ✓ Uploaded configuration. @@ -2318,10 +2368,11 @@ public async Task WithSourceSchema_WithEnvVars_ReturnsSuccess() // assert result.AssertSuccess( """ - Publishing Fusion configuration to stage 'dev' of API 'api-1' + Publishing new Fusion configuration version 'v1' of API 'api-1' to stage 'dev' ├── Downloading 1 source schema(s) │ └── ✓ Downloaded 1 source schema(s). ├── Requesting deployment slot + │ ├── Publication request created. (ID: request-id) │ └── ✓ Deployment slot ready. ├── Claiming deployment slot │ └── ✓ Claimed deployment slot. @@ -2330,6 +2381,7 @@ Publishing Fusion configuration to stage 'dev' of API 'api-1' ├── Composing new configuration │ └── ✓ Composed new configuration. ├── Validating configuration against 'dev' + │ ├── Validating... │ └── ✓ Validated configuration. ├── Uploading configuration to 'dev' │ └── ✓ Uploaded configuration. @@ -2371,10 +2423,11 @@ public async Task WithSourceSchema_WithExplicitSourceSchemaVersion_ReturnsSucces // assert result.AssertSuccess( """ - Publishing Fusion configuration to stage 'dev' of API 'api-1' + Publishing new Fusion configuration version 'v1' of API 'api-1' to stage 'dev' ├── Downloading 1 source schema(s) │ └── ✓ Downloaded 1 source schema(s). ├── Requesting deployment slot + │ ├── Publication request created. (ID: request-id) │ └── ✓ Deployment slot ready. ├── Claiming deployment slot │ └── ✓ Claimed deployment slot. @@ -2383,6 +2436,7 @@ Publishing Fusion configuration to stage 'dev' of API 'api-1' ├── Composing new configuration │ └── ✓ Composed new configuration. ├── Validating configuration against 'dev' + │ ├── Validating... │ └── ✓ Validated configuration. ├── Uploading configuration to 'dev' │ └── ✓ Uploaded configuration. @@ -2422,12 +2476,12 @@ public async Task WithSourceSchema_RequestDeploymentSlotHasErrors_ReturnsError( """); result.StdOut.MatchInlineSnapshot( """ - Publishing Fusion configuration to stage 'dev' of API 'api-1' + Publishing new Fusion configuration version 'v1' of API 'api-1' to stage 'dev' ├── Downloading 1 source schema(s) │ └── ✓ Downloaded 1 source schema(s). ├── Requesting deployment slot │ └── ✕ Failed to request a deployment slot. - └── ✕ Failed to publish Fusion configuration. + └── ✕ Failed to publish a new Fusion configuration version. """); Assert.Equal(1, result.ExitCode); } @@ -2459,12 +2513,12 @@ public async Task WithSourceSchema_RequestDeploymentSlotThrows_ReturnsError() """); result.StdOut.MatchInlineSnapshot( """ - Publishing Fusion configuration to stage 'dev' of API 'api-1' + Publishing new Fusion configuration version 'v1' of API 'api-1' to stage 'dev' ├── Downloading 1 source schema(s) │ └── ✓ Downloaded 1 source schema(s). ├── Requesting deployment slot │ └── ✕ Failed to request a deployment slot. - └── ✕ Failed to publish Fusion configuration. + └── ✕ Failed to publish a new Fusion configuration version. """); Assert.Equal(1, result.ExitCode); } @@ -2502,14 +2556,15 @@ public async Task WithSourceSchema_ClaimDeploymentSlotHasError_ReturnsError( """); result.StdOut.MatchInlineSnapshot( """ - Publishing Fusion configuration to stage 'dev' of API 'api-1' + Publishing new Fusion configuration version 'v1' of API 'api-1' to stage 'dev' ├── Downloading 1 source schema(s) │ └── ✓ Downloaded 1 source schema(s). ├── Requesting deployment slot + │ ├── Publication request created. (ID: request-id) │ └── ✓ Deployment slot ready. ├── Claiming deployment slot │ └── ✕ Failed to claim the deployment slot. - └── ✕ Failed to publish Fusion configuration. + └── ✕ Failed to publish a new Fusion configuration version. """); Assert.Equal(1, result.ExitCode); } @@ -2544,14 +2599,15 @@ public async Task WithSourceSchema_ClaimDeploymentSlotThrows_ReturnsError() """); result.StdOut.MatchInlineSnapshot( """ - Publishing Fusion configuration to stage 'dev' of API 'api-1' + Publishing new Fusion configuration version 'v1' of API 'api-1' to stage 'dev' ├── Downloading 1 source schema(s) │ └── ✓ Downloaded 1 source schema(s). ├── Requesting deployment slot + │ ├── Publication request created. (ID: request-id) │ └── ✓ Deployment slot ready. ├── Claiming deployment slot │ └── ✕ Failed to claim the deployment slot. - └── ✕ Failed to publish Fusion configuration. + └── ✕ Failed to publish a new Fusion configuration version. """); Assert.Equal(1, result.ExitCode); } @@ -2587,10 +2643,11 @@ Source schema validation failed. """); result.StdOut.MatchInlineSnapshot( """ - Publishing Fusion configuration to stage 'dev' of API 'api-1' + Publishing new Fusion configuration version 'v1' of API 'api-1' to stage 'dev' ├── Downloading 1 source schema(s) │ └── ✓ Downloaded 1 source schema(s). ├── Requesting deployment slot + │ ├── Publication request created. (ID: request-id) │ └── ✓ Deployment slot ready. ├── Claiming deployment slot │ └── ✓ Claimed deployment slot. @@ -2598,7 +2655,7 @@ Publishing Fusion configuration to stage 'dev' of API 'api-1' │ └── ✓ Downloaded existing configuration from 'dev'. ├── Composing new configuration │ └── ✕ Failed to compose new configuration. - └── ✕ Failed to publish Fusion configuration. + └── ✕ Failed to publish a new Fusion configuration version. ## Composition log @@ -2638,16 +2695,17 @@ public async Task WithSourceSchema_ConfigurationDownloadThrows_ReturnsError() """); result.StdOut.MatchInlineSnapshot( """ - Publishing Fusion configuration to stage 'dev' of API 'api-1' + Publishing new Fusion configuration version 'v1' of API 'api-1' to stage 'dev' ├── Downloading 1 source schema(s) │ └── ✓ Downloaded 1 source schema(s). ├── Requesting deployment slot + │ ├── Publication request created. (ID: request-id) │ └── ✓ Deployment slot ready. ├── Claiming deployment slot │ └── ✓ Claimed deployment slot. ├── Downloading existing configuration from 'dev' │ └── ✕ Failed to download the existing Fusion configuration. - └── ✕ Failed to publish Fusion configuration. + └── ✕ Failed to publish a new Fusion configuration version. """); Assert.Equal(1, result.ExitCode); } @@ -2687,10 +2745,11 @@ public async Task WithSourceSchema_ValidationHasErrors_ReturnsError( """); result.StdOut.MatchInlineSnapshot( """ - Publishing Fusion configuration to stage 'dev' of API 'api-1' + Publishing new Fusion configuration version 'v1' of API 'api-1' to stage 'dev' ├── Downloading 1 source schema(s) │ └── ✓ Downloaded 1 source schema(s). ├── Requesting deployment slot + │ ├── Publication request created. (ID: request-id) │ └── ✓ Deployment slot ready. ├── Claiming deployment slot │ └── ✓ Claimed deployment slot. @@ -2700,7 +2759,7 @@ Publishing Fusion configuration to stage 'dev' of API 'api-1' │ └── ✓ Composed new configuration. ├── Validating configuration against 'dev' │ └── ✕ Failed to validate the new configuration. - └── ✕ Failed to publish Fusion configuration. + └── ✕ Failed to publish a new Fusion configuration version. """); Assert.Equal(1, result.ExitCode); } @@ -2738,10 +2797,11 @@ public async Task WithSourceSchema_ValidationThrows_ReturnsError() """); result.StdOut.MatchInlineSnapshot( """ - Publishing Fusion configuration to stage 'dev' of API 'api-1' + Publishing new Fusion configuration version 'v1' of API 'api-1' to stage 'dev' ├── Downloading 1 source schema(s) │ └── ✓ Downloaded 1 source schema(s). ├── Requesting deployment slot + │ ├── Publication request created. (ID: request-id) │ └── ✓ Deployment slot ready. ├── Claiming deployment slot │ └── ✓ Claimed deployment slot. @@ -2751,7 +2811,7 @@ Publishing Fusion configuration to stage 'dev' of API 'api-1' │ └── ✓ Composed new configuration. ├── Validating configuration against 'dev' │ └── ✕ Failed to validate the new configuration. - └── ✕ Failed to publish Fusion configuration. + └── ✕ Failed to publish a new Fusion configuration version. """); Assert.Equal(1, result.ExitCode); } @@ -2787,14 +2847,15 @@ public async Task WithSourceSchema_BreakingChanges_ReturnsError() // assert result.StdErr.MatchInlineSnapshot( """ - Failed to validate configuration. + Fusion configuration validation failed. """); result.StdOut.MatchInlineSnapshot( """ - Publishing Fusion configuration to stage 'dev' of API 'api-1' + Publishing new Fusion configuration version 'v1' of API 'api-1' to stage 'dev' ├── Downloading 1 source schema(s) │ └── ✓ Downloaded 1 source schema(s). ├── Requesting deployment slot + │ ├── Publication request created. (ID: request-id) │ └── ✓ Deployment slot ready. ├── Claiming deployment slot │ └── ✓ Claimed deployment slot. @@ -2803,6 +2864,7 @@ Publishing Fusion configuration to stage 'dev' of API 'api-1' ├── Composing new configuration │ └── ✓ Composed new configuration. ├── Validating configuration against 'dev' + │ ├── Validating... │ └── ✕ Failed to validate the new configuration. │ ├── GraphQL schema changes │ │ ├── ✕ Directive foo was modified @@ -2828,7 +2890,7 @@ Publishing Fusion configuration to stage 'dev' of API 'api-1' │ │ └── Tool 'Fail' │ │ └── The field `person` does not exist on the type `Query`. (1:14) │ └── An unexpected error occurred. - └── ✕ Failed to publish Fusion configuration. + └── ✕ Failed to publish a new Fusion configuration version. """); Assert.Equal(1, result.ExitCode); } @@ -2866,11 +2928,12 @@ public async Task WithSourceSchema_BreakingChanges_Force_ReturnsSuccess() // assert result.AssertSuccess( """ - Publishing Fusion configuration to stage 'dev' of API 'api-1' + Publishing new Fusion configuration version 'v1' of API 'api-1' to stage 'dev' ├── ! Force push is enabled. ├── Downloading 1 source schema(s) │ └── ✓ Downloaded 1 source schema(s). ├── Requesting deployment slot + │ ├── Publication request created. (ID: request-id) │ └── ✓ Deployment slot ready. ├── Claiming deployment slot │ └── ✓ Claimed deployment slot. @@ -2879,6 +2942,7 @@ Publishing Fusion configuration to stage 'dev' of API 'api-1' ├── Composing new configuration │ └── ✓ Composed new configuration. ├── Validating configuration against 'dev' + │ ├── Validating... │ └── ✕ Failed to validate the new configuration. │ ├── GraphQL schema changes │ │ ├── ✕ Directive foo was modified @@ -2942,10 +3006,11 @@ public async Task WithSourceSchema_WaitForApproval_NoBreakingChanges_ReturnsSucc // assert result.AssertSuccess( """ - Publishing Fusion configuration to stage 'dev' of API 'api-1' + Publishing new Fusion configuration version 'v1' of API 'api-1' to stage 'dev' ├── Downloading 1 source schema(s) │ └── ✓ Downloaded 1 source schema(s). ├── Requesting deployment slot + │ ├── Publication request created. (ID: request-id) │ └── ✓ Deployment slot ready. ├── Claiming deployment slot │ └── ✓ Claimed deployment slot. @@ -2993,10 +3058,11 @@ public async Task WithSourceSchema_WaitForApproval_BreakingChanges_Approved_Retu // assert result.AssertSuccess( """ - Publishing Fusion configuration to stage 'dev' of API 'api-1' + Publishing new Fusion configuration version 'v1' of API 'api-1' to stage 'dev' ├── Downloading 1 source schema(s) │ └── ✓ Downloaded 1 source schema(s). ├── Requesting deployment slot + │ ├── Publication request created. (ID: request-id) │ └── ✓ Deployment slot ready. ├── Claiming deployment slot │ └── ✓ Claimed deployment slot. @@ -3062,10 +3128,11 @@ public async Task WithSourceSchema_WaitForApproval_BreakingChanges_NotApproved_R """); result.StdOut.MatchInlineSnapshot( """ - Publishing Fusion configuration to stage 'dev' of API 'api-1' + Publishing new Fusion configuration version 'v1' of API 'api-1' to stage 'dev' ├── Downloading 1 source schema(s) │ └── ✓ Downloaded 1 source schema(s). ├── Requesting deployment slot + │ ├── Publication request created. (ID: request-id) │ └── ✓ Deployment slot ready. ├── Claiming deployment slot │ └── ✓ Claimed deployment slot. @@ -3088,7 +3155,7 @@ Publishing Fusion configuration to stage 'dev' of API 'api-1' │ │ └── The field `person` does not exist on the type `Query`. (1:14) │ ├── ⏳ Waiting for approval. Approve in Nitro to continue. │ └── ✕ Failed to upload the new configuration. - └── ✕ Failed to publish Fusion configuration. + └── ✕ Failed to publish a new Fusion configuration version. """); Assert.Equal(1, result.ExitCode); } @@ -3130,10 +3197,11 @@ public async Task WithSourceSchema_UploadHasErrors_ReturnsError( """); result.StdOut.MatchInlineSnapshot( """ - Publishing Fusion configuration to stage 'dev' of API 'api-1' + Publishing new Fusion configuration version 'v1' of API 'api-1' to stage 'dev' ├── Downloading 1 source schema(s) │ └── ✓ Downloaded 1 source schema(s). ├── Requesting deployment slot + │ ├── Publication request created. (ID: request-id) │ └── ✓ Deployment slot ready. ├── Claiming deployment slot │ └── ✓ Claimed deployment slot. @@ -3142,10 +3210,11 @@ Publishing Fusion configuration to stage 'dev' of API 'api-1' ├── Composing new configuration │ └── ✓ Composed new configuration. ├── Validating configuration against 'dev' + │ ├── Validating... │ └── ✓ Validated configuration. ├── Uploading configuration to 'dev' │ └── ✕ Failed to upload the new configuration. - └── ✕ Failed to publish Fusion configuration. + └── ✕ Failed to publish a new Fusion configuration version. """); Assert.Equal(1, result.ExitCode); } @@ -3184,10 +3253,11 @@ public async Task WithSourceSchema_UploadThrows_ReturnsError() """); result.StdOut.MatchInlineSnapshot( """ - Publishing Fusion configuration to stage 'dev' of API 'api-1' + Publishing new Fusion configuration version 'v1' of API 'api-1' to stage 'dev' ├── Downloading 1 source schema(s) │ └── ✓ Downloaded 1 source schema(s). ├── Requesting deployment slot + │ ├── Publication request created. (ID: request-id) │ └── ✓ Deployment slot ready. ├── Claiming deployment slot │ └── ✓ Claimed deployment slot. @@ -3196,10 +3266,11 @@ Publishing Fusion configuration to stage 'dev' of API 'api-1' ├── Composing new configuration │ └── ✓ Composed new configuration. ├── Validating configuration against 'dev' + │ ├── Validating... │ └── ✓ Validated configuration. ├── Uploading configuration to 'dev' │ └── ✕ Failed to upload the new configuration. - └── ✕ Failed to publish Fusion configuration. + └── ✕ Failed to publish a new Fusion configuration version. """); Assert.Equal(1, result.ExitCode); } @@ -3239,10 +3310,11 @@ public async Task WithSourceSchema_PublishingFailedWithErrors_ReturnsError() """); result.StdOut.MatchInlineSnapshot( """ - Publishing Fusion configuration to stage 'dev' of API 'api-1' + Publishing new Fusion configuration version 'v1' of API 'api-1' to stage 'dev' ├── Downloading 1 source schema(s) │ └── ✓ Downloaded 1 source schema(s). ├── Requesting deployment slot + │ ├── Publication request created. (ID: request-id) │ └── ✓ Deployment slot ready. ├── Claiming deployment slot │ └── ✓ Claimed deployment slot. @@ -3251,13 +3323,14 @@ Publishing Fusion configuration to stage 'dev' of API 'api-1' ├── Composing new configuration │ └── ✓ Composed new configuration. ├── Validating configuration against 'dev' + │ ├── Validating... │ └── ✓ Validated configuration. ├── Uploading configuration to 'dev' │ └── ✕ Failed to upload the new configuration. │ ├── Invalid GraphQL schema │ │ └── Field 'Query.foo' has no type. (SCHEMA_ERROR) │ └── An error occurred. - └── ✕ Failed to publish Fusion configuration. + └── ✕ Failed to publish a new Fusion configuration version. """); Assert.Equal(1, result.ExitCode); } @@ -3296,14 +3369,15 @@ Something unexpected happened. """); result.StdOut.MatchInlineSnapshot( """ - Publishing Fusion configuration to stage 'dev' of API 'api-1' + Publishing new Fusion configuration version 'v1' of API 'api-1' to stage 'dev' ├── Downloading 1 source schema(s) │ └── ✓ Downloaded 1 source schema(s). ├── Requesting deployment slot + │ ├── Publication request created. (ID: request-id) │ └── ✓ Deployment slot ready. ├── Claiming deployment slot │ └── ✕ Failed to claim the deployment slot. - └── ✕ Failed to publish Fusion configuration. + └── ✕ Failed to publish a new Fusion configuration version. """); Assert.Equal(1, result.ExitCode); } @@ -3345,14 +3419,15 @@ public async Task WithSourceSchema_ReleaseDeploymentSlotHasErrors_ReturnsError( """); result.StdOut.MatchInlineSnapshot( """ - Publishing Fusion configuration to stage 'dev' of API 'api-1' + Publishing new Fusion configuration version 'v1' of API 'api-1' to stage 'dev' ├── Downloading 1 source schema(s) │ └── ✓ Downloaded 1 source schema(s). ├── Requesting deployment slot + │ ├── Publication request created. (ID: request-id) │ └── ✓ Deployment slot ready. ├── Claiming deployment slot │ └── ✕ Failed to claim the deployment slot. - └── ✕ Failed to publish Fusion configuration. + └── ✕ Failed to publish a new Fusion configuration version. """); Assert.Equal(1, result.ExitCode); } diff --git a/src/Nitro/CommandLine/test/CommandLine.Tests/Commands/Fusion/FusionValidateCommandTests.cs b/src/Nitro/CommandLine/test/CommandLine.Tests/Commands/Fusion/FusionValidateCommandTests.cs index 7a00f635286..3e18b0d5487 100644 --- a/src/Nitro/CommandLine/test/CommandLine.Tests/Commands/Fusion/FusionValidateCommandTests.cs +++ b/src/Nitro/CommandLine/test/CommandLine.Tests/Commands/Fusion/FusionValidateCommandTests.cs @@ -204,7 +204,7 @@ public async Task WithArchive_ReturnsSuccess() // assert result.AssertSuccess( """ - Validating Fusion configuration against stage 'dev' of API 'api-1' + Validating Fusion configuration of API 'api-1' against stage 'dev' ├── Validation request created. (ID: request-id) └── ✓ Schema validation successful. """); @@ -231,7 +231,7 @@ public async Task WithArchive_WithEnvVars_ReturnsSuccess() // assert result.AssertSuccess( """ - Validating Fusion configuration against stage 'dev' of API 'api-1' + Validating Fusion configuration of API 'api-1' against stage 'dev' ├── Validation request created. (ID: request-id) └── ✓ Schema validation successful. """); @@ -263,7 +263,7 @@ public async Task WithArchive_ValidateSchemaVersionHasErrors_ReturnsError( result.StdErr.MatchInlineSnapshot(expectedErrorMessage); result.StdOut.MatchInlineSnapshot( """ - Validating Fusion configuration against stage 'dev' of API 'api-1' + Validating Fusion configuration of API 'api-1' against stage 'dev' └── ✕ Failed to validate the Fusion configuration. """); Assert.Equal(1, result.ExitCode); @@ -294,7 +294,7 @@ public async Task WithArchive_ValidateSchemaVersionThrows_ReturnsError() """); result.StdOut.MatchInlineSnapshot( """ - Validating Fusion configuration against stage 'dev' of API 'api-1' + Validating Fusion configuration of API 'api-1' against stage 'dev' └── ✕ Failed to validate the Fusion configuration. """); Assert.Equal(1, result.ExitCode); @@ -330,7 +330,7 @@ Schema validation failed. """); result.StdOut.MatchInlineSnapshot( """ - Validating Fusion configuration against stage 'dev' of API 'api-1' + Validating Fusion configuration of API 'api-1' against stage 'dev' ├── Validation request created. (ID: request-id) ├── Validating... ├── Validating... @@ -418,7 +418,7 @@ public async Task WithSourceSchemaFile_ReturnsSuccess() // assert result.AssertSuccess( """ - Validating Fusion configuration against stage 'dev' of API 'api-1' + Validating Fusion configuration of API 'api-1' against stage 'dev' ├── Downloading existing Fusion configuration │ └── ✓ Downloaded existing configuration from 'dev'. ├── Composing new Fusion configuration @@ -451,7 +451,7 @@ public async Task WithSourceSchemaFile_WithEnvVars_ReturnsSuccess() // assert result.AssertSuccess( """ - Validating Fusion configuration against stage 'dev' of API 'api-1' + Validating Fusion configuration of API 'api-1' against stage 'dev' ├── Downloading existing Fusion configuration │ └── ✓ Downloaded existing configuration from 'dev'. ├── Composing new Fusion configuration @@ -488,7 +488,7 @@ public async Task WithSourceSchemaFile_ValidateSchemaVersionHasErrors_ReturnsErr result.StdErr.MatchInlineSnapshot(expectedErrorMessage); result.StdOut.MatchInlineSnapshot( """ - Validating Fusion configuration against stage 'dev' of API 'api-1' + Validating Fusion configuration of API 'api-1' against stage 'dev' ├── Downloading existing Fusion configuration │ └── ✓ Downloaded existing configuration from 'dev'. ├── Composing new Fusion configuration @@ -524,7 +524,7 @@ public async Task WithSourceSchemaFile_ValidateSchemaVersionThrows_ReturnsError( """); result.StdOut.MatchInlineSnapshot( """ - Validating Fusion configuration against stage 'dev' of API 'api-1' + Validating Fusion configuration of API 'api-1' against stage 'dev' ├── Downloading existing Fusion configuration │ └── ✓ Downloaded existing configuration from 'dev'. ├── Composing new Fusion configuration @@ -564,7 +564,7 @@ Schema validation failed. """); result.StdOut.MatchInlineSnapshot( """ - Validating Fusion configuration against stage 'dev' of API 'api-1' + Validating Fusion configuration of API 'api-1' against stage 'dev' ├── Downloading existing Fusion configuration │ └── ✓ Downloaded existing configuration from 'dev'. ├── Composing new Fusion configuration @@ -626,7 +626,7 @@ public async Task WithSourceSchemaFile_ConfigurationDownloadThrows_ReturnsError( """); result.StdOut.MatchInlineSnapshot( """ - Validating Fusion configuration against stage 'dev' of API 'api-1' + Validating Fusion configuration of API 'api-1' against stage 'dev' ├── Downloading existing Fusion configuration │ └── ✕ Failed to download existing Fusion configuration. └── ✕ Failed to validate the Fusion configuration. @@ -659,7 +659,7 @@ Source schema validation failed. """); result.StdOut.MatchInlineSnapshot( """ - Validating Fusion configuration against stage 'dev' of API 'api-1' + Validating Fusion configuration of API 'api-1' against stage 'dev' ├── Downloading existing Fusion configuration │ └── ✓ Downloaded existing configuration from 'dev'. ├── Composing new Fusion configuration diff --git a/src/Nitro/CommandLine/test/CommandLine.Tests/Commands/Session/LogoutCommandTests.cs b/src/Nitro/CommandLine/test/CommandLine.Tests/Commands/Session/LogoutCommandTests.cs index 01f3766af9c..728192a54d9 100644 --- a/src/Nitro/CommandLine/test/CommandLine.Tests/Commands/Session/LogoutCommandTests.cs +++ b/src/Nitro/CommandLine/test/CommandLine.Tests/Commands/Session/LogoutCommandTests.cs @@ -45,10 +45,10 @@ Logging out } [Fact] - public async Task LogoutThrowsExitException_ReturnsError() + public async Task LogoutThrows_ReturnsError() { // arrange - SetupLogoutThrowsExitException(); + SetupLogoutThrows(); // act var result = await ExecuteCommandAsync("logout"); @@ -59,23 +59,6 @@ public async Task LogoutThrowsExitException_ReturnsError() Logging out └── ✕ Failed to log out. """); - result.StdErr.MatchInlineSnapshot( - """ - Session deletion failed. - """); - Assert.Equal(1, result.ExitCode); - } - - [Fact] - public async Task LogoutThrows_ReturnsError() - { - // arrange - SetupLogoutThrows(); - - // act - var result = await ExecuteCommandAsync("logout"); - - // assert result.StdErr.MatchInlineSnapshot( """ There was an unexpected error: Something unexpected happened. diff --git a/src/Nitro/CommandLine/test/CommandLine.Tests/Commands/Session/SessionCommandTestBase.cs b/src/Nitro/CommandLine/test/CommandLine.Tests/Commands/Session/SessionCommandTestBase.cs index b74dc4feeb3..d83d0dcc932 100644 --- a/src/Nitro/CommandLine/test/CommandLine.Tests/Commands/Session/SessionCommandTestBase.cs +++ b/src/Nitro/CommandLine/test/CommandLine.Tests/Commands/Session/SessionCommandTestBase.cs @@ -68,13 +68,6 @@ protected void SetupLogoutThrows() .ThrowsAsync(new InvalidOperationException("Something unexpected happened.")); } - protected void SetupLogoutThrowsExitException(string message = "Session deletion failed.") - { - _sessionServiceMock - .Setup(x => x.LogoutAsync(It.IsAny())) - .ThrowsAsync(new ExitException(message)); - } - #endregion #region Workspace Selection diff --git a/src/Nitro/CommandLine/test/CommandLine.Tests/Console/InteractiveNitroConsoleActivityTests.cs b/src/Nitro/CommandLine/test/CommandLine.Tests/Console/InteractiveNitroConsoleActivityTests.cs index 21bbab07f7b..98dd3d60661 100644 --- a/src/Nitro/CommandLine/test/CommandLine.Tests/Console/InteractiveNitroConsoleActivityTests.cs +++ b/src/Nitro/CommandLine/test/CommandLine.Tests/Console/InteractiveNitroConsoleActivityTests.cs @@ -79,7 +79,7 @@ public async Task Fail_Should_RenderCrossGlyph_When_CalledWithMessage() } [Fact] - public async Task Fail_Should_RenderCrossGlyphWithDetails_When_CalledWithRenderable() + public async Task FailAllAsync_Should_RenderCrossGlyphWithDetails_When_CalledWithRenderable() { // arrange var (console, writer) = CreateConsole(); @@ -87,7 +87,7 @@ public async Task Fail_Should_RenderCrossGlyphWithDetails_When_CalledWithRendera // act await using (var activity = console.StartActivity("Doing work", "Work failed")) { - activity.Fail(new Text("Error detail line 1\nError detail line 2")); + await activity.FailAllAsync(new Text("Error detail line 1\nError detail line 2")); } // assert @@ -127,7 +127,62 @@ public async Task StartChildActivity_Should_RenderTreeStructure_When_ChildSuccee } [Fact] - public async Task StartChildActivity_Should_RenderTreeStructure_When_ChildFails() + public async Task ChildSuccess_Should_CollapseToSingleLine_When_ChildHasNoUpdates() + { + // arrange + var (console, writer) = CreateConsole(); + + // act + await using (var activity = console.StartActivity("Root", "Root failed")) + { + await using (var child = activity.StartChildActivity("Child step", "Child failed")) + { + child.Success("Child done"); + } + + activity.Success("Root done"); + } + + // assert — child collapses: entry text replaced with success message, no nested terminator + GetOutput(writer).MatchInlineSnapshot( + """ + ✓ Root + ├── ✓ Child done + └── ✓ Root done + """); + } + + [Fact] + public async Task ChildSuccess_Should_NotCollapse_When_ChildHasUpdates() + { + // arrange + var (console, writer) = CreateConsole(); + + // act + await using (var activity = console.StartActivity("Root", "Root failed")) + { + await using (var child = activity.StartChildActivity("Child step", "Child failed")) + { + child.Update("working..."); + child.Success("Child done"); + } + + activity.Success("Root done"); + } + + // assert — child entry stays, success appended as a nested terminator + GetOutput(writer).MatchInlineSnapshot( + """ + ✓ Root + ├── ✓ Child step + │ ├── working... + │ └── ✓ Child done + └── ✓ Root done + """); + } + + [Fact] + public async Task ChildFail_Should_FoldIntoTitle_When_ChildHasNoUpdates() { // arrange var (console, writer) = CreateConsole(); @@ -143,12 +198,41 @@ public async Task StartChildActivity_Should_RenderTreeStructure_When_ChildFails( activity.Fail("Root error"); } - // assert + // assert — child title collapses; root's own terminator is suppressed + // because the last child already conveys the failure GetOutput(writer).MatchInlineSnapshot( """ ✕ Root - ├── ✕ Child error - └── ✕ Root error + └── ✕ Child error + """); + } + + [Fact] + public async Task ChildFail_Should_NotFold_When_ChildHasUpdates() + { + // arrange + var (console, writer) = CreateConsole(); + + // act + await using (var activity = console.StartActivity("Root", "Root failed")) + { + await using (var child = activity.StartChildActivity("Child step", "Child failed")) + { + child.Update("working..."); + child.Fail("Child error"); + } + + activity.Fail("Root error"); + } + + // assert — child retains title; root's own terminator is suppressed because + // the last child already conveys the failure + GetOutput(writer).MatchInlineSnapshot( + """ + ✕ Root + └── ✕ Child step + ├── working... + └── ✕ Child error """); } @@ -185,6 +269,72 @@ public async Task StartChildActivity_Should_RenderNestedTreeStructure_When_Grand """); } + [Fact] + public async Task GrandchildFail_Should_CollapseTerminators_AtEveryLevel() + { + // arrange + var (console, writer) = CreateConsole(); + + // act + await using (var activity = console.StartActivity("Root", "Root failed")) + { + await using (var child = activity.StartChildActivity("Child", "Child failed")) + { + await using (var grandchild = child.StartChildActivity("Grandchild", "Grandchild failed")) + { + grandchild.Fail("Grandchild error"); + } + + child.Fail("Child aborted"); + } + + activity.Fail("Root error"); + } + + // assert — explicit terminators at root and child are suppressed; the grandchild + // failure stays as the final error line + GetOutput(writer).MatchInlineSnapshot( + """ + ✕ Root + └── ✕ Child + └── ✕ Grandchild error + """); + } + + [Fact] + public async Task GrandchildFail_Should_PreserveUpdatesAtEachLevel() + { + // arrange + var (console, writer) = CreateConsole(); + + // act + await using (var activity = console.StartActivity("Root", "Root failed")) + { + await using (var child = activity.StartChildActivity("Child", "Child failed")) + { + child.Update("Child step 1"); + + await using (var grandchild = child.StartChildActivity("Grandchild", "Grandchild failed")) + { + grandchild.Update("Grandchild step 1"); + grandchild.Fail("Grandchild error"); + } + } + } + + // assert — updates at child and grandchild stay visible; dispose-driven failure + // cascade doesn't add extra terminators at child or root + GetOutput(writer).MatchInlineSnapshot( + """ + ✕ Root + └── ✕ Child + ├── Child step 1 + └── ✕ Grandchild + ├── Grandchild step 1 + └── ✕ Grandchild error + """); + } + [Fact] public async Task FailAllAsync_Should_PropagateFailure_When_ChildDisposesWithoutCompletion() { @@ -204,7 +354,7 @@ public async Task FailAllAsync_Should_PropagateFailure_When_ChildDisposesWithout GetOutput(writer).MatchInlineSnapshot( """ ✕ Root - └── ✕ Child + └── ✕ Child failed """); } @@ -479,9 +629,8 @@ public async Task ChildFail_Should_WrapMessage_When_MessageExceedsWidth() GetOutput(writer).MatchInlineSnapshot( """ ✕ Root - ├── ✕ This child failure - │ message wraps - └── ✕ Root error + └── ✕ This child failure + message wraps """); } @@ -541,7 +690,7 @@ public async Task ChildUpdate_Should_RenderDetails_When_CalledWithRenderable() } [Fact] - public async Task ChildFail_Should_RenderCrossGlyphWithDetails_When_CalledWithRenderable() + public async Task ChildFailAllAsync_Should_RenderCrossGlyphWithDetails_When_CalledWithRenderable() { // arrange var (console, writer) = CreateConsole(); @@ -551,20 +700,17 @@ public async Task ChildFail_Should_RenderCrossGlyphWithDetails_When_CalledWithRe { await using (var child = activity.StartChildActivity("Child", "Child failed")) { - child.Fail(new Text("Error detail line 1\nError detail line 2")); + await child.FailAllAsync(new Text("Error detail line 1\nError detail line 2")); } - - activity.Fail("Root error"); } // assert GetOutput(writer).MatchInlineSnapshot( """ ✕ Root - ├── ✕ Child failed - │ Error detail line 1 - │ Error detail line 2 - └── ✕ Root error + └── ✕ Child failed + Error detail line 1 + Error detail line 2 """); } @@ -649,7 +795,7 @@ public async Task FailAllAsync_Should_PropagateFailure_When_GrandchildDisposesWi """ ✕ Root └── ✕ Child - └── ✕ Grandchild + └── ✕ Grandchild failed """); } @@ -695,6 +841,39 @@ public async Task DisposeAsync_Should_TriggerFailure_When_NoCompletionCalled() """); } + [Fact] + public async Task FailAllAsync_Should_KeepAllSiblings_When_MultipleSiblingsFailed() + { + // arrange + var (console, writer) = CreateConsole(); + + // act — one sibling explicitly fails, the next propagates failure via + // FailAllAsync (no details). Parent ends up with two failed children and + // no terminator of its own. Guards against the earlier heuristic that + // hid the last sibling whenever two consecutive children were failed. + await using (var activity = console.StartActivity("Root", "Root failed")) + { + await using (var first = activity.StartChildActivity("First", "First failed")) + { + first.Fail("First error"); + } + + await using (var second = activity.StartChildActivity("Second", "Second failed")) + { + await second.FailAllAsync(); + } + } + + // assert — both siblings stay visible; suppression only fires for explicit + // terminator children added via CompleteChild + GetOutput(writer).MatchInlineSnapshot( + """ + ✕ Root + ├── ✕ First error + └── ✕ Second failed + """); + } + [Fact] public async Task ChildSuccess_Should_FailActiveDescendants_When_LeakedGrandchild() { diff --git a/src/Nitro/CommandLine/test/CommandLine.Tests/Console/NitroConsoleActivityTests.cs b/src/Nitro/CommandLine/test/CommandLine.Tests/Console/NitroConsoleActivityTests.cs index d86240190a4..0d3b5dfe453 100644 --- a/src/Nitro/CommandLine/test/CommandLine.Tests/Console/NitroConsoleActivityTests.cs +++ b/src/Nitro/CommandLine/test/CommandLine.Tests/Console/NitroConsoleActivityTests.cs @@ -79,7 +79,7 @@ Doing work } [Fact] - public async Task Fail_Should_WriteCrossGlyphWithDetails_When_CalledWithRenderable() + public async Task FailAllAsync_Should_WriteCrossGlyphWithDetails_When_CalledWithRenderable() { // arrange var (console, writer) = CreateConsole(); @@ -87,7 +87,7 @@ public async Task Fail_Should_WriteCrossGlyphWithDetails_When_CalledWithRenderab // act await using (var activity = console.StartActivity( "Doing work", "Work failed")) { - activity.Fail(new Text("Error detail line 1\nError detail line 2")); + await activity.FailAllAsync(new Text("Error detail line 1\nError detail line 2")); } // assert @@ -547,7 +547,7 @@ public async Task ChildUpdate_Should_WriteDetails_When_CalledWithRenderable() } [Fact] - public async Task ChildFail_Should_WriteCrossGlyphWithDetails_When_CalledWithRenderable() + public async Task ChildFailAllAsync_Should_WriteCrossGlyphWithDetails_When_CalledWithRenderable() { // arrange var (console, writer) = CreateConsole(); @@ -557,10 +557,8 @@ public async Task ChildFail_Should_WriteCrossGlyphWithDetails_When_CalledWithRen { await using (var child = activity.StartChildActivity("Child", "Child failed")) { - child.Fail(new Text("Error detail line 1\nError detail line 2")); + await child.FailAllAsync(new Text("Error detail line 1\nError detail line 2")); } - - activity.Fail("Root error"); } // assert @@ -571,7 +569,7 @@ public async Task ChildFail_Should_WriteCrossGlyphWithDetails_When_CalledWithRen │ └── ✕ Child failed │ Error detail line 1 │ Error detail line 2 - └── ✕ Root error + └── ✕ Root failed """); } @@ -692,6 +690,40 @@ Doing work """); } + [Fact] + public async Task FailAllAsync_Should_KeepAllSiblings_When_MultipleSiblingsFailed() + { + // arrange + var (console, writer) = CreateConsole(); + + // act — one sibling explicitly fails, the next propagates failure via + // FailAllAsync (no details). Parent ends up with two failed children and + // no terminator of its own. + await using (var activity = console.StartActivity("Root", "Root failed")) + { + await using (var first = activity.StartChildActivity("First", "First failed")) + { + first.Fail("First error"); + } + + await using (var second = activity.StartChildActivity("Second", "Second failed")) + { + await second.FailAllAsync(); + } + } + + // assert — every sibling stays visible; none is dropped by the renderer + GetOutput(writer).MatchInlineSnapshot( + """ + Root + ├── First + │ └── ✕ First error + ├── Second + │ └── ✕ Second failed + └── ✕ Root failed + """); + } + [Fact] public async Task DisposeAsync_Should_TriggerFailure_When_NoCompletionCalled() { diff --git a/src/Nitro/CommandLine/test/CommandLine.Tests/Console/SnapshotActivitySink.cs b/src/Nitro/CommandLine/test/CommandLine.Tests/Console/SnapshotActivitySink.cs index 3f04ce4bf17..18a643c9b77 100644 --- a/src/Nitro/CommandLine/test/CommandLine.Tests/Console/SnapshotActivitySink.cs +++ b/src/Nitro/CommandLine/test/CommandLine.Tests/Console/SnapshotActivitySink.cs @@ -26,7 +26,7 @@ public ActivityEntry AddChild(ActivityEntry parent, string text, ActivityState s public ActivityEntry CompleteChild(ActivityEntry parent, string text, ActivityState state) { - return _tree.AddChild(parent, text, state); + return _tree.AddChild(parent, text, state, isTerminator: true); } public void SetState(ActivityEntry entry, ActivityState state) @@ -49,12 +49,6 @@ public void FailActiveDescendants(ActivityEntry entry) _tree.FailActiveDescendants(entry); } - public void FailSilent(ActivityEntry entry, string failureMessage) - { - _tree.SetEntryState(entry, ActivityState.Failed); - _tree.FailActiveDescendants(entry); - } - public void Stop() { _console.Write(_tree);