Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commite7dbbcd

Browse files
fix: do not notify marked for deletion for deleted workspaces (#20937)
Closes#20913I've ran the test without the fix, verified the test caught the issue,then applied the fix, and confirmed the issue no longer happens.---🤖 PR was initially written by Claude Opus 4.5 Thinking using Claude Codeand then review by a human 👩
1 parentbbf7b13 commite7dbbcd

File tree

3 files changed

+101
-0
lines changed

3 files changed

+101
-0
lines changed

‎coderd/database/queries.sql.go‎

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more aboutcustomizing how changed files appear on GitHub.

‎coderd/database/queries/workspaces.sql‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -846,6 +846,7 @@ SET
846846
WHERE
847847
template_id= @template_id
848848
AND dormant_atIS NOT NULL
849+
AND deleted= false
849850
-- Prebuilt workspaces (identified by having the prebuilds system user as owner_id)
850851
-- should not have their dormant or deleting at set, as these are handled by the
851852
-- prebuilds reconciliation loop.

‎enterprise/coderd/schedule/template_test.go‎

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -737,6 +737,105 @@ func TestNotifications(t *testing.T) {
737737
require.Contains(t,sent[i].Targets,dormantWs.OwnerID)
738738
}
739739
})
740+
741+
// Regression test for https://github.com/coder/coder/issues/20913
742+
// Deleted workspaces should not receive dormancy notifications.
743+
t.Run("DeletedWorkspacesNotNotified",func(t*testing.T) {
744+
t.Parallel()
745+
746+
var (
747+
db,_=dbtestutil.NewDB(t)
748+
ctx=testutil.Context(t,testutil.WaitLong)
749+
user=dbgen.User(t,db, database.User{})
750+
file=dbgen.File(t,db, database.File{
751+
CreatedBy:user.ID,
752+
})
753+
templateJob=dbgen.ProvisionerJob(t,db,nil, database.ProvisionerJob{
754+
FileID:file.ID,
755+
InitiatorID:user.ID,
756+
Tags: database.StringMap{
757+
"foo":"bar",
758+
},
759+
})
760+
timeTilDormant=time.Minute*2
761+
templateVersion=dbgen.TemplateVersion(t,db, database.TemplateVersion{
762+
CreatedBy:user.ID,
763+
JobID:templateJob.ID,
764+
OrganizationID:templateJob.OrganizationID,
765+
})
766+
template=dbgen.Template(t,db, database.Template{
767+
ActiveVersionID:templateVersion.ID,
768+
CreatedBy:user.ID,
769+
OrganizationID:templateJob.OrganizationID,
770+
TimeTilDormant:int64(timeTilDormant),
771+
TimeTilDormantAutoDelete:int64(timeTilDormant),
772+
})
773+
)
774+
775+
// Create a dormant workspace that is NOT deleted.
776+
activeDormantWorkspace:=dbgen.Workspace(t,db, database.WorkspaceTable{
777+
OwnerID:user.ID,
778+
TemplateID:template.ID,
779+
OrganizationID:templateJob.OrganizationID,
780+
LastUsedAt:time.Now().Add(-time.Hour),
781+
})
782+
_,err:=db.UpdateWorkspaceDormantDeletingAt(ctx, database.UpdateWorkspaceDormantDeletingAtParams{
783+
ID:activeDormantWorkspace.ID,
784+
DormantAt: sql.NullTime{
785+
Time:activeDormantWorkspace.LastUsedAt.Add(timeTilDormant),
786+
Valid:true,
787+
},
788+
})
789+
require.NoError(t,err)
790+
791+
// Create a dormant workspace that IS deleted.
792+
deletedDormantWorkspace:=dbgen.Workspace(t,db, database.WorkspaceTable{
793+
OwnerID:user.ID,
794+
TemplateID:template.ID,
795+
OrganizationID:templateJob.OrganizationID,
796+
LastUsedAt:time.Now().Add(-time.Hour),
797+
Deleted:true,// Mark as deleted
798+
})
799+
_,err=db.UpdateWorkspaceDormantDeletingAt(ctx, database.UpdateWorkspaceDormantDeletingAtParams{
800+
ID:deletedDormantWorkspace.ID,
801+
DormantAt: sql.NullTime{
802+
Time:deletedDormantWorkspace.LastUsedAt.Add(timeTilDormant),
803+
Valid:true,
804+
},
805+
})
806+
require.NoError(t,err)
807+
808+
// Setup dependencies
809+
notifyEnq:=notificationstest.NewFakeEnqueuer()
810+
logger:=slogtest.Make(t,&slogtest.Options{IgnoreErrors:true}).Leveled(slog.LevelDebug)
811+
constuserQuietHoursSchedule="CRON_TZ=UTC 0 0 * * *"// midnight UTC
812+
userQuietHoursStore,err:=schedule.NewEnterpriseUserQuietHoursScheduleStore(userQuietHoursSchedule,true)
813+
require.NoError(t,err)
814+
userQuietHoursStorePtr:=&atomic.Pointer[agplschedule.UserQuietHoursScheduleStore]{}
815+
userQuietHoursStorePtr.Store(&userQuietHoursStore)
816+
templateScheduleStore:=schedule.NewEnterpriseTemplateScheduleStore(userQuietHoursStorePtr,notifyEnq,logger,nil)
817+
818+
// Lower the dormancy TTL to ensure the schedule recalculates deadlines and
819+
// triggers notifications.
820+
_,err=templateScheduleStore.Set(dbauthz.AsNotifier(ctx),db,template, agplschedule.TemplateScheduleOptions{
821+
TimeTilDormant:timeTilDormant/2,
822+
TimeTilDormantAutoDelete:timeTilDormant/2,
823+
})
824+
require.NoError(t,err)
825+
826+
// We should only receive a notification for the non-deleted dormant workspace.
827+
sent:=notifyEnq.Sent()
828+
require.Len(t,sent,1,"expected exactly 1 notification for the non-deleted workspace")
829+
require.Equal(t,sent[0].UserID,activeDormantWorkspace.OwnerID)
830+
require.Equal(t,sent[0].TemplateID,notifications.TemplateWorkspaceMarkedForDeletion)
831+
require.Contains(t,sent[0].Targets,activeDormantWorkspace.ID)
832+
833+
// Ensure the deleted workspace was NOT notified
834+
for_,notification:=rangesent {
835+
require.NotContains(t,notification.Targets,deletedDormantWorkspace.ID,
836+
"deleted workspace should not receive notifications")
837+
}
838+
})
740839
}
741840

742841
funcTestTemplateTTL(t*testing.T) {

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp