package reconciliation import ( "context" "testing" "time" "gl/domain/ledger" ) type repositoryStub struct { pages [][]ledger.Journal } func (r repositoryStub) List(_ context.Context, filter ledger.JournalFilter) ([]ledger.Journal, error) { index := filter.Offset / filter.Limit if index >= len(r.pages) { return nil, nil } return r.pages[index], nil } func validJournal() ledger.Journal { return ledger.Journal{ID: "j1", SourceService: "wallet", IdempotencyKey: "k1", SourceTransactionID: "t1", EffectKind: "transfer", EventVersion: 1, OccurredAt: ledgerTestTime(), PayloadHash: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", Entries: []ledger.Entry{{LineNumber: 1, Account: ledger.AccountReference{Class: ledger.AccountClassTreasury, AssetID: 1}, Amount: mustAmount("-1")}, {LineNumber: 2, Account: ledger.AccountReference{Class: ledger.AccountClassTreasury, AssetID: 1}, Amount: mustAmount("1")}}} } func ledgerTestTime() (t time.Time) { return time.Unix(1, 0).UTC() } func mustAmount(value string) ledger.Amount { amount, _ := ledger.ParseAmount(value); return amount } func TestRunScansAndReportsInvalidJournals(t *testing.T) { valid := validJournal() invalid := valid invalid.Entries = append([]ledger.Entry(nil), valid.Entries...) invalid.ID = "bad" invalid.Entries[1].Amount = mustAmount("2") report, err := Run(context.Background(), repositoryStub{pages: [][]ledger.Journal{{valid}, {invalid}}}, 1) if err != nil { t.Fatal(err) } if report.Scanned != 2 || report.Valid != 1 || len(report.Invalid) != 1 || report.Invalid[0].JournalID != "bad" { t.Fatalf("unexpected report: %+v", report) } } func TestRunReportsDuplicateTransactionVersions(t *testing.T) { first := validJournal() second := validJournal() second.ID = "j2" report, err := Run(context.Background(), repositoryStub{pages: [][]ledger.Journal{{first, second}}}, 10) if err != nil { t.Fatal(err) } if report.Valid != 1 || len(report.Invalid) != 1 { t.Fatalf("unexpected report: %+v", report) } }