Skip to content
Merged
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
932e47b
feat: add the existing email routes and handler to the API
williamchiii Apr 4, 2026
4137e9d
feat: add email campaigns migration with status, format, and recipien…
williamchiii Apr 10, 2026
8502581
feat: add EmailCampaign model with status, format, and recipient types
williamchiii Apr 10, 2026
b41e45f
Merge remote-tracking branch 'origin/dev' into wchi/email_campaigns
williamchiii Apr 21, 2026
7dfea22
Merge remote-tracking branch 'origin/dev' into wchi/email_campaigns
williamchiii May 3, 2026
1b7e5de
feat: update SQLC version to v1.30.0
williamchiii May 3, 2026
d449c4a
feat: add SQL queries for email campaigns management
williamchiii May 3, 2026
e649338
Potential fix for pull request finding
williamchiii May 3, 2026
7552d39
fix: use sqlc.narg for nullable scheduled_at and sent_at in update qu…
Copilot May 3, 2026
9745602
feat: implement EmailCampaignRepository with CRUD operations
williamchiii May 18, 2026
e5036ea
style: group stdlib and third-party imports in email_campaigns reposi…
Copilot May 18, 2026
19ed0ba
feat: implement EmailCampaignService with CRUD operations and validation
williamchiii May 25, 2026
cd30e22
Merge remote-tracking branch 'origin/dev' into wchi/email_campaigns
williamchiii May 26, 2026
5c446c1
Merge remote-tracking branch 'origin/dev' into wchi/email_campaigns
williamchiii Jun 3, 2026
686fb35
Add email campaign HTTP routes and handler implementations
williamchiii Jun 3, 2026
3c95e64
Potential fix for pull request finding
williamchiii Jun 3, 2026
862914d
Merge remote-tracking branch 'origin/dev' into wchi/email_campaigns
williamchiii Jun 9, 2026
65f986b
feat: register/manually tested email campaign CRUD API and fix pgx en…
williamchiii Jun 9, 2026
c56146b
Merge remote-tracking branch 'origin/dev' into wchi/email_campaigns
williamchiii Jun 15, 2026
1db4e58
added unit testing for email campaign api endpoints
williamchiii Jun 15, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
139 changes: 139 additions & 0 deletions apps/api/internal/domains/email/campaign_service_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
package email

import (
"context"
"errors"
"testing"

"github.com/swamphacks/core/apps/api/internal/database/sqlc"
)

func TestValidateCampaignContent(t *testing.T) {
tests := []struct {
name string
title string
subject string
body string
recipientTypes []string
expectedError error
}{
{
name: "valid campaign",
title: "Welcome",
subject: "Welcome to SwampHacks",
body: "Campaign body",
recipientTypes: []string{"admins"},
expectedError: nil,
},
{
name: "missing title",
title: " ",
subject: "Subject",
body: "Body",
recipientTypes: []string{"admins"},
expectedError: ErrEmailCampaignTitleRequired,
},
{
name: "missing subject",
title: "Title",
subject: "",
body: "Body",
recipientTypes: []string{"admins"},
expectedError: ErrEmailCampaignSubjectRequired,
},
{
name: "missing body",
title: "Title",
subject: "Subject",
body: " ",
recipientTypes: []string{"admins"},
expectedError: ErrEmailCampaignBodyRequired,
},
{
name: "missing recipients",
title: "Title",
subject: "Subject",
body: "Body",
recipientTypes: nil,
expectedError: ErrEmailCampaignRecipientsRequired,
},
}

for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
err := validateCampaignContent(
test.title,
test.subject,
test.body,
test.recipientTypes,
)

if !errors.Is(err, test.expectedError) {
t.Fatalf("expected error %v, got %v", test.expectedError, err)
}
})
}
}

func TestCanEditCampaign(t *testing.T) {
tests := []struct {
status sqlc.EmailCampaignStatus
expected bool
}{
{status: sqlc.EmailCampaignStatusDraft, expected: true},
{status: sqlc.EmailCampaignStatusScheduled, expected: true},
{status: sqlc.EmailCampaignStatusSending, expected: false},
{status: sqlc.EmailCampaignStatusSent, expected: false},
{status: sqlc.EmailCampaignStatusFailed, expected: false},
}

for _, test := range tests {
t.Run(string(test.status), func(t *testing.T) {
result := canEditCampaign(test.status)

if result != test.expected {
t.Fatalf("expected %v, got %v", test.expected, result)
}
})
}
}

func TestUpdateCampaignStatusRequiresScheduledAt(t *testing.T) {
service := &EmailCampaignService{}

_, err := service.UpdateCampaignStatus(
context.Background(),
sqlc.UpdateEmailCampaignStatusParams{
Status: sqlc.EmailCampaignStatusScheduled,
ScheduledAt: nil,
},
)

if !errors.Is(err, ErrEmailCampaignScheduledAtRequired) {
t.Fatalf(
"expected %v, got %v",
ErrEmailCampaignScheduledAtRequired,
err,
)
}
}

func TestUpdateCampaignStatusRequiresSentAt(t *testing.T) {
service := &EmailCampaignService{}

_, err := service.UpdateCampaignStatus(
context.Background(),
sqlc.UpdateEmailCampaignStatusParams{
Status: sqlc.EmailCampaignStatusSent,
SentAt: nil,
},
)

if !errors.Is(err, ErrEmailCampaignSentAtRequired) {
t.Fatalf(
"expected %v, got %v",
ErrEmailCampaignSentAtRequired,
err,
)
}
}