mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
fix(coderd): show correct deletion time in dormancy notification (#26488)
The dormancy notification's "will be automatically deleted in X" sentence rendered the dormancy threshold instead of the auto-delete duration. A 30-day threshold rendered as "4 weeks" even when auto-delete was 90 days; a 60-day threshold rendered as "1 month" with a 7-day auto-delete. Render the countdown from the auto-delete setting, and skip the deletion sentence entirely when auto-delete is disabled so the notification no longer promises a deletion that will never happen.
This commit is contained in:
@@ -1437,6 +1437,94 @@ func TestNotifications(t *testing.T) {
|
||||
require.Contains(t, sent[0].Targets, workspace.ID)
|
||||
require.Contains(t, sent[0].Targets, workspace.OrganizationID)
|
||||
require.Contains(t, sent[0].Targets, workspace.OwnerID)
|
||||
|
||||
// The template does not configure auto-delete, so the body must not
|
||||
// indicate a deletion timeline.
|
||||
require.NotContains(t, sent[0].Labels, "timeTilDelete")
|
||||
require.Equal(t, workspace.Name, sent[0].Labels["name"])
|
||||
require.Equal(t, "inactivity exceeded the dormancy threshold", sent[0].Labels["reason"])
|
||||
})
|
||||
|
||||
t.Run("DormancyAutoDelete", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// Setup template with dormancy and auto-delete and create a workspace
|
||||
// with it. The two durations are intentionally far apart to reliably
|
||||
// check what's rendered in the notification.
|
||||
var (
|
||||
ticker = make(chan time.Time)
|
||||
statCh = make(chan autobuild.Stats)
|
||||
notifyEnq = notificationstest.FakeEnqueuer{}
|
||||
// 35 days is inside humanize.Time's "1 month" bucket (between 30 and 60 days).
|
||||
timeTilDormant = time.Minute
|
||||
timeTilDormantAutoDelete = 35 * 24 * time.Hour
|
||||
client, db = coderdtest.NewWithDatabase(t, &coderdtest.Options{
|
||||
AutobuildTicker: ticker,
|
||||
AutobuildStats: statCh,
|
||||
IncludeProvisionerDaemon: true,
|
||||
NotificationsEnqueuer: ¬ifyEnq,
|
||||
TemplateScheduleStore: schedule.MockTemplateScheduleStore{
|
||||
SetFn: func(ctx context.Context, db database.Store, template database.Template, options schedule.TemplateScheduleOptions) (database.Template, error) {
|
||||
template.TimeTilDormant = int64(options.TimeTilDormant)
|
||||
template.TimeTilDormantAutoDelete = int64(options.TimeTilDormantAutoDelete)
|
||||
return schedule.NewAGPLTemplateScheduleStore().Set(ctx, db, template, options)
|
||||
},
|
||||
GetFn: func(_ context.Context, _ database.Store, _ uuid.UUID) (schedule.TemplateScheduleOptions, error) {
|
||||
return schedule.TemplateScheduleOptions{
|
||||
UserAutostartEnabled: false,
|
||||
UserAutostopEnabled: true,
|
||||
DefaultTTL: 0,
|
||||
AutostopRequirement: schedule.TemplateAutostopRequirement{},
|
||||
TimeTilDormant: timeTilDormant,
|
||||
TimeTilDormantAutoDelete: timeTilDormantAutoDelete,
|
||||
}, nil
|
||||
},
|
||||
},
|
||||
})
|
||||
admin = coderdtest.CreateFirstUser(t, client)
|
||||
version = coderdtest.CreateTemplateVersion(t, client, admin.OrganizationID, nil)
|
||||
)
|
||||
|
||||
coderdtest.AwaitTemplateVersionJobCompleted(t, client, version.ID)
|
||||
template := coderdtest.CreateTemplate(t, client, admin.OrganizationID, version.ID, func(ctr *codersdk.CreateTemplateRequest) {
|
||||
ctr.TimeTilDormantMillis = ptr.Ref(timeTilDormant.Milliseconds())
|
||||
ctr.TimeTilDormantAutoDeleteMillis = ptr.Ref(timeTilDormantAutoDelete.Milliseconds())
|
||||
})
|
||||
userClient, _ := coderdtest.CreateAnotherUser(t, client, admin.OrganizationID)
|
||||
workspace := coderdtest.CreateWorkspace(t, userClient, template.ID)
|
||||
coderdtest.AwaitWorkspaceBuildJobCompleted(t, userClient, workspace.LatestBuild.ID)
|
||||
|
||||
// Stop workspace
|
||||
workspace = coderdtest.MustTransitionWorkspace(t, client, workspace.ID, codersdk.WorkspaceTransitionStart, codersdk.WorkspaceTransitionStop)
|
||||
_ = coderdtest.AwaitWorkspaceBuildJobCompleted(t, userClient, workspace.LatestBuild.ID)
|
||||
|
||||
p, err := coderdtest.GetProvisionerForTags(db, time.Now(), workspace.OrganizationID, nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Wait for workspace to become dormant
|
||||
notifyEnq.Clear()
|
||||
tickTime := workspace.LastUsedAt.Add(timeTilDormant * 3)
|
||||
coderdtest.UpdateProvisionerLastSeenAt(t, db, p.ID, tickTime)
|
||||
ticker <- tickTime
|
||||
_ = testutil.TryReceive(testutil.Context(t, testutil.WaitShort), t, statCh)
|
||||
|
||||
// Check that the workspace is dormant
|
||||
workspace = coderdtest.MustWorkspace(t, client, workspace.ID)
|
||||
require.NotNil(t, workspace.DormantAt)
|
||||
|
||||
// The notification body should render the deletion countdown using the template's
|
||||
// `time_til_dormant_autodelete` value. With auto-delete at 35 days and dormancy
|
||||
// at 1 minute, humanize.Time renders the label as "1 month from now".
|
||||
sent := notifyEnq.Sent()
|
||||
require.Len(t, sent, 1)
|
||||
require.Equal(t, sent[0].TemplateID, notifications.TemplateWorkspaceDormant)
|
||||
require.Contains(t, sent[0].Labels, "timeTilDelete")
|
||||
require.Contains(t, sent[0].Labels["timeTilDelete"], "1 month",
|
||||
"timeTilDelete must humanize TimeTilDormantAutoDelete, got %q",
|
||||
sent[0].Labels["timeTilDelete"])
|
||||
require.NotContains(t, sent[0].Labels["timeTilDelete"], "ago",
|
||||
"timeTilDelete must be a future timestamp, got %q",
|
||||
sent[0].Labels["timeTilDelete"])
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user