storj/satellite/payments/paymentsconfig/config_test.go
Jeremy Wharton add3034b43 satellite/payments/stripecoinpayments: forbid replacing partner coupons
Users with a partner package plan should be unable to replace their
plan's coupon. This change enforces this behavior by rejecting coupon
application attempts from users that meet this criteria.

Change-Id: I6383d19f2c7fbd9e1a2826473b2f867ea8a8ea3e
2023-02-03 17:15:01 +00:00

244 lines
5.9 KiB
Go

// Copyright (C) 2022 Storj Labs, Inc.
// See LICENSE for copying information.
package paymentsconfig_test
import (
"fmt"
"sort"
"strings"
"testing"
"github.com/shopspring/decimal"
"github.com/stretchr/testify/require"
"storj.io/storj/satellite/payments"
"storj.io/storj/satellite/payments/paymentsconfig"
)
func TestProjectUsagePriceOverrides(t *testing.T) {
type Prices map[string]payments.ProjectUsagePriceModel
cases := []struct {
testID string
configValue string
expectedModel Prices
}{
{
testID: "empty",
configValue: "",
expectedModel: Prices{},
}, {
testID: "missing prices",
configValue: "partner",
}, {
testID: "missing partner",
configValue: ":1,2,3",
}, {
testID: "too few prices",
configValue: "partner:1",
}, {
testID: "single price override",
configValue: "partner:1,2,3",
expectedModel: Prices{
// Shift is to change the precision from TB dollars to MB cents
"partner": payments.ProjectUsagePriceModel{
StorageMBMonthCents: decimal.NewFromInt(1).Shift(-4),
EgressMBCents: decimal.NewFromInt(2).Shift(-4),
SegmentMonthCents: decimal.NewFromInt(3).Shift(2),
},
},
}, {
testID: "too many prices",
configValue: "partner:1,2,3,4",
}, {
testID: "invalid decimal",
configValue: "partner:0.0.1,2,3",
}, {
testID: "multiple price overrides",
configValue: "partner1:1,2,3;partner2:4,5,6",
expectedModel: Prices{
"partner1": payments.ProjectUsagePriceModel{
StorageMBMonthCents: decimal.NewFromInt(1).Shift(-4),
EgressMBCents: decimal.NewFromInt(2).Shift(-4),
SegmentMonthCents: decimal.NewFromInt(3).Shift(2),
},
"partner2": payments.ProjectUsagePriceModel{
StorageMBMonthCents: decimal.NewFromInt(4).Shift(-4),
EgressMBCents: decimal.NewFromInt(5).Shift(-4),
SegmentMonthCents: decimal.NewFromInt(6).Shift(2),
},
},
},
}
for _, c := range cases {
c := c
t.Run(c.testID, func(t *testing.T) {
price := &paymentsconfig.ProjectUsagePriceOverrides{}
err := price.Set(c.configValue)
if c.expectedModel == nil {
require.Error(t, err)
return
}
require.NoError(t, err)
strParts := strings.Split(price.String(), ";")
sort.Strings(strParts)
require.Equal(t, c.configValue, strings.Join(strParts, ";"))
models, err := price.ToModels()
require.NoError(t, err)
require.Len(t, models, len(c.expectedModel))
for partner, price := range c.expectedModel {
model := models[partner]
require.Contains(t, models, partner)
require.Equal(t, price.StorageMBMonthCents, model.StorageMBMonthCents)
require.Equal(t, price.EgressMBCents, model.EgressMBCents)
require.Equal(t, price.SegmentMonthCents, model.SegmentMonthCents)
}
})
}
}
func TestPackagePlans(t *testing.T) {
type packages map[string]payments.PackagePlan
cases := []struct {
testID string
configValue string
expectedPackagePlans packages
}{
{
testID: "empty",
configValue: "",
expectedPackagePlans: packages{},
},
{
testID: "missing couponID and price",
configValue: "partner",
},
{
testID: "missing partner",
configValue: ":abc123,100",
}, {
testID: "empty coupon ID",
configValue: "partner:,1",
}, {
testID: "empty price",
configValue: "partner:abc123,",
},
{
testID: "too few values",
configValue: "partner:abc123",
},
{
testID: "too many values",
configValue: "partner:abc123,100,200",
},
{
testID: "single package plan",
configValue: "partner1:abc123,100",
expectedPackagePlans: packages{
"partner1": payments.PackagePlan{
CouponID: "abc123",
Price: 100,
},
},
},
{
testID: "multiple package plans",
configValue: "partner1:abc123,100;partner2:321bca,200",
expectedPackagePlans: packages{
"partner1": payments.PackagePlan{
CouponID: "abc123",
Price: 100,
},
"partner2": payments.PackagePlan{
CouponID: "321bca",
Price: 200,
},
},
},
}
for _, c := range cases {
c := c
t.Run(c.testID, func(t *testing.T) {
packagePlans := paymentsconfig.PackagePlans{}
err := packagePlans.Set(c.configValue)
if c.expectedPackagePlans == nil {
require.Error(t, err)
return
}
require.NoError(t, err)
strParts := strings.Split(packagePlans.String(), ";")
sort.Strings(strParts)
require.Equal(t, c.configValue, strings.Join(strParts, ";"))
for k, v := range c.expectedPackagePlans {
p, err := packagePlans.Get([]byte(k))
require.NoError(t, err)
require.Equal(t, v, p)
}
})
}
}
func TestPackagePlansGet(t *testing.T) {
partner := "partnerName1"
coupon := "abc123"
price := int64(100)
configStr := fmt.Sprintf("%s:%s,%d", partner, coupon, price)
packagePlans := paymentsconfig.PackagePlans{}
require.NoError(t, packagePlans.Set(configStr))
cases := []struct {
testID string
userAgent []byte
shouldPass bool
}{
{
testID: "user agent matches partner",
userAgent: []byte(partner),
shouldPass: true,
},
{
testID: "partner is first entry of user agent",
userAgent: []byte(partner + "/0.1.2"),
shouldPass: true,
},
{
testID: "partner is not first entry of user agent",
userAgent: []byte("app2/1.2.3 " + partner + "/1.2.3"),
shouldPass: true,
},
{
testID: "partner is a prefix of user agent, but not equal",
userAgent: []byte("partnerName12/1.2.3"),
shouldPass: false,
},
{
testID: "partner does not exist in user agent",
userAgent: []byte("partnerName2/1.2.3"),
shouldPass: false,
},
}
for _, c := range cases {
t.Run(c.testID, func(t *testing.T) {
p, err := packagePlans.Get(c.userAgent)
if c.shouldPass {
require.NoError(t, err)
require.Equal(t, coupon, p.CouponID)
require.Equal(t, price, p.Price)
} else {
require.Error(t, err)
require.Empty(t, p)
}
})
}
}