Files
worktimeBot/internal/domain/dateutil_test.go
db123 d15ed46066 fix: resolve go vet error and dayStart logic in ComputeDailyTotals
- Fix 'string(d)' int-to-rune conversion in rpg_test.go by using fmt.Sprintf
- Fix ComputeDailyTotals dayStart logic: initial state should be StateWorking
  when dayStart > 0, correctly counting work from dayStart to first out event
- Update README project structure to reflect new domain/handler/repo layout
- Implement missing achievements: rpg_pioneer (on RPG enable) and
  salary_setter (on first rate set)
- Add tryUnlockAchievement helper to Handler
2026-06-25 09:54:22 +03:30

239 lines
6.6 KiB
Go

package domain
import (
"testing"
)
func TestGregorianToJDN_RoundTrip(t *testing.T) {
cases := []struct{ y, m, d int }{
{2024, 1, 1},
{2024, 12, 31},
{1970, 1, 1},
{1, 1, 1},
{2024, 2, 29},
{1900, 2, 28},
{2000, 2, 29},
}
for _, c := range cases {
jdn := GregorianToJDN(c.y, c.m, c.d)
y, m, d := JDNToGregorian(jdn)
if y != c.y || m != c.m || d != c.d {
t.Errorf("round-trip %04d-%02d-%02d -> JDN %d -> %04d-%02d-%02d", c.y, c.m, c.d, jdn, y, m, d)
}
}
}
func TestGregorianToJalali_RoundTrip(t *testing.T) {
cases := []struct{ gy, gm, gd int }{
{2024, 3, 20},
{2024, 3, 21},
{2024, 9, 22},
{2025, 3, 20},
{1979, 2, 11},
{622, 3, 22},
{2024, 12, 31},
{2024, 6, 21},
{2024, 1, 1},
}
for _, c := range cases {
jy, jm, jd := GregorianToJalali(c.gy, c.gm, c.gd)
if jy == 0 {
t.Errorf("GregorianToJalali(%d, %d, %d) returned 0", c.gy, c.gm, c.gd)
continue
}
gy2, gm2, gd2 := JalaliToGregorian(jy, jm, jd)
if gy2 != c.gy || gm2 != c.gm || gd2 != c.gd {
t.Errorf("round-trip Gregorian %04d-%02d-%02d -> Jalali %04d-%02d-%02d -> Gregorian %04d-%02d-%02d",
c.gy, c.gm, c.gd, jy, jm, jd, gy2, gm2, gd2)
}
}
}
func TestGregorianToHijri_RoundTrip(t *testing.T) {
cases := []struct{ gy, gm, gd int }{
{2024, 3, 21},
{2024, 1, 1},
{2024, 12, 31},
{1979, 2, 11},
{622, 7, 16},
{2024, 7, 7},
{2024, 6, 21},
{2000, 1, 1},
}
for _, c := range cases {
hy, hm, hd := GregorianToHijri(c.gy, c.gm, c.gd)
if hy == 0 {
t.Errorf("GregorianToHijri(%d, %d, %d) returned 0", c.gy, c.gm, c.gd)
continue
}
gy2, gm2, gd2 := HijriToGregorian(hy, hm, hd)
diff := (gy2-c.gy)*365 + (gm2-c.gm)*30 + (gd2 - c.gd)
if diff < 0 {
diff = -diff
}
if diff > 1 {
t.Errorf("round-trip off by %d days: Gregorian %04d-%02d-%02d -> Hijri %04d-%02d-%02d -> Gregorian %04d-%02d-%02d",
diff, c.gy, c.gm, c.gd, hy, hm, hd, gy2, gm2, gd2)
}
}
}
func TestIsLeapYear(t *testing.T) {
gregLeaps := map[int]bool{
2000: true, 2020: true, 2024: true, 1900: false, 2100: false, 2023: false,
}
for y, want := range gregLeaps {
if got := IsGregorianLeap(y); got != want {
t.Errorf("IsGregorianLeap(%d) = %v, want %v", y, got, want)
}
}
jalaliLeaps := map[int]bool{
1: true, 5: true, 9: true, 13: true, 17: true, 22: true, 26: true, 30: true,
2: false, 33: false, 1403: true,
}
for y, want := range jalaliLeaps {
if got := IsJalaliLeap(y); got != want {
t.Errorf("IsJalaliLeap(%d) = %v, want %v", y, got, want)
}
}
hijriLeaps := map[int]bool{
2: true, 5: true, 7: true, 10: true, 13: true, 16: true,
18: true, 21: true, 24: true, 27: true, 29: true,
1: false, 3: false, 30: false,
}
for y, want := range hijriLeaps {
if got := IsHijriLeap(y); got != want {
t.Errorf("IsHijriLeap(%d) = %v, want %v", y, got, want)
}
}
}
func TestMonthLengths(t *testing.T) {
if got := MonthLenGreg(2024, 2); got != 29 {
t.Errorf("MonthLenGreg(2024, 2) = %d, want 29", got)
}
if got := MonthLenGreg(2023, 2); got != 28 {
t.Errorf("MonthLenGreg(2023, 2) = %d, want 28", got)
}
if got := MonthLenGreg(2024, 1); got != 31 {
t.Errorf("MonthLenGreg(2024, 1) = %d, want 31", got)
}
if got := MonthLenJalali(1, 12); got != 30 {
t.Errorf("MonthLenJalali(1, 12) = %d, want 30 (leap)", got)
}
if got := MonthLenJalali(2, 12); got != 29 {
t.Errorf("MonthLenJalali(2, 12) = %d, want 29 (non-leap)", got)
}
if got := MonthLenJalali(1403, 1); got != 31 {
t.Errorf("MonthLenJalali(1403, 1) = %d, want 31", got)
}
if got := MonthLenHijri(1, 1); got != 30 {
t.Errorf("MonthLenHijri(1, 1) = %d, want 30 (odd month)", got)
}
if got := MonthLenHijri(1, 2); got != 29 {
t.Errorf("MonthLenHijri(1, 2) = %d, want 29 (even non-leap)", got)
}
if got := MonthLenHijri(2, 12); got != 30 {
t.Errorf("MonthLenHijri(2, 12) = %d, want 30 (leap year, month 12)", got)
}
}
func TestParseGregorianDateKey(t *testing.T) {
y, m, d := ParseGregorianDateKey("2024-03-21")
if y != 2024 || m != 3 || d != 21 {
t.Errorf("ParseGregorianDateKey got %d-%d-%d, want 2024-3-21", y, m, d)
}
}
func TestFormatDateForCalendar(t *testing.T) {
if got := FormatDateForCalendar("2024-03-21", "gregorian"); got != "2024-03-21" {
t.Errorf("FormatDateForCalendar gregorian: got %s", got)
}
if got := FormatDateForCalendar("2024-03-21", "jalali"); got != "1403-01-02" {
t.Errorf("FormatDateForCalendar jalali: got %s, want 1403-01-02", got)
}
if got := FormatDateForCalendar("2024-03-21", "hijri"); got != "1445-09-11" {
t.Errorf("FormatDateForCalendar hijri: got %s, want 1445-09-11", got)
}
}
func TestUserDateToGregorian(t *testing.T) {
if got := UserDateToGregorian("1403-01-02", "jalali"); got != "2024-03-21" {
t.Errorf("UserDateToGregorian jalali: got %s, want 2024-03-21", got)
}
if got := UserDateToGregorian("1445-09-11", "hijri"); got != "2024-03-21" {
t.Errorf("UserDateToGregorian hijri: got %s, want 2024-03-21", got)
}
}
func TestMonthGregorianRange(t *testing.T) {
start, end := MonthGregorianRange("gregorian", 2024, 1)
if start != "2024-01-01" || end != "2024-01-31" {
t.Errorf("gregorian range: got %s - %s", start, end)
}
start, end = MonthGregorianRange("jalali", 1403, 1)
if start != "2024-03-20" || end != "2024-04-19" {
t.Errorf("jalali range: got %s - %s", start, end)
}
start, end = MonthGregorianRange("hijri", 1445, 9)
if start != "2024-03-11" || end != "2024-04-09" {
t.Errorf("hijri range: got %s - %s", start, end)
}
}
func TestBuildCalendarMonth(t *testing.T) {
cm := BuildCalendarMonth("gregorian", 2024, 1)
if cm.Title != "January 2024" {
t.Errorf("title: got %q", cm.Title)
}
if len(cm.WeekDays) != 7 {
t.Errorf("weekDays count: %d", len(cm.WeekDays))
}
if len(cm.Days) < 28 {
t.Errorf("too few days: %d", len(cm.Days))
}
cm2 := BuildCalendarMonth("jalali", 1403, 1)
if cm2.Title != "Farvardin 1403" {
t.Errorf("jalali title: got %q", cm2.Title)
}
if len(cm2.Days) < 31 {
t.Errorf("too few jalali days: %d", len(cm2.Days))
}
cm3 := BuildCalendarMonth("hijri", 1445, 9)
if cm3.Title != "Ramadan 1445" {
t.Errorf("hijri title: got %q", cm3.Title)
}
}
func TestFormatMonthTitle(t *testing.T) {
tests := []struct {
cal string
year, m int
want string
}{
{"gregorian", 2026, 6, "June 2026"},
{"gregorian", 2024, 1, "January 2024"},
{"jalali", 1404, 1, "Farvardin 1404"},
{"jalali", 1405, 12, "Esfand 1405"},
{"hijri", 1447, 1, "Muharram 1447"},
{"hijri", 1447, 12, "Dhu al-Hijjah 1447"},
}
for _, tc := range tests {
t.Run(tc.want, func(t *testing.T) {
got := FormatMonthTitle(tc.cal, tc.year, tc.m)
if got != tc.want {
t.Errorf("FormatMonthTitle(%q, %d, %d) = %q, want %q",
tc.cal, tc.year, tc.m, got, tc.want)
}
})
}
}