Skip to content

Commit 4bc19df

Browse files
committed
fix(azure): guard EstimatedSavings when OnDemandCost is zero (CR #682)
Three findings from CodeRabbit review 4348367061: 1. `converter.go:287-292` (recurring Major): the OnDemandCost-zero guard protected savingsPct (divide-by-zero) but the savings = totalOnDemand - totalReservation line ran unconditionally. Result: when OnDemandCost was zero and CommitmentCost was non-zero, EstimatedSavings was emitted as a negative number (-CommitmentCost), contradicting the function contract. Move savings inside the guard so both fields stay 0 when on-demand is zero. 2. `converter_test.go`: add TestExpandPaymentVariants_ZeroOnDemand_NonZeroCommitment regression asserting EstimatedSavings == 0 (not -CommitmentCost) when OnDemand=0, Commitment>0. The existing _ZeroOnDemand_NoSavings test only covered the 0/0 sub-case and would have missed the regression. 3. `client_test.go`: add cost-field assertions to TestSearchClient_ConvertAzureSearchRecommendation_PopulatesAllFields so the WithCosts(120, 80, 40) fixture is actually checked through to rec.OnDemandCost / rec.CommitmentCost / rec.EstimatedSavings. Locks in the converter behaviour this PR depends on.
1 parent c6c2df9 commit 4bc19df

3 files changed

Lines changed: 18 additions & 2 deletions

File tree

‎providers/azure/internal/recommendations/converter.go‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -285,10 +285,11 @@ func ExpandPaymentVariants(base common.Recommendation) []common.Recommendation {
285285
totalOnDemand := base.OnDemandCost
286286

287287
var savingsPct float64
288+
var savings float64
288289
if totalOnDemand != 0 {
289-
savingsPct = (totalOnDemand - totalReservation) / totalOnDemand * 100
290+
savings = totalOnDemand - totalReservation
291+
savingsPct = savings / totalOnDemand * 100
290292
}
291-
savings := totalOnDemand - totalReservation
292293

293294
months := termToMonths(base.Term)
294295
recurringMonthly := totalReservation / float64(months)

‎providers/azure/internal/recommendations/converter_test.go‎

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -318,6 +318,18 @@ func TestExpandPaymentVariants_ZeroOnDemand_NoSavings(t *testing.T) {
318318
}
319319
}
320320

321+
func TestExpandPaymentVariants_ZeroOnDemand_NonZeroCommitment(t *testing.T) {
322+
// Regression: when OnDemandCost == 0 but CommitmentCost > 0, the guard
323+
// must also force EstimatedSavings to 0. Without it, savings would be
324+
// computed as 0 - CommitmentCost and emit negative savings.
325+
variants := ExpandPaymentVariants(baseRec(common.ServiceCompute, "1yr", 0, 10))
326+
require.Len(t, variants, 2)
327+
for _, v := range variants {
328+
assert.InDelta(t, 0.0, v.EstimatedSavings, 1e-9)
329+
assert.InDelta(t, 0.0, v.SavingsPercentage, 1e-9)
330+
}
331+
}
332+
321333
func TestExpandPaymentVariants_ZeroCommitmentCost(t *testing.T) {
322334
// Zero reservation total: both variants still emitted; no-upfront monthly = 0.
323335
variants := ExpandPaymentVariants(baseRec(common.ServiceCompute, "1yr", 50, 0))

‎providers/azure/services/search/client_test.go‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -616,6 +616,9 @@ func TestSearchClient_ConvertAzureSearchRecommendation_PopulatesAllFields(t *tes
616616
assert.Equal(t, "eastus", rec.Region)
617617
assert.Equal(t, "standard2", rec.ResourceType)
618618
assert.Equal(t, 2, rec.Count)
619+
assert.InDelta(t, 120.0, rec.OnDemandCost, 1e-9)
620+
assert.InDelta(t, 80.0, rec.CommitmentCost, 1e-9)
621+
assert.InDelta(t, 40.0, rec.EstimatedSavings, 1e-9)
619622
assert.Equal(t, common.CommitmentReservedInstance, rec.CommitmentType)
620623
assert.Equal(t, "1yr", rec.Term)
621624
assert.Equal(t, "upfront", rec.PaymentOption)

0 commit comments

Comments
 (0)