-
-
Notifications
You must be signed in to change notification settings - Fork 150
Fix: Validate subscription before execution to prevent crash on missing credentials #275
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
a70503d
891e25c
4989ecf
323fae9
a7e9870
d06cec9
2947988
3a72041
895e941
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -27,6 +27,22 @@ public AccumulatedCostCommand(ICostRetriever costRetriever) | |||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public override ValidationResult Validate(CommandContext context, AccumulatedCostSettings settings) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Check if we have any scope parameters when the scope requires subscription | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (settings.GetScope.IsSubscriptionBased && !settings.Subscription.HasValue) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Try to get subscription from Azure CLI | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| try | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| var subscriptionId = Guid.Parse(AzCommand.GetDefaultAzureSubscriptionId()); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| settings.Subscription = subscriptionId; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| catch (Exception) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+36
to
+40
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| var subscriptionId = Guid.Parse(AzCommand.GetDefaultAzureSubscriptionId()); | |
| settings.Subscription = subscriptionId; | |
| } | |
| catch (Exception) | |
| { | |
| if (settings.Debug) | |
| { | |
| AnsiConsole.MarkupLine("[grey]Attempting to retrieve default Azure subscription ID from Azure CLI...[/]"); | |
| } | |
| var subscriptionId = Guid.Parse(AzCommand.GetDefaultAzureSubscriptionId()); | |
| if (settings.Debug) | |
| { | |
| AnsiConsole.MarkupLine($"[grey]Retrieved subscription ID '{subscriptionId}' from Azure CLI.[/]"); | |
| } | |
| settings.Subscription = subscriptionId; | |
| } | |
| catch (Exception ex) | |
| { | |
| if (settings.Debug) | |
| { | |
| AnsiConsole.MarkupLine("[grey]Failed to retrieve subscription ID from Azure CLI.[/]"); | |
| AnsiConsole.WriteException(ex); | |
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -26,6 +26,7 @@ public void Validate_WithCustomTimeframeAndValidDates_ReturnsSuccess() | |
| // Arrange | ||
| var settings = new AccumulatedCostSettings | ||
| { | ||
| Subscription = Guid.NewGuid(), | ||
| Timeframe = TimeframeType.Custom, | ||
| From = new DateOnly(2023, 1, 1), | ||
| To = new DateOnly(2023, 1, 31) | ||
|
|
@@ -45,6 +46,7 @@ public void Validate_WithCustomTimeframeAndFromDateAfterToDate_ReturnsError() | |
| // Arrange | ||
| var settings = new AccumulatedCostSettings | ||
| { | ||
| Subscription = Guid.NewGuid(), | ||
| Timeframe = TimeframeType.Custom, | ||
| From = new DateOnly(2023, 1, 31), | ||
| To = new DateOnly(2023, 1, 1) | ||
|
|
@@ -65,6 +67,7 @@ public void Validate_WithNonCustomTimeframe_ReturnsSuccess() | |
| // Arrange | ||
| var settings = new AccumulatedCostSettings | ||
| { | ||
| Subscription = Guid.NewGuid(), | ||
| Timeframe = TimeframeType.MonthToDate | ||
| }; | ||
| var context = CreateCommandContext(); | ||
|
|
@@ -82,6 +85,7 @@ public void Validate_WithBothFromAndToDates_AutomaticallySetsTimeframeToCustom() | |
| // Arrange | ||
| var settings = new AccumulatedCostSettings | ||
| { | ||
| Subscription = Guid.NewGuid(), | ||
| Timeframe = TimeframeType.BillingMonthToDate, // Not Custom initially | ||
| From = new DateOnly(2023, 1, 1), | ||
| To = new DateOnly(2023, 1, 31) | ||
|
|
@@ -102,6 +106,7 @@ public void Validate_WithOnlyFromDate_DoesNotSetTimeframeToCustom() | |
| // Arrange | ||
| var settings = new AccumulatedCostSettings | ||
| { | ||
| Subscription = Guid.NewGuid(), | ||
| Timeframe = TimeframeType.BillingMonthToDate, | ||
| From = new DateOnly(2023, 1, 1) | ||
| // To is not set | ||
|
|
@@ -122,6 +127,7 @@ public void Validate_WithAutoCustomAndInvalidDates_ReturnsError() | |
| // Arrange | ||
| var settings = new AccumulatedCostSettings | ||
| { | ||
| Subscription = Guid.NewGuid(), | ||
| Timeframe = TimeframeType.BillingMonthToDate, // Will be auto-set to Custom | ||
| From = new DateOnly(2023, 1, 31), // From after To | ||
| To = new DateOnly(2023, 1, 1) | ||
|
|
@@ -144,6 +150,62 @@ public void Constructor_SetsUpOutputFormatters() | |
| command.ShouldNotBeNull(); | ||
| } | ||
|
|
||
| [Fact] | ||
| public void Validate_WithNoSubscriptionAndNoAzureCLI_ReturnsError() | ||
| { | ||
| // Arrange | ||
| var settings = new AccumulatedCostSettings | ||
| { | ||
| Subscription = null // No subscription provided | ||
| }; | ||
| var context = CreateCommandContext(); | ||
|
|
||
| // Act | ||
| var result = _command.Validate(context, settings); | ||
|
|
||
| // Assert | ||
| result.Successful.ShouldBeFalse(); | ||
| result.Message.ShouldContain("No subscription ID provided"); | ||
| result.Message.ShouldContain("--help"); | ||
| } | ||
|
Comment on lines
+153
to
+170
|
||
|
|
||
| [Fact] | ||
| public void Validate_WithSubscription_ReturnsSuccess() | ||
| { | ||
| // Arrange | ||
| var settings = new AccumulatedCostSettings | ||
| { | ||
| Subscription = Guid.NewGuid(), | ||
| Timeframe = TimeframeType.BillingMonthToDate | ||
| }; | ||
| var context = CreateCommandContext(); | ||
|
|
||
| // Act | ||
| var result = _command.Validate(context, settings); | ||
|
|
||
| // Assert | ||
| result.Successful.ShouldBeTrue(); | ||
| } | ||
|
|
||
| [Fact] | ||
| public void Validate_WithBillingAccountAndNoSubscription_ReturnsSuccess() | ||
| { | ||
| // Arrange | ||
| var settings = new AccumulatedCostSettings | ||
| { | ||
| Subscription = null, | ||
| BillingAccountId = "12345", | ||
| Timeframe = TimeframeType.BillingMonthToDate | ||
| }; | ||
| var context = CreateCommandContext(); | ||
|
|
||
| // Act | ||
| var result = _command.Validate(context, settings); | ||
|
|
||
| // Assert | ||
| result.Successful.ShouldBeTrue(); | ||
| } | ||
|
|
||
| private static CommandContext CreateCommandContext() | ||
| { | ||
| var remainingArguments = Mock.Of<IRemainingArguments>(); | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The Validate method modifies the settings object by setting the Subscription property. This violates the principle that validation should not have side effects. If validation fails later or if the command is not executed, the settings object is left in a modified state. Consider moving the subscription retrieval logic to the beginning of ExecuteAsync, or return the retrieved subscription ID as part of a validation context without modifying the settings.