package explorer import ( "context" "fmt" "testing" "time" "gl/domain/ledger" ) type repositoryStub struct { stats Stats journals []ledger.Journal transactions []ledger.Journal journal ledger.Journal journalErr error holders []Holder balance ledger.Amount lastFilter *ledger.JournalFilter } func (r repositoryStub) Stats(context.Context) (Stats, error) { return r.stats, nil } func (r repositoryStub) List(_ context.Context, filter ledger.JournalFilter) ([]ledger.Journal, error) { if r.lastFilter != nil { *r.lastFilter = filter } return r.journals, nil } func (r repositoryStub) GetByTransactionHash(context.Context, string) ([]ledger.Journal, error) { return r.transactions, nil } func (r repositoryStub) GetByID(context.Context, string) (ledger.Journal, error) { return r.journal, r.journalErr } func (r repositoryStub) Balance(context.Context, ledger.AccountReference, time.Time) (ledger.Amount, error) { return r.balance, nil } func (r repositoryStub) TopHolders(context.Context, int, int) ([]Holder, error) { return r.holders, nil } func TestAssetsIncludesTopHolders(t *testing.T) { balance, err := ledger.ParseAmount("125.5") if err != nil { t.Fatal(err) } service := NewService(repositoryStub{holders: []Holder{{Rank: 1, OwnerType: "user", OwnerID: "42", AssetID: 7, Balance: balance}}}) result, err := service.Assets(context.Background()) if err != nil { t.Fatal(err) } if len(result.TopHolders) != 1 || result.TopHolders[0].Balance.String() != "125.5" { t.Fatalf("unexpected top holders: %+v", result.TopHolders) } } func TestTransactionsPaginatesWithLookahead(t *testing.T) { journals := make([]ledger.Journal, transactionPageSize+1) for index := range journals { journals[index].ID = fmt.Sprintf("journal-%d", index) } var repositoryFilter ledger.JournalFilter service := NewService(repositoryStub{journals: journals, lastFilter: &repositoryFilter}) result, err := service.Transactions(context.Background(), TransactionFilter{Page: 2, Wallet: " wallet-42 ", EffectKind: " transfer "}) if err != nil { t.Fatal(err) } if result.Filter.Page != 2 || result.Filter.Wallet != "wallet-42" || result.Filter.EffectKind != "transfer" || !result.HasPrevious || !result.HasNext || len(result.Journals) != transactionPageSize { t.Fatalf("unexpected transaction page: %+v", result) } if repositoryFilter.OwnerID == nil || *repositoryFilter.OwnerID != "wallet-42" || repositoryFilter.EffectKind == nil || *repositoryFilter.EffectKind != "transfer" || repositoryFilter.Offset != transactionPageSize { t.Fatalf("unexpected repository filter: %+v", repositoryFilter) } } func TestHolderCombinesAvailableAndFrozenBalances(t *testing.T) { balance, err := ledger.ParseAmount("10.25") if err != nil { t.Fatal(err) } service := NewService(repositoryStub{balance: balance, journals: []ledger.Journal{{ID: "journal-1"}}}) result, err := service.Holder(context.Background(), HolderReference{OwnerType: "user", OwnerID: "42", AssetID: 7}) if err != nil { t.Fatal(err) } if result.Balance.String() != "20.5" || len(result.Journals) != 1 { t.Fatalf("unexpected aggregate holder account: %+v", result) } } func TestTransactionRequiresAResult(t *testing.T) { service := NewService(repositoryStub{}) if _, err := service.Transaction(context.Background(), "hash"); !IsNotFound(err) { t.Fatalf("expected not found, got %v", err) } } func TestTransactionFallsBackToInternalJournalID(t *testing.T) { const journalID = "5c1e31b0-0000-4000-8000-0000084accc8" service := NewService(repositoryStub{journal: ledger.Journal{ID: journalID}}) result, err := service.Transaction(context.Background(), journalID) if err != nil { t.Fatal(err) } if len(result) != 1 || result[0].ID != journalID { t.Fatalf("unexpected internal transaction result: %+v", result) } } func TestAccountReturnsBalanceAndHistory(t *testing.T) { balance, err := ledger.ParseAmount("12.5") if err != nil { t.Fatal(err) } reference := ledger.AccountReference{ Class: ledger.AccountClassUserAvailable, OwnerType: "user", OwnerID: "42", AssetID: 7, } service := NewService(repositoryStub{balance: balance, journals: []ledger.Journal{{ID: "journal-1"}}}) result, err := service.Account(context.Background(), reference) if err != nil { t.Fatal(err) } if result.Balance.String() != "12.5" || len(result.Journals) != 1 { t.Fatalf("unexpected account result: %+v", result) } }