package store import ( "context" "errors" "testing" "github.com/tech/sendico/ledger/storage" "github.com/tech/sendico/ledger/storage/model" "github.com/tech/sendico/pkg/db/repository/builder" "github.com/tech/sendico/pkg/db/storable" "github.com/tech/sendico/pkg/merrors" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.mongodb.org/mongo-driver/bson/primitive" "go.uber.org/zap" ) func TestBalancesStore_Get(t *testing.T) { ctx := context.Background() logger := zap.NewNop() t.Run("Success", func(t *testing.T) { accountRef := primitive.NewObjectID() stub := &repositoryStub{ FindOneByFilterFunc: func(ctx context.Context, _ builder.Query, result storable.Storable) error { balance := result.(*model.AccountBalance) balance.AccountRef = accountRef balance.Balance = "1500.50" balance.Version = 10 return nil }, } store := &balancesStore{logger: logger, repo: stub} result, err := store.Get(ctx, accountRef) require.NoError(t, err) assert.NotNil(t, result) assert.Equal(t, accountRef, result.AccountRef) assert.Equal(t, "1500.50", result.Balance) assert.Equal(t, int64(10), result.Version) }) t.Run("ZeroAccountID", func(t *testing.T) { stub := &repositoryStub{} store := &balancesStore{logger: logger, repo: stub} result, err := store.Get(ctx, primitive.NilObjectID) require.Error(t, err) assert.Nil(t, result) assert.True(t, errors.Is(err, merrors.ErrInvalidArg)) }) t.Run("NotFound", func(t *testing.T) { accountRef := primitive.NewObjectID() stub := &repositoryStub{ FindOneByFilterFunc: func(ctx context.Context, _ builder.Query, result storable.Storable) error { return merrors.ErrNoData }, } store := &balancesStore{logger: logger, repo: stub} result, err := store.Get(ctx, accountRef) require.Error(t, err) assert.Nil(t, result) assert.True(t, errors.Is(err, storage.ErrBalanceNotFound)) }) t.Run("FindError", func(t *testing.T) { accountRef := primitive.NewObjectID() expectedErr := errors.New("database error") stub := &repositoryStub{ FindOneByFilterFunc: func(ctx context.Context, _ builder.Query, result storable.Storable) error { return expectedErr }, } store := &balancesStore{logger: logger, repo: stub} result, err := store.Get(ctx, accountRef) require.Error(t, err) assert.Nil(t, result) assert.Equal(t, expectedErr, err) }) } func TestBalancesStore_Upsert(t *testing.T) { ctx := context.Background() logger := zap.NewNop() t.Run("Insert_NewBalance", func(t *testing.T) { accountRef := primitive.NewObjectID() var insertedBalance *model.AccountBalance stub := &repositoryStub{ FindOneByFilterFunc: func(ctx context.Context, _ builder.Query, result storable.Storable) error { return merrors.ErrNoData // Balance doesn't exist }, InsertFunc: func(ctx context.Context, object storable.Storable, _ builder.Query) error { insertedBalance = object.(*model.AccountBalance) return nil }, } store := &balancesStore{logger: logger, repo: stub} balance := &model.AccountBalance{ AccountRef: accountRef, Balance: "1000.00", Version: 1, } err := store.Upsert(ctx, balance) require.NoError(t, err) assert.NotNil(t, insertedBalance) assert.Equal(t, "1000.00", insertedBalance.Balance) }) t.Run("Update_ExistingBalance", func(t *testing.T) { accountRef := primitive.NewObjectID() existingID := primitive.NewObjectID() var updatedBalance *model.AccountBalance stub := &repositoryStub{ FindOneByFilterFunc: func(ctx context.Context, _ builder.Query, result storable.Storable) error { existing := result.(*model.AccountBalance) existing.SetID(existingID) existing.AccountRef = accountRef existing.Balance = "500.00" existing.Version = 5 return nil }, UpdateFunc: func(ctx context.Context, object storable.Storable) error { updatedBalance = object.(*model.AccountBalance) return nil }, } store := &balancesStore{logger: logger, repo: stub} balance := &model.AccountBalance{ AccountRef: accountRef, Balance: "1500.00", Version: 6, } err := store.Upsert(ctx, balance) require.NoError(t, err) assert.NotNil(t, updatedBalance) assert.Equal(t, existingID, *updatedBalance.GetID()) assert.Equal(t, "1500.00", updatedBalance.Balance) assert.Equal(t, int64(6), updatedBalance.Version) }) t.Run("NilBalance", func(t *testing.T) { stub := &repositoryStub{} store := &balancesStore{logger: logger, repo: stub} err := store.Upsert(ctx, nil) require.Error(t, err) assert.True(t, errors.Is(err, merrors.ErrInvalidArg)) }) t.Run("ZeroAccountID", func(t *testing.T) { stub := &repositoryStub{} store := &balancesStore{logger: logger, repo: stub} balance := &model.AccountBalance{ AccountRef: primitive.NilObjectID, Balance: "100.00", } err := store.Upsert(ctx, balance) require.Error(t, err) assert.True(t, errors.Is(err, merrors.ErrInvalidArg)) }) t.Run("FindError", func(t *testing.T) { accountRef := primitive.NewObjectID() expectedErr := errors.New("database error") stub := &repositoryStub{ FindOneByFilterFunc: func(ctx context.Context, _ builder.Query, result storable.Storable) error { return expectedErr }, } store := &balancesStore{logger: logger, repo: stub} balance := &model.AccountBalance{ AccountRef: accountRef, Balance: "100.00", } err := store.Upsert(ctx, balance) require.Error(t, err) assert.Equal(t, expectedErr, err) }) t.Run("InsertError", func(t *testing.T) { accountRef := primitive.NewObjectID() expectedErr := errors.New("insert error") stub := &repositoryStub{ FindOneByFilterFunc: func(ctx context.Context, _ builder.Query, result storable.Storable) error { return merrors.ErrNoData // Balance doesn't exist }, InsertFunc: func(ctx context.Context, object storable.Storable, _ builder.Query) error { return expectedErr }, } store := &balancesStore{logger: logger, repo: stub} balance := &model.AccountBalance{ AccountRef: accountRef, Balance: "100.00", } err := store.Upsert(ctx, balance) require.Error(t, err) assert.Equal(t, expectedErr, err) }) t.Run("UpdateError", func(t *testing.T) { accountRef := primitive.NewObjectID() existingID := primitive.NewObjectID() expectedErr := errors.New("update error") stub := &repositoryStub{ FindOneByFilterFunc: func(ctx context.Context, _ builder.Query, result storable.Storable) error { existing := result.(*model.AccountBalance) existing.SetID(existingID) existing.AccountRef = accountRef existing.Balance = "500.00" return nil }, UpdateFunc: func(ctx context.Context, object storable.Storable) error { return expectedErr }, } store := &balancesStore{logger: logger, repo: stub} balance := &model.AccountBalance{ AccountRef: accountRef, Balance: "1500.00", } err := store.Upsert(ctx, balance) require.Error(t, err) assert.Equal(t, expectedErr, err) }) } func TestBalancesStore_IncrementBalance(t *testing.T) { ctx := context.Background() logger := zap.NewNop() t.Run("NotImplemented", func(t *testing.T) { accountRef := primitive.NewObjectID() stub := &repositoryStub{} store := &balancesStore{logger: logger, repo: stub} err := store.IncrementBalance(ctx, accountRef, "100.00") require.Error(t, err) assert.True(t, errors.Is(err, merrors.ErrNotImplemented)) }) t.Run("ZeroAccountID", func(t *testing.T) { stub := &repositoryStub{} store := &balancesStore{logger: logger, repo: stub} err := store.IncrementBalance(ctx, primitive.NilObjectID, "100.00") require.Error(t, err) assert.True(t, errors.Is(err, merrors.ErrInvalidArg)) }) }