diff --git a/frontend/src/__tests__/recommendations.test.ts b/frontend/src/__tests__/recommendations.test.ts index 7aab4389f..5295d2b64 100644 --- a/frontend/src/__tests__/recommendations.test.ts +++ b/frontend/src/__tests__/recommendations.test.ts @@ -8180,6 +8180,27 @@ describe('Issue #135: SP group parent row rendered in table', () => { // Should reflect sum: 100 + 200 = 300 expect(groupSavings!.textContent).toContain('$300'); }); + + test('issue #252: SP group savings does not double-apply the cost-period factor', async () => { + const recs = [ + mkSpRec('savings-plans-compute', { id: 'sp-c', savings: 100 }), + mkSpRec('savings-plans-sagemaker', { id: 'sp-s', savings: 200 }), + ]; + (api.getRecommendations as jest.Mock).mockResolvedValue({ summary: {}, recommendations: recs, regions: [] }); + (state.getRecommendations as jest.Mock).mockReturnValue(recs); + (state.getVisibleRecommendations as jest.Mock).mockReturnValue(recs); + + // Monthly total is $300. On "yearly" (factor 12) the correct figure is + // $3,600/yr. Pre-fix, scaleCost was applied once outside and again + // inside formatCostForPeriod, yielding $36,000/yr (12x too high). + (state.getCostPeriod as jest.Mock).mockReturnValue('yearly'); + await loadRecommendations(); + + const groupSavings = document.querySelector('.rec-sp-group-savings'); + expect(groupSavings).not.toBeNull(); + expect(groupSavings!.textContent).toContain('$3600'); + expect(groupSavings!.textContent).not.toContain('$36000'); + }); }); // ============================================================================ diff --git a/frontend/src/recommendations.ts b/frontend/src/recommendations.ts index 519287dd6..dcec23158 100644 --- a/frontend/src/recommendations.ts +++ b/frontend/src/recommendations.ts @@ -3203,8 +3203,9 @@ function buildListMarkup( const cv = groups.get(ck)!; spSavingsTotal += Math.max(...cv.map((r) => r.savings)); } - const scaledSpSavings = scaleCost(spSavingsTotal, period) ?? spSavingsTotal; - const spSavingsText = `${formatCostForPeriod(scaledSpSavings, period)}${sfxLabel}`; + // formatCostForPeriod scales internally; passing the already-scaled + // scaledSpSavings here double-applied the period factor (issue #252). + const spSavingsText = `${formatCostForPeriod(spSavingsTotal, period)}${sfxLabel}`; const sgkAttr = escapeHtml(sgk); rows.push(`