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
20 changes: 20 additions & 0 deletions internal/archera/recommendation_alternatives.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package archera

import "math/big"

type CandidateIdentity struct {
OfferID, Provider, Type, Payment string
SelectedAmount *big.Rat
}

type CandidateAssociation struct {
OfferID, Status, Reason, NativeIdentityStatus string
}

func associateCandidate(r *RecommendationIdentity, c *CandidateIdentity) CandidateAssociation {
out := CandidateAssociation{OfferID: c.OfferID, Status: "unavailable", Reason: "candidate_basis_unproven", NativeIdentityStatus: "unavailable"}
if validMatchUUID(c.OfferID) && c.Provider == r.Provider && c.Type == "savings_plan" && validPayment(c.Payment) && sameAmount(r.HourlyCommitment, c.SelectedAmount) {
out.Status, out.Reason = "vendor_alternative", ""
}
return out
}
82 changes: 82 additions & 0 deletions internal/archera/recommendation_alternatives_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package archera

import (
"encoding/json"
"math/big"
"strings"
"testing"

"github.com/stretchr/testify/require"
)

func candidateFixture() CandidateIdentity {
return CandidateIdentity{OfferID: "faadf00d-ffff-4aba-8ada-abcdefabcdef", Provider: "aws",
Type: "savings_plan", Payment: "all_upfront", SelectedAmount: big.NewRat(1, 2)}
}

func TestMatchRecommendationPreservesVendorAlternativesWithoutNativeClaims(t *testing.T) {
r, rows, items := matchFixture()
candidate := candidateFixture()
rows[0].Candidates = []CandidateIdentity{candidate, candidate, candidate}
rows[0].Candidates[1].Payment = "partial_upfront"
rows[0].Candidates[2].OfferID = strings.ToUpper(candidate.OfferID)
rows[0].Candidates[2].Payment = "no_upfront"
before, err := json.Marshal([]any{r, rows, items})
require.NoError(t, err)
got, err := MatchRecommendation(matchPlan, r, rows, items)
require.NoError(t, err)
require.Equal(t, ResourceAssociated, got.Status)
require.Len(t, got.Candidates, 3)
for i, association := range got.Candidates {
require.Equal(t, rows[0].Candidates[i].OfferID, association.OfferID)
require.Equal(t, "vendor_alternative", association.Status)
require.Empty(t, association.Reason)
require.Equal(t, "unavailable", association.NativeIdentityStatus)
}
require.Equal(t, "unavailable_source_precision", got.EconomicStatus)
after, err := json.Marshal([]any{r, rows, items})
require.NoError(t, err)
require.Equal(t, before, after)
}

func TestMatchRecommendationDoesNotBorrowCandidateBasis(t *testing.T) {
tests := []struct {
name string
change func(*CandidateIdentity)
}{
{"id", func(c *CandidateIdentity) { c.OfferID = "invalid" }},
{"invalid36", func(c *CandidateIdentity) { c.OfferID = "zzzzzzzz-zzzz-4zzz-8zzz-zzzzzzzzzzzz" }},
{"type", func(c *CandidateIdentity) { c.Type = "ri" }},
{"amount", func(c *CandidateIdentity) { c.SelectedAmount = big.NewRat(2, 1) }},
{"missing_amount", func(c *CandidateIdentity) { c.SelectedAmount = nil }},
{"zero_amount", func(c *CandidateIdentity) { c.SelectedAmount = new(big.Rat) }},
{"negative_amount", func(c *CandidateIdentity) { c.SelectedAmount = big.NewRat(-1, 2) }},
{"provider", func(c *CandidateIdentity) { c.Provider = "azure" }},
{"payment", func(c *CandidateIdentity) { c.Payment = "" }},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
r, rows, items := matchFixture()
c := candidateFixture()
tt.change(&c)
rows[0].Candidates = []CandidateIdentity{c}
got, err := MatchRecommendation(matchPlan, r, rows, items)
require.NoError(t, err)
require.Equal(t, ResourceAssociated, got.Status)
require.Len(t, got.Candidates, 1)
require.Equal(t, "unavailable", got.Candidates[0].Status)
require.NotEmpty(t, got.Candidates[0].Reason)
require.Equal(t, "unavailable", got.Candidates[0].NativeIdentityStatus)
})
}
}

func TestMatchRecommendationOmitsCandidatesWithoutCurrentAssociation(t *testing.T) {
r, rows, items := matchFixture()
rows[0].Candidates = []CandidateIdentity{candidateFixture()}
items[0].SourceAccountID = "another-source"
got, err := MatchRecommendation(matchPlan, r, rows, items)
require.NoError(t, err)
require.Equal(t, MatchUnavailable, got.Status)
require.Empty(t, got.Candidates)
}
9 changes: 7 additions & 2 deletions internal/archera/recommendation_match.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ type ComparisonIdentity struct {
IsCurrent bool
LineItemID, CurrentOfferID, Provider, CurrentType, CurrentPayment string
CurrentSelectedAmount *big.Rat
Candidates []CandidateIdentity
}

type MatchStatus string
Expand All @@ -44,6 +45,7 @@ const (
type RecommendationMatch struct {
Status MatchStatus
LineItemID, Reason, AmountProvenance, EconomicStatus string
Candidates []CandidateAssociation
}

func MatchRecommendation(planID string, rec *RecommendationIdentity, rows []ComparisonIdentity, items []LineItemIdentity) (RecommendationMatch, error) {
Expand Down Expand Up @@ -81,6 +83,9 @@ func MatchRecommendation(planID string, rec *RecommendationIdentity, rows []Comp
Status: ResourceAssociated, LineItemID: row.LineItemID,
AmountProvenance: "stored_decimal_only", EconomicStatus: "unavailable_source_precision",
}
for i := range row.Candidates {
result.Candidates = append(result.Candidates, associateCandidate(rec, &row.Candidates[i]))
}
return result, nil
}

Expand All @@ -94,7 +99,7 @@ func validRecommendation(r *RecommendationIdentity) bool {
if r.NativeDurationSeconds != awsSPYearSeconds && r.NativeDurationSeconds != awsSPThreeYearSeconds {
return false
}
return validDimensions(r) && validPayment(r.Payment) && sameAmount(r.HourlyCommitment, r.HourlyCommitment)
return validDimensions(r) && validPayment(r.Payment)
}

func validDimensions(r *RecommendationIdentity) bool {
Expand Down Expand Up @@ -183,7 +188,7 @@ func validMatchUUID(id string) bool {
}

func validLineReference(planID string, item *LineItemIdentity) bool {
return validMatchUUID(item.ID) && validMatchUUID(item.OfferID) && validMatchUUID(item.PlanID) && strings.EqualFold(planID, item.PlanID)
return validMatchUUID(item.ID) && validMatchUUID(item.PlanID) && strings.EqualFold(planID, item.PlanID)
}

func validComparisonReference(row *ComparisonIdentity, items map[string]*LineItemIdentity) bool {
Expand Down
13 changes: 13 additions & 0 deletions internal/archera/recommendation_match_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,19 @@ func TestMatchRecommendationRefusesUnprovenAssociation(t *testing.T) {
c.Region = &i.InstanceFamily
}},
{"missing_current_region", func(_ *RecommendationIdentity, c *ComparisonIdentity, _ *LineItemIdentity) { c.Region = nil }},
{"line_item_region", func(_ *RecommendationIdentity, _ *ComparisonIdentity, i *LineItemIdentity) { i.Region = "eu-west-1" }},
{"current_payment", func(_ *RecommendationIdentity, c *ComparisonIdentity, _ *LineItemIdentity) {
c.CurrentPayment = "all_upfront"
}},
{"other_plan", func(_ *RecommendationIdentity, _ *ComparisonIdentity, i *LineItemIdentity) {
i.PlanID = "faadf00d-ffff-4aba-8ada-abcdefabcdef"
}},
{"non_aws_recommendation", func(r *RecommendationIdentity, c *ComparisonIdentity, i *LineItemIdentity) {
r.Provider, c.Provider, i.Provider = "gcp", "gcp", "gcp"
}},
{"invalid_payment", func(r *RecommendationIdentity, c *ComparisonIdentity, i *LineItemIdentity) {
r.Payment, c.CurrentPayment, i.Payment = "monthly", "monthly", "monthly"
}},
{"current_offer", func(_ *RecommendationIdentity, c *ComparisonIdentity, _ *LineItemIdentity) {
c.CurrentOfferID = "faadf00d-ffff-4aba-8ada-abcdefabcdef"
}},
Expand Down
Loading