diff --git a/coderd/database/dbauthz/dbauthz.go b/coderd/database/dbauthz/dbauthz.go index 096056201a..c040df0619 100644 --- a/coderd/database/dbauthz/dbauthz.go +++ b/coderd/database/dbauthz/dbauthz.go @@ -236,20 +236,23 @@ var ( Identifier: rbac.RoleIdentifier{Name: "system"}, DisplayName: "Coder", Site: rbac.Permissions(map[string][]policy.Action{ - rbac.ResourceWildcard.Type: {policy.ActionRead}, - rbac.ResourceApiKey.Type: rbac.ResourceApiKey.AvailableActions(), - rbac.ResourceGroup.Type: {policy.ActionCreate, policy.ActionUpdate}, - rbac.ResourceAssignRole.Type: rbac.ResourceAssignRole.AvailableActions(), - rbac.ResourceAssignOrgRole.Type: rbac.ResourceAssignOrgRole.AvailableActions(), - rbac.ResourceSystem.Type: {policy.WildcardSymbol}, - rbac.ResourceOrganization.Type: {policy.ActionCreate, policy.ActionRead}, - rbac.ResourceOrganizationMember.Type: {policy.ActionCreate}, - rbac.ResourceProvisionerDaemon.Type: {policy.ActionCreate, policy.ActionUpdate}, - rbac.ResourceProvisionerKeys.Type: {policy.ActionCreate, policy.ActionRead, policy.ActionDelete}, - rbac.ResourceUser.Type: rbac.ResourceUser.AvailableActions(), - rbac.ResourceWorkspaceDormant.Type: {policy.ActionUpdate, policy.ActionDelete, policy.ActionWorkspaceStop}, - rbac.ResourceWorkspace.Type: {policy.ActionUpdate, policy.ActionDelete, policy.ActionWorkspaceStart, policy.ActionWorkspaceStop, policy.ActionSSH}, - rbac.ResourceWorkspaceProxy.Type: {policy.ActionCreate, policy.ActionUpdate, policy.ActionDelete}, + rbac.ResourceWildcard.Type: {policy.ActionRead}, + rbac.ResourceApiKey.Type: rbac.ResourceApiKey.AvailableActions(), + rbac.ResourceGroup.Type: {policy.ActionCreate, policy.ActionUpdate}, + rbac.ResourceAssignRole.Type: rbac.ResourceAssignRole.AvailableActions(), + rbac.ResourceAssignOrgRole.Type: rbac.ResourceAssignOrgRole.AvailableActions(), + rbac.ResourceSystem.Type: {policy.WildcardSymbol}, + rbac.ResourceOrganization.Type: {policy.ActionCreate, policy.ActionRead}, + rbac.ResourceOrganizationMember.Type: {policy.ActionCreate}, + rbac.ResourceProvisionerDaemon.Type: {policy.ActionCreate, policy.ActionUpdate}, + rbac.ResourceProvisionerKeys.Type: {policy.ActionCreate, policy.ActionRead, policy.ActionDelete}, + rbac.ResourceUser.Type: rbac.ResourceUser.AvailableActions(), + rbac.ResourceWorkspaceDormant.Type: {policy.ActionUpdate, policy.ActionDelete, policy.ActionWorkspaceStop}, + rbac.ResourceWorkspace.Type: {policy.ActionUpdate, policy.ActionDelete, policy.ActionWorkspaceStart, policy.ActionWorkspaceStop, policy.ActionSSH}, + rbac.ResourceWorkspaceProxy.Type: {policy.ActionCreate, policy.ActionUpdate, policy.ActionDelete}, + rbac.ResourceDeploymentConfig.Type: {policy.ActionCreate, policy.ActionUpdate, policy.ActionDelete}, + rbac.ResourceNotificationPreference.Type: {policy.ActionCreate, policy.ActionUpdate, policy.ActionDelete}, + rbac.ResourceNotificationTemplate.Type: {policy.ActionCreate, policy.ActionUpdate, policy.ActionDelete}, }), Org: map[string][]rbac.Permission{}, User: []rbac.Permission{}, diff --git a/coderd/notifications/manager_test.go b/coderd/notifications/manager_test.go index 4b7eff4a01..ddbdb0b518 100644 --- a/coderd/notifications/manager_test.go +++ b/coderd/notifications/manager_test.go @@ -15,7 +15,9 @@ import ( "github.com/coder/quartz" "github.com/coder/serpent" + "github.com/coder/coder/v2/coderd/coderdtest" "github.com/coder/coder/v2/coderd/database" + "github.com/coder/coder/v2/coderd/database/dbauthz" "github.com/coder/coder/v2/coderd/database/dbgen" "github.com/coder/coder/v2/coderd/notifications" "github.com/coder/coder/v2/coderd/notifications/dispatch" @@ -27,31 +29,34 @@ func TestBufferedUpdates(t *testing.T) { t.Parallel() // setup - ctx, logger, db := setupInMemory(t) - interceptor := &syncInterceptor{Store: db} + // nolint:gocritic // Unit test. + ctx := dbauthz.AsSystemRestricted(testutil.Context(t, testutil.WaitSuperLong)) + _, _, api := coderdtest.NewWithAPI(t, nil) + + interceptor := &syncInterceptor{Store: api.Database} santa := &santaHandler{} cfg := defaultNotificationsConfig(database.NotificationMethodSmtp) cfg.StoreSyncInterval = serpent.Duration(time.Hour) // Ensure we don't sync the store automatically. // GIVEN: a manager which will pass or fail notifications based on their "nice" labels - mgr, err := notifications.NewManager(cfg, interceptor, defaultHelpers(), createMetrics(), logger.Named("notifications-manager")) + mgr, err := notifications.NewManager(cfg, interceptor, defaultHelpers(), createMetrics(), api.Logger.Named("notifications-manager")) require.NoError(t, err) mgr.WithHandlers(map[database.NotificationMethod]notifications.Handler{ database.NotificationMethodSmtp: santa, }) - enq, err := notifications.NewStoreEnqueuer(cfg, interceptor, defaultHelpers(), logger.Named("notifications-enqueuer"), quartz.NewReal()) + enq, err := notifications.NewStoreEnqueuer(cfg, interceptor, defaultHelpers(), api.Logger.Named("notifications-enqueuer"), quartz.NewReal()) require.NoError(t, err) - user := dbgen.User(t, db, database.User{}) + user := dbgen.User(t, api.Database, database.User{}) // WHEN: notifications are enqueued which should succeed and fail - _, err = enq.Enqueue(ctx, user.ID, notifications.TemplateWorkspaceDeleted, map[string]string{"nice": "true"}, "") // Will succeed. + _, err = enq.Enqueue(ctx, user.ID, notifications.TemplateWorkspaceDeleted, map[string]string{"nice": "true", "i": "0"}, "") // Will succeed. require.NoError(t, err) - _, err = enq.Enqueue(ctx, user.ID, notifications.TemplateWorkspaceDeleted, map[string]string{"nice": "true"}, "") // Will succeed. + _, err = enq.Enqueue(ctx, user.ID, notifications.TemplateWorkspaceDeleted, map[string]string{"nice": "true", "i": "1"}, "") // Will succeed. require.NoError(t, err) - _, err = enq.Enqueue(ctx, user.ID, notifications.TemplateWorkspaceDeleted, map[string]string{"nice": "false"}, "") // Will fail. + _, err = enq.Enqueue(ctx, user.ID, notifications.TemplateWorkspaceDeleted, map[string]string{"nice": "false", "i": "2"}, "") // Will fail. require.NoError(t, err) mgr.Run(ctx) @@ -95,7 +100,10 @@ func TestBuildPayload(t *testing.T) { t.Parallel() // SETUP - ctx, logger, db := setupInMemory(t) + + // nolint:gocritic // Unit test. + ctx := dbauthz.AsSystemRestricted(testutil.Context(t, testutil.WaitSuperLong)) + _, _, api := coderdtest.NewWithAPI(t, nil) // GIVEN: a set of helpers to be injected into the templates const label = "Click here!" @@ -107,7 +115,7 @@ func TestBuildPayload(t *testing.T) { } // GIVEN: an enqueue interceptor which returns mock metadata - interceptor := newEnqueueInterceptor(db, + interceptor := newEnqueueInterceptor(api.Database, // Inject custom message metadata to influence the payload construction. func() database.FetchNewMessageMetadataRow { // Inject template actions which use injected help functions. @@ -129,7 +137,7 @@ func TestBuildPayload(t *testing.T) { } }) - enq, err := notifications.NewStoreEnqueuer(defaultNotificationsConfig(database.NotificationMethodSmtp), interceptor, helpers, logger.Named("notifications-enqueuer"), quartz.NewReal()) + enq, err := notifications.NewStoreEnqueuer(defaultNotificationsConfig(database.NotificationMethodSmtp), interceptor, helpers, api.Logger.Named("notifications-enqueuer"), quartz.NewReal()) require.NoError(t, err) // WHEN: a notification is enqueued @@ -149,10 +157,13 @@ func TestStopBeforeRun(t *testing.T) { t.Parallel() // SETUP - ctx, logger, db := setupInMemory(t) + + // nolint:gocritic // Unit test. + ctx := dbauthz.AsSystemRestricted(testutil.Context(t, testutil.WaitSuperLong)) + _, _, api := coderdtest.NewWithAPI(t, nil) // GIVEN: a standard manager - mgr, err := notifications.NewManager(defaultNotificationsConfig(database.NotificationMethodSmtp), db, defaultHelpers(), createMetrics(), logger.Named("notifications-manager")) + mgr, err := notifications.NewManager(defaultNotificationsConfig(database.NotificationMethodSmtp), api.Database, defaultHelpers(), createMetrics(), api.Logger.Named("notifications-manager")) require.NoError(t, err) // THEN: validate that the manager can be stopped safely without Run() having been called yet diff --git a/coderd/notifications/metrics_test.go b/coderd/notifications/metrics_test.go index 5f2e605aaf..9989a8a7fd 100644 --- a/coderd/notifications/metrics_test.go +++ b/coderd/notifications/metrics_test.go @@ -2,6 +2,7 @@ package notifications_test import ( "context" + "strconv" "testing" "time" @@ -17,7 +18,9 @@ import ( "github.com/coder/serpent" + "github.com/coder/coder/v2/coderd/coderdtest" "github.com/coder/coder/v2/coderd/database" + "github.com/coder/coder/v2/coderd/database/dbauthz" "github.com/coder/coder/v2/coderd/database/dbtestutil" "github.com/coder/coder/v2/coderd/notifications" "github.com/coder/coder/v2/coderd/notifications/dispatch" @@ -33,7 +36,9 @@ func TestMetrics(t *testing.T) { t.Skip("This test requires postgres; it relies on business-logic only implemented in the database") } - ctx, logger, store := setup(t) + // nolint:gocritic // Unit test. + ctx := dbauthz.AsSystemRestricted(testutil.Context(t, testutil.WaitSuperLong)) + _, _, api := coderdtest.NewWithAPI(t, nil) reg := prometheus.NewRegistry() metrics := notifications.NewMetrics(reg) @@ -53,7 +58,7 @@ func TestMetrics(t *testing.T) { cfg.RetryInterval = serpent.Duration(time.Millisecond * 50) cfg.StoreSyncInterval = serpent.Duration(time.Millisecond * 100) // Twice as long as fetch interval to ensure we catch pending updates. - mgr, err := notifications.NewManager(cfg, store, defaultHelpers(), metrics, logger.Named("manager")) + mgr, err := notifications.NewManager(cfg, api.Database, defaultHelpers(), metrics, api.Logger.Named("manager")) require.NoError(t, err) t.Cleanup(func() { assert.NoError(t, mgr.Stop(ctx)) @@ -63,10 +68,10 @@ func TestMetrics(t *testing.T) { method: handler, }) - enq, err := notifications.NewStoreEnqueuer(cfg, store, defaultHelpers(), logger.Named("enqueuer"), quartz.NewReal()) + enq, err := notifications.NewStoreEnqueuer(cfg, api.Database, defaultHelpers(), api.Logger.Named("enqueuer"), quartz.NewReal()) require.NoError(t, err) - user := createSampleUser(t, store) + user := createSampleUser(t, api.Database) // Build fingerprints for the two different series we expect. methodTemplateFP := fingerprintLabels(notifications.LabelMethod, string(method), notifications.LabelTemplateID, template.String()) @@ -204,7 +209,9 @@ func TestPendingUpdatesMetric(t *testing.T) { t.Parallel() // SETUP - ctx, logger, store := setupInMemory(t) + // nolint:gocritic // Unit test. + ctx := dbauthz.AsSystemRestricted(testutil.Context(t, testutil.WaitSuperLong)) + _, _, api := coderdtest.NewWithAPI(t, nil) reg := prometheus.NewRegistry() metrics := notifications.NewMetrics(reg) @@ -218,9 +225,9 @@ func TestPendingUpdatesMetric(t *testing.T) { cfg.RetryInterval = serpent.Duration(time.Hour) // Delay retries so they don't interfere. cfg.StoreSyncInterval = serpent.Duration(time.Millisecond * 100) - syncer := &syncInterceptor{Store: store} + syncer := &syncInterceptor{Store: api.Database} interceptor := newUpdateSignallingInterceptor(syncer) - mgr, err := notifications.NewManager(cfg, interceptor, defaultHelpers(), metrics, logger.Named("manager")) + mgr, err := notifications.NewManager(cfg, interceptor, defaultHelpers(), metrics, api.Logger.Named("manager")) require.NoError(t, err) t.Cleanup(func() { assert.NoError(t, mgr.Stop(ctx)) @@ -230,10 +237,10 @@ func TestPendingUpdatesMetric(t *testing.T) { method: handler, }) - enq, err := notifications.NewStoreEnqueuer(cfg, store, defaultHelpers(), logger.Named("enqueuer"), quartz.NewReal()) + enq, err := notifications.NewStoreEnqueuer(cfg, api.Database, defaultHelpers(), api.Logger.Named("enqueuer"), quartz.NewReal()) require.NoError(t, err) - user := createSampleUser(t, store) + user := createSampleUser(t, api.Database) // WHEN: 2 notifications are enqueued, one of which will fail and one which will succeed _, err = enq.Enqueue(ctx, user.ID, template, map[string]string{"type": "success"}, "test") // this will succeed @@ -279,7 +286,9 @@ func TestInflightDispatchesMetric(t *testing.T) { t.Parallel() // SETUP - ctx, logger, store := setupInMemory(t) + // nolint:gocritic // Unit test. + ctx := dbauthz.AsSystemRestricted(testutil.Context(t, testutil.WaitSuperLong)) + _, _, api := coderdtest.NewWithAPI(t, nil) reg := prometheus.NewRegistry() metrics := notifications.NewMetrics(reg) @@ -294,7 +303,7 @@ func TestInflightDispatchesMetric(t *testing.T) { cfg.RetryInterval = serpent.Duration(time.Hour) // Delay retries so they don't interfere. cfg.StoreSyncInterval = serpent.Duration(time.Millisecond * 100) - mgr, err := notifications.NewManager(cfg, store, defaultHelpers(), metrics, logger.Named("manager")) + mgr, err := notifications.NewManager(cfg, api.Database, defaultHelpers(), metrics, api.Logger.Named("manager")) require.NoError(t, err) t.Cleanup(func() { assert.NoError(t, mgr.Stop(ctx)) @@ -307,15 +316,15 @@ func TestInflightDispatchesMetric(t *testing.T) { method: delayer, }) - enq, err := notifications.NewStoreEnqueuer(cfg, store, defaultHelpers(), logger.Named("enqueuer"), quartz.NewReal()) + enq, err := notifications.NewStoreEnqueuer(cfg, api.Database, defaultHelpers(), api.Logger.Named("enqueuer"), quartz.NewReal()) require.NoError(t, err) - user := createSampleUser(t, store) + user := createSampleUser(t, api.Database) // WHEN: notifications are enqueued which will succeed (and be delayed during dispatch) const msgCount = 2 for i := 0; i < msgCount; i++ { - _, err = enq.Enqueue(ctx, user.ID, template, map[string]string{"type": "success"}, "test") + _, err = enq.Enqueue(ctx, user.ID, template, map[string]string{"type": "success", "i": strconv.Itoa(i)}, "test") require.NoError(t, err) } @@ -349,7 +358,10 @@ func TestCustomMethodMetricCollection(t *testing.T) { // UpdateNotificationTemplateMethodByID only makes sense with a real database. t.Skip("This test requires postgres; it relies on business-logic only implemented in the database") } - ctx, logger, store := setup(t) + + // nolint:gocritic // Unit test. + ctx := dbauthz.AsSystemRestricted(testutil.Context(t, testutil.WaitSuperLong)) + _, _, api := coderdtest.NewWithAPI(t, nil) var ( reg = prometheus.NewRegistry() @@ -364,7 +376,7 @@ func TestCustomMethodMetricCollection(t *testing.T) { ) // GIVEN: a template whose notification method differs from the default. - out, err := store.UpdateNotificationTemplateMethodByID(ctx, database.UpdateNotificationTemplateMethodByIDParams{ + out, err := api.Database.UpdateNotificationTemplateMethodByID(ctx, database.UpdateNotificationTemplateMethodByIDParams{ ID: template, Method: database.NullNotificationMethod{NotificationMethod: customMethod, Valid: true}, }) @@ -373,7 +385,7 @@ func TestCustomMethodMetricCollection(t *testing.T) { // WHEN: two notifications (each with different templates) are enqueued. cfg := defaultNotificationsConfig(defaultMethod) - mgr, err := notifications.NewManager(cfg, store, defaultHelpers(), metrics, logger.Named("manager")) + mgr, err := notifications.NewManager(cfg, api.Database, defaultHelpers(), metrics, api.Logger.Named("manager")) require.NoError(t, err) t.Cleanup(func() { assert.NoError(t, mgr.Stop(ctx)) @@ -386,10 +398,10 @@ func TestCustomMethodMetricCollection(t *testing.T) { customMethod: webhookHandler, }) - enq, err := notifications.NewStoreEnqueuer(cfg, store, defaultHelpers(), logger.Named("enqueuer"), quartz.NewReal()) + enq, err := notifications.NewStoreEnqueuer(cfg, api.Database, defaultHelpers(), api.Logger.Named("enqueuer"), quartz.NewReal()) require.NoError(t, err) - user := createSampleUser(t, store) + user := createSampleUser(t, api.Database) _, err = enq.Enqueue(ctx, user.ID, template, map[string]string{"type": "success"}, "test") require.NoError(t, err) diff --git a/coderd/notifications/notifications_test.go b/coderd/notifications/notifications_test.go index 20487e8ffe..2eb2cd6b10 100644 --- a/coderd/notifications/notifications_test.go +++ b/coderd/notifications/notifications_test.go @@ -33,6 +33,7 @@ import ( "github.com/coder/coder/v2/coderd/coderdtest" "github.com/coder/coder/v2/coderd/database" + "github.com/coder/coder/v2/coderd/database/dbauthz" "github.com/coder/coder/v2/coderd/database/dbgen" "github.com/coder/coder/v2/coderd/database/dbtestutil" "github.com/coder/coder/v2/coderd/notifications" @@ -59,24 +60,26 @@ func TestBasicNotificationRoundtrip(t *testing.T) { t.Skip("This test requires postgres; it relies on business-logic only implemented in the database") } - ctx, logger, db := setup(t) + // nolint:gocritic // Unit test. + ctx := dbauthz.AsSystemRestricted(testutil.Context(t, testutil.WaitSuperLong)) + _, _, api := coderdtest.NewWithAPI(t, nil) method := database.NotificationMethodSmtp // GIVEN: a manager with standard config but a faked dispatch handler handler := &fakeHandler{} - interceptor := &syncInterceptor{Store: db} + interceptor := &syncInterceptor{Store: api.Database} cfg := defaultNotificationsConfig(method) cfg.RetryInterval = serpent.Duration(time.Hour) // Ensure retries don't interfere with the test - mgr, err := notifications.NewManager(cfg, interceptor, defaultHelpers(), createMetrics(), logger.Named("manager")) + mgr, err := notifications.NewManager(cfg, interceptor, defaultHelpers(), createMetrics(), api.Logger.Named("manager")) require.NoError(t, err) mgr.WithHandlers(map[database.NotificationMethod]notifications.Handler{method: handler}) t.Cleanup(func() { assert.NoError(t, mgr.Stop(ctx)) }) - enq, err := notifications.NewStoreEnqueuer(cfg, db, defaultHelpers(), logger.Named("enqueuer"), quartz.NewReal()) + enq, err := notifications.NewStoreEnqueuer(cfg, api.Database, defaultHelpers(), api.Logger.Named("enqueuer"), quartz.NewReal()) require.NoError(t, err) - user := createSampleUser(t, db) + user := createSampleUser(t, api.Database) // WHEN: 2 messages are enqueued sid, err := enq.Enqueue(ctx, user.ID, notifications.TemplateWorkspaceDeleted, map[string]string{"type": "success"}, "test") @@ -101,13 +104,13 @@ func TestBasicNotificationRoundtrip(t *testing.T) { }, testutil.WaitLong, testutil.IntervalFast) // THEN: we verify that the store contains notifications in their expected state - success, err := db.GetNotificationMessagesByStatus(ctx, database.GetNotificationMessagesByStatusParams{ + success, err := api.Database.GetNotificationMessagesByStatus(ctx, database.GetNotificationMessagesByStatusParams{ Status: database.NotificationMessageStatusSent, Limit: 10, }) require.NoError(t, err) require.Len(t, success, 1) - failed, err := db.GetNotificationMessagesByStatus(ctx, database.GetNotificationMessagesByStatusParams{ + failed, err := api.Database.GetNotificationMessagesByStatus(ctx, database.GetNotificationMessagesByStatusParams{ Status: database.NotificationMessageStatusTemporaryFailure, Limit: 10, }) @@ -119,7 +122,10 @@ func TestSMTPDispatch(t *testing.T) { t.Parallel() // SETUP - ctx, logger, db := setupInMemory(t) + + // nolint:gocritic // Unit test. + ctx := dbauthz.AsSystemRestricted(testutil.Context(t, testutil.WaitSuperLong)) + _, _, api := coderdtest.NewWithAPI(t, nil) // start mock SMTP server mockSMTPSrv := smtpmock.New(smtpmock.ConfigurationAttr{ @@ -140,17 +146,17 @@ func TestSMTPDispatch(t *testing.T) { Smarthost: serpent.HostPort{Host: "localhost", Port: fmt.Sprintf("%d", mockSMTPSrv.PortNumber())}, Hello: "localhost", } - handler := newDispatchInterceptor(dispatch.NewSMTPHandler(cfg.SMTP, defaultHelpers(), logger.Named("smtp"))) - mgr, err := notifications.NewManager(cfg, db, defaultHelpers(), createMetrics(), logger.Named("manager")) + handler := newDispatchInterceptor(dispatch.NewSMTPHandler(cfg.SMTP, defaultHelpers(), api.Logger.Named("smtp"))) + mgr, err := notifications.NewManager(cfg, api.Database, defaultHelpers(), createMetrics(), api.Logger.Named("manager")) require.NoError(t, err) mgr.WithHandlers(map[database.NotificationMethod]notifications.Handler{method: handler}) t.Cleanup(func() { assert.NoError(t, mgr.Stop(ctx)) }) - enq, err := notifications.NewStoreEnqueuer(cfg, db, defaultHelpers(), logger.Named("enqueuer"), quartz.NewReal()) + enq, err := notifications.NewStoreEnqueuer(cfg, api.Database, defaultHelpers(), api.Logger.Named("enqueuer"), quartz.NewReal()) require.NoError(t, err) - user := createSampleUser(t, db) + user := createSampleUser(t, api.Database) // WHEN: a message is enqueued msgID, err := enq.Enqueue(ctx, user.ID, notifications.TemplateWorkspaceDeleted, map[string]string{}, "test") @@ -177,7 +183,10 @@ func TestWebhookDispatch(t *testing.T) { t.Parallel() // SETUP - ctx, logger, db := setupInMemory(t) + + // nolint:gocritic // Unit test. + ctx := dbauthz.AsSystemRestricted(testutil.Context(t, testutil.WaitSuperLong)) + _, _, api := coderdtest.NewWithAPI(t, nil) sent := make(chan dispatch.WebhookPayload, 1) // Mock server to simulate webhook endpoint. @@ -202,12 +211,12 @@ func TestWebhookDispatch(t *testing.T) { cfg.Webhook = codersdk.NotificationsWebhookConfig{ Endpoint: *serpent.URLOf(endpoint), } - mgr, err := notifications.NewManager(cfg, db, defaultHelpers(), createMetrics(), logger.Named("manager")) + mgr, err := notifications.NewManager(cfg, api.Database, defaultHelpers(), createMetrics(), api.Logger.Named("manager")) require.NoError(t, err) t.Cleanup(func() { assert.NoError(t, mgr.Stop(ctx)) }) - enq, err := notifications.NewStoreEnqueuer(cfg, db, defaultHelpers(), logger.Named("enqueuer"), quartz.NewReal()) + enq, err := notifications.NewStoreEnqueuer(cfg, api.Database, defaultHelpers(), api.Logger.Named("enqueuer"), quartz.NewReal()) require.NoError(t, err) const ( @@ -215,7 +224,7 @@ func TestWebhookDispatch(t *testing.T) { name = "Robert McBobbington" username = "bob" ) - user := dbgen.User(t, db, database.User{ + user := dbgen.User(t, api.Database, database.User{ Email: email, Username: username, Name: name, @@ -256,7 +265,9 @@ func TestBackpressure(t *testing.T) { t.Skip("This test requires postgres; it relies on business-logic only implemented in the database") } - ctx, logger, db := setup(t) + // nolint:gocritic // Unit test. + ctx := dbauthz.AsSystemRestricted(testutil.Context(t, testutil.WaitSuperLong)) + _, _, api := coderdtest.NewWithAPI(t, nil) // Mock server to simulate webhook endpoint. var received atomic.Int32 @@ -294,19 +305,19 @@ func TestBackpressure(t *testing.T) { cfg.StoreSyncInterval = serpent.Duration(syncInterval) cfg.StoreSyncBufferSize = serpent.Int64(2) - handler := newDispatchInterceptor(dispatch.NewWebhookHandler(cfg.Webhook, logger.Named("webhook"))) + handler := newDispatchInterceptor(dispatch.NewWebhookHandler(cfg.Webhook, api.Logger.Named("webhook"))) // Intercept calls to submit the buffered updates to the store. - storeInterceptor := &syncInterceptor{Store: db} + storeInterceptor := &syncInterceptor{Store: api.Database} // GIVEN: a notification manager whose updates will be intercepted - mgr, err := notifications.NewManager(cfg, storeInterceptor, defaultHelpers(), createMetrics(), logger.Named("manager")) + mgr, err := notifications.NewManager(cfg, storeInterceptor, defaultHelpers(), createMetrics(), api.Logger.Named("manager")) require.NoError(t, err) mgr.WithHandlers(map[database.NotificationMethod]notifications.Handler{method: handler}) - enq, err := notifications.NewStoreEnqueuer(cfg, db, defaultHelpers(), logger.Named("enqueuer"), quartz.NewReal()) + enq, err := notifications.NewStoreEnqueuer(cfg, api.Database, defaultHelpers(), api.Logger.Named("enqueuer"), quartz.NewReal()) require.NoError(t, err) - user := createSampleUser(t, db) + user := createSampleUser(t, api.Database) // WHEN: a set of notifications are enqueued, which causes backpressure due to the batchSize which can be processed per fetch const totalMessages = 30 @@ -345,7 +356,9 @@ func TestRetries(t *testing.T) { } const maxAttempts = 3 - ctx, logger, db := setup(t) + // nolint:gocritic // Unit test. + ctx := dbauthz.AsSystemRestricted(testutil.Context(t, testutil.WaitSuperLong)) + _, _, api := coderdtest.NewWithAPI(t, nil) // GIVEN: a mock HTTP server which will receive webhooksand a map to track the dispatch attempts @@ -390,21 +403,21 @@ func TestRetries(t *testing.T) { cfg.RetryInterval = serpent.Duration(time.Second) // query uses second-precision cfg.FetchInterval = serpent.Duration(time.Millisecond * 100) - handler := newDispatchInterceptor(dispatch.NewWebhookHandler(cfg.Webhook, logger.Named("webhook"))) + handler := newDispatchInterceptor(dispatch.NewWebhookHandler(cfg.Webhook, api.Logger.Named("webhook"))) // Intercept calls to submit the buffered updates to the store. - storeInterceptor := &syncInterceptor{Store: db} + storeInterceptor := &syncInterceptor{Store: api.Database} - mgr, err := notifications.NewManager(cfg, storeInterceptor, defaultHelpers(), createMetrics(), logger.Named("manager")) + mgr, err := notifications.NewManager(cfg, storeInterceptor, defaultHelpers(), createMetrics(), api.Logger.Named("manager")) require.NoError(t, err) t.Cleanup(func() { assert.NoError(t, mgr.Stop(ctx)) }) mgr.WithHandlers(map[database.NotificationMethod]notifications.Handler{method: handler}) - enq, err := notifications.NewStoreEnqueuer(cfg, db, defaultHelpers(), logger.Named("enqueuer"), quartz.NewReal()) + enq, err := notifications.NewStoreEnqueuer(cfg, api.Database, defaultHelpers(), api.Logger.Named("enqueuer"), quartz.NewReal()) require.NoError(t, err) - user := createSampleUser(t, db) + user := createSampleUser(t, api.Database) // WHEN: a few notifications are enqueued, which will all fail until their final retry (determined by the mock server) const msgCount = 5 @@ -436,7 +449,9 @@ func TestExpiredLeaseIsRequeued(t *testing.T) { t.Skip("This test requires postgres; it relies on business-logic only implemented in the database") } - ctx, logger, db := setup(t) + // nolint:gocritic // Unit test. + ctx := dbauthz.AsSystemRestricted(testutil.Context(t, testutil.WaitSuperLong)) + _, _, api := coderdtest.NewWithAPI(t, nil) // GIVEN: a manager which has its updates intercepted and paused until measurements can be taken @@ -451,17 +466,18 @@ func TestExpiredLeaseIsRequeued(t *testing.T) { cfg.LeasePeriod = serpent.Duration(leasePeriod) cfg.DispatchTimeout = serpent.Duration(leasePeriod - time.Millisecond) - noopInterceptor := newNoopStoreSyncer(db) + noopInterceptor := newNoopStoreSyncer(api.Database) - mgrCtx, cancelManagerCtx := context.WithCancel(context.Background()) + // nolint:gocritic // Unit test. + mgrCtx, cancelManagerCtx := context.WithCancel(dbauthz.AsSystemRestricted(context.Background())) t.Cleanup(cancelManagerCtx) - mgr, err := notifications.NewManager(cfg, noopInterceptor, defaultHelpers(), createMetrics(), logger.Named("manager")) + mgr, err := notifications.NewManager(cfg, noopInterceptor, defaultHelpers(), createMetrics(), api.Logger.Named("manager")) require.NoError(t, err) - enq, err := notifications.NewStoreEnqueuer(cfg, db, defaultHelpers(), logger.Named("enqueuer"), quartz.NewReal()) + enq, err := notifications.NewStoreEnqueuer(cfg, api.Database, defaultHelpers(), api.Logger.Named("enqueuer"), quartz.NewReal()) require.NoError(t, err) - user := createSampleUser(t, db) + user := createSampleUser(t, api.Database) // WHEN: a few notifications are enqueued which will all succeed var msgs []string @@ -482,7 +498,7 @@ func TestExpiredLeaseIsRequeued(t *testing.T) { cancelManagerCtx() // Fetch any messages currently in "leased" status, and verify that they're exactly the ones we enqueued. - leased, err := db.GetNotificationMessagesByStatus(ctx, database.GetNotificationMessagesByStatusParams{ + leased, err := api.Database.GetNotificationMessagesByStatus(ctx, database.GetNotificationMessagesByStatusParams{ Status: database.NotificationMessageStatusLeased, Limit: msgCount, }) @@ -502,9 +518,9 @@ func TestExpiredLeaseIsRequeued(t *testing.T) { // Start a new notification manager. // Intercept calls to submit the buffered updates to the store. - storeInterceptor := &syncInterceptor{Store: db} + storeInterceptor := &syncInterceptor{Store: api.Database} handler := newDispatchInterceptor(&fakeHandler{}) - mgr, err = notifications.NewManager(cfg, storeInterceptor, defaultHelpers(), createMetrics(), logger.Named("manager")) + mgr, err = notifications.NewManager(cfg, storeInterceptor, defaultHelpers(), createMetrics(), api.Logger.Named("manager")) require.NoError(t, err) mgr.WithHandlers(map[database.NotificationMethod]notifications.Handler{method: handler}) @@ -521,7 +537,7 @@ func TestExpiredLeaseIsRequeued(t *testing.T) { }, testutil.WaitLong, testutil.IntervalFast) // Validate that no more messages are in "leased" status. - leased, err = db.GetNotificationMessagesByStatus(ctx, database.GetNotificationMessagesByStatusParams{ + leased, err = api.Database.GetNotificationMessagesByStatus(ctx, database.GetNotificationMessagesByStatusParams{ Status: database.NotificationMessageStatusLeased, Limit: msgCount, }) @@ -533,7 +549,7 @@ func TestExpiredLeaseIsRequeued(t *testing.T) { func TestInvalidConfig(t *testing.T) { t.Parallel() - _, logger, db := setupInMemory(t) + _, _, api := coderdtest.NewWithAPI(t, nil) // GIVEN: invalid config with dispatch period <= lease period const ( @@ -545,7 +561,7 @@ func TestInvalidConfig(t *testing.T) { cfg.DispatchTimeout = serpent.Duration(leasePeriod) // WHEN: the manager is created with invalid config - _, err := notifications.NewManager(cfg, db, defaultHelpers(), createMetrics(), logger.Named("manager")) + _, err := notifications.NewManager(cfg, api.Database, defaultHelpers(), createMetrics(), api.Logger.Named("manager")) // THEN: the manager will fail to be created, citing invalid config as error require.ErrorIs(t, err, notifications.ErrInvalidDispatchTimeout) @@ -554,56 +570,60 @@ func TestInvalidConfig(t *testing.T) { func TestNotifierPaused(t *testing.T) { t.Parallel() - // setup - ctx, logger, db := setupInMemory(t) + // Setup. + + // nolint:gocritic // Unit test. + ctx := dbauthz.AsSystemRestricted(testutil.Context(t, testutil.WaitSuperLong)) + _, _, api := coderdtest.NewWithAPI(t, nil) // Prepare the test handler := &fakeHandler{} method := database.NotificationMethodSmtp - user := createSampleUser(t, db) + user := createSampleUser(t, api.Database) + const fetchInterval = time.Millisecond * 100 cfg := defaultNotificationsConfig(method) - mgr, err := notifications.NewManager(cfg, db, defaultHelpers(), createMetrics(), logger.Named("manager")) + cfg.FetchInterval = serpent.Duration(fetchInterval) + mgr, err := notifications.NewManager(cfg, api.Database, defaultHelpers(), createMetrics(), api.Logger.Named("manager")) require.NoError(t, err) mgr.WithHandlers(map[database.NotificationMethod]notifications.Handler{method: handler}) t.Cleanup(func() { assert.NoError(t, mgr.Stop(ctx)) }) - enq, err := notifications.NewStoreEnqueuer(cfg, db, defaultHelpers(), logger.Named("enqueuer"), quartz.NewReal()) + enq, err := notifications.NewStoreEnqueuer(cfg, api.Database, defaultHelpers(), api.Logger.Named("enqueuer"), quartz.NewReal()) require.NoError(t, err) mgr.Run(ctx) - // Notifier is on, enqueue the first message. - sid, err := enq.Enqueue(ctx, user.ID, notifications.TemplateWorkspaceDeleted, map[string]string{"type": "success"}, "test") - require.NoError(t, err) - require.Eventually(t, func() bool { - handler.mu.RLock() - defer handler.mu.RUnlock() - return slices.Contains(handler.succeeded, sid.String()) - }, testutil.WaitShort, testutil.IntervalFast) - // Pause the notifier. settingsJSON, err := json.Marshal(&codersdk.NotificationsSettings{NotifierPaused: true}) require.NoError(t, err) - err = db.UpsertNotificationsSettings(ctx, string(settingsJSON)) + err = api.Database.UpsertNotificationsSettings(ctx, string(settingsJSON)) require.NoError(t, err) // Notifier is paused, enqueue the next message. - sid, err = enq.Enqueue(ctx, user.ID, notifications.TemplateWorkspaceDeleted, map[string]string{"type": "success"}, "test") + sid, err := enq.Enqueue(ctx, user.ID, notifications.TemplateWorkspaceDeleted, map[string]string{"type": "success", "i": "1"}, "test") require.NoError(t, err) + + // Sleep for a few fetch intervals to be sure we aren't getting false-positives in the next step. + // TODO: use quartz instead. + time.Sleep(fetchInterval * 5) + + // Ensure we have a pending message and it's the expected one. require.Eventually(t, func() bool { - pendingMessages, err := db.GetNotificationMessagesByStatus(ctx, database.GetNotificationMessagesByStatusParams{ + pendingMessages, err := api.Database.GetNotificationMessagesByStatus(ctx, database.GetNotificationMessagesByStatusParams{ Status: database.NotificationMessageStatusPending, + Limit: 10, }) assert.NoError(t, err) - return len(pendingMessages) == 1 + return len(pendingMessages) == 1 && + pendingMessages[0].ID.String() == sid.String() }, testutil.WaitShort, testutil.IntervalFast) // Unpause the notifier. settingsJSON, err = json.Marshal(&codersdk.NotificationsSettings{NotifierPaused: false}) require.NoError(t, err) - err = db.UpsertNotificationsSettings(ctx, string(settingsJSON)) + err = api.Database.UpsertNotificationsSettings(ctx, string(settingsJSON)) require.NoError(t, err) // Notifier is running again, message should be dequeued. @@ -723,19 +743,6 @@ func TestNotificationTemplatesCanRender(t *testing.T) { }, }, }, - { - name: "TemplateWorkspaceMarkedForDeletionInOneWeek", - id: notifications.TemplateWorkspaceMarkedForDeletion, - payload: types.MessagePayload{ - UserName: "bobby", - Labels: map[string]string{ - "name": "bobby-workspace", - "reason": "template updated to new dormancy policy", - "dormancyHours": "168", // 168 hours = 7 days = 1 week - "timeTilDormant": "1 week", - }, - }, - }, { name: "TemplateUserAccountCreated", id: notifications.TemplateUserAccountCreated, @@ -860,17 +867,19 @@ func TestDisabledBeforeEnqueue(t *testing.T) { t.Skip("This test requires postgres; it is testing business-logic implemented in the database") } - ctx, logger, db := setup(t) + // nolint:gocritic // Unit test. + ctx := dbauthz.AsSystemRestricted(testutil.Context(t, testutil.WaitSuperLong)) + _, _, api := coderdtest.NewWithAPI(t, nil) // GIVEN: an enqueuer & a sample user cfg := defaultNotificationsConfig(database.NotificationMethodSmtp) - enq, err := notifications.NewStoreEnqueuer(cfg, db, defaultHelpers(), logger.Named("enqueuer"), quartz.NewReal()) + enq, err := notifications.NewStoreEnqueuer(cfg, api.Database, defaultHelpers(), api.Logger.Named("enqueuer"), quartz.NewReal()) require.NoError(t, err) - user := createSampleUser(t, db) + user := createSampleUser(t, api.Database) // WHEN: the user has a preference set to not receive the "workspace deleted" notification templateID := notifications.TemplateWorkspaceDeleted - n, err := db.UpdateUserNotificationPreferences(ctx, database.UpdateUserNotificationPreferencesParams{ + n, err := api.Database.UpdateUserNotificationPreferences(ctx, database.UpdateUserNotificationPreferencesParams{ UserID: user.ID, NotificationTemplateIds: []uuid.UUID{templateID}, Disableds: []bool{true}, @@ -893,20 +902,22 @@ func TestDisabledAfterEnqueue(t *testing.T) { t.Skip("This test requires postgres; it is testing business-logic implemented in the database") } - ctx, logger, db := setup(t) + // nolint:gocritic // Unit test. + ctx := dbauthz.AsSystemRestricted(testutil.Context(t, testutil.WaitSuperLong)) + _, _, api := coderdtest.NewWithAPI(t, nil) method := database.NotificationMethodSmtp cfg := defaultNotificationsConfig(method) - mgr, err := notifications.NewManager(cfg, db, defaultHelpers(), createMetrics(), logger.Named("manager")) + mgr, err := notifications.NewManager(cfg, api.Database, defaultHelpers(), createMetrics(), api.Logger.Named("manager")) require.NoError(t, err) t.Cleanup(func() { assert.NoError(t, mgr.Stop(ctx)) }) - enq, err := notifications.NewStoreEnqueuer(cfg, db, defaultHelpers(), logger.Named("enqueuer"), quartz.NewReal()) + enq, err := notifications.NewStoreEnqueuer(cfg, api.Database, defaultHelpers(), api.Logger.Named("enqueuer"), quartz.NewReal()) require.NoError(t, err) - user := createSampleUser(t, db) + user := createSampleUser(t, api.Database) // GIVEN: a notification is enqueued which has not (yet) been disabled templateID := notifications.TemplateWorkspaceDeleted @@ -914,7 +925,7 @@ func TestDisabledAfterEnqueue(t *testing.T) { require.NoError(t, err) // Disable the notification template. - n, err := db.UpdateUserNotificationPreferences(ctx, database.UpdateUserNotificationPreferencesParams{ + n, err := api.Database.UpdateUserNotificationPreferences(ctx, database.UpdateUserNotificationPreferencesParams{ UserID: user.ID, NotificationTemplateIds: []uuid.UUID{templateID}, Disableds: []bool{true}, @@ -927,7 +938,7 @@ func TestDisabledAfterEnqueue(t *testing.T) { // THEN: the message should not be sent, and must be set to "inhibited" require.EventuallyWithT(t, func(ct *assert.CollectT) { - m, err := db.GetNotificationMessagesByStatus(ctx, database.GetNotificationMessagesByStatusParams{ + m, err := api.Database.GetNotificationMessagesByStatus(ctx, database.GetNotificationMessagesByStatusParams{ Status: database.NotificationMessageStatusInhibited, Limit: 10, }) @@ -947,7 +958,9 @@ func TestCustomNotificationMethod(t *testing.T) { t.Skip("This test requires postgres; it relies on business-logic only implemented in the database") } - ctx, logger, db := setup(t) + // nolint:gocritic // Unit test. + ctx := dbauthz.AsSystemRestricted(testutil.Context(t, testutil.WaitSuperLong)) + _, _, api := coderdtest.NewWithAPI(t, nil) received := make(chan uuid.UUID, 1) @@ -986,7 +999,7 @@ func TestCustomNotificationMethod(t *testing.T) { defaultMethod = database.NotificationMethodSmtp customMethod = database.NotificationMethodWebhook ) - out, err := db.UpdateNotificationTemplateMethodByID(ctx, database.UpdateNotificationTemplateMethodByIDParams{ + out, err := api.Database.UpdateNotificationTemplateMethodByID(ctx, database.UpdateNotificationTemplateMethodByIDParams{ ID: template, Method: database.NullNotificationMethod{NotificationMethod: customMethod, Valid: true}, }) @@ -1004,17 +1017,17 @@ func TestCustomNotificationMethod(t *testing.T) { Endpoint: *serpent.URLOf(endpoint), } - mgr, err := notifications.NewManager(cfg, db, defaultHelpers(), createMetrics(), logger.Named("manager")) + mgr, err := notifications.NewManager(cfg, api.Database, defaultHelpers(), createMetrics(), api.Logger.Named("manager")) require.NoError(t, err) t.Cleanup(func() { _ = mgr.Stop(ctx) }) - enq, err := notifications.NewStoreEnqueuer(cfg, db, defaultHelpers(), logger, quartz.NewReal()) + enq, err := notifications.NewStoreEnqueuer(cfg, api.Database, defaultHelpers(), api.Logger, quartz.NewReal()) require.NoError(t, err) // WHEN: a notification of that template is enqueued, it should be delivered with the configured method - not the default. - user := createSampleUser(t, db) + user := createSampleUser(t, api.Database) msgID, err := enq.Enqueue(ctx, user.ID, template, map[string]string{}, "test") require.NoError(t, err) @@ -1048,7 +1061,8 @@ func TestNotificationsTemplates(t *testing.T) { t.Skip("This test requires postgres; it relies on business-logic only implemented in the database") } - ctx := testutil.Context(t, testutil.WaitLong) + // nolint:gocritic // Unit test. + ctx := dbauthz.AsSystemRestricted(testutil.Context(t, testutil.WaitSuperLong)) api := coderdtest.New(t, createOpts(t)) // GIVEN: the first user (owner) and a regular member @@ -1085,12 +1099,14 @@ func TestNotificationDuplicates(t *testing.T) { t.Skip("This test requires postgres; it is testing the dedupe hash trigger in the database") } - ctx, logger, db := setup(t) + // nolint:gocritic // Unit test. + ctx := dbauthz.AsSystemRestricted(testutil.Context(t, testutil.WaitSuperLong)) + _, _, api := coderdtest.NewWithAPI(t, nil) method := database.NotificationMethodSmtp cfg := defaultNotificationsConfig(method) - mgr, err := notifications.NewManager(cfg, db, defaultHelpers(), createMetrics(), logger.Named("manager")) + mgr, err := notifications.NewManager(cfg, api.Database, defaultHelpers(), createMetrics(), api.Logger.Named("manager")) require.NoError(t, err) t.Cleanup(func() { assert.NoError(t, mgr.Stop(ctx)) @@ -1100,9 +1116,9 @@ func TestNotificationDuplicates(t *testing.T) { mClock := quartz.NewMock(t) mClock.Set(time.Date(2024, 1, 15, 9, 0, 0, 0, time.UTC)) - enq, err := notifications.NewStoreEnqueuer(cfg, db, defaultHelpers(), logger.Named("enqueuer"), mClock) + enq, err := notifications.NewStoreEnqueuer(cfg, api.Database, defaultHelpers(), api.Logger.Named("enqueuer"), mClock) require.NoError(t, err) - user := createSampleUser(t, db) + user := createSampleUser(t, api.Database) // GIVEN: two notifications are enqueued with identical properties. _, err = enq.Enqueue(ctx, user.ID, notifications.TemplateWorkspaceDeleted, diff --git a/coderd/notifications/utils_test.go b/coderd/notifications/utils_test.go index b15d27d32a..124b8554c5 100644 --- a/coderd/notifications/utils_test.go +++ b/coderd/notifications/utils_test.go @@ -2,63 +2,23 @@ package notifications_test import ( "context" - "database/sql" "sync/atomic" "testing" "time" "github.com/google/uuid" "github.com/prometheus/client_golang/prometheus" - "github.com/stretchr/testify/require" - "cdr.dev/slog" - "cdr.dev/slog/sloggers/slogtest" "github.com/coder/serpent" "github.com/coder/coder/v2/coderd/database" - "github.com/coder/coder/v2/coderd/database/dbauthz" "github.com/coder/coder/v2/coderd/database/dbgen" - "github.com/coder/coder/v2/coderd/database/dbmem" - "github.com/coder/coder/v2/coderd/database/dbtestutil" "github.com/coder/coder/v2/coderd/notifications" "github.com/coder/coder/v2/coderd/notifications/dispatch" "github.com/coder/coder/v2/coderd/notifications/types" "github.com/coder/coder/v2/codersdk" - "github.com/coder/coder/v2/testutil" ) -func setup(t *testing.T) (context.Context, slog.Logger, database.Store) { - t.Helper() - - connectionURL, closeFunc, err := dbtestutil.Open() - require.NoError(t, err) - t.Cleanup(closeFunc) - - ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitSuperLong) - t.Cleanup(cancel) - logger := slogtest.Make(t, &slogtest.Options{IgnoreErrors: true, IgnoredErrorIs: []error{}}).Leveled(slog.LevelDebug) - - sqlDB, err := sql.Open("postgres", connectionURL) - require.NoError(t, err) - t.Cleanup(func() { - require.NoError(t, sqlDB.Close()) - }) - - // nolint:gocritic // unit tests. - return dbauthz.AsSystemRestricted(ctx), logger, database.New(sqlDB) -} - -func setupInMemory(t *testing.T) (context.Context, slog.Logger, database.Store) { - t.Helper() - - ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitShort) - t.Cleanup(cancel) - logger := slogtest.Make(t, &slogtest.Options{IgnoreErrors: true, IgnoredErrorIs: []error{}}).Leveled(slog.LevelDebug) - - // nolint:gocritic // unit tests. - return dbauthz.AsSystemRestricted(ctx), logger, dbmem.New() -} - func defaultNotificationsConfig(method database.NotificationMethod) codersdk.NotificationsConfig { return codersdk.NotificationsConfig{ Method: serpent.String(method), diff --git a/coderd/notifications_test.go b/coderd/notifications_test.go index 6cea84af11..17598cd812 100644 --- a/coderd/notifications_test.go +++ b/coderd/notifications_test.go @@ -115,7 +115,7 @@ func TestNotificationPreferences(t *testing.T) { t.Run("Initial state", func(t *testing.T) { t.Parallel() - ctx := testutil.Context(t, testutil.WaitLong) + ctx := testutil.Context(t, testutil.WaitSuperLong) api := coderdtest.New(t, createOpts(t)) firstUser := coderdtest.CreateFirstUser(t, api) @@ -133,7 +133,7 @@ func TestNotificationPreferences(t *testing.T) { t.Run("Insufficient permissions", func(t *testing.T) { t.Parallel() - ctx := testutil.Context(t, testutil.WaitLong) + ctx := testutil.Context(t, testutil.WaitSuperLong) api := coderdtest.New(t, createOpts(t)) firstUser := coderdtest.CreateFirstUser(t, api) @@ -156,7 +156,7 @@ func TestNotificationPreferences(t *testing.T) { t.Run("Admin may read any users' preferences", func(t *testing.T) { t.Parallel() - ctx := testutil.Context(t, testutil.WaitLong) + ctx := testutil.Context(t, testutil.WaitSuperLong) api := coderdtest.New(t, createOpts(t)) firstUser := coderdtest.CreateFirstUser(t, api) @@ -174,7 +174,7 @@ func TestNotificationPreferences(t *testing.T) { t.Run("Admin may update any users' preferences", func(t *testing.T) { t.Parallel() - ctx := testutil.Context(t, testutil.WaitLong) + ctx := testutil.Context(t, testutil.WaitSuperLong) api := coderdtest.New(t, createOpts(t)) firstUser := coderdtest.CreateFirstUser(t, api) @@ -202,7 +202,7 @@ func TestNotificationPreferences(t *testing.T) { t.Run("Add preferences", func(t *testing.T) { t.Parallel() - ctx := testutil.Context(t, testutil.WaitLong) + ctx := testutil.Context(t, testutil.WaitSuperLong) api := coderdtest.New(t, createOpts(t)) firstUser := coderdtest.CreateFirstUser(t, api) @@ -230,7 +230,7 @@ func TestNotificationPreferences(t *testing.T) { t.Run("Modify preferences", func(t *testing.T) { t.Parallel() - ctx := testutil.Context(t, testutil.WaitLong) + ctx := testutil.Context(t, testutil.WaitSuperLong) api := coderdtest.New(t, createOpts(t)) firstUser := coderdtest.CreateFirstUser(t, api) @@ -305,7 +305,7 @@ func TestNotificationDispatchMethods(t *testing.T) { t.Run(tc.name, func(t *testing.T) { t.Parallel() - ctx := testutil.Context(t, testutil.WaitShort) + ctx := testutil.Context(t, testutil.WaitSuperLong) api := coderdtest.New(t, tc.opts) _ = coderdtest.CreateFirstUser(t, api)