@@ -4,18 +4,19 @@ import (
4
4
"context"
5
5
"fmt"
6
6
"net/http"
7
+ "strings"
7
8
"testing"
8
9
"time"
9
10
10
- "github.com/coder/coder/coderd/rbac"
11
- "github.com/coder/coder/coderd/util/ptr"
12
-
13
11
"github.com/google/uuid"
14
12
"github.com/stretchr/testify/require"
15
13
16
14
"github.com/coder/coder/coderd/autobuild/schedule"
17
15
"github.com/coder/coder/coderd/coderdtest"
16
+ "github.com/coder/coder/coderd/rbac"
17
+ "github.com/coder/coder/coderd/util/ptr"
18
18
"github.com/coder/coder/codersdk"
19
+ "github.com/coder/coder/cryptorand"
19
20
"github.com/coder/coder/provisioner/echo"
20
21
"github.com/coder/coder/provisionersdk/proto"
21
22
)
@@ -336,8 +337,200 @@ func TestWorkspaceByOwnerAndName(t *testing.T) {
336
337
})
337
338
}
338
339
340
+ // TestWorkspaceFilter creates a set of workspaces, users, and organizations
341
+ // to run various filters against for testing.
339
342
func TestWorkspaceFilter (t * testing.T ) {
340
343
t .Parallel ()
344
+ type coderUser struct {
345
+ * codersdk.Client
346
+ User codersdk.User
347
+ Org codersdk.Organization
348
+ }
349
+
350
+ client := coderdtest .New (t , & coderdtest.Options {IncludeProvisionerD : true })
351
+ first := coderdtest .CreateFirstUser (t , client )
352
+
353
+ users := make ([]coderUser , 0 )
354
+ for i := 0 ; i < 10 ; i ++ {
355
+ userClient := coderdtest .CreateAnotherUser (t , client , first .OrganizationID , rbac .RoleAdmin ())
356
+ user , err := userClient .User (context .Background (), codersdk .Me )
357
+ require .NoError (t , err , "fetch me" )
358
+
359
+ org , err := userClient .CreateOrganization (context .Background (), codersdk.CreateOrganizationRequest {
360
+ Name : user .Username + "-org" ,
361
+ })
362
+ require .NoError (t , err , "create org" )
363
+
364
+ users = append (users , coderUser {
365
+ Client : userClient ,
366
+ User : user ,
367
+ Org : org ,
368
+ })
369
+ }
370
+
371
+ type madeWorkspace struct {
372
+ Owner codersdk.User
373
+ Workspace codersdk.Workspace
374
+ Template codersdk.Template
375
+ }
376
+
377
+ availTemplates := make ([]codersdk.Template , 0 )
378
+ allWorkspaces := make ([]madeWorkspace , 0 )
379
+
380
+ // Create some random workspaces
381
+ for i , user := range users {
382
+ version := coderdtest .CreateTemplateVersion (t , client , user .Org .ID , nil )
383
+
384
+ // Create a template & workspace in the user's org
385
+ coderdtest .AwaitTemplateVersionJob (t , client , version .ID )
386
+ template := coderdtest .CreateTemplate (t , client , user .Org .ID , version .ID , func (request * codersdk.CreateTemplateRequest ) {
387
+ // Have even templates share the same name for filter complexity.
388
+ if i % 2 == 0 {
389
+ request .Name = "even-template"
390
+ }
391
+ })
392
+ availTemplates = append (availTemplates , template )
393
+ workspace := coderdtest .CreateWorkspace (t , user .Client , template .OrganizationID , template .ID )
394
+ allWorkspaces = append (allWorkspaces , madeWorkspace {
395
+ Workspace : workspace ,
396
+ Template : template ,
397
+ Owner : user .User ,
398
+ })
399
+
400
+ // Make a workspace with a random template
401
+ idx , _ := cryptorand .Intn (len (availTemplates ))
402
+ randTemplate := availTemplates [idx ]
403
+ randWorkspace := coderdtest .CreateWorkspace (t , user .Client , randTemplate .OrganizationID , randTemplate .ID )
404
+ allWorkspaces = append (allWorkspaces , madeWorkspace {
405
+ Workspace : randWorkspace ,
406
+ Template : randTemplate ,
407
+ Owner : user .User ,
408
+ })
409
+ }
410
+
411
+ // Make sure all workspaces are done. Do it after all are made
412
+ for i , w := range allWorkspaces {
413
+ latest := coderdtest .AwaitWorkspaceBuildJob (t , client , w .Workspace .LatestBuild .ID )
414
+ allWorkspaces [i ].Workspace .LatestBuild = latest
415
+ }
416
+
417
+ // --- Setup done ---
418
+ testCases := []struct {
419
+ Name string
420
+ Filter codersdk.WorkspaceFilter
421
+ // If FilterF is true, we include it in the expected results
422
+ FilterF func (f codersdk.WorkspaceFilter , workspace madeWorkspace ) bool
423
+ }{
424
+ {
425
+ Name : "All" ,
426
+ Filter : codersdk.WorkspaceFilter {},
427
+ FilterF : func (_ codersdk.WorkspaceFilter , _ madeWorkspace ) bool {
428
+ return true
429
+ },
430
+ },
431
+ {
432
+ Name : "Owner" ,
433
+ Filter : codersdk.WorkspaceFilter {
434
+ Owner : users [must (cryptorand .Intn (len (users )))].User .Username ,
435
+ },
436
+ FilterF : func (f codersdk.WorkspaceFilter , workspace madeWorkspace ) bool {
437
+ return workspace .Owner .Username == f .Owner
438
+ },
439
+ },
440
+ {
441
+ Name : "OrgID" ,
442
+ Filter : codersdk.WorkspaceFilter {
443
+ OrganizationID : users [must (cryptorand .Intn (len (users )))].Org .ID ,
444
+ },
445
+ FilterF : func (f codersdk.WorkspaceFilter , workspace madeWorkspace ) bool {
446
+ return workspace .Template .OrganizationID == f .OrganizationID
447
+ },
448
+ },
449
+ {
450
+ Name : "TemplateName" ,
451
+ Filter : codersdk.WorkspaceFilter {
452
+ Template : "even-template" ,
453
+ },
454
+ FilterF : func (f codersdk.WorkspaceFilter , workspace madeWorkspace ) bool {
455
+ return workspace .Template .Name == f .Template
456
+ },
457
+ },
458
+ {
459
+ Name : "Template&Name" ,
460
+ Filter : codersdk.WorkspaceFilter {
461
+ Template : "even-template" ,
462
+ // Use a common letter... one has to have this letter in it
463
+ Name : "a" ,
464
+ },
465
+ FilterF : func (f codersdk.WorkspaceFilter , workspace madeWorkspace ) bool {
466
+ return workspace .Template .Name == f .Template && strings .Contains (workspace .Workspace .Name , f .Name )
467
+ },
468
+ },
469
+ {
470
+ Name : "Q-Owner/Name" ,
471
+ Filter : codersdk.WorkspaceFilter {
472
+ FilterQuery : allWorkspaces [5 ].Owner .Username + "/" + allWorkspaces [5 ].Workspace .Name ,
473
+ },
474
+ FilterF : func (_ codersdk.WorkspaceFilter , workspace madeWorkspace ) bool {
475
+ return workspace .Workspace .ID == allWorkspaces [5 ].Workspace .ID
476
+ },
477
+ },
478
+ {
479
+ Name : "Org&Owner" ,
480
+ Filter : codersdk.WorkspaceFilter {
481
+ OrganizationID : users [2 ].Org .ID ,
482
+ Owner : users [2 ].User .Username ,
483
+ },
484
+ FilterF : func (f codersdk.WorkspaceFilter , workspace madeWorkspace ) bool {
485
+ return workspace .Owner .Username == f .Owner && workspace .Template .OrganizationID == f .OrganizationID
486
+ },
487
+ },
488
+ {
489
+ Name : "Org&Owner" ,
490
+ Filter : codersdk.WorkspaceFilter {
491
+ OrganizationID : users [2 ].Org .ID ,
492
+ Owner : users [2 ].User .Username ,
493
+ },
494
+ FilterF : func (f codersdk.WorkspaceFilter , workspace madeWorkspace ) bool {
495
+ return workspace .Owner .Username == f .Owner && workspace .Template .OrganizationID == f .OrganizationID
496
+ },
497
+ },
498
+ {
499
+ Name : "Many filters" ,
500
+ Filter : codersdk.WorkspaceFilter {
501
+ OrganizationID : allWorkspaces [3 ].Template .OrganizationID ,
502
+ Owner : allWorkspaces [3 ].Owner .Username ,
503
+ Template : allWorkspaces [3 ].Template .Name ,
504
+ Name : allWorkspaces [3 ].Workspace .Name ,
505
+ },
506
+ FilterF : func (f codersdk.WorkspaceFilter , workspace madeWorkspace ) bool {
507
+ return workspace .Workspace .ID == allWorkspaces [3 ].Workspace .ID
508
+ },
509
+ },
510
+ }
511
+
512
+ for _ , c := range testCases {
513
+ c := c
514
+ t .Run (c .Name , func (t * testing.T ) {
515
+ t .Parallel ()
516
+ workspaces , err := client .Workspaces (context .Background (), c .Filter )
517
+ require .NoError (t , err , "fetch workspaces" )
518
+
519
+ exp := make ([]codersdk.Workspace , 0 )
520
+ for _ , made := range allWorkspaces {
521
+ if c .FilterF (c .Filter , made ) {
522
+ exp = append (exp , made .Workspace )
523
+ }
524
+ }
525
+ require .ElementsMatch (t , exp , workspaces , "expected workspaces returned" )
526
+ })
527
+ }
528
+ }
529
+
530
+ // TestWorkspaceFilterManual runs some specific setups with basic checks.
531
+ func TestWorkspaceFilterManual (t * testing.T ) {
532
+ t .Parallel ()
533
+
341
534
t .Run ("Name" , func (t * testing.T ) {
342
535
t .Parallel ()
343
536
client := coderdtest .New (t , & coderdtest.Options {IncludeProvisionerD : true })
@@ -910,3 +1103,11 @@ func mustLocation(t *testing.T, location string) *time.Location {
910
1103
911
1104
return loc
912
1105
}
1106
+
1107
+ func must [T any ](s T , err error ) T {
1108
+ if err != nil {
1109
+ panic (err )
1110
+ }
1111
+
1112
+ return s
1113
+ }
0 commit comments