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
17 changes: 17 additions & 0 deletions pkg/insurance/amounts.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package insurance

import (
"errors"
"math/big"
)

func nonnegativeRat(n wireNum) (*big.Rat, error) {
r, err := requiredRat(n)
if err != nil {
return nil, err
}
if r.Sign() < 0 {
return nil, errors.New("must be nonnegative")
}
return r, nil
}
15 changes: 9 additions & 6 deletions pkg/insurance/quote.go
Original file line number Diff line number Diff line change
Expand Up @@ -217,12 +217,11 @@ func (w wireHypothetical) toHypothetical() (Hypothetical, error) {
}

// wireOfferEntry mirrors the consumed subset of OfferComparisonEntry.
// selected_amount and offer_org_id feed the vendor's update operation and are
// deliberately not decoded.
type wireOfferEntry struct {
IsCurrent *bool `json:"is_current"`
OfferID string `json:"offer_id"`
Offer *struct {
IsCurrent *bool `json:"is_current"`
OfferID string `json:"offer_id"`
SelectedAmount wireNum `json:"selected_amount"`
Offer *struct {
Provider *string `json:"provider"`
Region *string `json:"region"`
GuaranteedDisplayName *string `json:"guaranteed_display_name"`
Expand Down Expand Up @@ -273,11 +272,15 @@ func (w *wireOfferEntry) toOfferEntry() (OfferEntry, error) {
}
e.IsCurrent = *w.IsCurrent
e.OfferID = w.OfferID
amount, err := nonnegativeRat(w.SelectedAmount)
if err != nil {
return e, fmt.Errorf("selected_amount: %w", err)
}
e.SelectedAmount = amount
e.CommitmentType = w.CommitmentType
e.LeaseMenuItemID = w.LeaseMenuItemID
e.GuaranteedDisplayName = w.Offer.GuaranteedDisplayName
e.Region = w.Offer.Region
var err error
if e.Provider, err = parseProvider(w.Offer.Provider); err != nil {
return e, fmt.Errorf("offer.provider: %w", err)
}
Expand Down
18 changes: 18 additions & 0 deletions pkg/insurance/quote_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ func TestDecodeComparison_Golden(t *testing.T) {
row := c.Rows[0]
assert.Equal(t, "22222222-2222-4222-8222-222222222222", row.LineItemID)
assert.True(t, row.Current.IsCurrent)
assert.Equal(t, rat(t, "3"), row.Current.SelectedAmount)
assert.False(t, row.Current.LeaseBacked())
assert.Nil(t, row.Current.ContractTerm)
assert.Nil(t, row.Current.PaymentOption)
Expand All @@ -141,6 +142,19 @@ func TestDecodeComparison_Golden(t *testing.T) {
assert.Nil(t, cand.Delta.BreakevenDays)
}

func TestDecodeComparisonSelectedAmount(t *testing.T) {
t.Parallel()
for _, value := range []string{"null", "-1", `"3"`} {
_, err := decodeComparison(t, strings.ReplaceAll(validComparisonJSON(), `"selected_amount": 3`, `"selected_amount": `+value))
require.ErrorContains(t, err, "selected_amount")
}
_, err := decodeComparison(t, strings.ReplaceAll(validComparisonJSON(), `"selected_amount": 3,`, ``))
require.ErrorContains(t, err, "selected_amount")
c, err := decodeComparison(t, strings.ReplaceAll(validComparisonJSON(), `"selected_amount": 3`, `"selected_amount": 0.1234567890123456789`))
require.NoError(t, err)
assert.Equal(t, rat(t, "0.1234567890123456789"), c.Rows[0].Current.SelectedAmount)
}

func TestDecodeComparison_EmptyArraysAreValid(t *testing.T) {
c, err := decodeComparison(t, `{
"current_totals": {"commitment_financials_monthly_rate": {}, "commitment_upfront_cost": 0},
Expand Down Expand Up @@ -428,6 +442,7 @@ func TestDecodeComparison_DistinctValuesForEveryMappedField(t *testing.T) {
"line_item_id": "bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbbbbbb",
"current": {
"is_current": true, "offer_id": "cccccccc-cccc-4ccc-8ccc-cccccccccccc",
"selected_amount": 3.25,
"offer": {"provider": "gcp", "region": "europe-west4", "guaranteed_display_name": "Current G"},
"lease_menu_item_id": null, "commitment_type": "gcp/cud",
"contract_term": "three_year", "payment_option": "all_upfront",
Expand All @@ -437,6 +452,7 @@ func TestDecodeComparison_DistinctValuesForEveryMappedField(t *testing.T) {
},
"candidates": [{
"is_current": false, "offer_id": "dddddddd-dddd-4ddd-8ddd-dddddddddddd",
"selected_amount": 4.125,
"offer": {"provider": "azure", "region": "eu-west-1", "guaranteed_display_name": "Cand G"},
"lease_menu_item_id": "eeeeeeee-eeee-4eee-8eee-eeeeeeeeeeee", "commitment_type": "azure/Virtual Machines",
"contract_term": "one_year_gris", "payment_option": "partial_upfront",
Expand Down Expand Up @@ -480,6 +496,7 @@ func TestDecodeComparison_DistinctValuesForEveryMappedField(t *testing.T) {
cur := row.Current
assert.True(t, cur.IsCurrent)
assert.Equal(t, "cccccccc-cccc-4ccc-8ccc-cccccccccccc", cur.OfferID)
assert.Equal(t, rat(t, "3.25"), cur.SelectedAmount)
assert.Equal(t, "gcp/cud", cur.CommitmentType)
assert.Equal(t, common.ProviderGCP, cur.Provider)
require.NotNil(t, cur.Region)
Expand All @@ -497,6 +514,7 @@ func TestDecodeComparison_DistinctValuesForEveryMappedField(t *testing.T) {
cand := row.Candidates[0]
assert.False(t, cand.IsCurrent)
assert.Equal(t, "dddddddd-dddd-4ddd-8ddd-dddddddddddd", cand.OfferID)
assert.Equal(t, rat(t, "4.125"), cand.SelectedAmount)
assert.Equal(t, "azure/Virtual Machines", cand.CommitmentType)
assert.Equal(t, common.ProviderAzure, cand.Provider)
require.NotNil(t, cand.Region)
Expand Down
6 changes: 3 additions & 3 deletions pkg/insurance/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -135,14 +135,14 @@ type OfferDelta struct {
}

// OfferEntry mirrors the consumed subset of OfferComparisonEntry: one offer
// (current or candidate) for a line item. Fields that feed the vendor's
// line-item update operation (selected_amount, offer_org_id) are deliberately
// not mapped.
// (current or candidate) for a line item.
type OfferEntry struct {
// IsCurrent is true when this entry matches the line item's current
// offer and lease.
IsCurrent bool
OfferID string
// SelectedAmount is a count for RI/unit CUD, or $/hour for spend commitments.
SelectedAmount *big.Rat
// CommitmentType is the vendor's offer type string. The docs give both
// "aws/AmazonEC2"-style and "ri"/"savings_plan"/"cud"-style examples;
// treat it as opaque display text.
Expand Down
Loading