openmeter / ledger /validations_test.go
Leon4gr45's picture
Upload folder using huggingface_hub (part 5)
cee2387 verified
Raw
History Blame Contribute Delete
5.27 kB
package ledger_test
import (
"testing"
"time"
"github.com/alpacahq/alpacadecimal"
"github.com/samber/mo"
"github.com/stretchr/testify/require"
"github.com/openmeterio/openmeter/openmeter/ledger"
ledgeraccount "github.com/openmeterio/openmeter/openmeter/ledger/account"
"github.com/openmeterio/openmeter/openmeter/ledger/transactions/testutils"
"github.com/openmeterio/openmeter/pkg/currencyx"
"github.com/openmeterio/openmeter/pkg/models"
)
func TestValidateTransactionInputEntryAmountPrecision(t *testing.T) {
tests := []struct {
name string
currency currencyx.Code
amount string
wantErr bool
}{
{
name: "USD accepts cents",
currency: currencyx.Code("USD"),
amount: "10.01",
},
{
name: "USD rejects sub-cent amount",
currency: currencyx.Code("USD"),
amount: "10.001",
wantErr: true,
},
{
name: "USD accepts negative cents",
currency: currencyx.Code("USD"),
amount: "-10.01",
},
{
name: "JPY accepts whole amount",
currency: currencyx.Code("JPY"),
amount: "10",
},
{
name: "JPY rejects fractional amount",
currency: currencyx.Code("JPY"),
amount: "10.1",
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
amount := mustDecimal(t, tt.amount)
address := mustPostingAddress(t, tt.currency)
txInput := &testutils.AnyTransactionInput{
BookedAtValue: time.Now(),
EntryInputsValues: []*testutils.AnyEntryInput{
{
Address: address,
AmountValue: amount,
},
{
Address: address,
AmountValue: amount.Neg(),
},
},
}
err := ledger.ValidateTransactionInput(t.Context(), txInput)
if !tt.wantErr {
require.NoError(t, err)
return
}
require.Error(t, err)
require.ErrorIs(t, err, ledger.ErrTransactionAmountInvalid)
issues, issueErr := models.AsValidationIssues(err)
require.NoError(t, issueErr)
require.Len(t, issues, 1)
require.Equal(t, ledger.ErrCodeTransactionAmountInvalid, issues[0].Code())
attrs := issues[0].Attributes()
require.Equal(t, "amount_not_rounded_to_currency_precision", attrs["reason"])
currencyCode, ok := (attrs["currency"]).(currencyx.Code)
require.True(t, ok, "expected currency to be a currencyx.Code, got %T", attrs["currency"])
require.Equal(t, tt.currency, currencyCode)
require.Equal(t, amount.String(), attrs["amount"])
require.NotEmpty(t, attrs["rounded_amount"])
})
}
}
func TestListTransactionsInputValidateRouteFilter(t *testing.T) {
costBasis := alpacadecimal.NewFromFloat(0.7)
taxCode := "vat"
taxBehavior := ledger.TaxBehaviorInclusive
creditPriority := 1
authStatus := ledger.TransactionAuthorizationStatusAuthorized
tests := []struct {
name string
route ledger.RouteFilter
wantErr bool
}{
{
name: "currency route filter is supported",
route: ledger.RouteFilter{
Currency: currencyx.Code("USD"),
},
},
{
name: "exact features route filter is supported",
route: ledger.RouteFilter{
Features: mo.Some([]string{"feature-a"}),
},
},
{
name: "match feature route filter is supported",
route: ledger.RouteFilter{
MatchFeature: "feature-a",
},
},
{
name: "cost basis route filter is rejected",
route: ledger.RouteFilter{
CostBasis: mo.Some(&costBasis),
},
wantErr: true,
},
{
name: "tax code route filter is rejected",
route: ledger.RouteFilter{
TaxCode: mo.Some(&taxCode),
},
wantErr: true,
},
{
name: "tax behavior route filter is rejected",
route: ledger.RouteFilter{
TaxBehavior: mo.Some(&taxBehavior),
},
wantErr: true,
},
{
name: "credit priority route filter is rejected",
route: ledger.RouteFilter{
CreditPriority: &creditPriority,
},
wantErr: true,
},
{
name: "transaction authorization route filter is rejected",
route: ledger.RouteFilter{
TransactionAuthorizationStatus: &authStatus,
},
wantErr: true,
},
{
name: "exact features and match feature cannot be combined",
route: ledger.RouteFilter{
Features: mo.Some([]string{"feature-a"}),
MatchFeature: "feature-a",
},
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err := ledger.ListTransactionsInput{
Namespace: "ns-test",
Limit: 1,
Route: tt.route,
}.Validate()
if tt.wantErr {
require.Error(t, err)
require.ErrorIs(t, err, ledger.ErrListTransactionsInputInvalid)
return
}
require.NoError(t, err)
})
}
}
func mustPostingAddress(t *testing.T, currency currencyx.Code) ledger.PostingAddress {
t.Helper()
route := ledger.Route{Currency: currency}
key, err := ledger.BuildRoutingKey(route)
require.NoError(t, err)
address, err := ledgeraccount.NewAddressFromData(ledgeraccount.AddressData{
SubAccountID: "sub_" + string(currency),
AccountType: ledger.AccountTypeCustomerFBO,
Route: route,
RouteID: "route_" + string(currency),
RoutingKey: key,
})
require.NoError(t, err)
return address
}
func mustDecimal(t *testing.T, raw string) alpacadecimal.Decimal {
t.Helper()
value, err := alpacadecimal.NewFromString(raw)
require.NoError(t, err)
return value
}