Fix TestCampaign sending only subscribers, failing listmonk validation
CI / test (push) Successful in 9s
CI / test (push) Successful in 9s
listmonk's /api/campaigns/:id/test handler binds the request into the
same campReq struct create/update use and validates it in full, so a
request with only {"subscribers": [...]} fails with "Invalid length
for name" before subscribers is ever read. TestCampaign now sends the
campaign's full CampaignInput alongside subscribers.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -297,6 +297,23 @@ type Campaign struct {
|
||||
MediaIDs []int
|
||||
}
|
||||
|
||||
// AsInput converts a fetched Campaign back into the CampaignInput shape,
|
||||
// for callers (e.g. Test) that need to re-send a campaign's current fields
|
||||
// against an endpoint that validates the full campaign body.
|
||||
func (camp *Campaign) AsInput() CampaignInput {
|
||||
return CampaignInput{
|
||||
Name: camp.Name,
|
||||
Subject: camp.Subject,
|
||||
FromEmail: camp.FromEmail,
|
||||
TemplateID: camp.TemplateID,
|
||||
Type: camp.Type,
|
||||
Tags: camp.Tags,
|
||||
ListIDs: camp.ListIDs,
|
||||
Body: camp.Body,
|
||||
MediaIDs: camp.MediaIDs,
|
||||
}
|
||||
}
|
||||
|
||||
// CampaignInput is what sync.go builds from a campaign's frontmatter+body to
|
||||
// create or update a listmonk campaign.
|
||||
type CampaignInput struct {
|
||||
@@ -482,14 +499,18 @@ func (c *Client) SetCampaignStatus(id int, status string) error {
|
||||
}
|
||||
|
||||
// TestCampaign sends a preview of the campaign's current content to the
|
||||
// given addresses without touching its status. Request field confirmed
|
||||
// against knadh/listmonk's actual handler source (cmd/campaigns.go's
|
||||
// campReq.SubscriberEmails, json tag "subscribers").
|
||||
func (c *Client) TestCampaign(id int, emails []string) error {
|
||||
// given addresses without touching its status. listmonk's test-send handler
|
||||
// binds the request into the same campReq struct create/update use and
|
||||
// validates it in full, so the campaign's other fields (in particular a
|
||||
// non-empty "name") must be sent alongside "subscribers" — a request with
|
||||
// just {"subscribers": [...]} fails listmonk's own validation with "Invalid
|
||||
// length for name" before the "subscribers" field is ever looked at.
|
||||
func (c *Client) TestCampaign(id int, in CampaignInput, emails []string) error {
|
||||
if len(emails) == 0 {
|
||||
return nil
|
||||
}
|
||||
payload := map[string]any{"subscribers": emails}
|
||||
payload := in.payload()
|
||||
payload["subscribers"] = emails
|
||||
respBody, status, err := c.do(http.MethodPost, fmt.Sprintf("/api/campaigns/%d/test", id), payload)
|
||||
if err != nil {
|
||||
return fmt.Errorf("sending test for campaign %d: %w", id, err)
|
||||
|
||||
@@ -311,13 +311,17 @@ func TestTestCampaign_SendsSubscribersList(t *testing.T) {
|
||||
defer srv.Close()
|
||||
|
||||
c := New(srv.URL, "u", "t")
|
||||
if err := c.TestCampaign(7, []string{"me@example.com"}); err != nil {
|
||||
in := CampaignInput{Name: "launch", Subject: "Hi", Body: "body", Type: "regular"}
|
||||
if err := c.TestCampaign(7, in, []string{"me@example.com"}); err != nil {
|
||||
t.Fatalf("TestCampaign: %v", err)
|
||||
}
|
||||
subs, ok := gotBody["subscribers"].([]any)
|
||||
if !ok || len(subs) != 1 || subs[0] != "me@example.com" {
|
||||
t.Errorf("expected subscribers=[me@example.com], got %+v", gotBody)
|
||||
}
|
||||
if gotBody["name"] != "launch" {
|
||||
t.Errorf("expected the full campaign body (name=launch) alongside subscribers, got %+v", gotBody)
|
||||
}
|
||||
}
|
||||
|
||||
func TestErrorResponsesAreWrappedWithStatusAndBody(t *testing.T) {
|
||||
|
||||
Reference in New Issue
Block a user