2022-01-25 19:52:58 +00:00
package coderd_test
import (
"context"
2022-02-06 00:24:51 +00:00
"net/http"
2022-06-14 13:46:33 +00:00
"strings"
2022-01-25 19:52:58 +00:00
"testing"
2022-04-07 09:03:35 +00:00
"time"
2022-01-25 19:52:58 +00:00
"github.com/google/uuid"
"github.com/stretchr/testify/require"
2022-05-11 22:03:02 +00:00
"github.com/coder/coder/coderd/autobuild/schedule"
2022-01-25 19:52:58 +00:00
"github.com/coder/coder/coderd/coderdtest"
2022-06-14 13:46:33 +00:00
"github.com/coder/coder/coderd/rbac"
"github.com/coder/coder/coderd/util/ptr"
2022-01-25 19:52:58 +00:00
"github.com/coder/coder/codersdk"
2022-06-14 13:46:33 +00:00
"github.com/coder/coder/cryptorand"
2022-03-07 17:40:54 +00:00
"github.com/coder/coder/provisioner/echo"
"github.com/coder/coder/provisionersdk/proto"
2022-01-25 19:52:58 +00:00
)
2022-05-19 14:29:10 +00:00
func TestWorkspace ( t * testing . T ) {
t . Parallel ( )
t . Run ( "OK" , func ( t * testing . T ) {
t . Parallel ( )
2022-05-19 22:47:45 +00:00
client := coderdtest . New ( t , & coderdtest . Options { IncludeProvisionerD : true } )
2022-05-19 14:29:10 +00:00
user := coderdtest . CreateFirstUser ( 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 )
} )
t . Run ( "Deleted" , func ( t * testing . T ) {
t . Parallel ( )
2022-05-19 22:47:45 +00:00
client := coderdtest . New ( t , & coderdtest . Options { IncludeProvisionerD : true } )
2022-05-19 14:29:10 +00:00
user := coderdtest . CreateFirstUser ( 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 )
coderdtest . AwaitWorkspaceBuildJob ( t , client , workspace . LatestBuild . ID )
2022-06-06 17:23:02 +00:00
// Getting with deleted=true should still work.
2022-05-19 14:29:10 +00:00
_ , err := client . DeletedWorkspace ( context . Background ( ) , workspace . ID )
2022-06-06 17:23:02 +00:00
require . NoError ( t , err )
2022-05-19 14:29:10 +00:00
// Delete the workspace
build , err := client . CreateWorkspaceBuild ( context . Background ( ) , workspace . ID , codersdk . CreateWorkspaceBuildRequest {
2022-05-19 18:04:44 +00:00
Transition : codersdk . WorkspaceTransitionDelete ,
2022-05-19 14:29:10 +00:00
} )
require . NoError ( t , err , "delete the workspace" )
coderdtest . AwaitWorkspaceBuildJob ( t , client , build . ID )
// Getting with deleted=true should work.
workspaceNew , err := client . DeletedWorkspace ( context . Background ( ) , workspace . ID )
require . NoError ( t , err )
require . Equal ( t , workspace . ID , workspaceNew . ID )
// Getting with deleted=false should not work.
_ , err = client . Workspace ( context . Background ( ) , workspace . ID )
require . Error ( t , err )
require . ErrorContains ( t , err , "410" ) // gone
} )
}
2022-05-18 23:15:19 +00:00
func TestAdminViewAllWorkspaces ( t * testing . T ) {
2022-01-25 19:52:58 +00:00
t . Parallel ( )
2022-05-19 22:47:45 +00:00
client := coderdtest . New ( t , & coderdtest . Options { IncludeProvisionerD : true } )
2022-03-07 17:40:54 +00:00
user := coderdtest . CreateFirstUser ( t , client )
2022-04-06 17:42:40 +00:00
version := coderdtest . CreateTemplateVersion ( t , client , user . OrganizationID , nil )
coderdtest . AwaitTemplateVersionJob ( t , client , version . ID )
template := coderdtest . CreateTemplate ( t , client , user . OrganizationID , version . ID )
2022-04-25 21:11:03 +00:00
workspace := coderdtest . CreateWorkspace ( t , client , user . OrganizationID , template . ID )
2022-05-18 23:15:19 +00:00
coderdtest . AwaitWorkspaceBuildJob ( t , client , workspace . LatestBuild . ID )
2022-03-07 17:40:54 +00:00
_ , err := client . Workspace ( context . Background ( ) , workspace . ID )
require . NoError ( t , err )
2022-05-18 23:15:19 +00:00
2022-05-27 16:19:13 +00:00
otherOrg , err := client . CreateOrganization ( context . Background ( ) , codersdk . CreateOrganizationRequest {
2022-05-18 23:15:19 +00:00
Name : "default-test" ,
} )
require . NoError ( t , err , "create other org" )
// This other user is not in the first user's org. Since other is an admin, they can
// still see the "first" user's workspace.
2022-06-01 14:07:50 +00:00
other := coderdtest . CreateAnotherUser ( t , client , otherOrg . ID , rbac . RoleAdmin ( ) )
2022-05-18 23:15:19 +00:00
otherWorkspaces , err := other . Workspaces ( context . Background ( ) , codersdk . WorkspaceFilter { } )
require . NoError ( t , err , "(other) fetch workspaces" )
firstWorkspaces , err := other . Workspaces ( context . Background ( ) , codersdk . WorkspaceFilter { } )
require . NoError ( t , err , "(first) fetch workspaces" )
require . ElementsMatch ( t , otherWorkspaces , firstWorkspaces )
2022-02-06 00:24:51 +00:00
}
2022-01-25 19:52:58 +00:00
2022-05-16 19:36:27 +00:00
func TestPostWorkspacesByOrganization ( t * testing . T ) {
2022-03-22 19:17:50 +00:00
t . Parallel ( )
2022-05-16 19:36:27 +00:00
t . Run ( "InvalidTemplate" , func ( t * testing . T ) {
2022-03-22 19:17:50 +00:00
t . Parallel ( )
client := coderdtest . New ( t , nil )
user := coderdtest . CreateFirstUser ( t , client )
2022-05-16 19:36:27 +00:00
_ , err := client . CreateWorkspace ( context . Background ( ) , user . OrganizationID , codersdk . CreateWorkspaceRequest {
TemplateID : uuid . New ( ) ,
Name : "workspace" ,
} )
require . Error ( t , err )
var apiErr * codersdk . Error
require . ErrorAs ( t , err , & apiErr )
require . Equal ( t , http . StatusBadRequest , apiErr . StatusCode ( ) )
} )
t . Run ( "NoTemplateAccess" , func ( t * testing . T ) {
t . Parallel ( )
client := coderdtest . New ( t , nil )
first := coderdtest . CreateFirstUser ( t , client )
2022-05-18 23:15:19 +00:00
other := coderdtest . CreateAnotherUser ( t , client , first . OrganizationID , rbac . RoleMember ( ) , rbac . RoleAdmin ( ) )
2022-05-27 16:19:13 +00:00
org , err := other . CreateOrganization ( context . Background ( ) , codersdk . CreateOrganizationRequest {
2022-05-16 19:36:27 +00:00
Name : "another" ,
} )
require . NoError ( t , err )
version := coderdtest . CreateTemplateVersion ( t , other , org . ID , nil )
template := coderdtest . CreateTemplate ( t , other , org . ID , version . ID )
_ , err = client . CreateWorkspace ( context . Background ( ) , first . OrganizationID , codersdk . CreateWorkspaceRequest {
TemplateID : template . ID ,
Name : "workspace" ,
} )
require . Error ( t , err )
var apiErr * codersdk . Error
require . ErrorAs ( t , err , & apiErr )
require . Equal ( t , http . StatusUnauthorized , apiErr . StatusCode ( ) )
} )
t . Run ( "AlreadyExists" , func ( t * testing . T ) {
t . Parallel ( )
2022-05-19 22:47:45 +00:00
client := coderdtest . New ( t , & coderdtest . Options { IncludeProvisionerD : true } )
2022-05-16 19:36:27 +00:00
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 . CreateWorkspace ( context . Background ( ) , user . OrganizationID , codersdk . CreateWorkspaceRequest {
TemplateID : template . ID ,
Name : workspace . Name ,
} )
require . Error ( t , err )
var apiErr * codersdk . Error
require . ErrorAs ( t , err , & apiErr )
require . Equal ( t , http . StatusConflict , apiErr . StatusCode ( ) )
} )
t . Run ( "Create" , func ( t * testing . T ) {
t . Parallel ( )
2022-05-19 22:47:45 +00:00
client := coderdtest . New ( t , & coderdtest . Options { IncludeProvisionerD : true } )
2022-05-16 19:36:27 +00:00
user := coderdtest . CreateFirstUser ( t , client )
2022-04-06 17:42:40 +00:00
version := coderdtest . CreateTemplateVersion ( t , client , user . OrganizationID , nil )
template := coderdtest . CreateTemplate ( t , client , user . OrganizationID , version . ID )
coderdtest . AwaitTemplateVersionJob ( t , client , version . ID )
2022-05-16 19:36:27 +00:00
_ = coderdtest . CreateWorkspace ( t , client , user . OrganizationID , template . ID )
} )
2022-05-30 19:19:17 +00:00
2022-06-07 12:37:45 +00:00
t . Run ( "TemplateCustomTTL" , func ( t * testing . T ) {
t . Parallel ( )
client := coderdtest . New ( t , & coderdtest . Options { IncludeProvisionerD : true } )
user := coderdtest . CreateFirstUser ( t , client )
version := coderdtest . CreateTemplateVersion ( t , client , user . OrganizationID , nil )
templateTTL := 24 * time . Hour . Milliseconds ( )
template := coderdtest . CreateTemplate ( t , client , user . OrganizationID , version . ID , func ( ctr * codersdk . CreateTemplateRequest ) {
ctr . MaxTTLMillis = ptr . Ref ( templateTTL )
} )
coderdtest . AwaitTemplateVersionJob ( t , client , version . ID )
workspace := coderdtest . CreateWorkspace ( t , client , user . OrganizationID , template . ID , func ( cwr * codersdk . CreateWorkspaceRequest ) {
cwr . TTLMillis = nil // ensure that no default TTL is set
} )
// TTL should be set by the template
require . Equal ( t , template . MaxTTLMillis , templateTTL )
require . Equal ( t , template . MaxTTLMillis , template . MaxTTLMillis , workspace . TTLMillis )
} )
2022-05-30 19:19:17 +00:00
t . Run ( "InvalidTTL" , func ( t * testing . T ) {
t . Parallel ( )
t . Run ( "BelowMin" , func ( t * testing . T ) {
t . Parallel ( )
client := coderdtest . New ( t , & coderdtest . Options { IncludeProvisionerD : true } )
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 )
req := codersdk . CreateWorkspaceRequest {
2022-06-07 12:37:45 +00:00
TemplateID : template . ID ,
Name : "testing" ,
TTLMillis : ptr . Ref ( ( 59 * time . Second ) . Milliseconds ( ) ) ,
2022-05-30 19:19:17 +00:00
}
_ , err := client . CreateWorkspace ( context . Background ( ) , template . OrganizationID , req )
require . Error ( t , err )
var apiErr * codersdk . Error
require . ErrorAs ( t , err , & apiErr )
require . Equal ( t , http . StatusBadRequest , apiErr . StatusCode ( ) )
2022-06-07 12:37:45 +00:00
require . Len ( t , apiErr . Validations , 1 )
require . Equal ( t , apiErr . Validations [ 0 ] . Field , "ttl_ms" )
require . Equal ( t , apiErr . Validations [ 0 ] . Detail , "ttl must be at least one minute" )
2022-05-30 19:19:17 +00:00
} )
t . Run ( "AboveMax" , func ( t * testing . T ) {
t . Parallel ( )
client := coderdtest . New ( t , & coderdtest . Options { IncludeProvisionerD : true } )
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 )
req := codersdk . CreateWorkspaceRequest {
2022-06-07 12:37:45 +00:00
TemplateID : template . ID ,
Name : "testing" ,
TTLMillis : ptr . Ref ( ( 24 * 7 * time . Hour + time . Minute ) . Milliseconds ( ) ) ,
2022-05-30 19:19:17 +00:00
}
_ , err := client . CreateWorkspace ( context . Background ( ) , template . OrganizationID , req )
require . Error ( t , err )
var apiErr * codersdk . Error
require . ErrorAs ( t , err , & apiErr )
require . Equal ( t , http . StatusBadRequest , apiErr . StatusCode ( ) )
2022-06-07 12:37:45 +00:00
require . Len ( t , apiErr . Validations , 1 )
require . Equal ( t , apiErr . Validations [ 0 ] . Field , "ttl_ms" )
require . Equal ( t , apiErr . Validations [ 0 ] . Detail , "ttl must be less than 7 days" )
2022-05-30 19:19:17 +00:00
} )
} )
2022-06-07 12:37:45 +00:00
t . Run ( "InvalidAutostart" , func ( t * testing . T ) {
t . Parallel ( )
client := coderdtest . New ( t , & coderdtest . Options { IncludeProvisionerD : true } )
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 )
req := codersdk . CreateWorkspaceRequest {
TemplateID : template . ID ,
Name : "testing" ,
AutostartSchedule : ptr . Ref ( "CRON_TZ=US/Central * * * * *" ) ,
}
_ , err := client . CreateWorkspace ( context . Background ( ) , template . OrganizationID , req )
require . Error ( t , err )
var apiErr * codersdk . Error
require . ErrorAs ( t , err , & apiErr )
require . Equal ( t , http . StatusBadRequest , apiErr . StatusCode ( ) )
require . Len ( t , apiErr . Validations , 1 )
require . Equal ( t , apiErr . Validations [ 0 ] . Field , "schedule" )
require . Equal ( t , apiErr . Validations [ 0 ] . Detail , "Minimum autostart interval 1m0s below template minimum 1h0m0s" )
} )
2022-05-16 19:36:27 +00:00
}
func TestWorkspaceByOwnerAndName ( t * testing . T ) {
t . Parallel ( )
t . Run ( "NotFound" , func ( t * testing . T ) {
t . Parallel ( )
client := coderdtest . New ( t , nil )
2022-06-10 14:58:42 +00:00
_ , err := client . WorkspaceByOwnerAndName ( context . Background ( ) , codersdk . Me , "something" , codersdk . WorkspaceOptions { } )
2022-05-16 19:36:27 +00:00
var apiErr * codersdk . Error
require . ErrorAs ( t , err , & apiErr )
2022-06-03 19:36:08 +00:00
require . Equal ( t , http . StatusUnauthorized , apiErr . StatusCode ( ) )
2022-05-16 19:36:27 +00:00
} )
t . Run ( "Get" , func ( t * testing . T ) {
t . Parallel ( )
2022-05-19 22:47:45 +00:00
client := coderdtest . New ( t , & coderdtest . Options { IncludeProvisionerD : true } )
2022-05-16 19:36:27 +00:00
user := coderdtest . CreateFirstUser ( 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 )
2022-04-25 21:11:03 +00:00
workspace := coderdtest . CreateWorkspace ( t , client , user . OrganizationID , template . ID )
2022-06-10 14:58:42 +00:00
_ , err := client . WorkspaceByOwnerAndName ( context . Background ( ) , codersdk . Me , workspace . Name , codersdk . WorkspaceOptions { } )
2022-03-22 19:17:50 +00:00
require . NoError ( t , err )
} )
2022-06-08 18:04:05 +00:00
t . Run ( "Deleted" , func ( t * testing . T ) {
t . Parallel ( )
client := coderdtest . New ( t , & coderdtest . Options { IncludeProvisionerD : true } )
user := coderdtest . CreateFirstUser ( 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 )
coderdtest . AwaitWorkspaceBuildJob ( t , client , workspace . LatestBuild . ID )
// Given:
// We delete the workspace
build , err := client . CreateWorkspaceBuild ( context . Background ( ) , workspace . ID , codersdk . CreateWorkspaceBuildRequest {
Transition : codersdk . WorkspaceTransitionDelete ,
} )
require . NoError ( t , err , "delete the workspace" )
coderdtest . AwaitWorkspaceBuildJob ( t , client , build . ID )
// Then:
// When we call without includes_deleted, we don't expect to get the workspace back
2022-06-10 14:58:42 +00:00
_ , err = client . WorkspaceByOwnerAndName ( context . Background ( ) , workspace . OwnerName , workspace . Name , codersdk . WorkspaceOptions { } )
2022-06-14 15:14:05 +00:00
require . ErrorContains ( t , err , "404" )
2022-06-08 18:04:05 +00:00
// Then:
// When we call with includes_deleted, we should get the workspace back
2022-06-10 14:58:42 +00:00
workspaceNew , err := client . WorkspaceByOwnerAndName ( context . Background ( ) , workspace . OwnerName , workspace . Name , codersdk . WorkspaceOptions { IncludeDeleted : true } )
require . NoError ( t , err )
require . Equal ( t , workspace . ID , workspaceNew . ID )
// Given:
// We recreate the workspace with the same name
workspace , err = client . CreateWorkspace ( context . Background ( ) , user . OrganizationID , codersdk . CreateWorkspaceRequest {
TemplateID : workspace . TemplateID ,
Name : workspace . Name ,
AutostartSchedule : workspace . AutostartSchedule ,
TTLMillis : workspace . TTLMillis ,
} )
require . NoError ( t , err )
coderdtest . AwaitWorkspaceBuildJob ( t , client , workspace . LatestBuild . ID )
// Then:
// We can fetch the most recent workspace
workspaceNew , err = client . WorkspaceByOwnerAndName ( context . Background ( ) , workspace . OwnerName , workspace . Name , codersdk . WorkspaceOptions { } )
require . NoError ( t , err )
require . Equal ( t , workspace . ID , workspaceNew . ID )
// Given:
// We delete the workspace again
build , err = client . CreateWorkspaceBuild ( context . Background ( ) , workspace . ID , codersdk . CreateWorkspaceBuildRequest {
Transition : codersdk . WorkspaceTransitionDelete ,
} )
require . NoError ( t , err , "delete the workspace" )
coderdtest . AwaitWorkspaceBuildJob ( t , client , build . ID )
// Then:
// When we fetch the deleted workspace, we get the most recently deleted one
workspaceNew , err = client . WorkspaceByOwnerAndName ( context . Background ( ) , workspace . OwnerName , workspace . Name , codersdk . WorkspaceOptions { IncludeDeleted : true } )
2022-06-08 18:04:05 +00:00
require . NoError ( t , err )
require . Equal ( t , workspace . ID , workspaceNew . ID )
} )
2022-03-22 19:17:50 +00:00
}
2022-06-14 13:46:33 +00:00
// TestWorkspaceFilter creates a set of workspaces, users, and organizations
// to run various filters against for testing.
2022-06-06 19:43:16 +00:00
func TestWorkspaceFilter ( t * testing . T ) {
t . Parallel ( )
2022-06-14 13:46:33 +00:00
type coderUser struct {
* codersdk . Client
User codersdk . User
Org codersdk . Organization
}
client := coderdtest . New ( t , & coderdtest . Options { IncludeProvisionerD : true } )
first := coderdtest . CreateFirstUser ( t , client )
users := make ( [ ] coderUser , 0 )
for i := 0 ; i < 10 ; i ++ {
userClient := coderdtest . CreateAnotherUser ( t , client , first . OrganizationID , rbac . RoleAdmin ( ) )
user , err := userClient . User ( context . Background ( ) , codersdk . Me )
require . NoError ( t , err , "fetch me" )
org , err := userClient . CreateOrganization ( context . Background ( ) , codersdk . CreateOrganizationRequest {
Name : user . Username + "-org" ,
} )
require . NoError ( t , err , "create org" )
users = append ( users , coderUser {
Client : userClient ,
User : user ,
Org : org ,
} )
}
type madeWorkspace struct {
Owner codersdk . User
Workspace codersdk . Workspace
Template codersdk . Template
}
availTemplates := make ( [ ] codersdk . Template , 0 )
allWorkspaces := make ( [ ] madeWorkspace , 0 )
// Create some random workspaces
for _ , user := range users {
version := coderdtest . CreateTemplateVersion ( t , client , user . Org . ID , nil )
// Create a template & workspace in the user's org
coderdtest . AwaitTemplateVersionJob ( t , client , version . ID )
template := coderdtest . CreateTemplate ( t , client , user . Org . ID , version . ID )
availTemplates = append ( availTemplates , template )
workspace := coderdtest . CreateWorkspace ( t , user . Client , template . OrganizationID , template . ID )
allWorkspaces = append ( allWorkspaces , madeWorkspace {
Workspace : workspace ,
Template : template ,
Owner : user . User ,
} )
// Make a workspace with a random template
idx , _ := cryptorand . Intn ( len ( availTemplates ) )
randTemplate := availTemplates [ idx ]
randWorkspace := coderdtest . CreateWorkspace ( t , user . Client , randTemplate . OrganizationID , randTemplate . ID )
allWorkspaces = append ( allWorkspaces , madeWorkspace {
Workspace : randWorkspace ,
Template : randTemplate ,
Owner : user . User ,
} )
}
// Make sure all workspaces are done. Do it after all are made
for i , w := range allWorkspaces {
latest := coderdtest . AwaitWorkspaceBuildJob ( t , client , w . Workspace . LatestBuild . ID )
allWorkspaces [ i ] . Workspace . LatestBuild = latest
}
// --- Setup done ---
testCases := [ ] struct {
Name string
Filter codersdk . WorkspaceFilter
// If FilterF is true, we include it in the expected results
FilterF func ( f codersdk . WorkspaceFilter , workspace madeWorkspace ) bool
} {
{
Name : "All" ,
Filter : codersdk . WorkspaceFilter { } ,
FilterF : func ( _ codersdk . WorkspaceFilter , _ madeWorkspace ) bool {
return true
} ,
} ,
{
Name : "Owner" ,
Filter : codersdk . WorkspaceFilter {
Owner : users [ 2 ] . User . Username ,
} ,
FilterF : func ( f codersdk . WorkspaceFilter , workspace madeWorkspace ) bool {
return workspace . Owner . Username == f . Owner
} ,
} ,
{
Name : "TemplateName" ,
Filter : codersdk . WorkspaceFilter {
Template : allWorkspaces [ 5 ] . Template . Name ,
} ,
FilterF : func ( f codersdk . WorkspaceFilter , workspace madeWorkspace ) bool {
return workspace . Template . Name == f . Template
} ,
} ,
{
Name : "Name" ,
Filter : codersdk . WorkspaceFilter {
// Use a common letter... one has to have this letter in it
Name : "a" ,
} ,
FilterF : func ( f codersdk . WorkspaceFilter , workspace madeWorkspace ) bool {
return strings . Contains ( workspace . Workspace . Name , f . Name )
} ,
} ,
{
Name : "Q-Owner/Name" ,
Filter : codersdk . WorkspaceFilter {
FilterQuery : allWorkspaces [ 5 ] . Owner . Username + "/" + allWorkspaces [ 5 ] . Workspace . Name ,
} ,
FilterF : func ( _ codersdk . WorkspaceFilter , workspace madeWorkspace ) bool {
return workspace . Workspace . ID == allWorkspaces [ 5 ] . Workspace . ID
} ,
} ,
{
Name : "Many filters" ,
Filter : codersdk . WorkspaceFilter {
Owner : allWorkspaces [ 3 ] . Owner . Username ,
Template : allWorkspaces [ 3 ] . Template . Name ,
Name : allWorkspaces [ 3 ] . Workspace . Name ,
} ,
FilterF : func ( f codersdk . WorkspaceFilter , workspace madeWorkspace ) bool {
return workspace . Workspace . ID == allWorkspaces [ 3 ] . Workspace . ID
} ,
} ,
}
for _ , c := range testCases {
c := c
t . Run ( c . Name , func ( t * testing . T ) {
t . Parallel ( )
workspaces , err := client . Workspaces ( context . Background ( ) , c . Filter )
require . NoError ( t , err , "fetch workspaces" )
exp := make ( [ ] codersdk . Workspace , 0 )
for _ , made := range allWorkspaces {
if c . FilterF ( c . Filter , made ) {
exp = append ( exp , made . Workspace )
}
}
require . ElementsMatch ( t , exp , workspaces , "expected workspaces returned" )
} )
}
}
// TestWorkspaceFilterManual runs some specific setups with basic checks.
func TestWorkspaceFilterManual ( t * testing . T ) {
t . Parallel ( )
2022-06-06 19:43:16 +00:00
t . Run ( "Name" , func ( t * testing . T ) {
t . Parallel ( )
client := coderdtest . New ( t , & coderdtest . Options { IncludeProvisionerD : true } )
user := coderdtest . CreateFirstUser ( 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 )
// full match
ws , err := client . Workspaces ( context . Background ( ) , codersdk . WorkspaceFilter {
Name : workspace . Name ,
} )
require . NoError ( t , err )
require . Len ( t , ws , 1 , workspace . Name )
require . Equal ( t , workspace . ID , ws [ 0 ] . ID )
// partial match
ws , err = client . Workspaces ( context . Background ( ) , codersdk . WorkspaceFilter {
Name : workspace . Name [ 1 : len ( workspace . Name ) - 2 ] ,
} )
require . NoError ( t , err )
require . Len ( t , ws , 1 )
require . Equal ( t , workspace . ID , ws [ 0 ] . ID )
// no match
ws , err = client . Workspaces ( context . Background ( ) , codersdk . WorkspaceFilter {
Name : "$$$$" ,
} )
require . NoError ( t , err )
require . Len ( t , ws , 0 )
} )
2022-06-14 13:46:33 +00:00
t . Run ( "Template" , func ( t * testing . T ) {
t . Parallel ( )
client := coderdtest . New ( t , & coderdtest . Options { IncludeProvisionerD : true } )
user := coderdtest . CreateFirstUser ( 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 )
template2 := coderdtest . CreateTemplate ( t , client , user . OrganizationID , version . ID )
workspace := coderdtest . CreateWorkspace ( t , client , user . OrganizationID , template . ID )
_ = coderdtest . CreateWorkspace ( t , client , user . OrganizationID , template2 . ID )
// empty
ws , err := client . Workspaces ( context . Background ( ) , codersdk . WorkspaceFilter { } )
require . NoError ( t , err )
require . Len ( t , ws , 2 )
// single template
ws , err = client . Workspaces ( context . Background ( ) , codersdk . WorkspaceFilter {
Template : template . Name ,
} )
require . NoError ( t , err )
require . Len ( t , ws , 1 )
require . Equal ( t , workspace . ID , ws [ 0 ] . ID )
} )
t . Run ( "FilterQuery" , func ( t * testing . T ) {
t . Parallel ( )
client := coderdtest . New ( t , & coderdtest . Options { IncludeProvisionerD : true } )
user := coderdtest . CreateFirstUser ( 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 )
template2 := coderdtest . CreateTemplate ( t , client , user . OrganizationID , version . ID )
workspace := coderdtest . CreateWorkspace ( t , client , user . OrganizationID , template . ID )
_ = coderdtest . CreateWorkspace ( t , client , user . OrganizationID , template2 . ID )
// single workspace
ws , err := client . Workspaces ( context . Background ( ) , codersdk . WorkspaceFilter {
FilterQuery : fmt . Sprintf ( "template:%s %s/%s" , template . Name , workspace . OwnerName , workspace . Name ) ,
} )
require . NoError ( t , err )
require . Len ( t , ws , 1 )
require . Equal ( t , workspace . ID , ws [ 0 ] . ID )
} )
2022-06-06 19:43:16 +00:00
}
2022-03-07 17:40:54 +00:00
func TestPostWorkspaceBuild ( t * testing . T ) {
2022-02-06 00:24:51 +00:00
t . Parallel ( )
2022-04-06 17:42:40 +00:00
t . Run ( "NoTemplateVersion" , func ( t * testing . T ) {
2022-01-25 19:52:58 +00:00
t . Parallel ( )
2022-05-19 22:47:45 +00:00
client := coderdtest . New ( t , & coderdtest . Options { IncludeProvisionerD : true } )
2022-03-07 17:40:54 +00:00
user := coderdtest . CreateFirstUser ( t , client )
2022-04-06 17:42:40 +00:00
version := coderdtest . CreateTemplateVersion ( t , client , user . OrganizationID , nil )
template := coderdtest . CreateTemplate ( t , client , user . OrganizationID , version . ID )
coderdtest . AwaitTemplateVersionJob ( t , client , version . ID )
2022-04-25 21:11:03 +00:00
workspace := coderdtest . CreateWorkspace ( t , client , user . OrganizationID , template . ID )
2022-03-22 19:17:50 +00:00
_ , err := client . CreateWorkspaceBuild ( context . Background ( ) , workspace . ID , codersdk . CreateWorkspaceBuildRequest {
2022-04-06 17:42:40 +00:00
TemplateVersionID : uuid . New ( ) ,
2022-05-19 18:04:44 +00:00
Transition : codersdk . WorkspaceTransitionStart ,
2022-01-25 19:52:58 +00:00
} )
2022-02-06 00:24:51 +00:00
require . Error ( t , err )
var apiErr * codersdk . Error
require . ErrorAs ( t , err , & apiErr )
require . Equal ( t , http . StatusBadRequest , apiErr . StatusCode ( ) )
2022-01-25 19:52:58 +00:00
} )
2022-04-06 17:42:40 +00:00
t . Run ( "TemplateVersionFailedImport" , func ( t * testing . T ) {
2022-01-25 19:52:58 +00:00
t . Parallel ( )
2022-05-19 22:47:45 +00:00
client := coderdtest . New ( t , & coderdtest . Options { IncludeProvisionerD : true } )
2022-03-07 17:40:54 +00:00
user := coderdtest . CreateFirstUser ( t , client )
2022-04-06 17:42:40 +00:00
version := coderdtest . CreateTemplateVersion ( t , client , user . OrganizationID , & echo . Responses {
2022-03-07 17:40:54 +00:00
Provision : [ ] * proto . Provision_Response { { } } ,
2022-01-25 19:52:58 +00:00
} )
2022-04-06 17:42:40 +00:00
template := coderdtest . CreateTemplate ( t , client , user . OrganizationID , version . ID )
coderdtest . AwaitTemplateVersionJob ( t , client , version . ID )
2022-04-25 21:11:03 +00:00
_ , err := client . CreateWorkspace ( context . Background ( ) , user . OrganizationID , codersdk . CreateWorkspaceRequest {
2022-04-06 17:42:40 +00:00
TemplateID : template . ID ,
Name : "workspace" ,
2022-01-25 19:52:58 +00:00
} )
2022-02-06 00:24:51 +00:00
var apiErr * codersdk . Error
require . ErrorAs ( t , err , & apiErr )
2022-03-07 17:40:54 +00:00
require . Equal ( t , http . StatusPreconditionFailed , apiErr . StatusCode ( ) )
2022-01-25 19:52:58 +00:00
} )
2022-03-07 17:40:54 +00:00
t . Run ( "AlreadyActive" , func ( t * testing . T ) {
2022-01-25 19:52:58 +00:00
t . Parallel ( )
2022-05-26 03:14:08 +00:00
client , coderAPI := coderdtest . NewWithAPI ( t , nil )
2022-03-07 17:40:54 +00:00
user := coderdtest . CreateFirstUser ( t , client )
2022-05-26 03:14:08 +00:00
closeDaemon := coderdtest . NewProvisionerDaemon ( t , coderAPI )
2022-04-06 17:42:40 +00:00
version := coderdtest . CreateTemplateVersion ( t , client , user . OrganizationID , nil )
template := coderdtest . CreateTemplate ( t , client , user . OrganizationID , version . ID )
coderdtest . AwaitTemplateVersionJob ( t , client , version . ID )
2022-03-07 17:40:54 +00:00
// Close here so workspace build doesn't process!
closeDaemon . Close ( )
2022-04-25 21:11:03 +00:00
workspace := coderdtest . CreateWorkspace ( t , client , user . OrganizationID , template . ID )
2022-03-22 19:17:50 +00:00
_ , err := client . CreateWorkspaceBuild ( context . Background ( ) , workspace . ID , codersdk . CreateWorkspaceBuildRequest {
2022-04-06 17:42:40 +00:00
TemplateVersionID : template . ActiveVersionID ,
2022-05-19 18:04:44 +00:00
Transition : codersdk . WorkspaceTransitionStart ,
2022-01-25 19:52:58 +00:00
} )
require . Error ( t , err )
2022-02-06 00:24:51 +00:00
var apiErr * codersdk . Error
require . ErrorAs ( t , err , & apiErr )
require . Equal ( t , http . StatusConflict , apiErr . StatusCode ( ) )
2022-01-25 19:52:58 +00:00
} )
2022-05-18 16:33:33 +00:00
t . Run ( "IncrementBuildNumber" , func ( t * testing . T ) {
2022-01-25 19:52:58 +00:00
t . Parallel ( )
2022-05-19 22:47:45 +00:00
client := coderdtest . New ( t , & coderdtest . Options { IncludeProvisionerD : true } )
2022-03-07 17:40:54 +00:00
user := coderdtest . CreateFirstUser ( t , client )
2022-04-06 17:42:40 +00:00
version := coderdtest . CreateTemplateVersion ( t , client , user . OrganizationID , nil )
template := coderdtest . CreateTemplate ( t , client , user . OrganizationID , version . ID )
coderdtest . AwaitTemplateVersionJob ( t , client , version . ID )
2022-04-25 21:11:03 +00:00
workspace := coderdtest . CreateWorkspace ( t , client , user . OrganizationID , template . ID )
2022-03-22 19:17:50 +00:00
coderdtest . AwaitWorkspaceBuildJob ( t , client , workspace . LatestBuild . ID )
build , err := client . CreateWorkspaceBuild ( context . Background ( ) , workspace . ID , codersdk . CreateWorkspaceBuildRequest {
2022-04-06 17:42:40 +00:00
TemplateVersionID : template . ActiveVersionID ,
2022-05-19 18:04:44 +00:00
Transition : codersdk . WorkspaceTransitionStart ,
2022-03-07 17:40:54 +00:00
} )
require . NoError ( t , err )
2022-05-18 16:33:33 +00:00
require . Equal ( t , workspace . LatestBuild . BuildNumber + 1 , build . BuildNumber )
2022-01-25 19:52:58 +00:00
} )
2022-05-02 22:51:58 +00:00
t . Run ( "WithState" , func ( t * testing . T ) {
t . Parallel ( )
2022-05-26 03:14:08 +00:00
client , coderAPI := coderdtest . NewWithAPI ( t , nil )
2022-05-02 22:51:58 +00:00
user := coderdtest . CreateFirstUser ( t , client )
2022-05-26 03:14:08 +00:00
closeDaemon := coderdtest . NewProvisionerDaemon ( t , coderAPI )
2022-05-02 22:51:58 +00:00
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 ,
2022-05-19 18:04:44 +00:00
Transition : codersdk . WorkspaceTransitionStart ,
2022-05-02 22:51:58 +00:00
ProvisionerState : wantState ,
} )
require . NoError ( t , err )
gotState , err := client . WorkspaceBuildState ( context . Background ( ) , build . ID )
require . NoError ( t , err )
require . Equal ( t , wantState , gotState )
} )
2022-03-22 19:17:50 +00:00
t . Run ( "Delete" , func ( t * testing . T ) {
2022-03-07 17:40:54 +00:00
t . Parallel ( )
2022-05-19 22:47:45 +00:00
client := coderdtest . New ( t , & coderdtest . Options { IncludeProvisionerD : true } )
2022-03-07 17:40:54 +00:00
user := coderdtest . CreateFirstUser ( t , client )
2022-04-06 17:42:40 +00:00
version := coderdtest . CreateTemplateVersion ( t , client , user . OrganizationID , nil )
template := coderdtest . CreateTemplate ( t , client , user . OrganizationID , version . ID )
coderdtest . AwaitTemplateVersionJob ( t , client , version . ID )
2022-04-25 21:11:03 +00:00
workspace := coderdtest . CreateWorkspace ( t , client , user . OrganizationID , template . ID )
2022-03-22 19:17:50 +00:00
coderdtest . AwaitWorkspaceBuildJob ( t , client , workspace . LatestBuild . ID )
build , err := client . CreateWorkspaceBuild ( context . Background ( ) , workspace . ID , codersdk . CreateWorkspaceBuildRequest {
2022-05-19 18:04:44 +00:00
Transition : codersdk . WorkspaceTransitionDelete ,
2022-03-07 17:40:54 +00:00
} )
require . NoError ( t , err )
2022-05-18 16:33:33 +00:00
require . Equal ( t , workspace . LatestBuild . BuildNumber + 1 , build . BuildNumber )
2022-03-22 19:17:50 +00:00
coderdtest . AwaitWorkspaceBuildJob ( t , client , build . ID )
2022-06-03 19:36:08 +00:00
workspaces , err := client . Workspaces ( context . Background ( ) , codersdk . WorkspaceFilter {
Owner : user . UserID . String ( ) ,
} )
2022-03-07 17:40:54 +00:00
require . NoError ( t , err )
2022-03-22 19:17:50 +00:00
require . Len ( t , workspaces , 0 )
2022-03-07 17:40:54 +00:00
} )
2022-02-06 00:24:51 +00:00
}
2022-03-07 17:40:54 +00:00
func TestWorkspaceBuildByName ( t * testing . T ) {
2022-02-06 00:24:51 +00:00
t . Parallel ( )
2022-03-07 17:40:54 +00:00
t . Run ( "NotFound" , func ( t * testing . T ) {
2022-01-25 19:52:58 +00:00
t . Parallel ( )
2022-05-19 22:47:45 +00:00
client := coderdtest . New ( t , & coderdtest . Options { IncludeProvisionerD : true } )
2022-03-07 17:40:54 +00:00
user := coderdtest . CreateFirstUser ( t , client )
2022-04-06 17:42:40 +00:00
version := coderdtest . CreateTemplateVersion ( t , client , user . OrganizationID , nil )
template := coderdtest . CreateTemplate ( t , client , user . OrganizationID , version . ID )
coderdtest . AwaitTemplateVersionJob ( t , client , version . ID )
2022-04-25 21:11:03 +00:00
workspace := coderdtest . CreateWorkspace ( t , client , user . OrganizationID , template . ID )
2022-03-07 17:40:54 +00:00
_ , err := client . WorkspaceBuildByName ( context . Background ( ) , workspace . ID , "something" )
var apiErr * codersdk . Error
require . ErrorAs ( t , err , & apiErr )
require . Equal ( t , http . StatusNotFound , apiErr . StatusCode ( ) )
2022-01-25 19:52:58 +00:00
} )
2022-03-07 17:40:54 +00:00
t . Run ( "Found" , func ( t * testing . T ) {
2022-01-25 19:52:58 +00:00
t . Parallel ( )
2022-05-19 22:47:45 +00:00
client := coderdtest . New ( t , & coderdtest . Options { IncludeProvisionerD : true } )
2022-03-07 17:40:54 +00:00
user := coderdtest . CreateFirstUser ( t , client )
2022-04-06 17:42:40 +00:00
version := coderdtest . CreateTemplateVersion ( t , client , user . OrganizationID , nil )
coderdtest . AwaitTemplateVersionJob ( t , client , version . ID )
template := coderdtest . CreateTemplate ( t , client , user . OrganizationID , version . ID )
2022-04-25 21:11:03 +00:00
workspace := coderdtest . CreateWorkspace ( t , client , user . OrganizationID , template . ID )
2022-03-22 19:17:50 +00:00
build , err := client . WorkspaceBuild ( context . Background ( ) , workspace . LatestBuild . ID )
2022-03-07 17:40:54 +00:00
require . NoError ( t , err )
_ , err = client . WorkspaceBuildByName ( context . Background ( ) , workspace . ID , build . Name )
2022-01-25 19:52:58 +00:00
require . NoError ( t , err )
} )
}
2022-04-07 09:03:35 +00:00
func TestWorkspaceUpdateAutostart ( t * testing . T ) {
t . Parallel ( )
var dublinLoc = mustLocation ( t , "Europe/Dublin" )
testCases := [ ] struct {
name string
2022-06-02 10:23:34 +00:00
schedule * string
2022-04-07 09:03:35 +00:00
expectedError string
at time . Time
expectedNext time . Time
expectedInterval time . Duration
} {
{
name : "disable autostart" ,
2022-06-02 10:23:34 +00:00
schedule : ptr . Ref ( "" ) ,
2022-04-07 09:03:35 +00:00
expectedError : "" ,
} ,
{
name : "friday to monday" ,
2022-06-02 10:23:34 +00:00
schedule : ptr . Ref ( "CRON_TZ=Europe/Dublin 30 9 * * 1-5" ) ,
2022-04-07 09:03:35 +00:00
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" ,
2022-06-02 10:23:34 +00:00
schedule : ptr . Ref ( "CRON_TZ=Europe/Dublin 30 9 * * 1-5" ) ,
2022-04-07 09:03:35 +00:00
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" ,
2022-06-02 10:23:34 +00:00
schedule : ptr . Ref ( "CRON_TZ=Europe/Dublin 30 9 * * *" ) ,
2022-04-07 09:03:35 +00:00
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" ,
2022-06-02 10:23:34 +00:00
schedule : ptr . Ref ( "CRON_TZ=Europe/Dublin 30 9 * * *" ) ,
2022-04-07 09:03:35 +00:00
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" ,
2022-06-02 10:23:34 +00:00
schedule : ptr . Ref ( "CRON_TZ=Imaginary/Place 30 9 * * 1-5" ) ,
2022-06-07 12:37:45 +00:00
expectedError : "parse schedule: provided bad location Imaginary/Place: unknown time zone Imaginary/Place" ,
2022-04-07 09:03:35 +00:00
} ,
{
name : "invalid schedule" ,
2022-06-02 10:23:34 +00:00
schedule : ptr . Ref ( "asdf asdf asdf " ) ,
2022-06-07 12:37:45 +00:00
expectedError : ` validate weekly schedule: expected schedule to consist of 5 fields with an optional CRON_TZ=<timezone> prefix ` ,
2022-04-18 16:04:48 +00:00
} ,
{
name : "only 3 values" ,
2022-06-02 10:23:34 +00:00
schedule : ptr . Ref ( "CRON_TZ=Europe/Dublin 30 9 *" ) ,
2022-06-07 12:37:45 +00:00
expectedError : ` validate weekly schedule: expected schedule to consist of 5 fields with an optional CRON_TZ=<timezone> prefix ` ,
2022-04-07 09:03:35 +00:00
} ,
}
for _ , testCase := range testCases {
testCase := testCase
t . Run ( testCase . name , func ( t * testing . T ) {
t . Parallel ( )
var (
ctx = context . Background ( )
2022-05-19 22:47:45 +00:00
client = coderdtest . New ( t , & coderdtest . Options { IncludeProvisionerD : true } )
2022-04-07 09:03:35 +00:00
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 )
2022-05-23 22:31:41 +00:00
workspace = coderdtest . CreateWorkspace ( t , client , user . OrganizationID , project . ID , func ( cwr * codersdk . CreateWorkspaceRequest ) {
cwr . AutostartSchedule = nil
2022-06-02 10:23:34 +00:00
cwr . TTLMillis = nil
2022-05-23 22:31:41 +00:00
} )
2022-04-07 09:03:35 +00:00
)
// 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 != "" {
2022-06-03 21:48:09 +00:00
require . ErrorContains ( t , err , testCase . expectedError , "Invalid autostart schedule" )
2022-04-07 09:03:35 +00:00
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" )
2022-06-02 10:23:34 +00:00
if testCase . schedule == nil || * testCase . schedule == "" {
require . Nil ( t , updated . AutostartSchedule )
2022-04-07 09:03:35 +00:00
return
}
2022-06-02 10:23:34 +00:00
require . EqualValues ( t , * testCase . schedule , * updated . AutostartSchedule , "expected autostart schedule to equal requested" )
sched , err := schedule . Weekly ( * updated . AutostartSchedule )
2022-04-07 09:03:35 +00:00
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 {
2022-06-02 10:23:34 +00:00
Schedule : ptr . Ref ( "9 30 1-5" ) ,
2022-04-07 09:03:35 +00:00
}
)
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" )
2022-06-14 15:14:05 +00:00
require . Contains ( t , coderSDKErr . Message , "Resource not found" , "unexpected response code" )
2022-04-07 09:03:35 +00:00
} )
}
2022-05-26 17:08:11 +00:00
func TestWorkspaceUpdateTTL ( t * testing . T ) {
2022-04-07 09:03:35 +00:00
t . Parallel ( )
testCases := [ ] struct {
2022-06-09 21:10:24 +00:00
name string
ttlMillis * int64
expectedError string
expectedDeadline * time . Time
modifyTemplate func ( * codersdk . CreateTemplateRequest )
2022-04-07 09:03:35 +00:00
} {
{
2022-06-09 21:10:24 +00:00
name : "disable ttl" ,
ttlMillis : nil ,
expectedError : "" ,
expectedDeadline : ptr . Ref ( time . Time { } ) ,
} ,
{
name : "update ttl" ,
ttlMillis : ptr . Ref ( 12 * time . Hour . Milliseconds ( ) ) ,
expectedError : "" ,
expectedDeadline : ptr . Ref ( time . Now ( ) . Add ( 12 * time . Hour + time . Minute ) ) ,
2022-04-07 09:03:35 +00:00
} ,
{
2022-05-30 19:19:17 +00:00
name : "below minimum ttl" ,
2022-06-02 10:23:34 +00:00
ttlMillis : ptr . Ref ( ( 30 * time . Second ) . Milliseconds ( ) ) ,
2022-05-30 19:19:17 +00:00
expectedError : "ttl must be at least one minute" ,
} ,
{
2022-06-09 21:10:24 +00:00
name : "minimum ttl" ,
ttlMillis : ptr . Ref ( time . Minute . Milliseconds ( ) ) ,
expectedError : "" ,
expectedDeadline : ptr . Ref ( time . Now ( ) . Add ( 2 * time . Minute ) ) ,
2022-05-30 19:19:17 +00:00
} ,
{
2022-06-09 21:10:24 +00:00
name : "maximum ttl" ,
ttlMillis : ptr . Ref ( ( 24 * 7 * time . Hour ) . Milliseconds ( ) ) ,
expectedError : "" ,
expectedDeadline : ptr . Ref ( time . Now ( ) . Add ( 24 * 7 * time . Hour + time . Minute ) ) ,
2022-04-07 09:03:35 +00:00
} ,
2022-05-30 19:19:17 +00:00
{
name : "above maximum ttl" ,
2022-06-02 10:23:34 +00:00
ttlMillis : ptr . Ref ( ( 24 * 7 * time . Hour + time . Minute ) . Milliseconds ( ) ) ,
2022-05-30 19:19:17 +00:00
expectedError : "ttl must be less than 7 days" ,
} ,
2022-06-07 12:37:45 +00:00
{
name : "above template maximum ttl" ,
ttlMillis : ptr . Ref ( ( 12 * time . Hour ) . Milliseconds ( ) ) ,
expectedError : "ttl_ms: ttl must be below template maximum 8h0m0s" ,
modifyTemplate : func ( ctr * codersdk . CreateTemplateRequest ) { ctr . MaxTTLMillis = ptr . Ref ( ( 8 * time . Hour ) . Milliseconds ( ) ) } ,
} ,
2022-04-07 09:03:35 +00:00
}
for _ , testCase := range testCases {
testCase := testCase
t . Run ( testCase . name , func ( t * testing . T ) {
t . Parallel ( )
2022-06-07 12:37:45 +00:00
mutators := make ( [ ] func ( * codersdk . CreateTemplateRequest ) , 0 )
if testCase . modifyTemplate != nil {
mutators = append ( mutators , testCase . modifyTemplate )
}
2022-04-07 09:03:35 +00:00
var (
ctx = context . Background ( )
2022-05-19 22:47:45 +00:00
client = coderdtest . New ( t , & coderdtest . Options { IncludeProvisionerD : true } )
2022-04-07 09:03:35 +00:00
user = coderdtest . CreateFirstUser ( t , client )
version = coderdtest . CreateTemplateVersion ( t , client , user . OrganizationID , nil )
_ = coderdtest . AwaitTemplateVersionJob ( t , client , version . ID )
2022-06-07 12:37:45 +00:00
project = coderdtest . CreateTemplate ( t , client , user . OrganizationID , version . ID , mutators ... )
2022-05-23 22:31:41 +00:00
workspace = coderdtest . CreateWorkspace ( t , client , user . OrganizationID , project . ID , func ( cwr * codersdk . CreateWorkspaceRequest ) {
cwr . AutostartSchedule = nil
2022-06-02 10:23:34 +00:00
cwr . TTLMillis = nil
2022-05-23 22:31:41 +00:00
} )
2022-06-09 21:10:24 +00:00
_ = coderdtest . AwaitWorkspaceBuildJob ( t , client , workspace . LatestBuild . ID )
2022-04-07 09:03:35 +00:00
)
2022-05-19 19:09:27 +00:00
err := client . UpdateWorkspaceTTL ( ctx , workspace . ID , codersdk . UpdateWorkspaceTTLRequest {
2022-06-02 10:23:34 +00:00
TTLMillis : testCase . ttlMillis ,
2022-04-07 09:03:35 +00:00
} )
if testCase . expectedError != "" {
2022-05-30 19:19:17 +00:00
require . ErrorContains ( t , err , testCase . expectedError , "unexpected error when setting workspace autostop schedule" )
2022-04-07 09:03:35 +00:00
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" )
2022-06-02 10:23:34 +00:00
require . Equal ( t , testCase . ttlMillis , updated . TTLMillis , "expected autostop ttl to equal requested" )
2022-06-09 21:10:24 +00:00
if testCase . expectedDeadline != nil {
require . WithinDuration ( t , * testCase . expectedDeadline , updated . LatestBuild . Deadline , time . Minute , "expected autostop deadline to be equal expected" )
}
2022-04-07 09:03:35 +00:00
} )
}
t . Run ( "NotFound" , func ( t * testing . T ) {
var (
ctx = context . Background ( )
client = coderdtest . New ( t , nil )
_ = coderdtest . CreateFirstUser ( t , client )
wsid = uuid . New ( )
2022-05-19 19:09:27 +00:00
req = codersdk . UpdateWorkspaceTTLRequest {
2022-06-02 10:23:34 +00:00
TTLMillis : ptr . Ref ( time . Hour . Milliseconds ( ) ) ,
2022-04-07 09:03:35 +00:00
}
)
2022-05-19 19:09:27 +00:00
err := client . UpdateWorkspaceTTL ( ctx , wsid , req )
2022-04-07 09:03:35 +00:00
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" )
2022-06-14 15:14:05 +00:00
require . Contains ( t , coderSDKErr . Message , "Resource not found" , "unexpected response code" )
2022-04-07 09:03:35 +00:00
} )
}
2022-05-26 17:08:11 +00:00
func TestWorkspaceExtend ( t * testing . T ) {
t . Parallel ( )
var (
ctx = context . Background ( )
client = coderdtest . New ( t , & coderdtest . Options { IncludeProvisionerD : true } )
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 )
extend = 90 * time . Minute
_ = coderdtest . AwaitWorkspaceBuildJob ( t , client , workspace . LatestBuild . ID )
2022-06-02 10:23:34 +00:00
oldDeadline = time . Now ( ) . Add ( time . Duration ( * workspace . TTLMillis ) * time . Millisecond ) . UTC ( )
newDeadline = time . Now ( ) . Add ( time . Duration ( * workspace . TTLMillis ) * time . Millisecond + extend ) . UTC ( )
2022-05-26 17:08:11 +00:00
)
workspace , err := client . Workspace ( ctx , workspace . ID )
require . NoError ( t , err , "fetch provisioned workspace" )
require . InDelta ( t , oldDeadline . Unix ( ) , workspace . LatestBuild . Deadline . Unix ( ) , 60 )
// Updating the deadline should succeed
req := codersdk . PutExtendWorkspaceRequest {
Deadline : newDeadline ,
}
err = client . PutExtendWorkspace ( ctx , workspace . ID , req )
require . NoError ( t , err , "failed to extend workspace" )
// Ensure deadline set correctly
updated , err := client . Workspace ( ctx , workspace . ID )
require . NoError ( t , err , "failed to fetch updated workspace" )
require . InDelta ( t , newDeadline . Unix ( ) , updated . LatestBuild . Deadline . Unix ( ) , 60 )
// Zero time should fail
err = client . PutExtendWorkspace ( ctx , workspace . ID , codersdk . PutExtendWorkspaceRequest {
Deadline : time . Time { } ,
} )
2022-05-27 14:45:22 +00:00
require . ErrorContains ( t , err , "deadline: Validation failed for tag \"required\" with value: \"0001-01-01 00:00:00 +0000 UTC\"" , "setting an empty deadline on a workspace should fail" )
2022-05-26 17:08:11 +00:00
// Updating with an earlier time should also fail
err = client . PutExtendWorkspace ( ctx , workspace . ID , codersdk . PutExtendWorkspaceRequest {
Deadline : oldDeadline ,
} )
2022-05-30 19:19:17 +00:00
require . ErrorContains ( t , err , "deadline: minimum extension is one minute" , "setting an earlier deadline should fail" )
// Updating with a time far in the future should also fail
err = client . PutExtendWorkspace ( ctx , workspace . ID , codersdk . PutExtendWorkspaceRequest {
Deadline : oldDeadline . AddDate ( 1 , 0 , 0 ) ,
} )
require . ErrorContains ( t , err , "deadline: maximum extension is 24 hours" , "setting an earlier deadline should fail" )
2022-05-26 17:08:11 +00:00
// Ensure deadline still set correctly
updated , err = client . Workspace ( ctx , workspace . ID )
require . NoError ( t , err , "failed to fetch updated workspace" )
require . InDelta ( t , newDeadline . Unix ( ) , updated . LatestBuild . Deadline . Unix ( ) , 60 )
}
2022-05-18 21:16:26 +00:00
func TestWorkspaceWatcher ( t * testing . T ) {
t . Parallel ( )
2022-05-19 22:47:45 +00:00
client := coderdtest . New ( t , & coderdtest . Options { IncludeProvisionerD : true } )
2022-05-18 21:16:26 +00:00
user := coderdtest . CreateFirstUser ( 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 )
w , err := client . Workspace ( context . Background ( ) , workspace . ID )
require . NoError ( t , err )
ctx , cancel := context . WithCancel ( context . Background ( ) )
defer cancel ( )
wc , err := client . WatchWorkspace ( ctx , w . ID )
require . NoError ( t , err )
for i := 0 ; i < 3 ; i ++ {
_ , more := <- wc
require . True ( t , more )
}
cancel ( )
require . EqualValues ( t , codersdk . Workspace { } , <- wc )
}
2022-05-19 19:09:27 +00:00
func mustLocation ( t * testing . T , location string ) * time . Location {
t . Helper ( )
loc , err := time . LoadLocation ( location )
if err != nil {
t . Errorf ( "failed to load location %s: %s" , location , err . Error ( ) )
}
return loc
}