Skip to content

Commit 15e707c

Browse files
committed
Enhance label event handling in matchIssuesEvent function and add tests for label addition/removal scenarios
1 parent 7bd2ce7 commit 15e707c

File tree

3 files changed

+110
-9
lines changed

3 files changed

+110
-9
lines changed

modules/actions/workflows.go

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -362,15 +362,20 @@ func matchIssuesEvent(issuePayload *api.IssuePayload, evt *jobparser.Event) bool
362362
// Actions with the same name:
363363
// opened, edited, closed, reopened, assigned, unassigned, milestoned, demilestoned
364364
// Actions need to be converted:
365-
// label_updated -> labeled
365+
// label_updated -> labeled (when adding) or unlabeled (when removing)
366366
// label_cleared -> unlabeled
367367
// Unsupported activity types:
368368
// deleted, transferred, pinned, unpinned, locked, unlocked
369369

370370
action := issuePayload.Action
371371
switch action {
372372
case api.HookIssueLabelUpdated:
373-
action = "labeled"
373+
// Check if any labels were removed to determine if this should be "labeled" or "unlabeled"
374+
if len(issuePayload.RemovedLabels) > 0 {
375+
action = "unlabeled"
376+
} else {
377+
action = "labeled"
378+
}
374379
case api.HookIssueLabelCleared:
375380
action = "unlabeled"
376381
}

modules/actions/workflows_test.go

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,3 +136,98 @@ func TestDetectMatched(t *testing.T) {
136136
})
137137
}
138138
}
139+
140+
func TestMatchIssuesEvent(t *testing.T) {
141+
testCases := []struct {
142+
desc string
143+
payload *api.IssuePayload
144+
yamlOn string
145+
expected bool
146+
eventType string
147+
}{
148+
{
149+
desc: "Label deletion should trigger unlabeled event",
150+
payload: &api.IssuePayload{
151+
Action: api.HookIssueLabelUpdated,
152+
Issue: &api.Issue{
153+
Labels: []*api.Label{},
154+
},
155+
RemovedLabels: []*api.Label{
156+
{ID: 123, Name: "deleted-label"},
157+
},
158+
},
159+
yamlOn: "on:\n issues:\n types: [unlabeled]",
160+
expected: true,
161+
eventType: "unlabeled",
162+
},
163+
{
164+
desc: "Label deletion with existing labels should trigger unlabeled event",
165+
payload: &api.IssuePayload{
166+
Action: api.HookIssueLabelUpdated,
167+
Issue: &api.Issue{
168+
Labels: []*api.Label{
169+
{ID: 456, Name: "existing-label"},
170+
},
171+
},
172+
RemovedLabels: []*api.Label{
173+
{ID: 123, Name: "deleted-label"},
174+
},
175+
},
176+
yamlOn: "on:\n issues:\n types: [unlabeled]",
177+
expected: true,
178+
eventType: "unlabeled",
179+
},
180+
{
181+
desc: "Label addition should trigger labeled event",
182+
payload: &api.IssuePayload{
183+
Action: api.HookIssueLabelUpdated,
184+
Issue: &api.Issue{
185+
Labels: []*api.Label{
186+
{ID: 123, Name: "new-label"},
187+
},
188+
},
189+
RemovedLabels: []*api.Label{}, // Empty array, no labels removed
190+
},
191+
yamlOn: "on:\n issues:\n types: [labeled]",
192+
expected: true,
193+
eventType: "labeled",
194+
},
195+
{
196+
desc: "Label clear should trigger unlabeled event",
197+
payload: &api.IssuePayload{
198+
Action: api.HookIssueLabelCleared,
199+
Issue: &api.Issue{
200+
Labels: []*api.Label{},
201+
},
202+
},
203+
yamlOn: "on:\n issues:\n types: [unlabeled]",
204+
expected: true,
205+
eventType: "unlabeled",
206+
},
207+
}
208+
209+
for _, tc := range testCases {
210+
t.Run(tc.desc, func(t *testing.T) {
211+
evts, err := GetEventsFromContent([]byte(tc.yamlOn))
212+
assert.NoError(t, err)
213+
assert.Len(t, evts, 1)
214+
215+
// Test if the event matches as expected
216+
assert.Equal(t, tc.expected, matchIssuesEvent(tc.payload, evts[0]))
217+
218+
// For extra validation, use a direct call to test the actual mapping
219+
action := tc.payload.Action
220+
switch action {
221+
case api.HookIssueLabelUpdated:
222+
if len(tc.payload.RemovedLabels) > 0 {
223+
action = "unlabeled"
224+
} else {
225+
action = "labeled"
226+
}
227+
case api.HookIssueLabelCleared:
228+
action = "unlabeled"
229+
}
230+
assert.Equal(t, tc.eventType, string(action), "Event type should match expected")
231+
})
232+
}
233+
}

modules/structs/hook.go

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -310,13 +310,14 @@ const (
310310

311311
// IssuePayload represents the payload information that is sent along with an issue event.
312312
type IssuePayload struct {
313-
Action HookIssueAction `json:"action"`
314-
Index int64 `json:"number"`
315-
Changes *ChangesPayload `json:"changes,omitempty"`
316-
Issue *Issue `json:"issue"`
317-
Repository *Repository `json:"repository"`
318-
Sender *User `json:"sender"`
319-
CommitID string `json:"commit_id"`
313+
Action HookIssueAction `json:"action"`
314+
Index int64 `json:"number"`
315+
Changes *ChangesPayload `json:"changes,omitempty"`
316+
RemovedLabels []*Label `json:"removed_labels"`
317+
Issue *Issue `json:"issue"`
318+
Repository *Repository `json:"repository"`
319+
Sender *User `json:"sender"`
320+
CommitID string `json:"commit_id"`
320321
}
321322

322323
// JSONPayload encodes the IssuePayload to JSON, with an indentation of two spaces.

0 commit comments

Comments
 (0)