service backend
This commit is contained in:
336
api/ledger/storage/mongo/store/outbox_test.go
Normal file
336
api/ledger/storage/mongo/store/outbox_test.go
Normal file
@@ -0,0 +1,336 @@
|
||||
package store
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/tech/sendico/ledger/storage/model"
|
||||
"github.com/tech/sendico/pkg/db/repository"
|
||||
"github.com/tech/sendico/pkg/db/repository/builder"
|
||||
rd "github.com/tech/sendico/pkg/db/repository/decoder"
|
||||
"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.mongodb.org/mongo-driver/mongo"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
func TestOutboxStore_Create(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
logger := zap.NewNop()
|
||||
|
||||
t.Run("Success", func(t *testing.T) {
|
||||
var insertedEvent *model.OutboxEvent
|
||||
stub := &repositoryStub{
|
||||
InsertFunc: func(ctx context.Context, object storable.Storable, _ builder.Query) error {
|
||||
insertedEvent = object.(*model.OutboxEvent)
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
store := &outboxStore{logger: logger, repo: stub}
|
||||
event := &model.OutboxEvent{
|
||||
EventID: "evt_12345",
|
||||
Subject: "ledger.entry.created",
|
||||
Payload: []byte(`{"entryId":"123"}`),
|
||||
Status: model.OutboxStatusPending,
|
||||
}
|
||||
|
||||
err := store.Create(ctx, event)
|
||||
|
||||
require.NoError(t, err)
|
||||
assert.NotNil(t, insertedEvent)
|
||||
assert.Equal(t, "evt_12345", insertedEvent.EventID)
|
||||
assert.Equal(t, "ledger.entry.created", insertedEvent.Subject)
|
||||
assert.Equal(t, model.OutboxStatusPending, insertedEvent.Status)
|
||||
})
|
||||
|
||||
t.Run("NilEvent", func(t *testing.T) {
|
||||
stub := &repositoryStub{}
|
||||
store := &outboxStore{logger: logger, repo: stub}
|
||||
|
||||
err := store.Create(ctx, nil)
|
||||
|
||||
require.Error(t, err)
|
||||
assert.True(t, errors.Is(err, merrors.ErrInvalidArg))
|
||||
})
|
||||
|
||||
t.Run("DuplicateEventID", func(t *testing.T) {
|
||||
stub := &repositoryStub{
|
||||
InsertFunc: func(ctx context.Context, object storable.Storable, _ builder.Query) error {
|
||||
return mongo.WriteException{
|
||||
WriteErrors: []mongo.WriteError{
|
||||
{Code: 11000}, // Duplicate key error
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
store := &outboxStore{logger: logger, repo: stub}
|
||||
event := &model.OutboxEvent{
|
||||
EventID: "duplicate_event",
|
||||
Subject: "test.subject",
|
||||
Status: model.OutboxStatusPending,
|
||||
}
|
||||
|
||||
err := store.Create(ctx, event)
|
||||
|
||||
require.Error(t, err)
|
||||
assert.True(t, errors.Is(err, merrors.ErrDataConflict))
|
||||
})
|
||||
|
||||
t.Run("InsertError", func(t *testing.T) {
|
||||
expectedErr := errors.New("database error")
|
||||
stub := &repositoryStub{
|
||||
InsertFunc: func(ctx context.Context, object storable.Storable, _ builder.Query) error {
|
||||
return expectedErr
|
||||
},
|
||||
}
|
||||
|
||||
store := &outboxStore{logger: logger, repo: stub}
|
||||
event := &model.OutboxEvent{
|
||||
EventID: "evt_123",
|
||||
Subject: "test.subject",
|
||||
}
|
||||
|
||||
err := store.Create(ctx, event)
|
||||
|
||||
require.Error(t, err)
|
||||
assert.Equal(t, expectedErr, err)
|
||||
})
|
||||
}
|
||||
|
||||
func TestOutboxStore_ListPending(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
logger := zap.NewNop()
|
||||
|
||||
t.Run("Success", func(t *testing.T) {
|
||||
called := false
|
||||
stub := &repositoryStub{
|
||||
FindManyByFilterFunc: func(ctx context.Context, _ builder.Query, decoder rd.DecodingFunc) error {
|
||||
called = true
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
store := &outboxStore{logger: logger, repo: stub}
|
||||
results, err := store.ListPending(ctx, 10)
|
||||
|
||||
require.NoError(t, err)
|
||||
assert.True(t, called)
|
||||
assert.NotNil(t, results)
|
||||
})
|
||||
|
||||
t.Run("EmptyResult", func(t *testing.T) {
|
||||
stub := &repositoryStub{
|
||||
FindManyByFilterFunc: func(ctx context.Context, _ builder.Query, decoder rd.DecodingFunc) error {
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
store := &outboxStore{logger: logger, repo: stub}
|
||||
results, err := store.ListPending(ctx, 10)
|
||||
|
||||
require.NoError(t, err)
|
||||
assert.Len(t, results, 0)
|
||||
})
|
||||
|
||||
t.Run("WithLimit", func(t *testing.T) {
|
||||
called := false
|
||||
stub := &repositoryStub{
|
||||
FindManyByFilterFunc: func(ctx context.Context, _ builder.Query, decoder rd.DecodingFunc) error {
|
||||
called = true
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
store := &outboxStore{logger: logger, repo: stub}
|
||||
results, err := store.ListPending(ctx, 3)
|
||||
|
||||
require.NoError(t, err)
|
||||
assert.True(t, called)
|
||||
assert.NotNil(t, results)
|
||||
})
|
||||
|
||||
t.Run("FindError", func(t *testing.T) {
|
||||
expectedErr := errors.New("database error")
|
||||
stub := &repositoryStub{
|
||||
FindManyByFilterFunc: func(ctx context.Context, _ builder.Query, decoder rd.DecodingFunc) error {
|
||||
return expectedErr
|
||||
},
|
||||
}
|
||||
|
||||
store := &outboxStore{logger: logger, repo: stub}
|
||||
results, err := store.ListPending(ctx, 10)
|
||||
|
||||
require.Error(t, err)
|
||||
assert.Nil(t, results)
|
||||
assert.Equal(t, expectedErr, err)
|
||||
})
|
||||
}
|
||||
|
||||
func TestOutboxStore_MarkSent(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
logger := zap.NewNop()
|
||||
eventRef := primitive.NewObjectID()
|
||||
sentTime := time.Now()
|
||||
|
||||
t.Run("Success", func(t *testing.T) {
|
||||
var patchedID primitive.ObjectID
|
||||
stub := &repositoryStub{
|
||||
PatchFunc: func(ctx context.Context, id primitive.ObjectID, _ repository.PatchDoc) error {
|
||||
patchedID = id
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
store := &outboxStore{logger: logger, repo: stub}
|
||||
err := store.MarkSent(ctx, eventRef, sentTime)
|
||||
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, eventRef, patchedID)
|
||||
})
|
||||
|
||||
t.Run("ZeroEventID", func(t *testing.T) {
|
||||
stub := &repositoryStub{}
|
||||
store := &outboxStore{logger: logger, repo: stub}
|
||||
|
||||
err := store.MarkSent(ctx, primitive.NilObjectID, sentTime)
|
||||
|
||||
require.Error(t, err)
|
||||
assert.True(t, errors.Is(err, merrors.ErrInvalidArg))
|
||||
})
|
||||
|
||||
t.Run("PatchError", func(t *testing.T) {
|
||||
expectedErr := errors.New("database error")
|
||||
stub := &repositoryStub{
|
||||
PatchFunc: func(ctx context.Context, id primitive.ObjectID, _ repository.PatchDoc) error {
|
||||
return expectedErr
|
||||
},
|
||||
}
|
||||
|
||||
store := &outboxStore{logger: logger, repo: stub}
|
||||
err := store.MarkSent(ctx, eventRef, sentTime)
|
||||
|
||||
require.Error(t, err)
|
||||
assert.Equal(t, expectedErr, err)
|
||||
})
|
||||
}
|
||||
|
||||
func TestOutboxStore_MarkFailed(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
logger := zap.NewNop()
|
||||
eventRef := primitive.NewObjectID()
|
||||
|
||||
t.Run("Success", func(t *testing.T) {
|
||||
var patchedID primitive.ObjectID
|
||||
stub := &repositoryStub{
|
||||
PatchFunc: func(ctx context.Context, id primitive.ObjectID, _ repository.PatchDoc) error {
|
||||
patchedID = id
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
store := &outboxStore{logger: logger, repo: stub}
|
||||
err := store.MarkFailed(ctx, eventRef)
|
||||
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, eventRef, patchedID)
|
||||
})
|
||||
|
||||
t.Run("ZeroEventID", func(t *testing.T) {
|
||||
stub := &repositoryStub{}
|
||||
store := &outboxStore{logger: logger, repo: stub}
|
||||
|
||||
err := store.MarkFailed(ctx, primitive.NilObjectID)
|
||||
|
||||
require.Error(t, err)
|
||||
assert.True(t, errors.Is(err, merrors.ErrInvalidArg))
|
||||
})
|
||||
|
||||
t.Run("PatchError", func(t *testing.T) {
|
||||
expectedErr := errors.New("database error")
|
||||
stub := &repositoryStub{
|
||||
PatchFunc: func(ctx context.Context, id primitive.ObjectID, _ repository.PatchDoc) error {
|
||||
return expectedErr
|
||||
},
|
||||
}
|
||||
|
||||
store := &outboxStore{logger: logger, repo: stub}
|
||||
err := store.MarkFailed(ctx, eventRef)
|
||||
|
||||
require.Error(t, err)
|
||||
assert.Equal(t, expectedErr, err)
|
||||
})
|
||||
}
|
||||
|
||||
func TestOutboxStore_IncrementAttempts(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
logger := zap.NewNop()
|
||||
eventRef := primitive.NewObjectID()
|
||||
|
||||
t.Run("Success", func(t *testing.T) {
|
||||
var patchedID primitive.ObjectID
|
||||
stub := &repositoryStub{
|
||||
PatchFunc: func(ctx context.Context, id primitive.ObjectID, _ repository.PatchDoc) error {
|
||||
patchedID = id
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
store := &outboxStore{logger: logger, repo: stub}
|
||||
err := store.IncrementAttempts(ctx, eventRef)
|
||||
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, eventRef, patchedID)
|
||||
})
|
||||
|
||||
t.Run("ZeroEventID", func(t *testing.T) {
|
||||
stub := &repositoryStub{}
|
||||
store := &outboxStore{logger: logger, repo: stub}
|
||||
|
||||
err := store.IncrementAttempts(ctx, primitive.NilObjectID)
|
||||
|
||||
require.Error(t, err)
|
||||
assert.True(t, errors.Is(err, merrors.ErrInvalidArg))
|
||||
})
|
||||
|
||||
t.Run("PatchError", func(t *testing.T) {
|
||||
expectedErr := errors.New("database error")
|
||||
stub := &repositoryStub{
|
||||
PatchFunc: func(ctx context.Context, id primitive.ObjectID, _ repository.PatchDoc) error {
|
||||
return expectedErr
|
||||
},
|
||||
}
|
||||
|
||||
store := &outboxStore{logger: logger, repo: stub}
|
||||
err := store.IncrementAttempts(ctx, eventRef)
|
||||
|
||||
require.Error(t, err)
|
||||
assert.Equal(t, expectedErr, err)
|
||||
})
|
||||
|
||||
t.Run("MultipleIncrements", func(t *testing.T) {
|
||||
var callCount int
|
||||
stub := &repositoryStub{
|
||||
PatchFunc: func(ctx context.Context, id primitive.ObjectID, _ repository.PatchDoc) error {
|
||||
callCount++
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
store := &outboxStore{logger: logger, repo: stub}
|
||||
|
||||
// Simulate multiple retry attempts
|
||||
for i := 0; i < 3; i++ {
|
||||
err := store.IncrementAttempts(ctx, eventRef)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
assert.Equal(t, 3, callCount)
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user