124 lines
3.8 KiB
Go
124 lines
3.8 KiB
Go
package grpcadapter
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"testing"
|
|
"time"
|
|
|
|
applicationledger "gl/application/ledger"
|
|
domain "gl/domain/ledger"
|
|
ledgerv1 "gl/gen/ledger/v1"
|
|
|
|
"google.golang.org/grpc/codes"
|
|
"google.golang.org/grpc/status"
|
|
"google.golang.org/protobuf/types/known/timestamppb"
|
|
)
|
|
|
|
type ledgerRepositoryStub struct {
|
|
journal domain.Journal
|
|
}
|
|
|
|
func (r *ledgerRepositoryStub) Append(_ context.Context, journal domain.Journal) (domain.AppendResult, error) {
|
|
journal.RecordedAt = time.Unix(2, 0).UTC()
|
|
r.journal = journal
|
|
return domain.AppendResult{JournalID: journal.ID}, nil
|
|
}
|
|
|
|
func (r *ledgerRepositoryStub) AppendEvent(_ context.Context, event domain.TransactionEvent) (domain.TransactionEvent, bool, error) {
|
|
event.RecordedAt = time.Unix(2, 0).UTC()
|
|
return event, false, nil
|
|
}
|
|
|
|
func (r *ledgerRepositoryStub) GetByID(context.Context, string) (domain.Journal, error) {
|
|
return r.journal, nil
|
|
}
|
|
|
|
func (r *ledgerRepositoryStub) GetByIdempotencyKey(context.Context, string) (domain.Journal, error) {
|
|
return r.journal, nil
|
|
}
|
|
|
|
func (r *ledgerRepositoryStub) List(context.Context, domain.JournalFilter) ([]domain.Journal, error) {
|
|
return []domain.Journal{r.journal}, nil
|
|
}
|
|
|
|
func (r *ledgerRepositoryStub) Balance(context.Context, domain.AccountReference, time.Time) (domain.Amount, error) {
|
|
return domain.ParseAmount("12.5")
|
|
}
|
|
|
|
func TestAppendJournalMapsProtoToExactDomainAndBack(t *testing.T) {
|
|
repository := &ledgerRepositoryStub{}
|
|
service := applicationledger.NewService(repository, func() (string, error) {
|
|
return "11111111-1111-4111-8111-111111111111", nil
|
|
})
|
|
handler := NewHandler(nil, service)
|
|
|
|
response, err := handler.AppendJournal(context.Background(), &ledgerv1.AppendJournalRequest{
|
|
SourceService: "wallet",
|
|
IdempotencyKey: "wallet:1:internal-transfer:v1",
|
|
SourceTransactionId: "1",
|
|
EffectKind: "internal-transfer",
|
|
EventVersion: 1,
|
|
OccurredAt: timestamppb.New(time.Unix(1, 0).UTC()),
|
|
Entries: []*ledgerv1.JournalEntry{
|
|
{
|
|
LineNumber: 1,
|
|
Account: &ledgerv1.AccountReference{
|
|
AccountClass: ledgerv1.AccountClass_ACCOUNT_CLASS_USER_AVAILABLE,
|
|
OwnerType: "user",
|
|
OwnerId: "1",
|
|
AssetId: 5,
|
|
},
|
|
Amount: "-1.2500",
|
|
},
|
|
{
|
|
LineNumber: 2,
|
|
Account: &ledgerv1.AccountReference{
|
|
AccountClass: ledgerv1.AccountClass_ACCOUNT_CLASS_USER_AVAILABLE,
|
|
OwnerType: "user",
|
|
OwnerId: "2",
|
|
AssetId: 5,
|
|
},
|
|
Amount: "1.25",
|
|
},
|
|
},
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if got := response.GetJournal().GetEntries()[0].GetAmount(); got != "-1.25" {
|
|
t.Fatalf("unexpected canonical amount: %q", got)
|
|
}
|
|
if response.GetJournal().GetPayloadHash() == "" {
|
|
t.Fatal("payload hash is missing")
|
|
}
|
|
}
|
|
|
|
func TestAppendJournalRejectsMissingTimestamp(t *testing.T) {
|
|
handler := NewHandler(nil, nil)
|
|
_, err := handler.AppendJournal(context.Background(), &ledgerv1.AppendJournalRequest{})
|
|
if status.Code(err) != codes.InvalidArgument {
|
|
t.Fatalf("expected InvalidArgument, got %v", err)
|
|
}
|
|
}
|
|
|
|
func TestRPCErrorMapping(t *testing.T) {
|
|
for _, testCase := range []struct {
|
|
err error
|
|
code codes.Code
|
|
}{
|
|
{err: applicationledger.ErrInvalidArgument, code: codes.InvalidArgument},
|
|
{err: domain.ErrNotFound, code: codes.NotFound},
|
|
{err: domain.ErrIdempotencyConflict, code: codes.AlreadyExists},
|
|
{err: domain.ErrIncompleteJournal, code: codes.FailedPrecondition},
|
|
{err: domain.ErrAlreadyReversed, code: codes.FailedPrecondition},
|
|
{err: context.Canceled, code: codes.Canceled},
|
|
{err: context.DeadlineExceeded, code: codes.DeadlineExceeded},
|
|
{err: errors.New("database details must not escape"), code: codes.Internal},
|
|
} {
|
|
if got := status.Code(rpcError(testCase.err)); got != testCase.code {
|
|
t.Fatalf("rpcError(%v) = %v, want %v", testCase.err, got, testCase.code)
|
|
}
|
|
}
|
|
}
|