mirror of
https://github.com/coder/coder.git
synced 2025-07-15 22:20:27 +00:00
chore: use idiomatic test setup in notification tests (#14416)
This commit is contained in:
@ -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{},
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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,
|
||||
|
@ -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),
|
||||
|
@ -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)
|
||||
|
||||
|
Reference in New Issue
Block a user