mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
chore: stop creating coderd for notification unit tests (#15133)
A bunch of notification tests create a whole `coderd`, when all they use is the database and logger. This makes the tests more expensive to run, and pollutes the test logs with a bunch of stuff that doesn't matter (e.g. tailnet).
This commit is contained in:
@@ -12,10 +12,12 @@ import (
|
|||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
"golang.org/x/xerrors"
|
"golang.org/x/xerrors"
|
||||||
|
|
||||||
|
"cdr.dev/slog"
|
||||||
|
"cdr.dev/slog/sloggers/slogtest"
|
||||||
|
"github.com/coder/coder/v2/coderd/database/dbtestutil"
|
||||||
"github.com/coder/quartz"
|
"github.com/coder/quartz"
|
||||||
"github.com/coder/serpent"
|
"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"
|
||||||
"github.com/coder/coder/v2/coderd/database/dbauthz"
|
"github.com/coder/coder/v2/coderd/database/dbauthz"
|
||||||
"github.com/coder/coder/v2/coderd/database/dbgen"
|
"github.com/coder/coder/v2/coderd/database/dbgen"
|
||||||
@@ -32,24 +34,25 @@ func TestBufferedUpdates(t *testing.T) {
|
|||||||
|
|
||||||
// nolint:gocritic // Unit test.
|
// nolint:gocritic // Unit test.
|
||||||
ctx := dbauthz.AsSystemRestricted(testutil.Context(t, testutil.WaitSuperLong))
|
ctx := dbauthz.AsSystemRestricted(testutil.Context(t, testutil.WaitSuperLong))
|
||||||
_, _, api := coderdtest.NewWithAPI(t, nil)
|
store, _ := dbtestutil.NewDB(t)
|
||||||
|
logger := slogtest.Make(t, nil).Leveled(slog.LevelDebug)
|
||||||
|
|
||||||
interceptor := &syncInterceptor{Store: api.Database}
|
interceptor := &syncInterceptor{Store: store}
|
||||||
santa := &santaHandler{}
|
santa := &santaHandler{}
|
||||||
|
|
||||||
cfg := defaultNotificationsConfig(database.NotificationMethodSmtp)
|
cfg := defaultNotificationsConfig(database.NotificationMethodSmtp)
|
||||||
cfg.StoreSyncInterval = serpent.Duration(time.Hour) // Ensure we don't sync the store automatically.
|
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
|
// GIVEN: a manager which will pass or fail notifications based on their "nice" labels
|
||||||
mgr, err := notifications.NewManager(cfg, interceptor, defaultHelpers(), createMetrics(), api.Logger.Named("notifications-manager"))
|
mgr, err := notifications.NewManager(cfg, interceptor, defaultHelpers(), createMetrics(), logger.Named("notifications-manager"))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
mgr.WithHandlers(map[database.NotificationMethod]notifications.Handler{
|
mgr.WithHandlers(map[database.NotificationMethod]notifications.Handler{
|
||||||
database.NotificationMethodSmtp: santa,
|
database.NotificationMethodSmtp: santa,
|
||||||
})
|
})
|
||||||
enq, err := notifications.NewStoreEnqueuer(cfg, interceptor, defaultHelpers(), api.Logger.Named("notifications-enqueuer"), quartz.NewReal())
|
enq, err := notifications.NewStoreEnqueuer(cfg, interceptor, defaultHelpers(), logger.Named("notifications-enqueuer"), quartz.NewReal())
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
user := dbgen.User(t, api.Database, database.User{})
|
user := dbgen.User(t, store, database.User{})
|
||||||
|
|
||||||
// WHEN: notifications are enqueued which should succeed and fail
|
// WHEN: notifications are enqueued which should succeed and fail
|
||||||
_, err = enq.Enqueue(ctx, user.ID, notifications.TemplateWorkspaceDeleted, map[string]string{"nice": "true", "i": "0"}, "") // Will succeed.
|
_, err = enq.Enqueue(ctx, user.ID, notifications.TemplateWorkspaceDeleted, map[string]string{"nice": "true", "i": "0"}, "") // Will succeed.
|
||||||
@@ -103,7 +106,8 @@ func TestBuildPayload(t *testing.T) {
|
|||||||
|
|
||||||
// nolint:gocritic // Unit test.
|
// nolint:gocritic // Unit test.
|
||||||
ctx := dbauthz.AsSystemRestricted(testutil.Context(t, testutil.WaitSuperLong))
|
ctx := dbauthz.AsSystemRestricted(testutil.Context(t, testutil.WaitSuperLong))
|
||||||
_, _, api := coderdtest.NewWithAPI(t, nil)
|
store, _ := dbtestutil.NewDB(t)
|
||||||
|
logger := slogtest.Make(t, nil).Leveled(slog.LevelDebug)
|
||||||
|
|
||||||
// GIVEN: a set of helpers to be injected into the templates
|
// GIVEN: a set of helpers to be injected into the templates
|
||||||
const label = "Click here!"
|
const label = "Click here!"
|
||||||
@@ -115,7 +119,7 @@ func TestBuildPayload(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GIVEN: an enqueue interceptor which returns mock metadata
|
// GIVEN: an enqueue interceptor which returns mock metadata
|
||||||
interceptor := newEnqueueInterceptor(api.Database,
|
interceptor := newEnqueueInterceptor(store,
|
||||||
// Inject custom message metadata to influence the payload construction.
|
// Inject custom message metadata to influence the payload construction.
|
||||||
func() database.FetchNewMessageMetadataRow {
|
func() database.FetchNewMessageMetadataRow {
|
||||||
// Inject template actions which use injected help functions.
|
// Inject template actions which use injected help functions.
|
||||||
@@ -137,7 +141,7 @@ func TestBuildPayload(t *testing.T) {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
enq, err := notifications.NewStoreEnqueuer(defaultNotificationsConfig(database.NotificationMethodSmtp), interceptor, helpers, api.Logger.Named("notifications-enqueuer"), quartz.NewReal())
|
enq, err := notifications.NewStoreEnqueuer(defaultNotificationsConfig(database.NotificationMethodSmtp), interceptor, helpers, logger.Named("notifications-enqueuer"), quartz.NewReal())
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
// WHEN: a notification is enqueued
|
// WHEN: a notification is enqueued
|
||||||
@@ -160,10 +164,11 @@ func TestStopBeforeRun(t *testing.T) {
|
|||||||
|
|
||||||
// nolint:gocritic // Unit test.
|
// nolint:gocritic // Unit test.
|
||||||
ctx := dbauthz.AsSystemRestricted(testutil.Context(t, testutil.WaitSuperLong))
|
ctx := dbauthz.AsSystemRestricted(testutil.Context(t, testutil.WaitSuperLong))
|
||||||
_, _, api := coderdtest.NewWithAPI(t, nil)
|
store, _ := dbtestutil.NewDB(t)
|
||||||
|
logger := slogtest.Make(t, nil).Leveled(slog.LevelDebug)
|
||||||
|
|
||||||
// GIVEN: a standard manager
|
// GIVEN: a standard manager
|
||||||
mgr, err := notifications.NewManager(defaultNotificationsConfig(database.NotificationMethodSmtp), api.Database, defaultHelpers(), createMetrics(), api.Logger.Named("notifications-manager"))
|
mgr, err := notifications.NewManager(defaultNotificationsConfig(database.NotificationMethodSmtp), store, defaultHelpers(), createMetrics(), logger.Named("notifications-manager"))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
// THEN: validate that the manager can be stopped safely without Run() having been called yet
|
// THEN: validate that the manager can be stopped safely without Run() having been called yet
|
||||||
|
|||||||
@@ -15,11 +15,11 @@ import (
|
|||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
|
"cdr.dev/slog"
|
||||||
|
"cdr.dev/slog/sloggers/slogtest"
|
||||||
"github.com/coder/quartz"
|
"github.com/coder/quartz"
|
||||||
|
|
||||||
"github.com/coder/serpent"
|
"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"
|
||||||
"github.com/coder/coder/v2/coderd/database/dbauthz"
|
"github.com/coder/coder/v2/coderd/database/dbauthz"
|
||||||
"github.com/coder/coder/v2/coderd/database/dbtestutil"
|
"github.com/coder/coder/v2/coderd/database/dbtestutil"
|
||||||
@@ -39,7 +39,8 @@ func TestMetrics(t *testing.T) {
|
|||||||
|
|
||||||
// nolint:gocritic // Unit test.
|
// nolint:gocritic // Unit test.
|
||||||
ctx := dbauthz.AsSystemRestricted(testutil.Context(t, testutil.WaitSuperLong))
|
ctx := dbauthz.AsSystemRestricted(testutil.Context(t, testutil.WaitSuperLong))
|
||||||
_, _, api := coderdtest.NewWithAPI(t, nil)
|
store, _ := dbtestutil.NewDB(t)
|
||||||
|
logger := slogtest.Make(t, nil).Leveled(slog.LevelDebug)
|
||||||
|
|
||||||
reg := prometheus.NewRegistry()
|
reg := prometheus.NewRegistry()
|
||||||
metrics := notifications.NewMetrics(reg)
|
metrics := notifications.NewMetrics(reg)
|
||||||
@@ -59,7 +60,7 @@ func TestMetrics(t *testing.T) {
|
|||||||
cfg.RetryInterval = serpent.Duration(time.Millisecond * 50)
|
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.
|
cfg.StoreSyncInterval = serpent.Duration(time.Millisecond * 100) // Twice as long as fetch interval to ensure we catch pending updates.
|
||||||
|
|
||||||
mgr, err := notifications.NewManager(cfg, api.Database, defaultHelpers(), metrics, api.Logger.Named("manager"))
|
mgr, err := notifications.NewManager(cfg, store, defaultHelpers(), metrics, logger.Named("manager"))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
t.Cleanup(func() {
|
t.Cleanup(func() {
|
||||||
assert.NoError(t, mgr.Stop(ctx))
|
assert.NoError(t, mgr.Stop(ctx))
|
||||||
@@ -69,10 +70,10 @@ func TestMetrics(t *testing.T) {
|
|||||||
method: handler,
|
method: handler,
|
||||||
})
|
})
|
||||||
|
|
||||||
enq, err := notifications.NewStoreEnqueuer(cfg, api.Database, defaultHelpers(), api.Logger.Named("enqueuer"), quartz.NewReal())
|
enq, err := notifications.NewStoreEnqueuer(cfg, store, defaultHelpers(), logger.Named("enqueuer"), quartz.NewReal())
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
user := createSampleUser(t, api.Database)
|
user := createSampleUser(t, store)
|
||||||
|
|
||||||
// Build fingerprints for the two different series we expect.
|
// Build fingerprints for the two different series we expect.
|
||||||
methodTemplateFP := fingerprintLabels(notifications.LabelMethod, string(method), notifications.LabelTemplateID, template.String())
|
methodTemplateFP := fingerprintLabels(notifications.LabelMethod, string(method), notifications.LabelTemplateID, template.String())
|
||||||
@@ -212,7 +213,8 @@ func TestPendingUpdatesMetric(t *testing.T) {
|
|||||||
// SETUP
|
// SETUP
|
||||||
// nolint:gocritic // Unit test.
|
// nolint:gocritic // Unit test.
|
||||||
ctx := dbauthz.AsSystemRestricted(testutil.Context(t, testutil.WaitSuperLong))
|
ctx := dbauthz.AsSystemRestricted(testutil.Context(t, testutil.WaitSuperLong))
|
||||||
_, _, api := coderdtest.NewWithAPI(t, nil)
|
store, _ := dbtestutil.NewDB(t)
|
||||||
|
logger := slogtest.Make(t, nil).Leveled(slog.LevelDebug)
|
||||||
|
|
||||||
reg := prometheus.NewRegistry()
|
reg := prometheus.NewRegistry()
|
||||||
metrics := notifications.NewMetrics(reg)
|
metrics := notifications.NewMetrics(reg)
|
||||||
@@ -225,12 +227,12 @@ func TestPendingUpdatesMetric(t *testing.T) {
|
|||||||
cfg.RetryInterval = serpent.Duration(time.Hour) // Delay retries so they don't interfere.
|
cfg.RetryInterval = serpent.Duration(time.Hour) // Delay retries so they don't interfere.
|
||||||
cfg.StoreSyncInterval = serpent.Duration(time.Millisecond * 100)
|
cfg.StoreSyncInterval = serpent.Duration(time.Millisecond * 100)
|
||||||
|
|
||||||
syncer := &syncInterceptor{Store: api.Database}
|
syncer := &syncInterceptor{Store: store}
|
||||||
interceptor := newUpdateSignallingInterceptor(syncer)
|
interceptor := newUpdateSignallingInterceptor(syncer)
|
||||||
mClock := quartz.NewMock(t)
|
mClock := quartz.NewMock(t)
|
||||||
trap := mClock.Trap().NewTicker("Manager", "storeSync")
|
trap := mClock.Trap().NewTicker("Manager", "storeSync")
|
||||||
defer trap.Close()
|
defer trap.Close()
|
||||||
mgr, err := notifications.NewManager(cfg, interceptor, defaultHelpers(), metrics, api.Logger.Named("manager"),
|
mgr, err := notifications.NewManager(cfg, interceptor, defaultHelpers(), metrics, logger.Named("manager"),
|
||||||
notifications.WithTestClock(mClock))
|
notifications.WithTestClock(mClock))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
t.Cleanup(func() {
|
t.Cleanup(func() {
|
||||||
@@ -241,10 +243,10 @@ func TestPendingUpdatesMetric(t *testing.T) {
|
|||||||
method: handler,
|
method: handler,
|
||||||
})
|
})
|
||||||
|
|
||||||
enq, err := notifications.NewStoreEnqueuer(cfg, api.Database, defaultHelpers(), api.Logger.Named("enqueuer"), quartz.NewReal())
|
enq, err := notifications.NewStoreEnqueuer(cfg, store, defaultHelpers(), logger.Named("enqueuer"), quartz.NewReal())
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
user := createSampleUser(t, api.Database)
|
user := createSampleUser(t, store)
|
||||||
|
|
||||||
// WHEN: 2 notifications are enqueued, one of which will fail and one which will succeed
|
// 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
|
_, err = enq.Enqueue(ctx, user.ID, template, map[string]string{"type": "success"}, "test") // this will succeed
|
||||||
@@ -296,7 +298,8 @@ func TestInflightDispatchesMetric(t *testing.T) {
|
|||||||
// SETUP
|
// SETUP
|
||||||
// nolint:gocritic // Unit test.
|
// nolint:gocritic // Unit test.
|
||||||
ctx := dbauthz.AsSystemRestricted(testutil.Context(t, testutil.WaitSuperLong))
|
ctx := dbauthz.AsSystemRestricted(testutil.Context(t, testutil.WaitSuperLong))
|
||||||
_, _, api := coderdtest.NewWithAPI(t, nil)
|
store, _ := dbtestutil.NewDB(t)
|
||||||
|
logger := slogtest.Make(t, nil).Leveled(slog.LevelDebug)
|
||||||
|
|
||||||
reg := prometheus.NewRegistry()
|
reg := prometheus.NewRegistry()
|
||||||
metrics := notifications.NewMetrics(reg)
|
metrics := notifications.NewMetrics(reg)
|
||||||
@@ -311,7 +314,7 @@ func TestInflightDispatchesMetric(t *testing.T) {
|
|||||||
cfg.RetryInterval = serpent.Duration(time.Hour) // Delay retries so they don't interfere.
|
cfg.RetryInterval = serpent.Duration(time.Hour) // Delay retries so they don't interfere.
|
||||||
cfg.StoreSyncInterval = serpent.Duration(time.Millisecond * 100)
|
cfg.StoreSyncInterval = serpent.Duration(time.Millisecond * 100)
|
||||||
|
|
||||||
mgr, err := notifications.NewManager(cfg, api.Database, defaultHelpers(), metrics, api.Logger.Named("manager"))
|
mgr, err := notifications.NewManager(cfg, store, defaultHelpers(), metrics, logger.Named("manager"))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
t.Cleanup(func() {
|
t.Cleanup(func() {
|
||||||
assert.NoError(t, mgr.Stop(ctx))
|
assert.NoError(t, mgr.Stop(ctx))
|
||||||
@@ -326,10 +329,10 @@ func TestInflightDispatchesMetric(t *testing.T) {
|
|||||||
method: barrier,
|
method: barrier,
|
||||||
})
|
})
|
||||||
|
|
||||||
enq, err := notifications.NewStoreEnqueuer(cfg, api.Database, defaultHelpers(), api.Logger.Named("enqueuer"), quartz.NewReal())
|
enq, err := notifications.NewStoreEnqueuer(cfg, store, defaultHelpers(), logger.Named("enqueuer"), quartz.NewReal())
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
user := createSampleUser(t, api.Database)
|
user := createSampleUser(t, store)
|
||||||
|
|
||||||
// WHEN: notifications are enqueued which will succeed (and be delayed during dispatch)
|
// WHEN: notifications are enqueued which will succeed (and be delayed during dispatch)
|
||||||
for i := 0; i < msgCount; i++ {
|
for i := 0; i < msgCount; i++ {
|
||||||
@@ -374,7 +377,8 @@ func TestCustomMethodMetricCollection(t *testing.T) {
|
|||||||
|
|
||||||
// nolint:gocritic // Unit test.
|
// nolint:gocritic // Unit test.
|
||||||
ctx := dbauthz.AsSystemRestricted(testutil.Context(t, testutil.WaitSuperLong))
|
ctx := dbauthz.AsSystemRestricted(testutil.Context(t, testutil.WaitSuperLong))
|
||||||
_, _, api := coderdtest.NewWithAPI(t, nil)
|
store, _ := dbtestutil.NewDB(t)
|
||||||
|
logger := slogtest.Make(t, nil).Leveled(slog.LevelDebug)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
reg = prometheus.NewRegistry()
|
reg = prometheus.NewRegistry()
|
||||||
@@ -389,7 +393,7 @@ func TestCustomMethodMetricCollection(t *testing.T) {
|
|||||||
)
|
)
|
||||||
|
|
||||||
// GIVEN: a template whose notification method differs from the default.
|
// GIVEN: a template whose notification method differs from the default.
|
||||||
out, err := api.Database.UpdateNotificationTemplateMethodByID(ctx, database.UpdateNotificationTemplateMethodByIDParams{
|
out, err := store.UpdateNotificationTemplateMethodByID(ctx, database.UpdateNotificationTemplateMethodByIDParams{
|
||||||
ID: template,
|
ID: template,
|
||||||
Method: database.NullNotificationMethod{NotificationMethod: customMethod, Valid: true},
|
Method: database.NullNotificationMethod{NotificationMethod: customMethod, Valid: true},
|
||||||
})
|
})
|
||||||
@@ -398,7 +402,7 @@ func TestCustomMethodMetricCollection(t *testing.T) {
|
|||||||
|
|
||||||
// WHEN: two notifications (each with different templates) are enqueued.
|
// WHEN: two notifications (each with different templates) are enqueued.
|
||||||
cfg := defaultNotificationsConfig(defaultMethod)
|
cfg := defaultNotificationsConfig(defaultMethod)
|
||||||
mgr, err := notifications.NewManager(cfg, api.Database, defaultHelpers(), metrics, api.Logger.Named("manager"))
|
mgr, err := notifications.NewManager(cfg, store, defaultHelpers(), metrics, logger.Named("manager"))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
t.Cleanup(func() {
|
t.Cleanup(func() {
|
||||||
assert.NoError(t, mgr.Stop(ctx))
|
assert.NoError(t, mgr.Stop(ctx))
|
||||||
@@ -411,10 +415,10 @@ func TestCustomMethodMetricCollection(t *testing.T) {
|
|||||||
customMethod: webhookHandler,
|
customMethod: webhookHandler,
|
||||||
})
|
})
|
||||||
|
|
||||||
enq, err := notifications.NewStoreEnqueuer(cfg, api.Database, defaultHelpers(), api.Logger.Named("enqueuer"), quartz.NewReal())
|
enq, err := notifications.NewStoreEnqueuer(cfg, store, defaultHelpers(), logger.Named("enqueuer"), quartz.NewReal())
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
user := createSampleUser(t, api.Database)
|
user := createSampleUser(t, store)
|
||||||
|
|
||||||
_, 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"}, "test")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ import (
|
|||||||
"golang.org/x/xerrors"
|
"golang.org/x/xerrors"
|
||||||
|
|
||||||
"cdr.dev/slog"
|
"cdr.dev/slog"
|
||||||
|
"cdr.dev/slog/sloggers/slogtest"
|
||||||
"github.com/coder/coder/v2/coderd/coderdtest"
|
"github.com/coder/coder/v2/coderd/coderdtest"
|
||||||
"github.com/coder/coder/v2/coderd/database"
|
"github.com/coder/coder/v2/coderd/database"
|
||||||
"github.com/coder/coder/v2/coderd/database/dbauthz"
|
"github.com/coder/coder/v2/coderd/database/dbauthz"
|
||||||
@@ -71,24 +72,25 @@ func TestBasicNotificationRoundtrip(t *testing.T) {
|
|||||||
|
|
||||||
// nolint:gocritic // Unit test.
|
// nolint:gocritic // Unit test.
|
||||||
ctx := dbauthz.AsSystemRestricted(testutil.Context(t, testutil.WaitSuperLong))
|
ctx := dbauthz.AsSystemRestricted(testutil.Context(t, testutil.WaitSuperLong))
|
||||||
_, _, api := coderdtest.NewWithAPI(t, nil)
|
store, _ := dbtestutil.NewDB(t)
|
||||||
|
logger := slogtest.Make(t, nil).Leveled(slog.LevelDebug)
|
||||||
method := database.NotificationMethodSmtp
|
method := database.NotificationMethodSmtp
|
||||||
|
|
||||||
// GIVEN: a manager with standard config but a faked dispatch handler
|
// GIVEN: a manager with standard config but a faked dispatch handler
|
||||||
handler := &fakeHandler{}
|
handler := &fakeHandler{}
|
||||||
interceptor := &syncInterceptor{Store: api.Database}
|
interceptor := &syncInterceptor{Store: store}
|
||||||
cfg := defaultNotificationsConfig(method)
|
cfg := defaultNotificationsConfig(method)
|
||||||
cfg.RetryInterval = serpent.Duration(time.Hour) // Ensure retries don't interfere with the test
|
cfg.RetryInterval = serpent.Duration(time.Hour) // Ensure retries don't interfere with the test
|
||||||
mgr, err := notifications.NewManager(cfg, interceptor, defaultHelpers(), createMetrics(), api.Logger.Named("manager"))
|
mgr, err := notifications.NewManager(cfg, interceptor, defaultHelpers(), createMetrics(), logger.Named("manager"))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
mgr.WithHandlers(map[database.NotificationMethod]notifications.Handler{method: handler})
|
mgr.WithHandlers(map[database.NotificationMethod]notifications.Handler{method: handler})
|
||||||
t.Cleanup(func() {
|
t.Cleanup(func() {
|
||||||
assert.NoError(t, mgr.Stop(ctx))
|
assert.NoError(t, mgr.Stop(ctx))
|
||||||
})
|
})
|
||||||
enq, err := notifications.NewStoreEnqueuer(cfg, api.Database, defaultHelpers(), api.Logger.Named("enqueuer"), quartz.NewReal())
|
enq, err := notifications.NewStoreEnqueuer(cfg, store, defaultHelpers(), logger.Named("enqueuer"), quartz.NewReal())
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
user := createSampleUser(t, api.Database)
|
user := createSampleUser(t, store)
|
||||||
|
|
||||||
// WHEN: 2 messages are enqueued
|
// WHEN: 2 messages are enqueued
|
||||||
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"}, "test")
|
||||||
@@ -113,13 +115,13 @@ func TestBasicNotificationRoundtrip(t *testing.T) {
|
|||||||
}, testutil.WaitLong, testutil.IntervalFast)
|
}, testutil.WaitLong, testutil.IntervalFast)
|
||||||
|
|
||||||
// THEN: we verify that the store contains notifications in their expected state
|
// THEN: we verify that the store contains notifications in their expected state
|
||||||
success, err := api.Database.GetNotificationMessagesByStatus(ctx, database.GetNotificationMessagesByStatusParams{
|
success, err := store.GetNotificationMessagesByStatus(ctx, database.GetNotificationMessagesByStatusParams{
|
||||||
Status: database.NotificationMessageStatusSent,
|
Status: database.NotificationMessageStatusSent,
|
||||||
Limit: 10,
|
Limit: 10,
|
||||||
})
|
})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Len(t, success, 1)
|
require.Len(t, success, 1)
|
||||||
failed, err := api.Database.GetNotificationMessagesByStatus(ctx, database.GetNotificationMessagesByStatusParams{
|
failed, err := store.GetNotificationMessagesByStatus(ctx, database.GetNotificationMessagesByStatusParams{
|
||||||
Status: database.NotificationMessageStatusTemporaryFailure,
|
Status: database.NotificationMessageStatusTemporaryFailure,
|
||||||
Limit: 10,
|
Limit: 10,
|
||||||
})
|
})
|
||||||
@@ -134,7 +136,8 @@ func TestSMTPDispatch(t *testing.T) {
|
|||||||
|
|
||||||
// nolint:gocritic // Unit test.
|
// nolint:gocritic // Unit test.
|
||||||
ctx := dbauthz.AsSystemRestricted(testutil.Context(t, testutil.WaitSuperLong))
|
ctx := dbauthz.AsSystemRestricted(testutil.Context(t, testutil.WaitSuperLong))
|
||||||
_, _, api := coderdtest.NewWithAPI(t, nil)
|
store, _ := dbtestutil.NewDB(t)
|
||||||
|
logger := slogtest.Make(t, nil).Leveled(slog.LevelDebug)
|
||||||
|
|
||||||
// start mock SMTP server
|
// start mock SMTP server
|
||||||
mockSMTPSrv := smtpmock.New(smtpmock.ConfigurationAttr{
|
mockSMTPSrv := smtpmock.New(smtpmock.ConfigurationAttr{
|
||||||
@@ -155,17 +158,17 @@ func TestSMTPDispatch(t *testing.T) {
|
|||||||
Smarthost: serpent.HostPort{Host: "localhost", Port: fmt.Sprintf("%d", mockSMTPSrv.PortNumber())},
|
Smarthost: serpent.HostPort{Host: "localhost", Port: fmt.Sprintf("%d", mockSMTPSrv.PortNumber())},
|
||||||
Hello: "localhost",
|
Hello: "localhost",
|
||||||
}
|
}
|
||||||
handler := newDispatchInterceptor(dispatch.NewSMTPHandler(cfg.SMTP, defaultHelpers(), api.Logger.Named("smtp")))
|
handler := newDispatchInterceptor(dispatch.NewSMTPHandler(cfg.SMTP, defaultHelpers(), logger.Named("smtp")))
|
||||||
mgr, err := notifications.NewManager(cfg, api.Database, defaultHelpers(), createMetrics(), api.Logger.Named("manager"))
|
mgr, err := notifications.NewManager(cfg, store, defaultHelpers(), createMetrics(), logger.Named("manager"))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
mgr.WithHandlers(map[database.NotificationMethod]notifications.Handler{method: handler})
|
mgr.WithHandlers(map[database.NotificationMethod]notifications.Handler{method: handler})
|
||||||
t.Cleanup(func() {
|
t.Cleanup(func() {
|
||||||
assert.NoError(t, mgr.Stop(ctx))
|
assert.NoError(t, mgr.Stop(ctx))
|
||||||
})
|
})
|
||||||
enq, err := notifications.NewStoreEnqueuer(cfg, api.Database, defaultHelpers(), api.Logger.Named("enqueuer"), quartz.NewReal())
|
enq, err := notifications.NewStoreEnqueuer(cfg, store, defaultHelpers(), logger.Named("enqueuer"), quartz.NewReal())
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
user := createSampleUser(t, api.Database)
|
user := createSampleUser(t, store)
|
||||||
|
|
||||||
// WHEN: a message is enqueued
|
// WHEN: a message is enqueued
|
||||||
msgID, err := enq.Enqueue(ctx, user.ID, notifications.TemplateWorkspaceDeleted, map[string]string{}, "test")
|
msgID, err := enq.Enqueue(ctx, user.ID, notifications.TemplateWorkspaceDeleted, map[string]string{}, "test")
|
||||||
@@ -195,7 +198,8 @@ func TestWebhookDispatch(t *testing.T) {
|
|||||||
|
|
||||||
// nolint:gocritic // Unit test.
|
// nolint:gocritic // Unit test.
|
||||||
ctx := dbauthz.AsSystemRestricted(testutil.Context(t, testutil.WaitSuperLong))
|
ctx := dbauthz.AsSystemRestricted(testutil.Context(t, testutil.WaitSuperLong))
|
||||||
_, _, api := coderdtest.NewWithAPI(t, nil)
|
store, _ := dbtestutil.NewDB(t)
|
||||||
|
logger := slogtest.Make(t, nil).Leveled(slog.LevelDebug)
|
||||||
|
|
||||||
sent := make(chan dispatch.WebhookPayload, 1)
|
sent := make(chan dispatch.WebhookPayload, 1)
|
||||||
// Mock server to simulate webhook endpoint.
|
// Mock server to simulate webhook endpoint.
|
||||||
@@ -220,12 +224,12 @@ func TestWebhookDispatch(t *testing.T) {
|
|||||||
cfg.Webhook = codersdk.NotificationsWebhookConfig{
|
cfg.Webhook = codersdk.NotificationsWebhookConfig{
|
||||||
Endpoint: *serpent.URLOf(endpoint),
|
Endpoint: *serpent.URLOf(endpoint),
|
||||||
}
|
}
|
||||||
mgr, err := notifications.NewManager(cfg, api.Database, defaultHelpers(), createMetrics(), api.Logger.Named("manager"))
|
mgr, err := notifications.NewManager(cfg, store, defaultHelpers(), createMetrics(), logger.Named("manager"))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
t.Cleanup(func() {
|
t.Cleanup(func() {
|
||||||
assert.NoError(t, mgr.Stop(ctx))
|
assert.NoError(t, mgr.Stop(ctx))
|
||||||
})
|
})
|
||||||
enq, err := notifications.NewStoreEnqueuer(cfg, api.Database, defaultHelpers(), api.Logger.Named("enqueuer"), quartz.NewReal())
|
enq, err := notifications.NewStoreEnqueuer(cfg, store, defaultHelpers(), logger.Named("enqueuer"), quartz.NewReal())
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -233,7 +237,7 @@ func TestWebhookDispatch(t *testing.T) {
|
|||||||
name = "Robert McBobbington"
|
name = "Robert McBobbington"
|
||||||
username = "bob"
|
username = "bob"
|
||||||
)
|
)
|
||||||
user := dbgen.User(t, api.Database, database.User{
|
user := dbgen.User(t, store, database.User{
|
||||||
Email: email,
|
Email: email,
|
||||||
Username: username,
|
Username: username,
|
||||||
Name: name,
|
Name: name,
|
||||||
@@ -276,7 +280,8 @@ func TestBackpressure(t *testing.T) {
|
|||||||
|
|
||||||
// nolint:gocritic // Unit test.
|
// nolint:gocritic // Unit test.
|
||||||
ctx := dbauthz.AsSystemRestricted(testutil.Context(t, testutil.WaitSuperLong))
|
ctx := dbauthz.AsSystemRestricted(testutil.Context(t, testutil.WaitSuperLong))
|
||||||
_, _, api := coderdtest.NewWithAPI(t, nil)
|
store, _ := dbtestutil.NewDB(t)
|
||||||
|
logger := slogtest.Make(t, nil).Leveled(slog.LevelDebug)
|
||||||
|
|
||||||
// Mock server to simulate webhook endpoint.
|
// Mock server to simulate webhook endpoint.
|
||||||
var received atomic.Int32
|
var received atomic.Int32
|
||||||
@@ -314,19 +319,19 @@ func TestBackpressure(t *testing.T) {
|
|||||||
cfg.StoreSyncInterval = serpent.Duration(syncInterval)
|
cfg.StoreSyncInterval = serpent.Duration(syncInterval)
|
||||||
cfg.StoreSyncBufferSize = serpent.Int64(2)
|
cfg.StoreSyncBufferSize = serpent.Int64(2)
|
||||||
|
|
||||||
handler := newDispatchInterceptor(dispatch.NewWebhookHandler(cfg.Webhook, api.Logger.Named("webhook")))
|
handler := newDispatchInterceptor(dispatch.NewWebhookHandler(cfg.Webhook, logger.Named("webhook")))
|
||||||
|
|
||||||
// Intercept calls to submit the buffered updates to the store.
|
// Intercept calls to submit the buffered updates to the store.
|
||||||
storeInterceptor := &syncInterceptor{Store: api.Database}
|
storeInterceptor := &syncInterceptor{Store: store}
|
||||||
|
|
||||||
// GIVEN: a notification manager whose updates will be intercepted
|
// GIVEN: a notification manager whose updates will be intercepted
|
||||||
mgr, err := notifications.NewManager(cfg, storeInterceptor, defaultHelpers(), createMetrics(), api.Logger.Named("manager"))
|
mgr, err := notifications.NewManager(cfg, storeInterceptor, defaultHelpers(), createMetrics(), logger.Named("manager"))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
mgr.WithHandlers(map[database.NotificationMethod]notifications.Handler{method: handler})
|
mgr.WithHandlers(map[database.NotificationMethod]notifications.Handler{method: handler})
|
||||||
enq, err := notifications.NewStoreEnqueuer(cfg, api.Database, defaultHelpers(), api.Logger.Named("enqueuer"), quartz.NewReal())
|
enq, err := notifications.NewStoreEnqueuer(cfg, store, defaultHelpers(), logger.Named("enqueuer"), quartz.NewReal())
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
user := createSampleUser(t, api.Database)
|
user := createSampleUser(t, store)
|
||||||
|
|
||||||
// WHEN: a set of notifications are enqueued, which causes backpressure due to the batchSize which can be processed per fetch
|
// WHEN: a set of notifications are enqueued, which causes backpressure due to the batchSize which can be processed per fetch
|
||||||
const totalMessages = 30
|
const totalMessages = 30
|
||||||
@@ -367,7 +372,8 @@ func TestRetries(t *testing.T) {
|
|||||||
const maxAttempts = 3
|
const maxAttempts = 3
|
||||||
// nolint:gocritic // Unit test.
|
// nolint:gocritic // Unit test.
|
||||||
ctx := dbauthz.AsSystemRestricted(testutil.Context(t, testutil.WaitSuperLong))
|
ctx := dbauthz.AsSystemRestricted(testutil.Context(t, testutil.WaitSuperLong))
|
||||||
_, _, api := coderdtest.NewWithAPI(t, nil)
|
store, _ := dbtestutil.NewDB(t)
|
||||||
|
logger := slogtest.Make(t, nil).Leveled(slog.LevelDebug)
|
||||||
|
|
||||||
// GIVEN: a mock HTTP server which will receive webhooksand a map to track the dispatch attempts
|
// GIVEN: a mock HTTP server which will receive webhooksand a map to track the dispatch attempts
|
||||||
|
|
||||||
@@ -412,21 +418,21 @@ func TestRetries(t *testing.T) {
|
|||||||
cfg.RetryInterval = serpent.Duration(time.Second) // query uses second-precision
|
cfg.RetryInterval = serpent.Duration(time.Second) // query uses second-precision
|
||||||
cfg.FetchInterval = serpent.Duration(time.Millisecond * 100)
|
cfg.FetchInterval = serpent.Duration(time.Millisecond * 100)
|
||||||
|
|
||||||
handler := newDispatchInterceptor(dispatch.NewWebhookHandler(cfg.Webhook, api.Logger.Named("webhook")))
|
handler := newDispatchInterceptor(dispatch.NewWebhookHandler(cfg.Webhook, logger.Named("webhook")))
|
||||||
|
|
||||||
// Intercept calls to submit the buffered updates to the store.
|
// Intercept calls to submit the buffered updates to the store.
|
||||||
storeInterceptor := &syncInterceptor{Store: api.Database}
|
storeInterceptor := &syncInterceptor{Store: store}
|
||||||
|
|
||||||
mgr, err := notifications.NewManager(cfg, storeInterceptor, defaultHelpers(), createMetrics(), api.Logger.Named("manager"))
|
mgr, err := notifications.NewManager(cfg, storeInterceptor, defaultHelpers(), createMetrics(), logger.Named("manager"))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
t.Cleanup(func() {
|
t.Cleanup(func() {
|
||||||
assert.NoError(t, mgr.Stop(ctx))
|
assert.NoError(t, mgr.Stop(ctx))
|
||||||
})
|
})
|
||||||
mgr.WithHandlers(map[database.NotificationMethod]notifications.Handler{method: handler})
|
mgr.WithHandlers(map[database.NotificationMethod]notifications.Handler{method: handler})
|
||||||
enq, err := notifications.NewStoreEnqueuer(cfg, api.Database, defaultHelpers(), api.Logger.Named("enqueuer"), quartz.NewReal())
|
enq, err := notifications.NewStoreEnqueuer(cfg, store, defaultHelpers(), logger.Named("enqueuer"), quartz.NewReal())
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
user := createSampleUser(t, api.Database)
|
user := createSampleUser(t, store)
|
||||||
|
|
||||||
// WHEN: a few notifications are enqueued, which will all fail until their final retry (determined by the mock server)
|
// WHEN: a few notifications are enqueued, which will all fail until their final retry (determined by the mock server)
|
||||||
const msgCount = 5
|
const msgCount = 5
|
||||||
@@ -460,7 +466,8 @@ func TestExpiredLeaseIsRequeued(t *testing.T) {
|
|||||||
|
|
||||||
// nolint:gocritic // Unit test.
|
// nolint:gocritic // Unit test.
|
||||||
ctx := dbauthz.AsSystemRestricted(testutil.Context(t, testutil.WaitSuperLong))
|
ctx := dbauthz.AsSystemRestricted(testutil.Context(t, testutil.WaitSuperLong))
|
||||||
_, _, api := coderdtest.NewWithAPI(t, nil)
|
store, _ := dbtestutil.NewDB(t)
|
||||||
|
logger := slogtest.Make(t, nil).Leveled(slog.LevelDebug)
|
||||||
|
|
||||||
// GIVEN: a manager which has its updates intercepted and paused until measurements can be taken
|
// GIVEN: a manager which has its updates intercepted and paused until measurements can be taken
|
||||||
|
|
||||||
@@ -475,18 +482,18 @@ func TestExpiredLeaseIsRequeued(t *testing.T) {
|
|||||||
cfg.LeasePeriod = serpent.Duration(leasePeriod)
|
cfg.LeasePeriod = serpent.Duration(leasePeriod)
|
||||||
cfg.DispatchTimeout = serpent.Duration(leasePeriod - time.Millisecond)
|
cfg.DispatchTimeout = serpent.Duration(leasePeriod - time.Millisecond)
|
||||||
|
|
||||||
noopInterceptor := newNoopStoreSyncer(api.Database)
|
noopInterceptor := newNoopStoreSyncer(store)
|
||||||
|
|
||||||
// nolint:gocritic // Unit test.
|
// nolint:gocritic // Unit test.
|
||||||
mgrCtx, cancelManagerCtx := context.WithCancel(dbauthz.AsSystemRestricted(context.Background()))
|
mgrCtx, cancelManagerCtx := context.WithCancel(dbauthz.AsSystemRestricted(context.Background()))
|
||||||
t.Cleanup(cancelManagerCtx)
|
t.Cleanup(cancelManagerCtx)
|
||||||
|
|
||||||
mgr, err := notifications.NewManager(cfg, noopInterceptor, defaultHelpers(), createMetrics(), api.Logger.Named("manager"))
|
mgr, err := notifications.NewManager(cfg, noopInterceptor, defaultHelpers(), createMetrics(), logger.Named("manager"))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
enq, err := notifications.NewStoreEnqueuer(cfg, api.Database, defaultHelpers(), api.Logger.Named("enqueuer"), quartz.NewReal())
|
enq, err := notifications.NewStoreEnqueuer(cfg, store, defaultHelpers(), logger.Named("enqueuer"), quartz.NewReal())
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
user := createSampleUser(t, api.Database)
|
user := createSampleUser(t, store)
|
||||||
|
|
||||||
// WHEN: a few notifications are enqueued which will all succeed
|
// WHEN: a few notifications are enqueued which will all succeed
|
||||||
var msgs []string
|
var msgs []string
|
||||||
@@ -507,7 +514,7 @@ func TestExpiredLeaseIsRequeued(t *testing.T) {
|
|||||||
cancelManagerCtx()
|
cancelManagerCtx()
|
||||||
|
|
||||||
// Fetch any messages currently in "leased" status, and verify that they're exactly the ones we enqueued.
|
// Fetch any messages currently in "leased" status, and verify that they're exactly the ones we enqueued.
|
||||||
leased, err := api.Database.GetNotificationMessagesByStatus(ctx, database.GetNotificationMessagesByStatusParams{
|
leased, err := store.GetNotificationMessagesByStatus(ctx, database.GetNotificationMessagesByStatusParams{
|
||||||
Status: database.NotificationMessageStatusLeased,
|
Status: database.NotificationMessageStatusLeased,
|
||||||
Limit: msgCount,
|
Limit: msgCount,
|
||||||
})
|
})
|
||||||
@@ -527,9 +534,9 @@ func TestExpiredLeaseIsRequeued(t *testing.T) {
|
|||||||
|
|
||||||
// Start a new notification manager.
|
// Start a new notification manager.
|
||||||
// Intercept calls to submit the buffered updates to the store.
|
// Intercept calls to submit the buffered updates to the store.
|
||||||
storeInterceptor := &syncInterceptor{Store: api.Database}
|
storeInterceptor := &syncInterceptor{Store: store}
|
||||||
handler := newDispatchInterceptor(&fakeHandler{})
|
handler := newDispatchInterceptor(&fakeHandler{})
|
||||||
mgr, err = notifications.NewManager(cfg, storeInterceptor, defaultHelpers(), createMetrics(), api.Logger.Named("manager"))
|
mgr, err = notifications.NewManager(cfg, storeInterceptor, defaultHelpers(), createMetrics(), logger.Named("manager"))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
mgr.WithHandlers(map[database.NotificationMethod]notifications.Handler{method: handler})
|
mgr.WithHandlers(map[database.NotificationMethod]notifications.Handler{method: handler})
|
||||||
|
|
||||||
@@ -546,7 +553,7 @@ func TestExpiredLeaseIsRequeued(t *testing.T) {
|
|||||||
}, testutil.WaitLong, testutil.IntervalFast)
|
}, testutil.WaitLong, testutil.IntervalFast)
|
||||||
|
|
||||||
// Validate that no more messages are in "leased" status.
|
// Validate that no more messages are in "leased" status.
|
||||||
leased, err = api.Database.GetNotificationMessagesByStatus(ctx, database.GetNotificationMessagesByStatusParams{
|
leased, err = store.GetNotificationMessagesByStatus(ctx, database.GetNotificationMessagesByStatusParams{
|
||||||
Status: database.NotificationMessageStatusLeased,
|
Status: database.NotificationMessageStatusLeased,
|
||||||
Limit: msgCount,
|
Limit: msgCount,
|
||||||
})
|
})
|
||||||
@@ -558,7 +565,8 @@ func TestExpiredLeaseIsRequeued(t *testing.T) {
|
|||||||
func TestInvalidConfig(t *testing.T) {
|
func TestInvalidConfig(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
_, _, api := coderdtest.NewWithAPI(t, nil)
|
store, _ := dbtestutil.NewDB(t)
|
||||||
|
logger := slogtest.Make(t, nil).Leveled(slog.LevelDebug)
|
||||||
|
|
||||||
// GIVEN: invalid config with dispatch period <= lease period
|
// GIVEN: invalid config with dispatch period <= lease period
|
||||||
const (
|
const (
|
||||||
@@ -570,7 +578,7 @@ func TestInvalidConfig(t *testing.T) {
|
|||||||
cfg.DispatchTimeout = serpent.Duration(leasePeriod)
|
cfg.DispatchTimeout = serpent.Duration(leasePeriod)
|
||||||
|
|
||||||
// WHEN: the manager is created with invalid config
|
// WHEN: the manager is created with invalid config
|
||||||
_, err := notifications.NewManager(cfg, api.Database, defaultHelpers(), createMetrics(), api.Logger.Named("manager"))
|
_, err := notifications.NewManager(cfg, store, defaultHelpers(), createMetrics(), logger.Named("manager"))
|
||||||
|
|
||||||
// THEN: the manager will fail to be created, citing invalid config as error
|
// THEN: the manager will fail to be created, citing invalid config as error
|
||||||
require.ErrorIs(t, err, notifications.ErrInvalidDispatchTimeout)
|
require.ErrorIs(t, err, notifications.ErrInvalidDispatchTimeout)
|
||||||
@@ -583,29 +591,30 @@ func TestNotifierPaused(t *testing.T) {
|
|||||||
|
|
||||||
// nolint:gocritic // Unit test.
|
// nolint:gocritic // Unit test.
|
||||||
ctx := dbauthz.AsSystemRestricted(testutil.Context(t, testutil.WaitSuperLong))
|
ctx := dbauthz.AsSystemRestricted(testutil.Context(t, testutil.WaitSuperLong))
|
||||||
_, _, api := coderdtest.NewWithAPI(t, nil)
|
store, _ := dbtestutil.NewDB(t)
|
||||||
|
logger := slogtest.Make(t, nil).Leveled(slog.LevelDebug)
|
||||||
|
|
||||||
// Prepare the test.
|
// Prepare the test.
|
||||||
handler := &fakeHandler{}
|
handler := &fakeHandler{}
|
||||||
method := database.NotificationMethodSmtp
|
method := database.NotificationMethodSmtp
|
||||||
user := createSampleUser(t, api.Database)
|
user := createSampleUser(t, store)
|
||||||
|
|
||||||
const fetchInterval = time.Millisecond * 100
|
const fetchInterval = time.Millisecond * 100
|
||||||
cfg := defaultNotificationsConfig(method)
|
cfg := defaultNotificationsConfig(method)
|
||||||
cfg.FetchInterval = serpent.Duration(fetchInterval)
|
cfg.FetchInterval = serpent.Duration(fetchInterval)
|
||||||
mgr, err := notifications.NewManager(cfg, api.Database, defaultHelpers(), createMetrics(), api.Logger.Named("manager"))
|
mgr, err := notifications.NewManager(cfg, store, defaultHelpers(), createMetrics(), logger.Named("manager"))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
mgr.WithHandlers(map[database.NotificationMethod]notifications.Handler{method: handler})
|
mgr.WithHandlers(map[database.NotificationMethod]notifications.Handler{method: handler})
|
||||||
t.Cleanup(func() {
|
t.Cleanup(func() {
|
||||||
assert.NoError(t, mgr.Stop(ctx))
|
assert.NoError(t, mgr.Stop(ctx))
|
||||||
})
|
})
|
||||||
enq, err := notifications.NewStoreEnqueuer(cfg, api.Database, defaultHelpers(), api.Logger.Named("enqueuer"), quartz.NewReal())
|
enq, err := notifications.NewStoreEnqueuer(cfg, store, defaultHelpers(), logger.Named("enqueuer"), quartz.NewReal())
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
// Pause the notifier.
|
// Pause the notifier.
|
||||||
settingsJSON, err := json.Marshal(&codersdk.NotificationsSettings{NotifierPaused: true})
|
settingsJSON, err := json.Marshal(&codersdk.NotificationsSettings{NotifierPaused: true})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
err = api.Database.UpsertNotificationsSettings(ctx, string(settingsJSON))
|
err = store.UpsertNotificationsSettings(ctx, string(settingsJSON))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
// Start the manager so that notifications are processed, except it will be paused at this point.
|
// Start the manager so that notifications are processed, except it will be paused at this point.
|
||||||
@@ -618,7 +627,7 @@ func TestNotifierPaused(t *testing.T) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
// Ensure we have a pending message and it's the expected one.
|
// Ensure we have a pending message and it's the expected one.
|
||||||
pendingMessages, err := api.Database.GetNotificationMessagesByStatus(ctx, database.GetNotificationMessagesByStatusParams{
|
pendingMessages, err := store.GetNotificationMessagesByStatus(ctx, database.GetNotificationMessagesByStatusParams{
|
||||||
Status: database.NotificationMessageStatusPending,
|
Status: database.NotificationMessageStatusPending,
|
||||||
Limit: 10,
|
Limit: 10,
|
||||||
})
|
})
|
||||||
@@ -639,7 +648,7 @@ func TestNotifierPaused(t *testing.T) {
|
|||||||
// Unpause the notifier.
|
// Unpause the notifier.
|
||||||
settingsJSON, err = json.Marshal(&codersdk.NotificationsSettings{NotifierPaused: false})
|
settingsJSON, err = json.Marshal(&codersdk.NotificationsSettings{NotifierPaused: false})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
err = api.Database.UpsertNotificationsSettings(ctx, string(settingsJSON))
|
err = store.UpsertNotificationsSettings(ctx, string(settingsJSON))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
// Notifier is running again, message should be dequeued.
|
// Notifier is running again, message should be dequeued.
|
||||||
@@ -1287,17 +1296,18 @@ func TestDisabledBeforeEnqueue(t *testing.T) {
|
|||||||
|
|
||||||
// nolint:gocritic // Unit test.
|
// nolint:gocritic // Unit test.
|
||||||
ctx := dbauthz.AsSystemRestricted(testutil.Context(t, testutil.WaitSuperLong))
|
ctx := dbauthz.AsSystemRestricted(testutil.Context(t, testutil.WaitSuperLong))
|
||||||
_, _, api := coderdtest.NewWithAPI(t, nil)
|
store, _ := dbtestutil.NewDB(t)
|
||||||
|
logger := slogtest.Make(t, nil).Leveled(slog.LevelDebug)
|
||||||
|
|
||||||
// GIVEN: an enqueuer & a sample user
|
// GIVEN: an enqueuer & a sample user
|
||||||
cfg := defaultNotificationsConfig(database.NotificationMethodSmtp)
|
cfg := defaultNotificationsConfig(database.NotificationMethodSmtp)
|
||||||
enq, err := notifications.NewStoreEnqueuer(cfg, api.Database, defaultHelpers(), api.Logger.Named("enqueuer"), quartz.NewReal())
|
enq, err := notifications.NewStoreEnqueuer(cfg, store, defaultHelpers(), logger.Named("enqueuer"), quartz.NewReal())
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
user := createSampleUser(t, api.Database)
|
user := createSampleUser(t, store)
|
||||||
|
|
||||||
// WHEN: the user has a preference set to not receive the "workspace deleted" notification
|
// WHEN: the user has a preference set to not receive the "workspace deleted" notification
|
||||||
templateID := notifications.TemplateWorkspaceDeleted
|
templateID := notifications.TemplateWorkspaceDeleted
|
||||||
n, err := api.Database.UpdateUserNotificationPreferences(ctx, database.UpdateUserNotificationPreferencesParams{
|
n, err := store.UpdateUserNotificationPreferences(ctx, database.UpdateUserNotificationPreferencesParams{
|
||||||
UserID: user.ID,
|
UserID: user.ID,
|
||||||
NotificationTemplateIds: []uuid.UUID{templateID},
|
NotificationTemplateIds: []uuid.UUID{templateID},
|
||||||
Disableds: []bool{true},
|
Disableds: []bool{true},
|
||||||
@@ -1322,20 +1332,21 @@ func TestDisabledAfterEnqueue(t *testing.T) {
|
|||||||
|
|
||||||
// nolint:gocritic // Unit test.
|
// nolint:gocritic // Unit test.
|
||||||
ctx := dbauthz.AsSystemRestricted(testutil.Context(t, testutil.WaitSuperLong))
|
ctx := dbauthz.AsSystemRestricted(testutil.Context(t, testutil.WaitSuperLong))
|
||||||
_, _, api := coderdtest.NewWithAPI(t, nil)
|
store, _ := dbtestutil.NewDB(t)
|
||||||
|
logger := slogtest.Make(t, nil).Leveled(slog.LevelDebug)
|
||||||
|
|
||||||
method := database.NotificationMethodSmtp
|
method := database.NotificationMethodSmtp
|
||||||
cfg := defaultNotificationsConfig(method)
|
cfg := defaultNotificationsConfig(method)
|
||||||
|
|
||||||
mgr, err := notifications.NewManager(cfg, api.Database, defaultHelpers(), createMetrics(), api.Logger.Named("manager"))
|
mgr, err := notifications.NewManager(cfg, store, defaultHelpers(), createMetrics(), logger.Named("manager"))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
t.Cleanup(func() {
|
t.Cleanup(func() {
|
||||||
assert.NoError(t, mgr.Stop(ctx))
|
assert.NoError(t, mgr.Stop(ctx))
|
||||||
})
|
})
|
||||||
|
|
||||||
enq, err := notifications.NewStoreEnqueuer(cfg, api.Database, defaultHelpers(), api.Logger.Named("enqueuer"), quartz.NewReal())
|
enq, err := notifications.NewStoreEnqueuer(cfg, store, defaultHelpers(), logger.Named("enqueuer"), quartz.NewReal())
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
user := createSampleUser(t, api.Database)
|
user := createSampleUser(t, store)
|
||||||
|
|
||||||
// GIVEN: a notification is enqueued which has not (yet) been disabled
|
// GIVEN: a notification is enqueued which has not (yet) been disabled
|
||||||
templateID := notifications.TemplateWorkspaceDeleted
|
templateID := notifications.TemplateWorkspaceDeleted
|
||||||
@@ -1343,7 +1354,7 @@ func TestDisabledAfterEnqueue(t *testing.T) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
// Disable the notification template.
|
// Disable the notification template.
|
||||||
n, err := api.Database.UpdateUserNotificationPreferences(ctx, database.UpdateUserNotificationPreferencesParams{
|
n, err := store.UpdateUserNotificationPreferences(ctx, database.UpdateUserNotificationPreferencesParams{
|
||||||
UserID: user.ID,
|
UserID: user.ID,
|
||||||
NotificationTemplateIds: []uuid.UUID{templateID},
|
NotificationTemplateIds: []uuid.UUID{templateID},
|
||||||
Disableds: []bool{true},
|
Disableds: []bool{true},
|
||||||
@@ -1356,7 +1367,7 @@ func TestDisabledAfterEnqueue(t *testing.T) {
|
|||||||
|
|
||||||
// THEN: the message should not be sent, and must be set to "inhibited"
|
// THEN: the message should not be sent, and must be set to "inhibited"
|
||||||
require.EventuallyWithT(t, func(ct *assert.CollectT) {
|
require.EventuallyWithT(t, func(ct *assert.CollectT) {
|
||||||
m, err := api.Database.GetNotificationMessagesByStatus(ctx, database.GetNotificationMessagesByStatusParams{
|
m, err := store.GetNotificationMessagesByStatus(ctx, database.GetNotificationMessagesByStatusParams{
|
||||||
Status: database.NotificationMessageStatusInhibited,
|
Status: database.NotificationMessageStatusInhibited,
|
||||||
Limit: 10,
|
Limit: 10,
|
||||||
})
|
})
|
||||||
@@ -1378,7 +1389,8 @@ func TestCustomNotificationMethod(t *testing.T) {
|
|||||||
|
|
||||||
// nolint:gocritic // Unit test.
|
// nolint:gocritic // Unit test.
|
||||||
ctx := dbauthz.AsSystemRestricted(testutil.Context(t, testutil.WaitSuperLong))
|
ctx := dbauthz.AsSystemRestricted(testutil.Context(t, testutil.WaitSuperLong))
|
||||||
_, _, api := coderdtest.NewWithAPI(t, nil)
|
store, _ := dbtestutil.NewDB(t)
|
||||||
|
logger := slogtest.Make(t, nil).Leveled(slog.LevelDebug)
|
||||||
|
|
||||||
received := make(chan uuid.UUID, 1)
|
received := make(chan uuid.UUID, 1)
|
||||||
|
|
||||||
@@ -1417,7 +1429,7 @@ func TestCustomNotificationMethod(t *testing.T) {
|
|||||||
defaultMethod = database.NotificationMethodSmtp
|
defaultMethod = database.NotificationMethodSmtp
|
||||||
customMethod = database.NotificationMethodWebhook
|
customMethod = database.NotificationMethodWebhook
|
||||||
)
|
)
|
||||||
out, err := api.Database.UpdateNotificationTemplateMethodByID(ctx, database.UpdateNotificationTemplateMethodByIDParams{
|
out, err := store.UpdateNotificationTemplateMethodByID(ctx, database.UpdateNotificationTemplateMethodByIDParams{
|
||||||
ID: template,
|
ID: template,
|
||||||
Method: database.NullNotificationMethod{NotificationMethod: customMethod, Valid: true},
|
Method: database.NullNotificationMethod{NotificationMethod: customMethod, Valid: true},
|
||||||
})
|
})
|
||||||
@@ -1435,17 +1447,17 @@ func TestCustomNotificationMethod(t *testing.T) {
|
|||||||
Endpoint: *serpent.URLOf(endpoint),
|
Endpoint: *serpent.URLOf(endpoint),
|
||||||
}
|
}
|
||||||
|
|
||||||
mgr, err := notifications.NewManager(cfg, api.Database, defaultHelpers(), createMetrics(), api.Logger.Named("manager"))
|
mgr, err := notifications.NewManager(cfg, store, defaultHelpers(), createMetrics(), logger.Named("manager"))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
t.Cleanup(func() {
|
t.Cleanup(func() {
|
||||||
_ = mgr.Stop(ctx)
|
_ = mgr.Stop(ctx)
|
||||||
})
|
})
|
||||||
|
|
||||||
enq, err := notifications.NewStoreEnqueuer(cfg, api.Database, defaultHelpers(), api.Logger, quartz.NewReal())
|
enq, err := notifications.NewStoreEnqueuer(cfg, store, defaultHelpers(), logger.Named("enqueuer"), quartz.NewReal())
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
// WHEN: a notification of that template is enqueued, it should be delivered with the configured method - not the default.
|
// WHEN: a notification of that template is enqueued, it should be delivered with the configured method - not the default.
|
||||||
user := createSampleUser(t, api.Database)
|
user := createSampleUser(t, store)
|
||||||
msgID, err := enq.Enqueue(ctx, user.ID, template, map[string]string{}, "test")
|
msgID, err := enq.Enqueue(ctx, user.ID, template, map[string]string{}, "test")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
@@ -1518,12 +1530,13 @@ func TestNotificationDuplicates(t *testing.T) {
|
|||||||
|
|
||||||
// nolint:gocritic // Unit test.
|
// nolint:gocritic // Unit test.
|
||||||
ctx := dbauthz.AsSystemRestricted(testutil.Context(t, testutil.WaitSuperLong))
|
ctx := dbauthz.AsSystemRestricted(testutil.Context(t, testutil.WaitSuperLong))
|
||||||
_, _, api := coderdtest.NewWithAPI(t, nil)
|
store, _ := dbtestutil.NewDB(t)
|
||||||
|
logger := slogtest.Make(t, nil).Leveled(slog.LevelDebug)
|
||||||
|
|
||||||
method := database.NotificationMethodSmtp
|
method := database.NotificationMethodSmtp
|
||||||
cfg := defaultNotificationsConfig(method)
|
cfg := defaultNotificationsConfig(method)
|
||||||
|
|
||||||
mgr, err := notifications.NewManager(cfg, api.Database, defaultHelpers(), createMetrics(), api.Logger.Named("manager"))
|
mgr, err := notifications.NewManager(cfg, store, defaultHelpers(), createMetrics(), logger.Named("manager"))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
t.Cleanup(func() {
|
t.Cleanup(func() {
|
||||||
assert.NoError(t, mgr.Stop(ctx))
|
assert.NoError(t, mgr.Stop(ctx))
|
||||||
@@ -1533,9 +1546,9 @@ func TestNotificationDuplicates(t *testing.T) {
|
|||||||
mClock := quartz.NewMock(t)
|
mClock := quartz.NewMock(t)
|
||||||
mClock.Set(time.Date(2024, 1, 15, 9, 0, 0, 0, time.UTC))
|
mClock.Set(time.Date(2024, 1, 15, 9, 0, 0, 0, time.UTC))
|
||||||
|
|
||||||
enq, err := notifications.NewStoreEnqueuer(cfg, api.Database, defaultHelpers(), api.Logger.Named("enqueuer"), mClock)
|
enq, err := notifications.NewStoreEnqueuer(cfg, store, defaultHelpers(), logger.Named("enqueuer"), mClock)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
user := createSampleUser(t, api.Database)
|
user := createSampleUser(t, store)
|
||||||
|
|
||||||
// GIVEN: two notifications are enqueued with identical properties.
|
// GIVEN: two notifications are enqueued with identical properties.
|
||||||
_, err = enq.Enqueue(ctx, user.ID, notifications.TemplateWorkspaceDeleted,
|
_, err = enq.Enqueue(ctx, user.ID, notifications.TemplateWorkspaceDeleted,
|
||||||
|
|||||||
Reference in New Issue
Block a user