Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion cmd/campaigns.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ func addCampaignFieldFlags(cmd *cobra.Command) {
cmd.Flags().String("audience-filter-file", "", "Path to a JSON file with an ad-hoc audience filter")
cmd.Flags().Bool("schedule-now", false, "Send immediately when published")
cmd.Flags().String("schedule-at", "", "Send at the given ISO 8601 timestamp (e.g. 2026-07-01T12:00:00Z)")
cmd.MarkFlagsMutuallyExclusive("audience-segment-id", "audience-filter", "audience-filter-file")
cmd.MarkFlagsMutuallyExclusive("audience-filter", "audience-filter-file")
cmd.MarkFlagsMutuallyExclusive("schedule-now", "schedule-at")
}

Expand Down
36 changes: 36 additions & 0 deletions cmd/campaigns_create_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,4 +83,40 @@ func TestRunCampaignsCreate(t *testing.T) {
t.Errorf("scheduling = %v", sched)
}
})

t.Run("sends audience segment and filter together", func(t *testing.T) {
got := serveJSONCapture(t, http.StatusCreated, body)
segment := "seg_1"
_, err := runCampaignsCreate(cfg(t), loops.CreateCampaignRequest{
Name: "Spring",
AudienceSegmentID: &segment,
AudienceFilter: &loops.AudienceFilter{
Match: "all",
Conditions: []loops.AudienceFilterCondition{
{
Type: loops.AudienceConditionTypeProperty,
Property: &loops.PropertyCondition{Key: "plan", Operator: "equals"},
},
},
},
})
if err != nil {
t.Fatalf("unexpected error: %v", err)
}

var sent map[string]any
if err := json.Unmarshal(got.Body, &sent); err != nil {
t.Fatalf("decode request body: %v\nraw: %s", err, got.Body)
}
if sent["audienceSegmentId"] != "seg_1" {
t.Errorf("audienceSegmentId = %v, want seg_1", sent["audienceSegmentId"])
}
filter, ok := sent["audienceFilter"].(map[string]any)
if !ok {
t.Fatalf("audienceFilter not an object: %v", sent["audienceFilter"])
}
if filter["match"] != "all" {
t.Errorf("audienceFilter.match = %v, want all", filter["match"])
}
})
}
27 changes: 27 additions & 0 deletions cmd/campaigns_fields_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,9 @@ func TestCampaignFieldParamsFromCmd(t *testing.T) {
if err := cmd.ParseFlags([]string{"--audience-filter", "{}", "--audience-filter-file", "f.json"}); err != nil {
t.Fatalf("ParseFlags: %v", err)
}
if err := cmd.ValidateFlagGroups(); err == nil {
t.Error("ValidateFlagGroups: expected mutual-exclusion error, got nil")
}
_, err := campaignFieldParamsFromCmd(cmd)
if err == nil {
t.Fatal("expected error, got nil")
Expand All @@ -245,6 +248,30 @@ func TestCampaignFieldParamsFromCmd(t *testing.T) {
}
})

t.Run("audience-segment-id and audience-filter together set both fields", func(t *testing.T) {
cmd := newCampaignFieldCmd()
filter := `{"match":"all","conditions":[{"type":"property","key":"plan","operator":"equals","value":"pro"}]}`
if err := cmd.ParseFlags([]string{"--audience-segment-id", "seg_1", "--audience-filter", filter}); err != nil {
t.Fatalf("ParseFlags: %v", err)
}
if err := cmd.ValidateFlagGroups(); err != nil {
t.Fatalf("ValidateFlagGroups: %v", err)
}
p, err := campaignFieldParamsFromCmd(cmd)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if p.AudienceSegmentID == nil || *p.AudienceSegmentID != "seg_1" {
t.Errorf("AudienceSegmentID = %v, want pointer to seg_1", p.AudienceSegmentID)
}
if p.AudienceFilter == nil || p.AudienceFilter.Match != "all" {
t.Errorf("AudienceFilter = %v, want match=all filter", p.AudienceFilter)
}
if !p.Set["audienceSegmentId"] || !p.Set["audienceFilter"] {
t.Errorf("Set = %v, want both audienceSegmentId and audienceFilter", p.Set)
}
})

t.Run(`mailing-list-id "null" sentinel clears the field`, func(t *testing.T) {
cmd := newCampaignFieldCmd()
if err := cmd.ParseFlags([]string{"--mailing-list-id", "null"}); err != nil {
Expand Down