package analytics import ( "testing" "time" ) func TestJalaliGregorianRoundTrip(t *testing.T) { t.Parallel() tests := []struct { name string jy, jm, jd int gy, gm, gd int }{ {name: "Nowruz 1405", jy: 1405, jm: 1, jd: 1, gy: 2026, gm: 3, gd: 21}, {name: "leap Esfand", jy: 1399, jm: 12, jd: 30, gy: 2021, gm: 3, gd: 20}, {name: "Tir boundary", jy: 1405, jm: 4, jd: 1, gy: 2026, gm: 6, gd: 22}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { gy, gm, gd := jalaliToGregorian(tt.jy, tt.jm, tt.jd) if gy != tt.gy || gm != tt.gm || gd != tt.gd { t.Fatalf("jalaliToGregorian(%d,%d,%d) = %04d-%02d-%02d, want %04d-%02d-%02d", tt.jy, tt.jm, tt.jd, gy, gm, gd, tt.gy, tt.gm, tt.gd) } jy, jm, jd := gregorianToJalali(tt.gy, tt.gm, tt.gd) if jy != tt.jy || jm != tt.jm || jd != tt.jd { t.Fatalf("gregorianToJalali(%04d-%02d-%02d) = %d-%02d-%02d, want %d-%02d-%02d", tt.gy, tt.gm, tt.gd, jy, jm, jd, tt.jy, tt.jm, tt.jd) } }) } } func TestCompletePeriods(t *testing.T) { t.Parallel() location, err := time.LoadLocation("Asia/Tehran") if err != nil { t.Fatal(err) } datasetStart := time.Date(2026, time.January, 1, 0, 0, 0, 0, location) datasetMax := time.Date(2026, time.June, 30, 23, 59, 0, 0, location) periods := completePeriods(datasetStart, datasetMax, location) if len(periods) != 212 { t.Fatalf("completePeriods() returned %d periods, want 212", len(periods)) } want := []struct { index int id, kind, start, end string isDefault bool }{ {index: 0, id: "d-2026-06-30", kind: "DAY", start: "2026-06-30", end: "2026-07-01"}, {index: 180, id: "d-2026-01-01", kind: "DAY", start: "2026-01-01", end: "2026-01-02"}, {index: 181, id: "w-2026-06-20", kind: "WEEK", start: "2026-06-20", end: "2026-06-27"}, {index: 205, id: "w-2026-01-03", kind: "WEEK", start: "2026-01-03", end: "2026-01-10"}, {index: 206, id: "jm-1405-03", kind: "JALALI_MONTH", start: "2026-05-22", end: "2026-06-22", isDefault: true}, {index: 210, id: "jm-1404-11", kind: "JALALI_MONTH", start: "2026-01-21", end: "2026-02-20"}, {index: 211, id: "ps-1405-01", kind: "PERSIAN_SEASON", start: "2026-03-21", end: "2026-06-22"}, } for _, expected := range want { period := periods[expected.index] if period.PeriodID != expected.id || period.PeriodType != expected.kind { t.Errorf("period[%d] identity = %s/%s, want %s/%s", expected.index, period.PeriodID, period.PeriodType, expected.id, expected.kind) } if got := period.LocalStartAt.Format(time.DateOnly); got != expected.start { t.Errorf("period[%d] start = %s, want %s", expected.index, got, expected.start) } if got := period.LocalEndAt.Format(time.DateOnly); got != expected.end { t.Errorf("period[%d] end = %s, want %s", expected.index, got, expected.end) } if period.IsDefault != expected.isDefault { t.Errorf("period[%d] IsDefault = %v, want %v", expected.index, period.IsDefault, expected.isDefault) } if period.StartAt.Location() != time.UTC || period.EndAt.Location() != time.UTC { t.Errorf("period[%d] UTC boundaries do not use UTC", expected.index) } } defaultCount := 0 for _, period := range periods { if !period.IsComplete { t.Errorf("period %s is not complete", period.PeriodID) } if period.LocalStartAt.Before(datasetStart) || period.LocalEndAt.After(datasetMax.Add(time.Minute)) { t.Errorf("period %s lies outside dataset coverage", period.PeriodID) } if period.IsDefault { defaultCount++ } } if defaultCount != 1 { t.Fatalf("completePeriods() returned %d defaults, want 1", defaultCount) } } func TestPrecedingPeriodLabelUsesExactJalaliPeriod(t *testing.T) { location, err := time.LoadLocation("Asia/Tehran") if err != nil { t.Fatal(err) } tests := []struct { name string periodType string start time.Time want string }{ {name: "day", periodType: "DAY", start: time.Date(2026, 7, 23, 0, 0, 0, 0, location), want: "روز ۳۱ تیر ۱۴۰۵"}, {name: "week", periodType: "WEEK", start: time.Date(2026, 8, 15, 0, 0, 0, 0, location), want: "هفته ۱۷ مرداد تا ۲۳ مرداد ۱۴۰۵"}, {name: "month", periodType: "JALALI_MONTH", start: time.Date(2026, 7, 23, 0, 0, 0, 0, location), want: "تیر ۱۴۰۵"}, {name: "season", periodType: "PERSIAN_SEASON", start: time.Date(2026, 6, 22, 0, 0, 0, 0, location), want: "بهار ۱۴۰۵"}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got, ok := PrecedingPeriodLabel(tt.periodType, tt.start) if !ok || got != tt.want { t.Fatalf("PrecedingPeriodLabel() = %q, %v; want %q, true", got, ok, tt.want) } }) } }