package tools import ( "errors" "strings" "testing" "time" "nearle/models" ) var channelNow = time.Date(2026, 9, 23, 15, 0, 0, 0, time.Local) type fakeRevenue struct { summary *models.TenantRevenueSummary err error seenFrom string seenTo string seenLid int seenTid int } func (f *fakeRevenue) GetRevenueSummary(tid, lid int, fdate, tdate string) (*models.TenantRevenueSummary, error) { f.seenTid, f.seenLid, f.seenFrom, f.seenTo = tid, lid, fdate, tdate return f.summary, f.err } type fakeCounter struct { summary *models.PosSalesSummary err error seen models.PosSalesFilter } func (f *fakeCounter) SalesSummary(filter models.PosSalesFilter) (*models.PosSalesSummary, error) { f.seen = filter return f.summary, f.err } var atBranch = Caller{Userid: 904, Tenantid: 1147, Locationid: 1172} func channelTool(revenue *fakeRevenue, counter *fakeCounter) Tool { return SalesByChannel(revenue, counter, func() time.Time { return channelNow }) } /* ── The model never sends a date ──────────────────────────────────────── */ func TestTheDateRangeIsComputedHereNotSentByTheModel(t *testing.T) { // A format, a timezone, or an off-by-one on "last week" each produce a // plausible figure for the wrong period — undetectable in a sentence. revenue, counter := &fakeRevenue{}, &fakeCounter{} if _, err := call1(t, channelTool(revenue, counter), nil, atBranch); err != nil { t.Fatalf("calling: %v", err) } // Seven days ending today, inclusive. if revenue.seenFrom != "2026-09-17" || revenue.seenTo != "2026-09-23" { t.Fatalf("range was %s to %s", revenue.seenFrom, revenue.seenTo) } if counter.seen.Fromdate != revenue.seenFrom || counter.seen.Todate != revenue.seenTo { t.Fatalf("the two ledgers were read over different periods: %+v", counter.seen) } } func TestTheToolHasNoDateArgumentAtAll(t *testing.T) { tool := SalesByChannel(&fakeRevenue{}, &fakeCounter{}, nil) for _, field := range tool.Schema.Fields { if strings.Contains(field.Name, "date") || strings.Contains(field.Name, "from") { t.Fatalf("the schema offers %q for the model to fill in", field.Name) } } } func TestThePeriodCanBeWidened(t *testing.T) { revenue := &fakeRevenue{} if _, err := call1(t, channelTool(revenue, &fakeCounter{}), map[string]any{"days": 30}, atBranch); err != nil { t.Fatalf("calling: %v", err) } if revenue.seenFrom != "2026-08-25" { t.Fatalf("thirty days back was %s", revenue.seenFrom) } } /* ── Two ledgers, never one total ──────────────────────────────────────── */ func TestBothChannelsComeBackSeparately(t *testing.T) { revenue := &fakeRevenue{summary: &models.TenantRevenueSummary{OverallRevenue: 41250}} counter := &fakeCounter{summary: &models.PosSalesSummary{Grosssales: 18300, Billcount: 92}} result, err := call1(t, channelTool(revenue, counter), nil, atBranch) if err != nil { t.Fatalf("calling: %v", err) } rows, ok := result.Rows.([]ChannelTotals) if !ok || len(rows) != 2 { t.Fatalf("expected two channels: %+v", result.Rows) } if rows[0].Revenue != 41250 || rows[1].Revenue != 18300 { t.Fatalf("the figures did not survive: %+v", rows) } if rows[0].Ledger == rows[1].Ledger { t.Fatal("both channels claim the same ledger") } } func TestTheAnswerForbidsAddingTheTwoTogether(t *testing.T) { // The most useful-looking wrong number available on this page. revenue := &fakeRevenue{summary: &models.TenantRevenueSummary{OverallRevenue: 100}} counter := &fakeCounter{summary: &models.PosSalesSummary{Grosssales: 50}} result, _ := call1(t, channelTool(revenue, counter), nil, atBranch) if !strings.Contains(strings.ToLower(result.Note), "do not add") { t.Fatalf("nothing stops the model reporting one total: %q", result.Note) } } func TestTheCounterFigureSaysItIsGross(t *testing.T) { counter := &fakeCounter{summary: &models.PosSalesSummary{Grosssales: 50, Billcount: 3}} result, _ := call1(t, channelTool(&fakeRevenue{}, counter), nil, atBranch) rows := result.Rows.([]ChannelTotals) if rows[len(rows)-1].Caveat == "" { t.Fatal("a gross figure is presented as comparable without saying so") } } /* ── Scope ─────────────────────────────────────────────────────────────── */ func TestChannelsRefuseAnAllBranchesQuestion(t *testing.T) { // The counter side is readable one outlet at a time. Answering with the app // half alone would be presented as the whole comparison. _, err := call1(t, channelTool(&fakeRevenue{}, &fakeCounter{}), nil, merchantCaller) if err == nil { t.Fatal("an all-branches comparison was answered") } } func TestTheBranchComesFromTheSession(t *testing.T) { revenue := &fakeRevenue{} if _, err := call1(t, channelTool(revenue, &fakeCounter{}), nil, atBranch); err != nil { t.Fatalf("calling: %v", err) } if revenue.seenLid != 1172 { t.Fatalf("read branch %d", revenue.seenLid) } } /* ── Failure ───────────────────────────────────────────────────────────── */ func TestAHalfAnsweredComparisonIsAnErrorNotHalfAnAnswer(t *testing.T) { // One channel returned alone would be read as "the counter took nothing". revenue := &fakeRevenue{summary: &models.TenantRevenueSummary{OverallRevenue: 100}} counter := &fakeCounter{err: errors.New("pos database is down")} if _, err := call1(t, channelTool(revenue, counter), nil, atBranch); err == nil { t.Fatal("a failed counter read produced a one-sided comparison") } }