package schedule_test import ( "context" "testing" "github.com/google/uuid" "github.com/stretchr/testify/require" "go.uber.org/mock/gomock" "github.com/coder/coder/v2/coderd/database" "github.com/coder/coder/v2/coderd/database/dbmock" agpl "github.com/coder/coder/v2/coderd/schedule" "github.com/coder/coder/v2/enterprise/coderd/schedule" ) func TestEnterpriseUserQuietHoursSchedule(t *testing.T) { t.Parallel() const ( defaultSchedule = "CRON_TZ=UTC 15 10 * * *" userCustomSchedule1 = "CRON_TZ=Australia/Sydney 30 2 * * *" userCustomSchedule2 = "CRON_TZ=Australia/Sydney 0 18 * * *" ) t.Run("OK", func(t *testing.T) { t.Parallel() userID := uuid.New() s, err := schedule.NewEnterpriseUserQuietHoursScheduleStore(defaultSchedule, true) require.NoError(t, err) mDB := dbmock.NewMockStore(gomock.NewController(t)) // User has no schedule set, use default. mDB.EXPECT().GetUserByID(gomock.Any(), userID).Return(database.User{}, nil).Times(1) opts, err := s.Get(context.Background(), mDB, userID) require.NoError(t, err) require.NotNil(t, opts.Schedule) require.Equal(t, defaultSchedule, opts.Schedule.String()) require.False(t, opts.UserSet) require.True(t, opts.UserCanSet) // User has a custom schedule set. mDB.EXPECT().GetUserByID(gomock.Any(), userID).Return(database.User{ QuietHoursSchedule: userCustomSchedule1, }, nil).Times(1) opts, err = s.Get(context.Background(), mDB, userID) require.NoError(t, err) require.NotNil(t, opts.Schedule) require.Equal(t, userCustomSchedule1, opts.Schedule.String()) require.True(t, opts.UserSet) require.True(t, opts.UserCanSet) // Set user schedule. mDB.EXPECT().UpdateUserQuietHoursSchedule(gomock.Any(), database.UpdateUserQuietHoursScheduleParams{ ID: userID, QuietHoursSchedule: userCustomSchedule2, }).Return(database.User{}, nil).Times(1) opts, err = s.Set(context.Background(), mDB, userID, userCustomSchedule2) require.NoError(t, err) require.NotNil(t, opts.Schedule) require.Equal(t, userCustomSchedule2, opts.Schedule.String()) require.True(t, opts.UserSet) }) t.Run("BadDefaultSchedule", func(t *testing.T) { t.Parallel() _, err := schedule.NewEnterpriseUserQuietHoursScheduleStore("bad schedule", true) require.Error(t, err) require.ErrorContains(t, err, `parse daily schedule "bad schedule"`) }) t.Run("BadGotSchedule", func(t *testing.T) { t.Parallel() userID := uuid.New() s, err := schedule.NewEnterpriseUserQuietHoursScheduleStore(defaultSchedule, true) require.NoError(t, err) mDB := dbmock.NewMockStore(gomock.NewController(t)) // User has a custom schedule set. mDB.EXPECT().GetUserByID(gomock.Any(), userID).Return(database.User{ QuietHoursSchedule: "bad schedule", }, nil).Times(1) _, err = s.Get(context.Background(), mDB, userID) require.Error(t, err) require.ErrorContains(t, err, `parse daily schedule "bad schedule"`) }) t.Run("BadSetSchedule", func(t *testing.T) { t.Parallel() s, err := schedule.NewEnterpriseUserQuietHoursScheduleStore(defaultSchedule, true) require.NoError(t, err) // Use the mock DB here. It won't get used, but if it ever does it will // fail the test. mDB := dbmock.NewMockStore(gomock.NewController(t)) _, err = s.Set(context.Background(), mDB, uuid.New(), "bad schedule") require.Error(t, err) require.ErrorContains(t, err, `parse daily schedule "bad schedule"`) }) t.Run("UserCannotSet", func(t *testing.T) { t.Parallel() userID := uuid.New() s, err := schedule.NewEnterpriseUserQuietHoursScheduleStore(defaultSchedule, false) // <--- require.NoError(t, err) // Use the mock DB here. It won't get used, but if it ever does it will // fail the test. mDB := dbmock.NewMockStore(gomock.NewController(t)) // Should never reach out to DB to check user's custom schedule. opts, err := s.Get(context.Background(), mDB, userID) require.NoError(t, err) require.NotNil(t, opts.Schedule) require.Equal(t, defaultSchedule, opts.Schedule.String()) require.False(t, opts.UserSet) require.False(t, opts.UserCanSet) // Set user schedule should fail. _, err = s.Set(context.Background(), mDB, userID, userCustomSchedule1) require.Error(t, err) require.ErrorIs(t, err, agpl.ErrUserCannotSetQuietHoursSchedule) }) }