162 lines
5.6 KiB
Go
162 lines
5.6 KiB
Go
package processor
|
|
|
|
import (
|
|
"context"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/google/uuid"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"apskel-pos-be/internal/constants"
|
|
"apskel-pos-be/internal/entities"
|
|
"apskel-pos-be/internal/models"
|
|
"apskel-pos-be/internal/repository"
|
|
)
|
|
|
|
// walletTraceRepoFake reads the fake wallet the way the trace repository reads the
|
|
// tables.
|
|
type walletTraceRepoFake struct{ e *walletMoveEnv }
|
|
|
|
func (f walletTraceRepoFake) GetTransaction(_ context.Context, organizationID, id uuid.UUID) (*entities.WalletTransaction, error) {
|
|
for _, tx := range f.e.repo.transactions {
|
|
if tx.ID == id && tx.OrganizationID == organizationID {
|
|
c := *tx
|
|
return &c, nil
|
|
}
|
|
}
|
|
return nil, repository.ErrWalletTransactionNotFound
|
|
}
|
|
|
|
func (f walletTraceRepoFake) GetTransactions(_ context.Context, ids []uuid.UUID) ([]entities.WalletTransaction, error) {
|
|
var out []entities.WalletTransaction
|
|
for _, tx := range f.e.repo.transactions {
|
|
if containsID(ids, tx.ID) {
|
|
out = append(out, *tx)
|
|
}
|
|
}
|
|
return out, nil
|
|
}
|
|
|
|
func (f walletTraceRepoFake) ListAllocations(ctx context.Context, id uuid.UUID) ([]entities.WalletLotAllocation, error) {
|
|
return f.e.repo.ListAllocationsByTransaction(ctx, id)
|
|
}
|
|
|
|
func (f walletTraceRepoFake) ListLotsBySource(ctx context.Context, id uuid.UUID) ([]entities.WalletLot, error) {
|
|
return f.e.repo.ListLotsBySourceTransaction(ctx, id)
|
|
}
|
|
|
|
func (f walletTraceRepoFake) GetLots(ctx context.Context, ids []uuid.UUID) ([]entities.WalletLot, error) {
|
|
return f.e.repo.GetLotsByIDs(ctx, ids)
|
|
}
|
|
|
|
func (f walletTraceRepoFake) CustomerNames(_ context.Context, ids []uuid.UUID) (map[uuid.UUID]string, error) {
|
|
names := map[uuid.UUID]string{}
|
|
for _, id := range ids {
|
|
if c, ok := f.e.customers.byID[id]; ok {
|
|
names[id] = c.Name
|
|
}
|
|
}
|
|
return names, nil
|
|
}
|
|
|
|
func containsID(ids []uuid.UUID, id uuid.UUID) bool {
|
|
for _, v := range ids {
|
|
if v == id {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func findRow(t *testing.T, e *walletMoveEnv, customerID uuid.UUID, txType string) *entities.WalletTransaction {
|
|
t.Helper()
|
|
for _, tx := range e.repo.transactions {
|
|
if tx.CustomerID == customerID && tx.Type == txType {
|
|
return tx
|
|
}
|
|
}
|
|
t.Fatalf("no %s row for %s", txType, customerID)
|
|
return nil
|
|
}
|
|
|
|
// The example of ยง8: A has 100 from #ORD-1 and 50 from #ORD-2, sends 120 to B, and B
|
|
// pays 30. Tracing B's payment leads to A's order #ORD-1.
|
|
func TestWalletTrace_PaymentLeadsBackToTheSendersOrder(t *testing.T) {
|
|
e := newWalletMoveEnv(t)
|
|
a := e.member("Anita", "081200005678")
|
|
b := e.member("Budi Santoso", "081234561234")
|
|
ord1 := earn(a, 100, e.at(30*24*time.Hour))
|
|
ord1.Description = "Belanja #ORD-1"
|
|
ord2 := earn(a, 50, e.at(60*24*time.Hour))
|
|
ord2.Description = "Belanja #ORD-2"
|
|
e.credit(t, ord1)
|
|
e.credit(t, ord2)
|
|
_, err := e.transfers(nil).Transfer(e.ctx, a, sendPoints(120, "081234561234"), "482913", "key-1", models.CustomerPinRequestInfo{})
|
|
require.NoError(t, err)
|
|
payment, err := e.p.Debit(e.ctx, pay(b, 30))
|
|
require.NoError(t, err)
|
|
|
|
p := NewWalletTraceProcessor(walletTraceRepoFake{e})
|
|
trace, err := p.Trace(e.ctx, e.org, payment.Transaction.ID)
|
|
require.NoError(t, err)
|
|
|
|
assert.Equal(t, constants.WalletTxTypePayment, trace.Transaction.Type)
|
|
assert.Equal(t, "Budi Santoso", trace.Transaction.Customer.Name)
|
|
require.Len(t, trace.Lots, 1)
|
|
assert.Equal(t, int64(30), trace.Lots[0].Amount)
|
|
chain := trace.Lots[0].Chain
|
|
require.Len(t, chain, 2)
|
|
assert.Equal(t, constants.WalletTxTypeTransferIn, chain[0].Source.Type)
|
|
assert.Equal(t, b, chain[0].Source.Customer.ID)
|
|
assert.Equal(t, constants.WalletTxTypeEarn, chain[1].Source.Type)
|
|
assert.Equal(t, "Anita", chain[1].Source.Customer.Name)
|
|
assert.Equal(t, "Belanja #ORD-1", chain[1].Source.Description)
|
|
assert.Equal(t, constants.WalletRefTypeOrder, chain[1].Source.ReferenceType)
|
|
assert.Equal(t, ord1.ReferenceID, chain[1].Source.ReferenceID)
|
|
assert.Nil(t, chain[1].Lot.OriginLotID, "the chain ends at the lot the order created")
|
|
assert.Equal(t, *chain[0].Lot.OriginLotID, chain[1].Lot.ID)
|
|
}
|
|
|
|
func TestWalletTrace_DebitAndCreditOfATransfer(t *testing.T) {
|
|
e := newWalletMoveEnv(t)
|
|
a := e.member("Anita", "081200005678")
|
|
b := e.member("Budi", "081234561234")
|
|
e.credit(t, earn(a, 100, e.at(time.Hour)))
|
|
e.credit(t, earn(a, 50, nil))
|
|
_, err := e.transfers(nil).Transfer(e.ctx, a, sendPoints(120, "081234561234"), "482913", "key-1", models.CustomerPinRequestInfo{})
|
|
require.NoError(t, err)
|
|
p := NewWalletTraceProcessor(walletTraceRepoFake{e})
|
|
|
|
// The sending side took from both of A's lots, which were A's own earnings.
|
|
out, err := p.Trace(e.ctx, e.org, findRow(t, e, a, constants.WalletTxTypeTransferOut).ID)
|
|
require.NoError(t, err)
|
|
require.Len(t, out.Lots, 2)
|
|
assert.Equal(t, []int64{100, 20}, []int64{out.Lots[0].Amount, out.Lots[1].Amount})
|
|
for _, lot := range out.Lots {
|
|
require.Len(t, lot.Chain, 1)
|
|
assert.Equal(t, constants.WalletTxTypeEarn, lot.Chain[0].Source.Type)
|
|
}
|
|
|
|
// The receiving side created two lots, each carried over from one of A's.
|
|
in, err := p.Trace(e.ctx, e.org, findRow(t, e, b, constants.WalletTxTypeTransferIn).ID)
|
|
require.NoError(t, err)
|
|
require.Len(t, in.Lots, 2)
|
|
for _, lot := range in.Lots {
|
|
require.Len(t, lot.Chain, 2)
|
|
assert.Equal(t, b, lot.Chain[0].Source.Customer.ID)
|
|
assert.Equal(t, a, lot.Chain[1].Source.Customer.ID)
|
|
assert.Equal(t, lot.Chain[1].Lot.ExpiresAt, lot.Chain[0].Lot.ExpiresAt)
|
|
}
|
|
}
|
|
|
|
func TestWalletTrace_OtherOrganizationsRowsAreNotFound(t *testing.T) {
|
|
e := newWalletMoveEnv(t)
|
|
a := e.member("Anita", "081200005678")
|
|
res := e.credit(t, earn(a, 10, nil))
|
|
|
|
_, err := NewWalletTraceProcessor(walletTraceRepoFake{e}).Trace(e.ctx, uuid.New(), res.Transaction.ID)
|
|
assert.ErrorIs(t, err, repository.ErrWalletTransactionNotFound)
|
|
}
|