Skip to content

Commit

Permalink
Merge pull request #52 from cycloidio/vs-51
Browse files Browse the repository at this point in the history
Correct planned cost calculation for resource diff
  • Loading branch information
Vladimir authored Jul 15, 2021
2 parents f1024de + 03f6606 commit d0df2e3
Show file tree
Hide file tree
Showing 5 changed files with 160 additions and 2 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
## [Unreleased]

## [0.4.1] _2021-07-15_

### Fixed

- Correct calculation for planned cost in resource diff
([Issue #51](https://github.com/cycloidio/terracost/issues/51))

## [0.4.0] _2021-07-13_

### Added
Expand Down
5 changes: 5 additions & 0 deletions cost/cost.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,11 @@ func (c Cost) Hourly() decimal.Decimal {
// Add adds the values of two Cost structs.
// If the currency of both costs doesn't match, error is returned.
func (c Cost) Add(c2 Cost) (Cost, error) {
// if cost addition iz Zero, ignore it
if c2 == Zero {
return c, nil
}

// If there is no currency, use the currency of the addition
if c.Currency == "" {
c.Currency = c2.Currency
Expand Down
4 changes: 2 additions & 2 deletions cost/resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ func (rd ResourceDiff) PriorCost() (Cost, error) {
for _, cd := range rd.ComponentDiffs {
total, err = total.Add(cd.PriorCost())
if err != nil {
return Zero, fmt.Errorf("failed calculating prior cost : %w", err)
return Zero, fmt.Errorf("failed calculating prior cost: %w", err)
}
}
return total, nil
Expand All @@ -54,7 +54,7 @@ func (rd ResourceDiff) PlannedCost() (Cost, error) {
total := Zero
var err error
for _, cd := range rd.ComponentDiffs {
total, err = total.Add(cd.PriorCost())
total, err = total.Add(cd.PlannedCost())
if err != nil {
return Zero, fmt.Errorf("failed calculating planned cost: %w", err)
}
Expand Down
130 changes: 130 additions & 0 deletions cost/resource_test.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
package cost_test

import (
"fmt"
"testing"

"github.com/shopspring/decimal"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

"github.com/cycloidio/terracost/cost"
)
Expand Down Expand Up @@ -103,3 +106,130 @@ func TestResourceDiff_Valid(t *testing.T) {
})
}
}

func TestResourceDiff_Cost(t *testing.T) {
t.Run("Success", func(t *testing.T) {
rd := &cost.ResourceDiff{
ComponentDiffs: map[string]*cost.ComponentDiff{
"comp1": {
Prior: &cost.Component{
Rate: cost.NewMonthly(decimal.NewFromFloat(104.6), "USD"),
Quantity: decimal.NewFromInt(1),
},
Planned: &cost.Component{
Rate: cost.NewMonthly(decimal.NewFromFloat(208.8), "USD"),
Quantity: decimal.NewFromInt(1),
},
},
"comp2": {
Prior: &cost.Component{
Rate: cost.NewMonthly(decimal.NewFromFloat(10.5), "USD"),
Quantity: decimal.NewFromInt(2),
},
Planned: &cost.Component{
Rate: cost.NewMonthly(decimal.NewFromFloat(20.7), "USD"),
Quantity: decimal.NewFromInt(2),
},
},
},
}

prior, err := rd.PriorCost()
require.NoError(t, err)
assertDecimalEqual(t, decimal.NewFromFloat(125.6), prior.Monthly())
assert.Equal(t, "USD", prior.Currency)

planned, err := rd.PlannedCost()
require.NoError(t, err)
assertDecimalEqual(t, decimal.NewFromFloat(250.2), planned.Decimal)
assert.Equal(t, "USD", planned.Currency)
})
t.Run("PriorWithError", func(t *testing.T) {
rd := &cost.ResourceDiff{
ComponentDiffs: map[string]*cost.ComponentDiff{
"comp1": {
Prior: &cost.Component{
Rate: cost.NewMonthly(decimal.NewFromFloat(104.6), "USD"),
Quantity: decimal.NewFromInt(1),
},
},
"comp2": {
Prior: &cost.Component{
Error: fmt.Errorf("prior error"),
},
},
},
}

prior, err := rd.PriorCost()
require.NoError(t, err)
assertDecimalEqual(t, decimal.NewFromFloat(104.6), prior.Monthly())
assert.Equal(t, "USD", prior.Currency)
})
t.Run("PlannedWithError", func(t *testing.T) {
rd := &cost.ResourceDiff{
ComponentDiffs: map[string]*cost.ComponentDiff{
"comp1": {
Planned: &cost.Component{
Rate: cost.NewMonthly(decimal.NewFromFloat(208.8), "USD"),
Quantity: decimal.NewFromInt(1),
},
},
"comp2": {
Planned: &cost.Component{
Error: fmt.Errorf("planned error"),
},
},
},
}

planned, err := rd.PlannedCost()
require.NoError(t, err)
assertDecimalEqual(t, decimal.NewFromFloat(208.8), planned.Decimal)
assert.Equal(t, "USD", planned.Currency)
})
t.Run("PriorCurrencyMismatch", func(t *testing.T) {
rd := &cost.ResourceDiff{
ComponentDiffs: map[string]*cost.ComponentDiff{
"comp1": {
Prior: &cost.Component{
Rate: cost.NewMonthly(decimal.NewFromFloat(104.6), "USD"),
Quantity: decimal.NewFromInt(1),
},
},
"comp2": {
Prior: &cost.Component{
Rate: cost.NewMonthly(decimal.NewFromFloat(10.5), "EUR"),
Quantity: decimal.NewFromInt(2),
},
},
},
}

_, err := rd.PriorCost()
require.Error(t, err)
assert.Equal(t, "failed calculating prior cost: currency mismatch: expected USD, got EUR", err.Error())
})
t.Run("PlannedCurrencyMismatch", func(t *testing.T) {
rd := &cost.ResourceDiff{
ComponentDiffs: map[string]*cost.ComponentDiff{
"comp1": {
Planned: &cost.Component{
Rate: cost.NewMonthly(decimal.NewFromFloat(208.8), "USD"),
Quantity: decimal.NewFromInt(1),
},
},
"comp2": {
Planned: &cost.Component{
Rate: cost.NewMonthly(decimal.NewFromFloat(20.7), "EUR"),
Quantity: decimal.NewFromInt(2),
},
},
},
}

_, err := rd.PlannedCost()
require.Error(t, err)
assert.Equal(t, "failed calculating planned cost: currency mismatch: expected USD, got EUR", err.Error())
})
}
16 changes: 16 additions & 0 deletions e2e/aws_estimation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -161,11 +161,27 @@ func TestAWSEstimation(t *testing.T) {
assertCostEqual(t, cost.NewMonthly(decimal.NewFromFloat(3.6), "USD"), compute.PriorCost())
assertCostEqual(t, cost.NewMonthly(decimal.NewFromFloat(3.6), "USD"), compute.PlannedCost())

priorCost, err := diff.PriorCost()
require.NoError(t, err)
assertCostEqual(t, cost.NewMonthly(decimal.NewFromFloat(91.2), "USD"), priorCost)

plannedCost, err := diff.PlannedCost()
require.NoError(t, err)
assertCostEqual(t, cost.NewMonthly(decimal.NewFromFloat(901.5), "USD"), plannedCost)

case "aws_lb.example":
lb := diff.ComponentDiffs["Application Load Balancer"]
require.NotNil(t, lb)
assert.False(t, diff.Valid())
assertCostEqual(t, cost.NewMonthly(decimal.NewFromFloat(0), ""), lb.Planned.Cost())

priorCost, err := diff.PriorCost()
require.NoError(t, err)
assertCostEqual(t, cost.NewMonthly(decimal.NewFromFloat(0), ""), priorCost)

plannedCost, err := diff.PlannedCost()
require.NoError(t, err)
assertCostEqual(t, cost.NewMonthly(decimal.NewFromFloat(0), ""), plannedCost)
}
}
})
Expand Down

0 comments on commit d0df2e3

Please sign in to comment.