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", "

Hello

", "Hello") if err != nil { t.Fatalf("unexpected error: %v", err) } var p EmailPayload if err := json.Unmarshal(task.Payload(), &p); err != nil { t.Fatalf("unmarshal: %v", err) } if p.To != "user@example.com" { t.Errorf("To = %q, want %q", p.To, "user@example.com") } if p.Subject != "Welcome" { t.Errorf("Subject = %q, want %q", p.Subject, "Welcome") } if p.HTMLBody != "

Hello

" { t.Errorf("HTMLBody = %q, want %q", p.HTMLBody, "

Hello

") } if p.TextBody != "Hello" { t.Errorf("TextBody = %q, want %q", p.TextBody, "Hello") } } // --- NewSendPushTask --- func TestNewSendPushTask_ReturnsTask(t *testing.T) { task, err := NewSendPushTask(42, "Title", "Body", map[string]string{"key": "val"}) if err != nil { t.Fatalf("unexpected error: %v", err) } if task.Type() != TypeSendPush { t.Errorf("task type = %q, want %q", task.Type(), TypeSendPush) } } func TestNewSendPushTask_PayloadFields(t *testing.T) { data := map[string]string{"action": "open", "id": "123"} task, err := NewSendPushTask(7, "Alert", "Something happened", data) if err != nil { t.Fatalf("unexpected error: %v", err) } var p PushPayload if err := json.Unmarshal(task.Payload(), &p); err != nil { t.Fatalf("unmarshal: %v", err) } if p.UserID != 7 { t.Errorf("UserID = %d, want 7", p.UserID) } if p.Title != "Alert" { t.Errorf("Title = %q, want %q", p.Title, "Alert") } if p.Message != "Something happened" { t.Errorf("Message = %q, want %q", p.Message, "Something happened") } if p.Data["action"] != "open" || p.Data["id"] != "123" { t.Errorf("Data = %v, want map with action=open, id=123", p.Data) } } func TestNewSendPushTask_NilData(t *testing.T) { task, err := NewSendPushTask(1, "T", "M", nil) if err != nil { t.Fatalf("unexpected error: %v", err) } var p PushPayload if err := json.Unmarshal(task.Payload(), &p); err != nil { t.Fatalf("unmarshal: %v", err) } if p.Data != nil { t.Errorf("Data = %v, want nil", p.Data) } }