Files
backend_fiesta/services/tools/channels_test.go
2026-09-23 17:26:13 +05:30

159 lines
5.7 KiB
Go

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")
}
}