Files
sendico/api/fx/storage/mongo/store/quotes_test.go
Stephan D 62a6631b9a
All checks were successful
ci/woodpecker/push/db Pipeline was successful
ci/woodpecker/push/nats Pipeline was successful
service backend
2025-11-07 18:35:26 +01:00

185 lines
5.5 KiB
Go

package store
import (
"context"
"errors"
"testing"
"time"
"github.com/tech/sendico/fx/storage"
"github.com/tech/sendico/fx/storage/model"
"github.com/tech/sendico/pkg/db/repository/builder"
"github.com/tech/sendico/pkg/db/storable"
"github.com/tech/sendico/pkg/merrors"
"go.uber.org/zap"
)
func TestQuotesStoreIssue(t *testing.T) {
ctx := context.Background()
var inserted *model.Quote
repo := &repoStub{
insertFn: func(_ context.Context, obj storable.Storable, _ builder.Query) error {
inserted = cloneQuote(t, obj)
return nil
},
}
store := &quotesStore{logger: zap.NewNop(), repo: repo, txFactory: &txFactoryStub{}}
quote := &model.Quote{QuoteRef: "q1"}
if err := store.Issue(ctx, quote); err != nil {
t.Fatalf("unexpected error: %v", err)
}
if inserted == nil || inserted.Status != model.QuoteStatusIssued {
t.Fatalf("expected issued quote to be inserted")
}
}
func TestQuotesStoreIssueSetsExpiryDate(t *testing.T) {
ctx := context.Background()
var inserted *model.Quote
repo := &repoStub{
insertFn: func(_ context.Context, obj storable.Storable, _ builder.Query) error {
inserted = cloneQuote(t, obj)
return nil
},
}
store := &quotesStore{logger: zap.NewNop(), repo: repo, txFactory: &txFactoryStub{}}
expiry := time.Now().Add(2 * time.Minute).UnixMilli()
quote := &model.Quote{
QuoteRef: "q1",
ExpiresAtUnixMs: expiry,
}
if err := store.Issue(ctx, quote); err != nil {
t.Fatalf("unexpected error: %v", err)
}
if inserted == nil || inserted.ExpiresAt == nil {
t.Fatalf("expected expiry timestamp to be populated")
}
if inserted.ExpiresAt.UnixMilli() != expiry {
t.Fatalf("expected expiry to equal %d, got %d", expiry, inserted.ExpiresAt.UnixMilli())
}
}
func TestQuotesStoreIssueInvalidInput(t *testing.T) {
store := &quotesStore{logger: zap.NewNop(), repo: &repoStub{}, txFactory: &txFactoryStub{}}
if err := store.Issue(context.Background(), nil); !errors.Is(err, merrors.ErrInvalidArg) {
t.Fatalf("expected invalid argument error, got %v", err)
}
}
func TestQuotesStoreConsumeSuccess(t *testing.T) {
ctx := context.Background()
now := time.Now()
ledgerRef := "ledger-1"
stored := &model.Quote{
QuoteRef: "q1",
Firm: true,
Status: model.QuoteStatusIssued,
ExpiresAtUnixMs: now.Add(5 * time.Minute).UnixMilli(),
}
var updated *model.Quote
repo := &repoStub{
findOneFn: func(_ context.Context, _ builder.Query, result storable.Storable) error {
quote := result.(*model.Quote)
*quote = *stored
return nil
},
updateFn: func(_ context.Context, obj storable.Storable) error {
updated = cloneQuote(t, obj)
return nil
},
}
factory := &txFactoryStub{}
store := &quotesStore{logger: zap.NewNop(), repo: repo, txFactory: factory}
res, err := store.Consume(ctx, "q1", ledgerRef, now)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if res == nil || res.Status != model.QuoteStatusConsumed {
t.Fatalf("expected consumed quote")
}
if updated == nil || updated.ConsumedByLedgerTxnRef != ledgerRef {
t.Fatalf("expected update with ledger ref")
}
}
func TestQuotesStoreConsumeExpired(t *testing.T) {
ctx := context.Background()
stored := &model.Quote{
QuoteRef: "q1",
Firm: true,
Status: model.QuoteStatusIssued,
ExpiresAtUnixMs: time.Now().Add(-time.Minute).UnixMilli(),
}
var updated *model.Quote
repo := &repoStub{
findOneFn: func(_ context.Context, _ builder.Query, result storable.Storable) error {
quote := result.(*model.Quote)
*quote = *stored
return nil
},
updateFn: func(_ context.Context, obj storable.Storable) error {
updated = cloneQuote(t, obj)
return nil
},
}
factory := &txFactoryStub{}
store := &quotesStore{logger: zap.NewNop(), repo: repo, txFactory: factory}
_, err := store.Consume(ctx, "q1", "ledger", time.Now())
if !errors.Is(err, storage.ErrQuoteExpired) {
t.Fatalf("expected ErrQuoteExpired, got %v", err)
}
if updated == nil || updated.Status != model.QuoteStatusExpired {
t.Fatalf("expected quote marked expired")
}
}
func TestQuotesStoreExpireIssuedBefore(t *testing.T) {
repo := &repoStub{
patchManyFn: func(context.Context, builder.Query, builder.Patch) (int, error) {
return 3, nil
},
}
store := &quotesStore{logger: zap.NewNop(), repo: repo, txFactory: &txFactoryStub{}}
count, err := store.ExpireIssuedBefore(context.Background(), time.Now())
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if count != 3 {
t.Fatalf("expected 3 expired quotes, got %d", count)
}
}
func TestQuotesStoreExpireZeroCutoff(t *testing.T) {
store := &quotesStore{logger: zap.NewNop(), repo: &repoStub{}, txFactory: &txFactoryStub{}}
if _, err := store.ExpireIssuedBefore(context.Background(), time.Time{}); !errors.Is(err, merrors.ErrInvalidArg) {
t.Fatalf("expected invalid argument error")
}
}
func TestQuotesStoreGetByRefNotFound(t *testing.T) {
repo := &repoStub{
findOneFn: func(context.Context, builder.Query, storable.Storable) error {
return merrors.ErrNoData
},
}
store := &quotesStore{logger: zap.NewNop(), repo: repo, txFactory: &txFactoryStub{}}
if _, err := store.GetByRef(context.Background(), "missing"); !errors.Is(err, merrors.ErrNoData) {
t.Fatalf("expected ErrNoData, got %v", err)
}
}
func TestQuotesStoreGetByRefInvalid(t *testing.T) {
store := &quotesStore{logger: zap.NewNop(), repo: &repoStub{}, txFactory: &txFactoryStub{}}
if _, err := store.GetByRef(context.Background(), ""); !errors.Is(err, merrors.ErrInvalidArg) {
t.Fatalf("expected invalid argument error")
}
}