Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 29 additions & 3 deletions pkg/vcs/comment/costs.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,11 +49,37 @@ func (data *Data) buildCostTableEntries() []CostTableEntry {
project.PastTotalMonthlyCost, project.TotalMonthlyCost, data.Currency,
costChangeOpts{},
),
NewTotalCost: formatCost(project.TotalMonthlyCost, data.Currency),
NewTotalCost: formatCost(project.TotalMonthlyCost, data.Currency),
absTotalCostChange: safeSub(project.TotalMonthlyCost, project.PastTotalMonthlyCost).Abs(),
})
}

// If at least one project changed cost, hide the projects whose cost did not
// change. When no project changed cost, keep them all so the table is not
// empty.
hasCostChange := false
for _, entry := range entries {
if !entry.absTotalCostChange.IsZero() {
hasCostChange = true
break
}
}
if hasCostChange {
changed := make([]CostTableEntry, 0, len(entries))
for _, entry := range entries {
if !entry.absTotalCostChange.IsZero() {
changed = append(changed, entry)
}
}
entries = changed
}

// Biggest changes first, falling back to alphabetical ordering for entries
// with an equal absolute cost change.
sort.Slice(entries, func(i, j int) bool {
if !entries[i].absTotalCostChange.Equals(entries[j].absTotalCostChange) {
return entries[i].absTotalCostChange.GreaterThan(entries[j].absTotalCostChange)
}
if entries[i].ProjectName != entries[j].ProjectName {
return entries[i].ProjectName < entries[j].ProjectName
}
Expand Down Expand Up @@ -130,8 +156,8 @@ func projectHasDiff(project ProjectResult) bool {

// costChangeOpts controls formatting of cost change strings.
type costChangeOpts struct {
skipPercent bool
skipIfZero bool
skipPercent bool
skipIfZero bool
skipPlusMinus bool
}

Expand Down
83 changes: 83 additions & 0 deletions pkg/vcs/comment/costs_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,91 @@ package comment
import (
"testing"
"unicode/utf8"

"github.com/infracost/go-proto/pkg/rat"
)

func TestBuildCostTableEntriesOrdering(t *testing.T) {
project := func(name string, past, current int64) ProjectResult {
return ProjectResult{
Name: name,
PastTotalMonthlyCost: rat.New(past),
TotalMonthlyCost: rat.New(current),
DiffBreakdown: &CostBreakdown{
Resources: []BreakdownResource{{Name: "aws_instance.web"}},
},
}
}

data := &Data{
Projects: []ProjectResult{
project("b-same-change", 100, 110),
project("small-increase", 100, 150),
project("big-decrease", 1000, 100),
project("a-same-change", 100, 110),
project("big-increase", 100, 700),
},
}

entries := data.buildCostTableEntries()

want := []string{
"big-decrease", // -$900
"big-increase", // +$600
"small-increase", // +$50
"a-same-change", // +$10, alphabetical tie-break
"b-same-change", // +$10
}

if len(entries) != len(want) {
t.Fatalf("got %d entries, want %d", len(entries), len(want))
}
for i, name := range want {
if entries[i].ProjectName != name {
t.Errorf("entry %d = %q, want %q", i, entries[i].ProjectName, name)
}
}
}

func TestBuildCostTableEntriesHidesUnchangedProjects(t *testing.T) {
project := func(name string, past, current int64) ProjectResult {
return ProjectResult{
Name: name,
PastTotalMonthlyCost: rat.New(past),
TotalMonthlyCost: rat.New(current),
DiffBreakdown: &CostBreakdown{
Resources: []BreakdownResource{{Name: "aws_instance.web"}},
},
}
}

t.Run("some projects changed", func(t *testing.T) {
data := &Data{Projects: []ProjectResult{
project("unchanged", 100, 100),
project("changed", 100, 150),
}}

entries := data.buildCostTableEntries()

if len(entries) != 1 || entries[0].ProjectName != "changed" {
t.Errorf("got %+v, want only the changed project", entries)
}
})

t.Run("no projects changed", func(t *testing.T) {
data := &Data{Projects: []ProjectResult{
project("a", 100, 100),
project("b", 50, 50),
}}

entries := data.buildCostTableEntries()

if len(entries) != 2 {
t.Errorf("got %d entries, want both projects kept", len(entries))
}
})
}

func TestTruncateMiddle(t *testing.T) {
tests := []struct {
name string
Expand Down
6 changes: 6 additions & 0 deletions pkg/vcs/comment/template.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import (
"strings"
"text/template"
"unicode/utf8"

"github.com/infracost/go-proto/pkg/rat"
)

const (
Expand Down Expand Up @@ -541,6 +543,10 @@ type CostTableEntry struct {

// NewTotalCost is the formatted new monthly cost, e.g. "$560.00".
NewTotalCost string

// absTotalCostChange is the absolute total cost change, used to sort rows so
// the biggest changes show first. It is not rendered in the template.
absTotalCostChange *rat.Rat
}

type ProjectError struct {
Expand Down
Loading