fix(api): current_savings zero for services with no active commitments (closes #1031) - #1041
Conversation
|
@coderabbitai review |
|
Warning Review limit reachedYou’ve reached a temporary PR review limit under our Fair Usage Limits Policy. Next review available in: 17 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (3)
📝 WalkthroughWalkthroughsummarizeRecommendationsWithCoverage now only aggregates per-service PotentialSavings and documents that CurrentSavings is populated separately from purchase history. Deterministic first-service selection added for planned/upcoming purchases. Fixed in-place filter allocation, added logging on commitment fetch errors, and replaced 30-day-month heuristic with calendar-month counting. Tests updated/added. ChangesDashboard current/potential savings separation and fixes
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related issues
Possibly related PRs
Suggested labels
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
✅ Action performedReview finished.
|
FOLD-1041 scope expansion (commit a1446db)Four additional findings from the dashboard/types bucket (report 01) folded in: 01-L2 (filterDashboardRecommendations, line 175): was recs[:0] reusing input backing array; fix: fresh make allocation. Test: TestFilterDashboardRecommendations_FreshSlice. 01-M3 (fetchCommitmentPurchases, line 552): store error returned (nil,false) with no log; fix: logging.Errorf added before return. Existing TestHandler_calculateCommitmentMetrics/purchase-history-error-returns-zeros covers the path. 01-M2 (calculateCommitmentMetrics YTD): elapsed months were int(duration.Hours()/(24*30)), truncating February to 0 months; fix: elapsedWholeMonths helper using AddDate-stepping. Tests: TestElapsedWholeMonths (7 cases including Feb 28-day gap). 01-N1 (upcomingFromExecution + buildPlannedPurchase): both inlined identical map-range+break first-service loops; fix: firstServiceConfig(plan) helper with sorted keys for deterministic order. Test: TestFirstServiceConfig (determinism across 20 iterations). Build/vet/test: go build ./internal/api/... pass, go vet ./internal/api/... pass, go test ./internal/api/... 1506 passed (1493 pre-fold + 13 new). @coderabbitai review |
|
Tip For best results, initiate chat on the files or code changes. 🧩 Analysis chain🏁 Script executed: #!/bin/bash
# Get the diff for the PR to review the folded changes
gh pr diff 1041 --repo LeanerCloud/CUDly | head -600Length of output: 21938
01-L2 ( 01-M3 ( 01-M2 ( for !from.AddDate(0, months+1, 0).After(to) {
months++
}Not a bug — just slightly cleaner. Happy to leave it as-is given the negligible loop count (≤ 12 for any realistic YTD window). 01-N1 ( All 13 new tests cover meaningful edge cases. The scope expansion is well-controlled — nothing feels bolted on. LGTM. 🧠 Learnings used |
|
@coderabbitai review Generated by Claude Code |
|
Tip For best results, initiate chat on the files or code changes. 🧩 Analysis chain🏁 Script executed: #!/bin/bash
# Get recent commits on the PR
gh pr view 1041 --repo LeanerCloud/CUDly --json commits --jq '.commits[-5:] | .[] | {oid: .oid, messageHeadline: .messageHeadline}'
echo "---DIFF---"
# Get full diff
gh pr diff 1041 --repo LeanerCloud/CUDly | head -1000Length of output: 22383
For completeness, here's a quick re-confirmation of all changes in the diff:
The only previously noted micro-nit ( 🧠 Learnings used |
a1446db to
0c4d43e
Compare
|
@coderabbitai full review |
✅ Action performedFull review finished. |
|
@coderabbitai review |
✅ Action performedReview finished.
|
Adversarial review notesReviewed this as a money-path KPI regression (services with no active commitments displaying non-zero No actionable changes neededThe fix is correct, minimal, and the regression test reproduces the real failing scenario. Walked the contract end-to-end:
Considered, no change needed
Out-of-scope follow-upsNone requiring a new issue. The CR found no actionable items on the current head; this review concurs. |
|
@coderabbitai review |
✅ Action performedReview finished.
|
|
@coderabbitai review |
✅ Action performedReview finished.
|
2c1169f to
aa33a63
Compare
|
@coderabbitai review |
✅ Action performedReview finished.
|
closes #1031) summarizeRecommendationsWithCoverage incorrectly wrote CurrentSavings to the coverage-scaled potential amount (same as PotentialSavings). Because getDashboardSummary only overwrites CurrentSavings for services present in the purchase-history result, services with recommendations but no active purchases shipped a non-zero current_savings equal to potential_savings. This was wrong: the frontend renders current_savings as committed/realized savings from active purchase history, so an uncommitted service must ship 0. The partial fix in #926 (aggregateActiveCommitmentsPerService + purchase- history overwrite) was correct in concept but left the incorrect assignment in summarizeRecommendationsWithCoverage active, masking the bug for services that had no purchases. Fix: remove svc.CurrentSavings += scaled from summarizeRecommendationsWithCoverage. CurrentSavings is getDashboardSummary's responsibility (via the overwrite loop that calls aggregateActiveCommitmentsPerService). The function comment and the three affected test assertions/functions are updated accordingly. Regression test added: TestHandler_getDashboardSummary_CurrentSavingsZeroWhenNoCommitments asserts current_savings: 0 for services with recommendations but no purchases.
… first-service dedup
aa33a63 to
3fd944c
Compare
|
@coderabbitai review |
…vePurchaseHistory getDashboardSummary now calls GetActivePurchaseHistory (active-only, uncapped) instead of GetAllPurchaseHistory to aggregate commitment KPIs (see fetchCommitmentPurchases). The regression test was written before this change and still mocked the old method, causing a testify panic on unexpected method call. Replace the GetAllPurchaseHistory mock with GetActivePurchaseHistory using three mock.Anything matchers (asOf, accountUUIDs, accountExternalIDsByProvider), matching the pattern already used in the sibling CurrentSavingsPopulated and CurrentSavingsJSON tests.
|
@coderabbitai review |
golangci-lint (gocritic, godot) flagged three issues introduced by the dashboard commits: - Line 179: range-value copy of config.RecommendationRecord (312 bytes) per iteration; switch to index-based _rvc pattern consistent with the rest of handler_dashboard.go. - Line 229: godot: comment block before summarizeRecommendationsWithCoverage ended in ")" not "."; reword to end in period. - Line 703: godot: calculateCurrentCoverage doc comment missing trailing period. No behavioural change.
|
@coderabbitai review |
✅ Action performedReview finished.
|
Summary
Option chosen: (a) populate CurrentSavings per service from active purchase history. The per-service commitment data is readily available via
aggregateActiveCommitmentsPerService, which already runs as part ofcalculateCommitmentMetrics.Root cause: PR fix(api/dashboard): populate ServiceSavings.CurrentSavings from active commitments #926 added
svc.CurrentSavings += scaledinsidesummarizeRecommendationsWithCoverage, intending it to represent coverage-scaled potential savings. ButCurrentSavingssemantically means committed/realized savings from active purchase history (that's what the frontend chart's green bars show). For services with recommendations but no active purchases,getDashboardSummarynever ran the purchase-history overwrite loop, socurrent_savingsincorrectly equalledpotential_savingsinstead of 0.Fix: remove the
svc.CurrentSavings += scaledassignment fromsummarizeRecommendationsWithCoverage.CurrentSavingsis entirelygetDashboardSummary's responsibility, populated after the recommendations reducer runs, via theaggregateActiveCommitmentsPerServiceoverwrite loop (from fix(api/dashboard): populate ServiceSavings.CurrentSavings from active commitments #926). Services with no active purchases now correctly shipcurrent_savings: 0.Test plan
TestHandler_getDashboardSummary_CurrentSavingsZeroWhenNoCommitments(new): service with recommendations but no purchases shipscurrent_savings: 0, notcurrent_savings: potential_savingsTestSummarizeRecommendationsWithCoverage_DoesNotSetCurrentSavings(renamed from_PopulatesCurrentSavings): the reducer does NOT setCurrentSavings; onlyPotentialSavingsis setTestSummarizeRecommendationsWithCoveragetable-driven assertions updated:CurrentSavingsstays 0 fromsummarizeRecommendationsWithCoverageTestHandler_getDashboardSummary_CurrentSavingsPopulatedstill passes (services WITH active purchases get the correct non-zero value)go build ./...andgo vet ./internal/api/...passtsc --noEmitpasses (JSON schema unchanged)Closes #1031
Scope expanded
Additional findings from the FOLD-1041 bucket (report 01,
internal/api/handler_dashboard.go+handler_purchases.go) folded in as commita1446dbd5:filterDashboardRecommendationsusedrecs[:0](shared backing array)make([]config.RecommendationRecord, 0, len(recs))fetchCommitmentPurchasesswallowed store errors silently; dashboard showed zeroed KPIs with no loglogging.Errorfadded before returning falseelapsedWholeMonthshelper usingAddDate-steppingupcomingFromExecutionandbuildPlannedPurchaseeach inlined an identical map-range + break first-service loopfirstServiceConfig(plan)helper with sorted-key traversal for deterministic orderRegression tests added:
TestElapsedWholeMonths,TestFilterDashboardRecommendations_FreshSlice,TestFirstServiceConfig.Also closes #1059
Summary by CodeRabbit
Release Notes