package jobs import ( "encoding/json" "testing" "github.com/treytartt/honeydue-api/internal/models" ) // --- BuildDigestMessage --- func TestBuildDigestMessage_BothCounts(t *testing.T) { title, body := BuildDigestMessage(3, 5) if title != "Daily Task Summary" { t.Errorf("title = %q, want %q", title, "Daily Task Summary") } want := "You have 3 overdue task(s) and 5 task(s) due this week" if body != want { t.Errorf("body = %q, want %q", body, want) } } func TestBuildDigestMessage_OnlyOverdue(t *testing.T) { _, body := BuildDigestMessage(2, 0) want := "You have 2 overdue task(s) that need attention" if body != want { t.Errorf("body = %q, want %q", body, want) } } func TestBuildDigestMessage_OnlyDueSoon(t *testing.T) { _, body := BuildDigestMessage(0, 4) want := "You have 4 task(s) due this week" if body != want { t.Errorf("body = %q, want %q", body, want) } } func TestBuildDigestMessage_Title_AlwaysDailyTaskSummary(t *testing.T) { cases := [][2]int{{1, 1}, {0, 1}, {1, 0}} for _, c := range cases { title, _ := BuildDigestMessage(c[0], c[1]) if title != "Daily Task Summary" { t.Errorf("BuildDigestMessage(%d,%d) title = %q", c[0], c[1], title) } } } // --- IsOverdueStage --- func TestIsOverdueStage_Overdue1_True(t *testing.T) { if !IsOverdueStage("overdue_1") { t.Error("expected true for overdue_1") } } func TestIsOverdueStage_Overdue14_True(t *testing.T) { if !IsOverdueStage("overdue_14") { t.Error("expected true for overdue_14") } } func TestIsOverdueStage_Reminder7d_False(t *testing.T) { if IsOverdueStage("reminder_7d") { t.Error("expected false for reminder_7d") } } func TestIsOverdueStage_DayOf_False(t *testing.T) { if IsOverdueStage("day_of") { t.Error("expected false for day_of") } } func TestIsOverdueStage_Empty_False(t *testing.T) { if IsOverdueStage("") { t.Error("expected false for empty string") } } // --- ExtractFrequencyDays --- func TestExtractFrequencyDays_WithFrequency(t *testing.T) { days := 7 task := &models.Task{ Frequency: &models.TaskFrequency{Days: &days}, } got := ExtractFrequencyDays(task) if got == nil || *got != 7 { t.Errorf("got %v, want 7", got) } } func TestExtractFrequencyDays_WithCustomInterval(t *testing.T) { custom := 14 task := &models.Task{ CustomIntervalDays: &custom, } got := ExtractFrequencyDays(task) if got == nil || *got != 14 { t.Errorf("got %v, want 14", got) } } func TestExtractFrequencyDays_NilFrequency(t *testing.T) { task := &models.Task{} got := ExtractFrequencyDays(task) if got != nil { t.Errorf("got %v, want nil", got) } } func TestExtractFrequencyDays_NilDays(t *testing.T) { task := &models.Task{ Frequency: &models.TaskFrequency{}, } got := ExtractFrequencyDays(task) if got != nil { t.Errorf("got %v, want nil", got) } } // --- Email payload tests --- func TestEmailPayload_Unmarshal_Valid(t *testing.T) { data := []byte(`{"to":"a@b.com","subject":"hi","html_body":"hi","text_body":"hi"}`) var p EmailPayload if err := json.Unmarshal(data, &p); err != nil { t.Fatalf("unmarshal: %v", err) } if p.To != "a@b.com" || p.Subject != "hi" { t.Errorf("got %+v", p) } } func TestEmailPayload_Unmarshal_Invalid(t *testing.T) { var p EmailPayload if err := json.Unmarshal([]byte(`{invalid}`), &p); err == nil { t.Error("expected error for invalid JSON") } } // --- NewSendEmailTask --- func TestNewSendEmailTask_ReturnsTask(t *testing.T) { task, err := NewSendEmailTask("a@b.com", "Subject", "hi", "hi") if err != nil { t.Fatalf("unexpected error: %v", err) } if task.Type() != TypeSendEmail { t.Errorf("task type = %q, want %q", task.Type(), TypeSendEmail) } } func TestNewSendEmailTask_PayloadFields(t *testing.T) { task, err := NewSendEmailTask("user@example.com", "Welcome", "