mirror of
https://github.com/coder/coder.git
synced 2025-07-03 16:13:58 +00:00
This PR adds a package lifecycle and an Executor implementation that attempts to schedule a build of workspaces with autostart configured. - lifecycle.Executor takes a chan time.Time in its constructor (e.g. time.Tick(time.Minute)) - Whenever a value is received from this channel, it executes one iteration of looping through the workspaces and triggering lifecycle operations. - When the context passed to the executor is Done, it exits. - Only workspaces that meet the following criteria will have a lifecycle operation applied to them: - Workspace has a valid and non-empty autostart or autostop schedule (either) - Workspace's last build was successful - The following transitions will be applied depending on the current workspace state: - If the workspace is currently running, it will be stopped. - If the workspace is currently stopped, it will be started. - Otherwise, nothing will be done. - Workspace builds will be created with the same parameters and template version as the last successful build (for example, template version)
488 lines
19 KiB
Go
488 lines
19 KiB
Go
package coderd_test
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"net/http"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/google/uuid"
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"github.com/coder/coder/coderd/autobuild/schedule"
|
|
"github.com/coder/coder/coderd/coderdtest"
|
|
"github.com/coder/coder/coderd/database"
|
|
"github.com/coder/coder/codersdk"
|
|
"github.com/coder/coder/provisioner/echo"
|
|
"github.com/coder/coder/provisionersdk/proto"
|
|
)
|
|
|
|
func TestWorkspace(t *testing.T) {
|
|
t.Parallel()
|
|
client := coderdtest.New(t, nil)
|
|
user := coderdtest.CreateFirstUser(t, client)
|
|
coderdtest.NewProvisionerDaemon(t, client)
|
|
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil)
|
|
coderdtest.AwaitTemplateVersionJob(t, client, version.ID)
|
|
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
|
|
workspace := coderdtest.CreateWorkspace(t, client, user.OrganizationID, template.ID)
|
|
_, err := client.Workspace(context.Background(), workspace.ID)
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
func TestWorkspaceBuilds(t *testing.T) {
|
|
t.Parallel()
|
|
t.Run("Single", func(t *testing.T) {
|
|
t.Parallel()
|
|
client := coderdtest.New(t, nil)
|
|
user := coderdtest.CreateFirstUser(t, client)
|
|
coderdtest.NewProvisionerDaemon(t, client)
|
|
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil)
|
|
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
|
|
coderdtest.AwaitTemplateVersionJob(t, client, version.ID)
|
|
workspace := coderdtest.CreateWorkspace(t, client, user.OrganizationID, template.ID)
|
|
_, err := client.WorkspaceBuilds(context.Background(), workspace.ID)
|
|
require.NoError(t, err)
|
|
})
|
|
}
|
|
|
|
func TestPostWorkspaceBuild(t *testing.T) {
|
|
t.Parallel()
|
|
t.Run("NoTemplateVersion", func(t *testing.T) {
|
|
t.Parallel()
|
|
client := coderdtest.New(t, nil)
|
|
coderdtest.NewProvisionerDaemon(t, client)
|
|
user := coderdtest.CreateFirstUser(t, client)
|
|
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil)
|
|
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
|
|
coderdtest.AwaitTemplateVersionJob(t, client, version.ID)
|
|
workspace := coderdtest.CreateWorkspace(t, client, user.OrganizationID, template.ID)
|
|
_, err := client.CreateWorkspaceBuild(context.Background(), workspace.ID, codersdk.CreateWorkspaceBuildRequest{
|
|
TemplateVersionID: uuid.New(),
|
|
Transition: database.WorkspaceTransitionStart,
|
|
})
|
|
require.Error(t, err)
|
|
var apiErr *codersdk.Error
|
|
require.ErrorAs(t, err, &apiErr)
|
|
require.Equal(t, http.StatusBadRequest, apiErr.StatusCode())
|
|
})
|
|
|
|
t.Run("TemplateVersionFailedImport", func(t *testing.T) {
|
|
t.Parallel()
|
|
client := coderdtest.New(t, nil)
|
|
user := coderdtest.CreateFirstUser(t, client)
|
|
coderdtest.NewProvisionerDaemon(t, client)
|
|
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
|
Provision: []*proto.Provision_Response{{}},
|
|
})
|
|
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
|
|
coderdtest.AwaitTemplateVersionJob(t, client, version.ID)
|
|
_, err := client.CreateWorkspace(context.Background(), user.OrganizationID, codersdk.CreateWorkspaceRequest{
|
|
TemplateID: template.ID,
|
|
Name: "workspace",
|
|
})
|
|
var apiErr *codersdk.Error
|
|
require.ErrorAs(t, err, &apiErr)
|
|
require.Equal(t, http.StatusPreconditionFailed, apiErr.StatusCode())
|
|
})
|
|
|
|
t.Run("AlreadyActive", func(t *testing.T) {
|
|
t.Parallel()
|
|
client := coderdtest.New(t, nil)
|
|
user := coderdtest.CreateFirstUser(t, client)
|
|
closeDaemon := coderdtest.NewProvisionerDaemon(t, client)
|
|
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil)
|
|
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
|
|
coderdtest.AwaitTemplateVersionJob(t, client, version.ID)
|
|
// Close here so workspace build doesn't process!
|
|
closeDaemon.Close()
|
|
workspace := coderdtest.CreateWorkspace(t, client, user.OrganizationID, template.ID)
|
|
_, err := client.CreateWorkspaceBuild(context.Background(), workspace.ID, codersdk.CreateWorkspaceBuildRequest{
|
|
TemplateVersionID: template.ActiveVersionID,
|
|
Transition: database.WorkspaceTransitionStart,
|
|
})
|
|
require.Error(t, err)
|
|
var apiErr *codersdk.Error
|
|
require.ErrorAs(t, err, &apiErr)
|
|
require.Equal(t, http.StatusConflict, apiErr.StatusCode())
|
|
})
|
|
|
|
t.Run("UpdatePriorAfterField", func(t *testing.T) {
|
|
t.Parallel()
|
|
client := coderdtest.New(t, nil)
|
|
user := coderdtest.CreateFirstUser(t, client)
|
|
coderdtest.NewProvisionerDaemon(t, client)
|
|
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil)
|
|
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
|
|
coderdtest.AwaitTemplateVersionJob(t, client, version.ID)
|
|
workspace := coderdtest.CreateWorkspace(t, client, user.OrganizationID, template.ID)
|
|
coderdtest.AwaitWorkspaceBuildJob(t, client, workspace.LatestBuild.ID)
|
|
build, err := client.CreateWorkspaceBuild(context.Background(), workspace.ID, codersdk.CreateWorkspaceBuildRequest{
|
|
TemplateVersionID: template.ActiveVersionID,
|
|
Transition: database.WorkspaceTransitionStart,
|
|
})
|
|
require.NoError(t, err)
|
|
require.Equal(t, workspace.LatestBuild.ID.String(), build.BeforeID.String())
|
|
|
|
firstBuild, err := client.WorkspaceBuild(context.Background(), workspace.LatestBuild.ID)
|
|
require.NoError(t, err)
|
|
require.Equal(t, build.ID.String(), firstBuild.AfterID.String())
|
|
})
|
|
|
|
t.Run("WithState", func(t *testing.T) {
|
|
t.Parallel()
|
|
client := coderdtest.New(t, nil)
|
|
user := coderdtest.CreateFirstUser(t, client)
|
|
closeDaemon := coderdtest.NewProvisionerDaemon(t, client)
|
|
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil)
|
|
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
|
|
coderdtest.AwaitTemplateVersionJob(t, client, version.ID)
|
|
workspace := coderdtest.CreateWorkspace(t, client, user.OrganizationID, template.ID)
|
|
coderdtest.AwaitWorkspaceBuildJob(t, client, workspace.LatestBuild.ID)
|
|
_ = closeDaemon.Close()
|
|
wantState := []byte("something")
|
|
build, err := client.CreateWorkspaceBuild(context.Background(), workspace.ID, codersdk.CreateWorkspaceBuildRequest{
|
|
TemplateVersionID: template.ActiveVersionID,
|
|
Transition: database.WorkspaceTransitionStart,
|
|
ProvisionerState: wantState,
|
|
})
|
|
require.NoError(t, err)
|
|
gotState, err := client.WorkspaceBuildState(context.Background(), build.ID)
|
|
require.NoError(t, err)
|
|
require.Equal(t, wantState, gotState)
|
|
})
|
|
|
|
t.Run("Delete", func(t *testing.T) {
|
|
t.Parallel()
|
|
client := coderdtest.New(t, nil)
|
|
user := coderdtest.CreateFirstUser(t, client)
|
|
coderdtest.NewProvisionerDaemon(t, client)
|
|
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil)
|
|
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
|
|
coderdtest.AwaitTemplateVersionJob(t, client, version.ID)
|
|
workspace := coderdtest.CreateWorkspace(t, client, user.OrganizationID, template.ID)
|
|
coderdtest.AwaitWorkspaceBuildJob(t, client, workspace.LatestBuild.ID)
|
|
build, err := client.CreateWorkspaceBuild(context.Background(), workspace.ID, codersdk.CreateWorkspaceBuildRequest{
|
|
Transition: database.WorkspaceTransitionDelete,
|
|
})
|
|
require.NoError(t, err)
|
|
require.Equal(t, workspace.LatestBuild.ID.String(), build.BeforeID.String())
|
|
coderdtest.AwaitWorkspaceBuildJob(t, client, build.ID)
|
|
|
|
workspaces, err := client.WorkspacesByOwner(context.Background(), user.OrganizationID, user.UserID)
|
|
require.NoError(t, err)
|
|
require.Len(t, workspaces, 0)
|
|
})
|
|
}
|
|
|
|
func TestWorkspaceBuildByName(t *testing.T) {
|
|
t.Parallel()
|
|
t.Run("NotFound", func(t *testing.T) {
|
|
t.Parallel()
|
|
client := coderdtest.New(t, nil)
|
|
user := coderdtest.CreateFirstUser(t, client)
|
|
coderdtest.NewProvisionerDaemon(t, client)
|
|
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil)
|
|
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
|
|
coderdtest.AwaitTemplateVersionJob(t, client, version.ID)
|
|
workspace := coderdtest.CreateWorkspace(t, client, user.OrganizationID, template.ID)
|
|
_, err := client.WorkspaceBuildByName(context.Background(), workspace.ID, "something")
|
|
var apiErr *codersdk.Error
|
|
require.ErrorAs(t, err, &apiErr)
|
|
require.Equal(t, http.StatusNotFound, apiErr.StatusCode())
|
|
})
|
|
|
|
t.Run("Found", func(t *testing.T) {
|
|
t.Parallel()
|
|
client := coderdtest.New(t, nil)
|
|
user := coderdtest.CreateFirstUser(t, client)
|
|
coderdtest.NewProvisionerDaemon(t, client)
|
|
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil)
|
|
coderdtest.AwaitTemplateVersionJob(t, client, version.ID)
|
|
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
|
|
workspace := coderdtest.CreateWorkspace(t, client, user.OrganizationID, template.ID)
|
|
build, err := client.WorkspaceBuild(context.Background(), workspace.LatestBuild.ID)
|
|
require.NoError(t, err)
|
|
_, err = client.WorkspaceBuildByName(context.Background(), workspace.ID, build.Name)
|
|
require.NoError(t, err)
|
|
})
|
|
}
|
|
|
|
func TestWorkspaceUpdateAutostart(t *testing.T) {
|
|
t.Parallel()
|
|
var dublinLoc = mustLocation(t, "Europe/Dublin")
|
|
|
|
testCases := []struct {
|
|
name string
|
|
schedule string
|
|
expectedError string
|
|
at time.Time
|
|
expectedNext time.Time
|
|
expectedInterval time.Duration
|
|
}{
|
|
{
|
|
name: "disable autostart",
|
|
schedule: "",
|
|
expectedError: "",
|
|
},
|
|
{
|
|
name: "friday to monday",
|
|
schedule: "CRON_TZ=Europe/Dublin 30 9 * * 1-5",
|
|
expectedError: "",
|
|
at: time.Date(2022, 5, 6, 9, 31, 0, 0, dublinLoc),
|
|
expectedNext: time.Date(2022, 5, 9, 9, 30, 0, 0, dublinLoc),
|
|
expectedInterval: 71*time.Hour + 59*time.Minute,
|
|
},
|
|
{
|
|
name: "monday to tuesday",
|
|
schedule: "CRON_TZ=Europe/Dublin 30 9 * * 1-5",
|
|
expectedError: "",
|
|
at: time.Date(2022, 5, 9, 9, 31, 0, 0, dublinLoc),
|
|
expectedNext: time.Date(2022, 5, 10, 9, 30, 0, 0, dublinLoc),
|
|
expectedInterval: 23*time.Hour + 59*time.Minute,
|
|
},
|
|
{
|
|
// DST in Ireland began on Mar 27 in 2022 at 0100. Forward 1 hour.
|
|
name: "DST start",
|
|
schedule: "CRON_TZ=Europe/Dublin 30 9 * * *",
|
|
expectedError: "",
|
|
at: time.Date(2022, 3, 26, 9, 31, 0, 0, dublinLoc),
|
|
expectedNext: time.Date(2022, 3, 27, 9, 30, 0, 0, dublinLoc),
|
|
expectedInterval: 22*time.Hour + 59*time.Minute,
|
|
},
|
|
{
|
|
// DST in Ireland ends on Oct 30 in 2022 at 0200. Back 1 hour.
|
|
name: "DST end",
|
|
schedule: "CRON_TZ=Europe/Dublin 30 9 * * *",
|
|
expectedError: "",
|
|
at: time.Date(2022, 10, 29, 9, 31, 0, 0, dublinLoc),
|
|
expectedNext: time.Date(2022, 10, 30, 9, 30, 0, 0, dublinLoc),
|
|
expectedInterval: 24*time.Hour + 59*time.Minute,
|
|
},
|
|
{
|
|
name: "invalid location",
|
|
schedule: "CRON_TZ=Imaginary/Place 30 9 * * 1-5",
|
|
expectedError: "status code 500: invalid autostart schedule: parse schedule: provided bad location Imaginary/Place: unknown time zone Imaginary/Place",
|
|
},
|
|
{
|
|
name: "invalid schedule",
|
|
schedule: "asdf asdf asdf ",
|
|
expectedError: `status code 500: invalid autostart schedule: validate weekly schedule: expected schedule to consist of 5 fields with an optional CRON_TZ=<timezone> prefix`,
|
|
},
|
|
{
|
|
name: "only 3 values",
|
|
schedule: "CRON_TZ=Europe/Dublin 30 9 *",
|
|
expectedError: `status code 500: invalid autostart schedule: validate weekly schedule: expected schedule to consist of 5 fields with an optional CRON_TZ=<timezone> prefix`,
|
|
},
|
|
}
|
|
|
|
for _, testCase := range testCases {
|
|
testCase := testCase
|
|
t.Run(testCase.name, func(t *testing.T) {
|
|
t.Parallel()
|
|
var (
|
|
ctx = context.Background()
|
|
client = coderdtest.New(t, nil)
|
|
_ = coderdtest.NewProvisionerDaemon(t, client)
|
|
user = coderdtest.CreateFirstUser(t, client)
|
|
version = coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil)
|
|
_ = coderdtest.AwaitTemplateVersionJob(t, client, version.ID)
|
|
project = coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
|
|
workspace = coderdtest.CreateWorkspace(t, client, user.OrganizationID, project.ID)
|
|
)
|
|
|
|
// ensure test invariant: new workspaces have no autostart schedule.
|
|
require.Empty(t, workspace.AutostartSchedule, "expected newly-minted workspace to have no autostart schedule")
|
|
|
|
err := client.UpdateWorkspaceAutostart(ctx, workspace.ID, codersdk.UpdateWorkspaceAutostartRequest{
|
|
Schedule: testCase.schedule,
|
|
})
|
|
|
|
if testCase.expectedError != "" {
|
|
require.EqualError(t, err, testCase.expectedError, "unexpected error when setting workspace autostart schedule")
|
|
return
|
|
}
|
|
|
|
require.NoError(t, err, "expected no error setting workspace autostart schedule")
|
|
|
|
updated, err := client.Workspace(ctx, workspace.ID)
|
|
require.NoError(t, err, "fetch updated workspace")
|
|
|
|
require.Equal(t, testCase.schedule, updated.AutostartSchedule, "expected autostart schedule to equal requested")
|
|
|
|
if testCase.schedule == "" {
|
|
return
|
|
}
|
|
sched, err := schedule.Weekly(updated.AutostartSchedule)
|
|
require.NoError(t, err, "parse returned schedule")
|
|
|
|
next := sched.Next(testCase.at)
|
|
require.Equal(t, testCase.expectedNext, next, "unexpected next scheduled autostart time")
|
|
interval := next.Sub(testCase.at)
|
|
require.Equal(t, testCase.expectedInterval, interval, "unexpected interval")
|
|
})
|
|
}
|
|
|
|
t.Run("NotFound", func(t *testing.T) {
|
|
var (
|
|
ctx = context.Background()
|
|
client = coderdtest.New(t, nil)
|
|
_ = coderdtest.CreateFirstUser(t, client)
|
|
wsid = uuid.New()
|
|
req = codersdk.UpdateWorkspaceAutostartRequest{
|
|
Schedule: "9 30 1-5",
|
|
}
|
|
)
|
|
|
|
err := client.UpdateWorkspaceAutostart(ctx, wsid, req)
|
|
require.IsType(t, err, &codersdk.Error{}, "expected codersdk.Error")
|
|
coderSDKErr, _ := err.(*codersdk.Error) //nolint:errorlint
|
|
require.Equal(t, coderSDKErr.StatusCode(), 404, "expected status code 404")
|
|
require.Equal(t, fmt.Sprintf("workspace %q does not exist", wsid), coderSDKErr.Message, "unexpected response code")
|
|
})
|
|
}
|
|
|
|
func TestWorkspaceUpdateAutostop(t *testing.T) {
|
|
t.Parallel()
|
|
var dublinLoc = mustLocation(t, "Europe/Dublin")
|
|
|
|
testCases := []struct {
|
|
name string
|
|
schedule string
|
|
expectedError string
|
|
at time.Time
|
|
expectedNext time.Time
|
|
expectedInterval time.Duration
|
|
}{
|
|
{
|
|
name: "disable autostop",
|
|
schedule: "",
|
|
expectedError: "",
|
|
},
|
|
{
|
|
name: "friday to monday",
|
|
schedule: "CRON_TZ=Europe/Dublin 30 17 * * 1-5",
|
|
expectedError: "",
|
|
at: time.Date(2022, 5, 6, 17, 31, 0, 0, dublinLoc),
|
|
expectedNext: time.Date(2022, 5, 9, 17, 30, 0, 0, dublinLoc),
|
|
expectedInterval: 71*time.Hour + 59*time.Minute,
|
|
},
|
|
{
|
|
name: "monday to tuesday",
|
|
schedule: "CRON_TZ=Europe/Dublin 30 17 * * 1-5",
|
|
expectedError: "",
|
|
at: time.Date(2022, 5, 9, 17, 31, 0, 0, dublinLoc),
|
|
expectedNext: time.Date(2022, 5, 10, 17, 30, 0, 0, dublinLoc),
|
|
expectedInterval: 23*time.Hour + 59*time.Minute,
|
|
},
|
|
{
|
|
// DST in Ireland began on Mar 27 in 2022 at 0100. Forward 1 hour.
|
|
name: "DST start",
|
|
schedule: "CRON_TZ=Europe/Dublin 30 17 * * *",
|
|
expectedError: "",
|
|
at: time.Date(2022, 3, 26, 17, 31, 0, 0, dublinLoc),
|
|
expectedNext: time.Date(2022, 3, 27, 17, 30, 0, 0, dublinLoc),
|
|
expectedInterval: 22*time.Hour + 59*time.Minute,
|
|
},
|
|
{
|
|
// DST in Ireland ends on Oct 30 in 2022 at 0200. Back 1 hour.
|
|
name: "DST end",
|
|
schedule: "CRON_TZ=Europe/Dublin 30 17 * * *",
|
|
expectedError: "",
|
|
at: time.Date(2022, 10, 29, 17, 31, 0, 0, dublinLoc),
|
|
expectedNext: time.Date(2022, 10, 30, 17, 30, 0, 0, dublinLoc),
|
|
expectedInterval: 24*time.Hour + 59*time.Minute,
|
|
},
|
|
{
|
|
name: "invalid location",
|
|
schedule: "CRON_TZ=Imaginary/Place 30 17 * * 1-5",
|
|
expectedError: "status code 500: invalid autostop schedule: parse schedule: provided bad location Imaginary/Place: unknown time zone Imaginary/Place",
|
|
},
|
|
{
|
|
name: "invalid schedule",
|
|
schedule: "asdf asdf asdf ",
|
|
expectedError: `status code 500: invalid autostop schedule: validate weekly schedule: expected schedule to consist of 5 fields with an optional CRON_TZ=<timezone> prefix`,
|
|
},
|
|
{
|
|
name: "only 3 values",
|
|
schedule: "CRON_TZ=Europe/Dublin 30 9 *",
|
|
expectedError: `status code 500: invalid autostop schedule: validate weekly schedule: expected schedule to consist of 5 fields with an optional CRON_TZ=<timezone> prefix`,
|
|
},
|
|
}
|
|
|
|
for _, testCase := range testCases {
|
|
testCase := testCase
|
|
t.Run(testCase.name, func(t *testing.T) {
|
|
t.Parallel()
|
|
var (
|
|
ctx = context.Background()
|
|
client = coderdtest.New(t, nil)
|
|
_ = coderdtest.NewProvisionerDaemon(t, client)
|
|
user = coderdtest.CreateFirstUser(t, client)
|
|
version = coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil)
|
|
_ = coderdtest.AwaitTemplateVersionJob(t, client, version.ID)
|
|
project = coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
|
|
workspace = coderdtest.CreateWorkspace(t, client, user.OrganizationID, project.ID)
|
|
)
|
|
|
|
// ensure test invariant: new workspaces have no autostop schedule.
|
|
require.Empty(t, workspace.AutostopSchedule, "expected newly-minted workspace to have no autstop schedule")
|
|
|
|
err := client.UpdateWorkspaceAutostop(ctx, workspace.ID, codersdk.UpdateWorkspaceAutostopRequest{
|
|
Schedule: testCase.schedule,
|
|
})
|
|
|
|
if testCase.expectedError != "" {
|
|
require.EqualError(t, err, testCase.expectedError, "unexpected error when setting workspace autostop schedule")
|
|
return
|
|
}
|
|
|
|
require.NoError(t, err, "expected no error setting workspace autostop schedule")
|
|
|
|
updated, err := client.Workspace(ctx, workspace.ID)
|
|
require.NoError(t, err, "fetch updated workspace")
|
|
|
|
require.Equal(t, testCase.schedule, updated.AutostopSchedule, "expected autostop schedule to equal requested")
|
|
|
|
if testCase.schedule == "" {
|
|
return
|
|
}
|
|
sched, err := schedule.Weekly(updated.AutostopSchedule)
|
|
require.NoError(t, err, "parse returned schedule")
|
|
|
|
next := sched.Next(testCase.at)
|
|
require.Equal(t, testCase.expectedNext, next, "unexpected next scheduled autostop time")
|
|
interval := next.Sub(testCase.at)
|
|
require.Equal(t, testCase.expectedInterval, interval, "unexpected interval")
|
|
})
|
|
}
|
|
|
|
t.Run("NotFound", func(t *testing.T) {
|
|
var (
|
|
ctx = context.Background()
|
|
client = coderdtest.New(t, nil)
|
|
_ = coderdtest.CreateFirstUser(t, client)
|
|
wsid = uuid.New()
|
|
req = codersdk.UpdateWorkspaceAutostopRequest{
|
|
Schedule: "9 30 1-5",
|
|
}
|
|
)
|
|
|
|
err := client.UpdateWorkspaceAutostop(ctx, wsid, req)
|
|
require.IsType(t, err, &codersdk.Error{}, "expected codersdk.Error")
|
|
coderSDKErr, _ := err.(*codersdk.Error) //nolint:errorlint
|
|
require.Equal(t, coderSDKErr.StatusCode(), 404, "expected status code 404")
|
|
require.Equal(t, fmt.Sprintf("workspace %q does not exist", wsid), coderSDKErr.Message, "unexpected response code")
|
|
})
|
|
}
|
|
|
|
func mustLocation(t *testing.T, location string) *time.Location {
|
|
loc, err := time.LoadLocation(location)
|
|
if err != nil {
|
|
t.Errorf("failed to load location %s: %s", location, err.Error())
|
|
}
|
|
|
|
return loc
|
|
}
|