notify: a failed Mattermost delivery's error names Mattermost (closes #227)
check / check (push) Canceled after 0s

Mattermost is sent by the Slack sender, which wrapped every HTTP error
status in ErrSlackFailed, so a Mattermost endpoint answering 503 was
logged as "slack notification failed". The sender now takes the error
to wrap: the Slack endpoint passes ErrSlackFailed and the Mattermost
endpoint passes ErrMattermostFailed, which was defined but unused.

A new delivery test sets both endpoints to a stand-in server answering
503 and checks the error logged for each names its own endpoint.

Model: opus-5-5
This commit was merged in pull request #240.
This commit is contained in:
2026-10-02 08:40:19 +02:00
parent ceb24c5004
commit 26c9c74d8e
4 changed files with 82 additions and 7 deletions
+9 -3
View File
@@ -277,7 +277,8 @@ func (svc *Service) dispatchSlack(
svc.dispatch(ctx, "slack", func(c context.Context) error {
return svc.sendSlack(
c, svc.slackWebhookURL, title, message, priority,
c, svc.slackWebhookURL, ErrSlackFailed,
title, message, priority,
)
})
}
@@ -294,7 +295,7 @@ func (svc *Service) dispatchMattermost(
ctx, "mattermost",
func(c context.Context) error {
return svc.sendSlack(
c, svc.mattermostWebhookURL,
c, svc.mattermostWebhookURL, ErrMattermostFailed,
title, message, priority,
)
},
@@ -370,9 +371,14 @@ type SlackAttachment struct {
Text string `json:"text"`
}
// sendSlack posts to a Slack or Mattermost incoming webhook, which
// take the same payload. An HTTP error status is returned wrapped in
// failed, ErrSlackFailed or ErrMattermostFailed, so the error names
// the endpoint.
func (svc *Service) sendSlack(
ctx context.Context,
webhookURL *url.URL,
failed error,
title, message, priority string,
) error {
ctx, cancel := context.WithTimeout(
@@ -420,7 +426,7 @@ func (svc *Service) sendSlack(
if resp.StatusCode >= httpStatusClientError {
return fmt.Errorf(
"%w: status %d",
ErrSlackFailed, resp.StatusCode,
failed, resp.StatusCode,
)
}