package membership import ( "bytes" "context" "fuj-management/go/internal/domain/reconcile" "strings" "testing" ) type fakeSources struct { members []reconcile.Member months []string txns []reconcile.Transaction exceptions map[reconcile.ExceptionKey]reconcile.Exception } func (f fakeSources) LoadAdults(_ context.Context) ([]reconcile.Member, []string, error) { return f.members, f.months, nil } func (f fakeSources) LoadJuniors(_ context.Context) ([]reconcile.Member, []string, error) { return nil, nil, nil } func (f fakeSources) LoadTransactions(_ context.Context) ([]reconcile.Transaction, error) { return f.txns, nil } func (f fakeSources) LoadExceptions(_ context.Context) (map[reconcile.ExceptionKey]reconcile.Exception, error) { return f.exceptions, nil } func (fakeSources) FlushCache() (int, error) { return 0, nil } func TestReconcileReport(t *testing.T) { t.Parallel() s := fakeSources{ members: []reconcile.Member{ {Name: "Alice", Tier: "A", Fees: map[string]reconcile.FeeData{ "2026-04": {Expected: 700, Attendance: 3}, }}, }, months: []string{"2026-04"}, txns: []reconcile.Transaction{ { Date: "2026-04-10", Amount: 700, Person: "Alice", Purpose: "2026-04", Sender: "Alice Bank", Message: "fee", }, }, exceptions: map[reconcile.ExceptionKey]reconcile.Exception{}, } var buf bytes.Buffer if err := ReconcileReport(context.Background(), s, 2026, &buf); err != nil { t.Fatalf("unexpected error: %v", err) } got := buf.String() if !strings.Contains(got, "PAYMENT RECONCILIATION REPORT") { t.Error("missing report header") } if !strings.Contains(got, "OK") { t.Errorf("expected 'OK' for fully-paid Alice, got:\n%s", got) } } func TestReconcileReportStubErrors(t *testing.T) { t.Parallel() var buf bytes.Buffer err := ReconcileReport(context.Background(), NewStubSources(), 2026, &buf) if err == nil { t.Fatal("expected error from stub, got nil") } }