fix(notifications): improve torrent and automation event samples - #1501
Conversation
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
WalkthroughRefactors torrent notification handling: centralizes event builders, adds delayed refresh-before-send logic for added-torrent notifications, encapsulates tracker/tag parsing, expands automation sampling to include torrent context, and gates/injects torrent metrics with human-readable formatting in notifications. Changes
Sequence DiagramsequenceDiagram
participant Caller
participant Scheduler as Timer/Scheduler
participant TorrentSync
participant Notifier
Caller->>Scheduler: notifyTorrentAddedWithDelay(ctx, instanceID, torrent)
Scheduler->>Scheduler: wait torrentAddedNotificationDelay
Scheduler->>TorrentSync: refreshTorrentForNotification(hash) (timeout torrentAddedRefreshTimeout)
alt Refresh success
TorrentSync-->>Scheduler: refreshedTorrent
Scheduler->>Notifier: emit EventTorrentAdded (refreshed data)
else Refresh failed
TorrentSync-->>Scheduler: failure
Scheduler->>Notifier: emit EventTorrentAdded (initial snapshot)
end
Notifier-->>Caller: notification dispatched
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Suggested labels
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: e514bcf773
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (3)
internal/services/notifications/notifiarr_api_test.go (1)
170-214: Consider merging torrent-field tests into a table-driven case.This test is solid, but combining it with the existing non-zero metrics test in a table-driven pattern would reduce duplication and align with repo test style.
As per coding guidelines, "Prefer table-driven test cases and reuse integration fixtures in
internal/qbittorrent/".🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@internal/services/notifications/notifiarr_api_test.go` around lines 170 - 214, Combine the zero-value test (TestBuildNotifiarrAPIDataIncludesZeroValueTorrentMetrics) and the existing non-zero-metrics test into a single table-driven test that iterates cases with a name and Event input, runs svc.buildNotifiarrAPIData for each case inside t.Run, and asserts the same set of torrent field expectations per case; locate the Event struct instantiations and the buildNotifiarrAPIData call to create a []struct{ name string; event Event; } cases slice, loop over cases, and move the repeated require.* assertions into the loop so both zero and non-zero metric scenarios are covered without duplication.internal/services/automations/summary_test.go (1)
138-168: Consider table-driven cases for the new sampling tests.These two tests are very similar in shape; a small table would reduce duplication and ease future additions.
As per coding guidelines, "Prefer table-driven test cases and reuse integration fixtures in
internal/qbittorrent/".🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@internal/services/automations/summary_test.go` around lines 138 - 168, Replace the two nearly identical tests with a single table-driven test that iterates cases (e.g., name, setup type: call recordActivity with models.ActivityAction*/Outcome*/TorrentName or call addTorrentSamples with the sample list and limit, and expected substrings) and for each case create newAutomationSummary(), perform the setup (recordActivity or addTorrentSamples), call summary.message(), and assert the expected substrings are present; reference the existing functions newAutomationSummary(), summary.recordActivity(), summary.addTorrentSamples(), and summary.message() and, when applicable, reuse integration fixtures from internal/qbittorrent/ to drive inputs rather than duplicating sample data.internal/services/notifications/service_format_test.go (1)
13-69: Suggestion: fold the added/completed cases into a table-driven test.Both tests share structure and assertions; a table-driven format would cut repetition and keep future additions simpler.
As per coding guidelines, "Prefer table-driven test cases and reuse integration fixtures in
internal/qbittorrent/".🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@internal/services/notifications/service_format_test.go` around lines 13 - 69, The two nearly identical tests TestFormatEventTorrentAddedIncludesMetricLines and TestFormatEventTorrentCompletedIncludesMetricLines should be merged into a single table-driven test: create a slice of cases (each with a name, Event input using EventTorrentAdded or EventTorrentCompleted, expected title, and expected substrings), instantiate svc := &Service{} once, loop cases with t.Run(case.name, func(t *testing.T) { t.Parallel(); title, message := svc.formatEvent(context.Background(), case.event); require.Equal(t, case.expectedTitle, title); for _, sub := range case.expectedSubstrings { require.Contains(t, message, sub) } }), and remove the two original functions; reference the Service.formatEvent method and the Event constants EventTorrentAdded and EventTorrentCompleted when locating code to change.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@cmd/qui/torrent_notifications.go`:
- Around line 27-36: buildTorrentCompletedEvent currently returns a
notifications.Event without the metric fields formatEvent expects for
completed-torrent lines; update buildTorrentCompletedEvent to populate the same
metric fields you set for added events on the notifications.Event it returns
(use the incoming torrent and syncManager to fill metrics such as the torrent
size, bytes completed/downloaded, upload bytes/ratio, completion timestamp, and
any ETA/transfer-rate fields your formatEvent emits). Locate
buildTorrentCompletedEvent and the notifications.Event struct construction and
add assignments for those metric fields using torrent.* properties and any
helper functions you already use for added events so completed events no longer
show zeroed stats.
---
Nitpick comments:
In `@internal/services/automations/summary_test.go`:
- Around line 138-168: Replace the two nearly identical tests with a single
table-driven test that iterates cases (e.g., name, setup type: call
recordActivity with models.ActivityAction*/Outcome*/TorrentName or call
addTorrentSamples with the sample list and limit, and expected substrings) and
for each case create newAutomationSummary(), perform the setup (recordActivity
or addTorrentSamples), call summary.message(), and assert the expected
substrings are present; reference the existing functions newAutomationSummary(),
summary.recordActivity(), summary.addTorrentSamples(), and summary.message()
and, when applicable, reuse integration fixtures from internal/qbittorrent/ to
drive inputs rather than duplicating sample data.
In `@internal/services/notifications/notifiarr_api_test.go`:
- Around line 170-214: Combine the zero-value test
(TestBuildNotifiarrAPIDataIncludesZeroValueTorrentMetrics) and the existing
non-zero-metrics test into a single table-driven test that iterates cases with a
name and Event input, runs svc.buildNotifiarrAPIData for each case inside t.Run,
and asserts the same set of torrent field expectations per case; locate the
Event struct instantiations and the buildNotifiarrAPIData call to create a
[]struct{ name string; event Event; } cases slice, loop over cases, and move the
repeated require.* assertions into the loop so both zero and non-zero metric
scenarios are covered without duplication.
In `@internal/services/notifications/service_format_test.go`:
- Around line 13-69: The two nearly identical tests
TestFormatEventTorrentAddedIncludesMetricLines and
TestFormatEventTorrentCompletedIncludesMetricLines should be merged into a
single table-driven test: create a slice of cases (each with a name, Event input
using EventTorrentAdded or EventTorrentCompleted, expected title, and expected
substrings), instantiate svc := &Service{} once, loop cases with
t.Run(case.name, func(t *testing.T) { t.Parallel(); title, message :=
svc.formatEvent(context.Background(), case.event); require.Equal(t,
case.expectedTitle, title); for _, sub := range case.expectedSubstrings {
require.Contains(t, message, sub) } }), and remove the two original functions;
reference the Service.formatEvent method and the Event constants
EventTorrentAdded and EventTorrentCompleted when locating code to change.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: cdeb8e5ce0
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
🧹 Nitpick comments (1)
cmd/qui/torrent_notifications.go (1)
99-102: Minor: Redundantcontext.WithoutCancelcall.The caller (
notifyTorrentAddedWithDelayAfter) already wraps the context withcontext.WithoutCancelat line 77. Applying it again here is harmless but unnecessary.Consider simplifying to:
- refreshCtx := context.Background() - if ctx != nil { - refreshCtx = context.WithoutCancel(ctx) - } - refreshCtx, cancel := context.WithTimeout(refreshCtx, torrentAddedRefreshTimeout) + refreshCtx, cancel := context.WithTimeout(ctx, torrentAddedRefreshTimeout)This would require ensuring callers always pass a non-nil context, which is currently guaranteed by the goroutine in
notifyTorrentAddedWithDelayAfter.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@cmd/qui/torrent_notifications.go` around lines 99 - 102, The refreshCtx assignment in notifyTorrentAddedWithDelayAfter redundantly calls context.WithoutCancel again; remove the extra context.WithoutCancel usage and simply set refreshCtx = ctx (or assume ctx is non-nil) so you don't wrap the context twice—update the code in notifyTorrentAddedWithDelayAfter to rely on the caller's earlier context.WithoutCancel (ensure callers of notifyTorrentAddedWithDelayAfter, e.g., the goroutine that spawns it, always pass a non-nil context).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Duplicate comments:
In `@cmd/qui/torrent_notifications.go`:
- Around line 27-37: buildTorrentCompletedEvent currently omits the metric
fields that buildTorrentAddedEvent populates, causing formatEvent to render
zeroed stats for completed torrents; update buildTorrentCompletedEvent to set
the same metrics as buildTorrentAddedEvent (copy the metric-related fields from
the torrent into the returned notifications.Event) so completed events include
Size/SizeLeft/Percent/Ratio/seed/peer counts and transfer speeds/time-remaining
(i.e., mirror the metric field assignments present in buildTorrentAddedEvent);
ensure you still call trackerDomainForTorrent and parseTorrentTags as in the
current function.
---
Nitpick comments:
In `@cmd/qui/torrent_notifications.go`:
- Around line 99-102: The refreshCtx assignment in
notifyTorrentAddedWithDelayAfter redundantly calls context.WithoutCancel again;
remove the extra context.WithoutCancel usage and simply set refreshCtx = ctx (or
assume ctx is non-nil) so you don't wrap the context twice—update the code in
notifyTorrentAddedWithDelayAfter to rely on the caller's earlier
context.WithoutCancel (ensure callers of notifyTorrentAddedWithDelayAfter, e.g.,
the goroutine that spawns it, always pass a non-nil context).
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: d277b9fd48
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@internal/services/notifications/service.go`:
- Around line 505-518: The loop that scales `speed` uses a compound condition
accessing `units[i]` (units and index i) which flags a possible out-of-range
access; change the loop to iterate only on the index bound (e.g., for i := 1; i
< len(units); i++) and inside the body break when `speed < 1000`, updating
`speed` and setting `unit = units[i]` only after ensuring i is within range;
keep the final B/s special-case using the original `value` and return the
formatted string as before (update the loop in the same function where `units`,
`speed`, `unit` are defined).
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@internal/services/notifications/service.go`:
- Around line 178-181: The current guard in the send loop uses
s.formatEvent(...) and skips sending when message is blank, which drops valid
title-only notifications (formatCustomEvent returns title, "" for custom
events); update the guard in the loop that calls s.formatEvent (around the
title, message := s.formatEvent(ctx, event, ...)) to only continue/skip when
both strings.TrimSpace(title) == "" && strings.TrimSpace(message) == "" so
title-only notifications (e.g. EventCrossSeed* and EventAutomations* produced by
formatCustomEvent) are sent.
This PR contains the following updates: | Package | Update | Change | |---|---|---| | [ghcr.io/autobrr/qui](https://github.com/autobrr/qui) | patch | `v1.14.0` → `v1.14.1` | --- ### Release Notes <details> <summary>autobrr/qui (ghcr.io/autobrr/qui)</summary> ### [`v1.14.1`](https://github.com/autobrr/qui/releases/tag/v1.14.1) [Compare Source](autobrr/qui@v1.14.0...v1.14.1) #### Changelog ##### Bug Fixes - [`6f3171c`](autobrr/qui@6f3171c): fix(auth): avoid chromium basic auth resets on expired qui sessions ([#​1495](autobrr/qui#1495)) ([@​s0up4200](https://github.com/s0up4200)) - [`d5acab7`](autobrr/qui@d5acab7): fix(automations): make managed full tag sync idempotent ([#​1494](autobrr/qui#1494)) ([@​s0up4200](https://github.com/s0up4200)) - [`9fdc949`](autobrr/qui@9fdc949): fix(crossseed): avoid webhook notification spam on pending matches ([#​1496](autobrr/qui#1496)) ([@​s0up4200](https://github.com/s0up4200)) - [`350edb3`](autobrr/qui@350edb3): fix(notifications): improve torrent and automation event samples ([#​1501](autobrr/qui#1501)) ([@​s0up4200](https://github.com/s0up4200)) - [`e2c032c`](autobrr/qui@e2c032c): fix(qbittorrent): ignore pseudo tracker labels in domain parsing ([#​1489](autobrr/qui#1489)) ([@​s0up4200](https://github.com/s0up4200)) - [`e00c70d`](autobrr/qui@e00c70d): fix(qbittorrent): repair unified torrent sorting ([#​1490](autobrr/qui#1490)) ([@​s0up4200](https://github.com/s0up4200)) - [`4d95325`](autobrr/qui@4d95325): fix(themes): clarify crypto donation discount flow ([#​1503](autobrr/qui#1503)) ([@​s0up4200](https://github.com/s0up4200)) - [`579ea8d`](autobrr/qui@579ea8d): fix(themes): clarify premium checkout and crypto flow ([@​s0up4200](https://github.com/s0up4200)) - [`ece956a`](autobrr/qui@ece956a): fix(torrents): scope instance column to unified cross-instance data ([#​1502](autobrr/qui#1502)) ([@​s0up4200](https://github.com/s0up4200)) - [`27cf6d2`](autobrr/qui@27cf6d2): fix(ui): hide unified entry when one instance is active ([#​1493](autobrr/qui#1493)) ([@​s0up4200](https://github.com/s0up4200)) - [`80d911f`](autobrr/qui@80d911f): fix(ui): remove single-instance indent in header menu ([@​s0up4200](https://github.com/s0up4200)) - [`6ae7625`](autobrr/qui@6ae7625): fix(ui): restore instances header in menu ([@​s0up4200](https://github.com/s0up4200)) ##### Other Changes - [`336dc9a`](autobrr/qui@336dc9a): docs: update payment info ([#​1424](autobrr/qui#1424)) ([@​s0up4200](https://github.com/s0up4200)) **Full Changelog**: <autobrr/qui@v1.14.0...v1.14.1> #### Docker images - `docker pull ghcr.io/autobrr/qui:v1.14.1` - `docker pull ghcr.io/autobrr/qui:latest` #### What to do next? - Join our [Discord server](https://discord.autobrr.com/qui) Thank you for using qui! </details> --- ### Configuration 📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined). 🚦 **Automerge**: Enabled. ♻ **Rebasing**: Whenever PR is behind base branch, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box --- This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate). <!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4yNS43IiwidXBkYXRlZEluVmVyIjoiNDMuMjUuNyIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOlsiYXV0b21lcmdlIiwiaW1hZ2UiXX0=--> Reviewed-on: https://gitea.alexlebens.dev/alexlebens/infrastructure/pulls/4187 Co-authored-by: Renovate Bot <renovate-bot@alexlebens.net> Co-committed-by: Renovate Bot <renovate-bot@alexlebens.net>
Summary
torrent_addednotifications by 10 seconds and refresh torrent stats before dispatchValidated with
go test ./cmd/qui/... ./internal/services/notifications/... ./internal/services/automations/...and earlier fullmake test/make buildin this branch context.Summary by CodeRabbit
New Features
Improvements
Tests
Documentation