diff --git a/cmd/satellite/billing.go b/cmd/satellite/billing.go index 56c303c48..73e3f03bc 100644 --- a/cmd/satellite/billing.go +++ b/cmd/satellite/billing.go @@ -5,8 +5,6 @@ package main import ( "context" - "strconv" - "strings" "time" "github.com/spf13/cobra" @@ -70,29 +68,15 @@ func setupPayments(log *zap.Logger, db satellite.DB) (*stripecoinpayments.Servic pc.BonusRate) } -// parseBillingPeriodFromString parses provided date string and returns corresponding time.Time. -func parseBillingPeriod(s string) (time.Time, error) { - values := strings.Split(s, "/") - - if len(values) != 2 { - return time.Time{}, errs.New("invalid date format %s, use mm/yyyy", s) - } - - month, err := strconv.ParseInt(values[0], 10, 64) +// parseYearMonth parses year and month from the provided string and returns a corresponding time.Time for the first day +// of the month. The input year and month should be iso8601 format (yyyy-mm). +func parseYearMonth(yearMonth string) (time.Time, error) { + // parse using iso8601 yyyy-mm + t, err := time.Parse("2006-01", yearMonth) if err != nil { - return time.Time{}, errs.New("can not parse month: %v", err) + return time.Time{}, errs.New("invalid date specified. accepted format is yyyy-mm: %v", err) } - year, err := strconv.ParseInt(values[1], 10, 64) - if err != nil { - return time.Time{}, errs.New("can not parse year: %v", err) - } - - date := time.Date(int(year), time.Month(month), 1, 0, 0, 0, 0, time.UTC) - if date.Year() != int(year) || date.Month() != time.Month(month) || date.Day() != 1 { - return date, errs.New("dates mismatch have %s result %s", s, date) - } - - return date, nil + return time.Date(t.Year(), t.Month(), 1, 0, 0, 0, 0, time.UTC), nil } // userData contains the uuid and email of a satellite user. diff --git a/cmd/satellite/billing_test.go b/cmd/satellite/billing_test.go new file mode 100644 index 000000000..3544463b0 --- /dev/null +++ b/cmd/satellite/billing_test.go @@ -0,0 +1,36 @@ +// Copyright (C) 2022 Storj Labs, Inc. +// See LICENSE for copying information. + +package main + +import ( + "testing" + "time" + + "github.com/stretchr/testify/require" +) + +func TestValidYearMonth(t *testing.T) { + yearMonth := "2020-12" + periodStart, err := parseYearMonth(yearMonth) + require.NoError(t, err) + require.Equal(t, 2020, periodStart.Year()) + require.Equal(t, "December", periodStart.Month().String()) + require.Equal(t, 01, periodStart.Day()) + require.Equal(t, "UTC", periodStart.Location().String()) +} + +func TestInvalidYearMonth(t *testing.T) { + invalidYearMonth := []string{ + "2020-13", + "2020-00", + "123-01", + "1999-3", + } + + for _, invalid := range invalidYearMonth { + date, err := parseYearMonth(invalid) + require.Equal(t, date, time.Time{}) + require.Error(t, err) + } +} diff --git a/cmd/satellite/main.go b/cmd/satellite/main.go index 9c7a1ae6e..82ffd7ead 100644 --- a/cmd/satellite/main.go +++ b/cmd/satellite/main.go @@ -238,6 +238,7 @@ var ( Use: "pay-invoices", Short: "pay finalized invoices", Long: "attempts payment on all open finalized invoices according to subscriptions settings.", + Args: cobra.ExactArgs(1), RunE: cmdPayCustomerInvoices, } stripeCustomerCmd = &cobra.Command{ @@ -734,52 +735,52 @@ func cmdValueAttribution(cmd *cobra.Command, args []string) (err error) { func cmdPrepareCustomerInvoiceRecords(cmd *cobra.Command, args []string) (err error) { ctx, _ := process.Ctx(cmd) - period, err := parseBillingPeriod(args[0]) + periodStart, err := parseYearMonth(args[0]) if err != nil { - return errs.New("invalid period specified: %v", err) + return err } return runBillingCmd(ctx, func(ctx context.Context, payments *stripecoinpayments.Service, _ satellite.DB) error { - return payments.PrepareInvoiceProjectRecords(ctx, period) + return payments.PrepareInvoiceProjectRecords(ctx, periodStart) }) } func cmdCreateCustomerProjectInvoiceItems(cmd *cobra.Command, args []string) (err error) { ctx, _ := process.Ctx(cmd) - period, err := parseBillingPeriod(args[0]) + periodStart, err := parseYearMonth(args[0]) if err != nil { - return errs.New("invalid period specified: %v", err) + return err } return runBillingCmd(ctx, func(ctx context.Context, payments *stripecoinpayments.Service, _ satellite.DB) error { - return payments.InvoiceApplyProjectRecords(ctx, period) + return payments.InvoiceApplyProjectRecords(ctx, periodStart) }) } func cmdCreateCustomerInvoices(cmd *cobra.Command, args []string) (err error) { ctx, _ := process.Ctx(cmd) - period, err := parseBillingPeriod(args[0]) + periodStart, err := parseYearMonth(args[0]) if err != nil { - return errs.New("invalid period specified: %v", err) + return err } return runBillingCmd(ctx, func(ctx context.Context, payments *stripecoinpayments.Service, _ satellite.DB) error { - return payments.CreateInvoices(ctx, period) + return payments.CreateInvoices(ctx, periodStart) }) } func cmdGenerateCustomerInvoices(cmd *cobra.Command, args []string) (err error) { ctx, _ := process.Ctx(cmd) - period, err := parseBillingPeriod(args[0]) + periodStart, err := parseYearMonth(args[0]) if err != nil { - return errs.New("invalid period specified: %v", err) + return err } return runBillingCmd(ctx, func(ctx context.Context, payments *stripecoinpayments.Service, _ satellite.DB) error { - return payments.GenerateInvoices(ctx, period) + return payments.GenerateInvoices(ctx, periodStart) }) } @@ -794,17 +795,17 @@ func cmdFinalizeCustomerInvoices(cmd *cobra.Command, args []string) (err error) func cmdPayCustomerInvoices(cmd *cobra.Command, args []string) (err error) { ctx, _ := process.Ctx(cmd) - t, err := time.Parse("02/01/2006", args[0]) + periodStart, err := parseYearMonth(args[0]) if err != nil { - return errs.New("invalid date specified specified: %v", err) + return err } return runBillingCmd(ctx, func(ctx context.Context, payments *stripecoinpayments.Service, _ satellite.DB) error { - err := payments.InvoiceApplyTokenBalance(ctx, t) + err := payments.InvoiceApplyTokenBalance(ctx, periodStart) if err != nil { return errs.New("error applying native token payments: %v", err) } - return payments.PayInvoices(ctx, t) + return payments.PayInvoices(ctx, periodStart) }) } diff --git a/scripts/tests/integration/test-billing.sh b/scripts/tests/integration/test-billing.sh index 41dd26107..4d4b94808 100755 --- a/scripts/tests/integration/test-billing.sh +++ b/scripts/tests/integration/test-billing.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash set -ueo pipefail -PERIOD=$(date -d "$(date +%Y-%m)-15 last month" '+%m/%Y') +PERIOD=$(date -d "$(date +%Y-%m)-15 last month" '+%Y-%m') satellite --config-dir $SATELLITE_0_DIR billing prepare-invoice-records $PERIOD satellite --config-dir $SATELLITE_0_DIR billing create-invoice-items $PERIOD satellite --config-dir $SATELLITE_0_DIR billing create-invoice-coupons $PERIOD