@@ -576,7 +576,7 @@ func TestNotifierPaused(t *testing.T) {
576576ctx := dbauthz .AsSystemRestricted (testutil .Context (t ,testutil .WaitSuperLong ))
577577_ ,_ ,api := coderdtest .NewWithAPI (t ,nil )
578578
579- // Prepare the test
579+ // Prepare the test.
580580handler := & fakeHandler {}
581581method := database .NotificationMethodSmtp
582582user := createSampleUser (t ,api .Database )
@@ -593,32 +593,39 @@ func TestNotifierPaused(t *testing.T) {
593593enq ,err := notifications .NewStoreEnqueuer (cfg ,api .Database ,defaultHelpers (),api .Logger .Named ("enqueuer" ),quartz .NewReal ())
594594require .NoError (t ,err )
595595
596- mgr .Run (ctx )
597-
598596// Pause the notifier.
599597settingsJSON ,err := json .Marshal (& codersdk.NotificationsSettings {NotifierPaused :true })
600598require .NoError (t ,err )
601599err = api .Database .UpsertNotificationsSettings (ctx ,string (settingsJSON ))
602600require .NoError (t ,err )
603601
602+ // Start the manager so that notifications are processed, except it will be paused at this point.
603+ // If it is started before pausing, there's a TOCTOU possibility between checking whether the notifier is paused or
604+ // not, and processing the messages (see notifier.run).
605+ mgr .Run (ctx )
606+
604607// Notifier is paused, enqueue the next message.
605608sid ,err := enq .Enqueue (ctx ,user .ID ,notifications .TemplateWorkspaceDeleted ,map [string ]string {"type" :"success" ,"i" :"1" },"test" )
606609require .NoError (t ,err )
607610
608- // Sleep for a few fetch intervals to be sure we aren't getting false-positives in the next step.
609- // TODO: use quartz instead.
610- time .Sleep (fetchInterval * 5 )
611-
612611// Ensure we have a pending message and it's the expected one.
612+ pendingMessages ,err := api .Database .GetNotificationMessagesByStatus (ctx , database.GetNotificationMessagesByStatusParams {
613+ Status :database .NotificationMessageStatusPending ,
614+ Limit :10 ,
615+ })
616+ require .NoError (t ,err )
617+ require .Len (t ,pendingMessages ,1 )
618+ require .Equal (t ,pendingMessages [0 ].ID .String (),sid .String ())
619+
620+ // Wait a few fetch intervals to be sure that no new notifications are being sent.
621+ // TODO: use quartz instead.
622+ // nolint:gocritic // These magic numbers are fine.
613623require .Eventually (t ,func ()bool {
614- pendingMessages ,err := api .Database .GetNotificationMessagesByStatus (ctx , database.GetNotificationMessagesByStatusParams {
615- Status :database .NotificationMessageStatusPending ,
616- Limit :10 ,
617- })
618- assert .NoError (t ,err )
619- return len (pendingMessages )== 1 &&
620- pendingMessages [0 ].ID .String ()== sid .String ()
621- },testutil .WaitShort ,testutil .IntervalFast )
624+ handler .mu .RLock ()
625+ defer handler .mu .RUnlock ()
626+
627+ return len (handler .succeeded )+ len (handler .failed )== 0
628+ },fetchInterval * 5 ,testutil .IntervalFast )
622629
623630// Unpause the notifier.
624631settingsJSON ,err = json .Marshal (& codersdk.NotificationsSettings {NotifierPaused :false })
@@ -627,11 +634,12 @@ func TestNotifierPaused(t *testing.T) {
627634require .NoError (t ,err )
628635
629636// Notifier is running again, message should be dequeued.
637+ // nolint:gocritic // These magic numbers are fine.
630638require .Eventually (t ,func ()bool {
631639handler .mu .RLock ()
632640defer handler .mu .RUnlock ()
633641return slices .Contains (handler .succeeded ,sid .String ())
634- },testutil . WaitShort ,testutil .IntervalFast )
642+ },fetchInterval * 5 ,testutil .IntervalFast )
635643}
636644
637645//go:embed events.go