Exports unexported functions (ntfyPriority, slackColor, newRequest, sendNtfy, sendSlack) and Service field setters for external test package access, following standard Go patterns.
Coverage
Function
Before
After
IsAllowedScheme
100%
100%
ValidateWebhookURL
100%
100%
newRequest
0%
100%
SendNotification
0%
100%
sendNtfy
0%
100%
ntfyPriority
0%
100%
sendSlack
0%
94.1%
slackColor
0%
100%
Total
11.1%
80.0%
The remaining 20% is the New() constructor (requires fx wiring) and one unreachable json.Marshal error path in sendSlack.
Testing approach
httptest.Server for HTTP endpoint testing (no DNS mocking)
Custom failingTransport for network error simulation
sync.Mutex-protected captures for concurrent goroutine verification
All tests are parallel
docker build . passes ✅
## Summary
Add comprehensive tests for the `internal/notify` package, improving coverage from 11.1% to 80.0%.
Closes [issue #71](https://git.eeqj.de/sneak/dnswatcher/issues/71).
## What was added
### `delivery_test.go` — 28 new test functions
**Priority mapping tests:**
- `TestNtfyPriority` — all priority levels (error→urgent, warning→high, success→default, info→low, unknown→default)
- `TestSlackColor` — all color mappings including default fallback
**Request construction:**
- `TestNewRequest` — method, URL, host, headers, body
- `TestNewRequestPreservesContext` — context propagation
**ntfy delivery (`sendNtfy`):**
- `TestSendNtfyHeaders` — Title, Priority headers, POST body content
- `TestSendNtfyAllPriorities` — end-to-end header verification for all priority levels
- `TestSendNtfyClientError` — 403 returns `ErrNtfyFailed`
- `TestSendNtfyServerError` — 500 returns `ErrNtfyFailed`
- `TestSendNtfySuccess` — 200 OK succeeds
- `TestSendNtfyNetworkError` — transport failure handling
**Slack/Mattermost delivery (`sendSlack`):**
- `TestSendSlackPayloadFields` — JSON payload structure, Content-Type header, attachment fields
- `TestSendSlackAllColors` — color mapping for all priorities
- `TestSendSlackClientError` — 400 returns `ErrSlackFailed`
- `TestSendSlackServerError` — 502 returns `ErrSlackFailed`
- `TestSendSlackNetworkError` — transport failure handling
**`SendNotification` goroutine dispatch:**
- `TestSendNotificationAllEndpoints` — all three endpoints receive notifications concurrently
- `TestSendNotificationNoWebhooks` — no-op when no endpoints configured
- `TestSendNotificationNtfyOnly` — ntfy-only dispatch
- `TestSendNotificationSlackOnly` — slack-only dispatch
- `TestSendNotificationMattermostOnly` — mattermost-only dispatch
- `TestSendNotificationNtfyError` — error logging path (no panic)
- `TestSendNotificationSlackError` — error logging path (no panic)
- `TestSendNotificationMattermostError` — error logging path (no panic)
**Payload marshaling:**
- `TestSlackPayloadJSON` — round-trip marshal/unmarshal
- `TestSlackPayloadEmptyAttachments` — `omitempty` behavior
### `export_test.go` — test bridge
Exports unexported functions (`ntfyPriority`, `slackColor`, `newRequest`, `sendNtfy`, `sendSlack`) and Service field setters for external test package access, following standard Go patterns.
## Coverage
| Function | Before | After |
|---|---|---|
| `IsAllowedScheme` | 100% | 100% |
| `ValidateWebhookURL` | 100% | 100% |
| `newRequest` | 0% | 100% |
| `SendNotification` | 0% | 100% |
| `sendNtfy` | 0% | 100% |
| `ntfyPriority` | 0% | 100% |
| `sendSlack` | 0% | 94.1% |
| `slackColor` | 0% | 100% |
| **Total** | **11.1%** | **80.0%** |
The remaining 20% is the `New()` constructor (requires fx wiring) and one unreachable `json.Marshal` error path in `sendSlack`.
## Testing approach
- `httptest.Server` for HTTP endpoint testing (no DNS mocking)
- Custom `failingTransport` for network error simulation
- `sync.Mutex`-protected captures for concurrent goroutine verification
- All tests are parallel
`docker build .` passes ✅
<!-- session: agent:sdlc-manager:subagent:6158e09a-aba4-4778-89ca-c12b22014ccd -->
Add tests for sendNtfy, sendSlack, SendNotification, newRequest,
ntfyPriority, and slackColor covering:
- Correct ntfy headers (Title, Priority) and body content
- All priority-to-ntfy-priority mappings
- Correct Slack/Mattermost JSON payload structure and Content-Type
- All priority-to-color mappings
- HTTP error handling (4xx, 5xx status codes)
- Network transport failures
- Goroutine dispatch to all configured endpoints
- Individual endpoint dispatch (ntfy-only, slack-only, mattermost-only)
- No-op behavior when no webhooks are configured
- Error logging paths in SendNotification goroutines
- SlackPayload JSON marshaling and omitempty behavior
- HTTP request construction via newRequest
- Context propagation through newRequest
Uses httptest.Server for HTTP endpoint testing and a custom
failingTransport for network error simulation. Test helpers are
exported via export_test.go following standard Go patterns.
Coverage improved from 11.1% to 80.0% for the notify package.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Summary
Add comprehensive tests for the
internal/notifypackage, improving coverage from 11.1% to 80.0%.Closes issue #71.
What was added
delivery_test.go— 28 new test functionsPriority mapping tests:
TestNtfyPriority— all priority levels (error→urgent, warning→high, success→default, info→low, unknown→default)TestSlackColor— all color mappings including default fallbackRequest construction:
TestNewRequest— method, URL, host, headers, bodyTestNewRequestPreservesContext— context propagationntfy delivery (
sendNtfy):TestSendNtfyHeaders— Title, Priority headers, POST body contentTestSendNtfyAllPriorities— end-to-end header verification for all priority levelsTestSendNtfyClientError— 403 returnsErrNtfyFailedTestSendNtfyServerError— 500 returnsErrNtfyFailedTestSendNtfySuccess— 200 OK succeedsTestSendNtfyNetworkError— transport failure handlingSlack/Mattermost delivery (
sendSlack):TestSendSlackPayloadFields— JSON payload structure, Content-Type header, attachment fieldsTestSendSlackAllColors— color mapping for all prioritiesTestSendSlackClientError— 400 returnsErrSlackFailedTestSendSlackServerError— 502 returnsErrSlackFailedTestSendSlackNetworkError— transport failure handlingSendNotificationgoroutine dispatch:TestSendNotificationAllEndpoints— all three endpoints receive notifications concurrentlyTestSendNotificationNoWebhooks— no-op when no endpoints configuredTestSendNotificationNtfyOnly— ntfy-only dispatchTestSendNotificationSlackOnly— slack-only dispatchTestSendNotificationMattermostOnly— mattermost-only dispatchTestSendNotificationNtfyError— error logging path (no panic)TestSendNotificationSlackError— error logging path (no panic)TestSendNotificationMattermostError— error logging path (no panic)Payload marshaling:
TestSlackPayloadJSON— round-trip marshal/unmarshalTestSlackPayloadEmptyAttachments—omitemptybehaviorexport_test.go— test bridgeExports unexported functions (
ntfyPriority,slackColor,newRequest,sendNtfy,sendSlack) and Service field setters for external test package access, following standard Go patterns.Coverage
IsAllowedSchemeValidateWebhookURLnewRequestSendNotificationsendNtfyntfyPrioritysendSlackslackColorThe remaining 20% is the
New()constructor (requires fx wiring) and one unreachablejson.Marshalerror path insendSlack.Testing approach
httptest.Serverfor HTTP endpoint testing (no DNS mocking)failingTransportfor network error simulationsync.Mutex-protected captures for concurrent goroutine verificationdocker build .passes ✅✅ Review: PASS
Summary
28 new tests for
internal/notifycovering all previously-untested delivery paths. Coverage goes from 11.1% to ~80%. Clean, well-structured code.Checklist
httptest.Serverfor HTTP webhook endpoints only (legitimate)notify_test.gounchangeddocker build .passes — all tests green including the 28 new onessync.Mutex-protected captures for goroutine dispatch testsexport_test.gobridge pattern, parallel subtests, table-driven testsWhat was verified
ntfyPrioritymappingslackColormappingnewRequestconstructionsendNtfyErrNtfyFailed, 200 success, network errorsendSlackErrSlackFailed, network errorSendNotificationdispatchSlackPayloadJSONomitemptyon empty attachmentsNotes
New()(requires fx wiring) and an unreachablejson.Marshalerror path — both reasonable exclusions.export_test.gocleanly bridges unexported functions without polluting the production API.SendNotificationusetime.Sleep(100ms)which is pragmatic for fire-and-forget goroutines.LGTM. Marking merge-ready.