diff --git a/api/types/app_test.go b/api/types/app_test.go index 731edb7ffa1a2..9b5b12d23c806 100644 --- a/api/types/app_test.go +++ b/api/types/app_test.go @@ -141,8 +141,6 @@ func TestAppServerSorter(t *testing.T) { for _, c := range cases { c := c t.Run(fmt.Sprintf("%s desc", c.name), func(t *testing.T) { - t.Parallel() - sortBy := SortBy{Field: c.fieldName, IsDesc: true} servers := AppServers(makeServers(testValsUnordered, c.fieldName)) require.NoError(t, servers.SortByCustom(sortBy)) @@ -152,8 +150,6 @@ func TestAppServerSorter(t *testing.T) { }) t.Run(fmt.Sprintf("%s asc", c.name), func(t *testing.T) { - t.Parallel() - sortBy := SortBy{Field: c.fieldName} servers := AppServers(makeServers(testValsUnordered, c.fieldName)) require.NoError(t, servers.SortByCustom(sortBy)) diff --git a/api/types/appserver.go b/api/types/appserver.go index 10fb2bdea62db..aa5a96b43d2d4 100644 --- a/api/types/appserver.go +++ b/api/types/appserver.go @@ -304,7 +304,14 @@ func (s AppServers) Len() int { return len(s) } // Less compares app servers by name and host ID. func (s AppServers) Less(i, j int) bool { - return s[i].GetName() < s[j].GetName() && s[i].GetHostID() < s[j].GetHostID() + switch { + case s[i].GetName() < s[j].GetName(): + return true + case s[i].GetName() > s[j].GetName(): + return false + default: + return s[i].GetHostID() < s[j].GetHostID() + } } // Swap swaps two app servers. diff --git a/api/types/databaseserver.go b/api/types/databaseserver.go index 4a4e6cfb0e6a4..db2637513a3cc 100644 --- a/api/types/databaseserver.go +++ b/api/types/databaseserver.go @@ -291,7 +291,14 @@ func (s DatabaseServers) Len() int { return len(s) } // Less compares database servers by name and host ID. func (s DatabaseServers) Less(i, j int) bool { - return s[i].GetName() < s[j].GetName() && s[i].GetHostID() < s[j].GetHostID() + switch { + case s[i].GetName() < s[j].GetName(): + return true + case s[i].GetName() > s[j].GetName(): + return false + default: + return s[i].GetHostID() < s[j].GetHostID() + } } // Swap swaps two database servers. diff --git a/api/types/databaseserver_test.go b/api/types/databaseserver_test.go index babd27ac9a0d7..de45c5c788df8 100644 --- a/api/types/databaseserver_test.go +++ b/api/types/databaseserver_test.go @@ -154,8 +154,6 @@ func TestDatabaseServerSorter(t *testing.T) { for _, c := range cases { c := c t.Run(fmt.Sprintf("%s desc", c.name), func(t *testing.T) { - t.Parallel() - sortBy := SortBy{Field: c.fieldName, IsDesc: true} servers := DatabaseServers(makeServers(testValsUnordered, c.fieldName)) require.NoError(t, servers.SortByCustom(sortBy)) @@ -165,8 +163,6 @@ func TestDatabaseServerSorter(t *testing.T) { }) t.Run(fmt.Sprintf("%s asc", c.name), func(t *testing.T) { - t.Parallel() - sortBy := SortBy{Field: c.fieldName} servers := DatabaseServers(makeServers(testValsUnordered, c.fieldName)) require.NoError(t, servers.SortByCustom(sortBy)) diff --git a/api/types/desktop.go b/api/types/desktop.go index 666d231416a82..9d92176e22e68 100644 --- a/api/types/desktop.go +++ b/api/types/desktop.go @@ -229,7 +229,14 @@ func (s WindowsDesktops) Len() int { return len(s) } // Less compares desktops by name and host ID. func (s WindowsDesktops) Less(i, j int) bool { - return s[i].GetName() < s[j].GetName() && s[i].GetHostID() < s[j].GetHostID() + switch { + case s[i].GetName() < s[j].GetName(): + return true + case s[i].GetName() > s[j].GetName(): + return false + default: + return s[i].GetHostID() < s[j].GetHostID() + } } // Swap swaps two windows desktops. diff --git a/api/types/desktop_test.go b/api/types/desktop_test.go index 88bf7d5300b93..f17c480bd0685 100644 --- a/api/types/desktop_test.go +++ b/api/types/desktop_test.go @@ -63,8 +63,6 @@ func TestWindowsDesktopsSorter(t *testing.T) { for _, c := range cases { c := c t.Run(fmt.Sprintf("%s desc", c.name), func(t *testing.T) { - t.Parallel() - sortBy := SortBy{Field: c.fieldName, IsDesc: true} servers := WindowsDesktops(makeDesktops(testValsUnordered, c.fieldName)) require.NoError(t, servers.SortByCustom(sortBy)) @@ -74,8 +72,6 @@ func TestWindowsDesktopsSorter(t *testing.T) { }) t.Run(fmt.Sprintf("%s asc", c.name), func(t *testing.T) { - t.Parallel() - sortBy := SortBy{Field: c.fieldName} servers := WindowsDesktops(makeDesktops(testValsUnordered, c.fieldName)) require.NoError(t, servers.SortByCustom(sortBy)) @@ -85,10 +81,8 @@ func TestWindowsDesktopsSorter(t *testing.T) { }) } - t.Run("error", func(t *testing.T) { - t.Parallel() - sortBy := SortBy{Field: "unsupported"} - desktops := makeDesktops(testValsUnordered, "does-not-matter") - require.True(t, trace.IsNotImplemented(WindowsDesktops(desktops).SortByCustom(sortBy))) - }) + // Test error. + sortBy := SortBy{Field: "unsupported"} + desktops := makeDesktops(testValsUnordered, "does-not-matter") + require.True(t, trace.IsNotImplemented(WindowsDesktops(desktops).SortByCustom(sortBy))) } diff --git a/api/types/server_test.go b/api/types/server_test.go index b5de2617ae0e4..4b145d7bb0ad2 100644 --- a/api/types/server_test.go +++ b/api/types/server_test.go @@ -76,8 +76,6 @@ func TestServerSorter(t *testing.T) { for _, c := range cases { c := c t.Run(fmt.Sprintf("%s desc", c.name), func(t *testing.T) { - t.Parallel() - sortBy := SortBy{Field: c.fieldName, IsDesc: true} servers := Servers(makeServers(testValsUnordered, c.fieldName)) require.NoError(t, servers.SortByCustom(sortBy)) @@ -87,8 +85,6 @@ func TestServerSorter(t *testing.T) { }) t.Run(fmt.Sprintf("%s asc", c.name), func(t *testing.T) { - t.Parallel() - sortBy := SortBy{Field: c.fieldName} servers := Servers(makeServers(testValsUnordered, c.fieldName)) require.NoError(t, servers.SortByCustom(sortBy)) diff --git a/lib/auth/api.go b/lib/auth/api.go index 826cff484b45d..bda207dd233de 100644 --- a/lib/auth/api.go +++ b/lib/auth/api.go @@ -809,7 +809,7 @@ type Cache interface { // ListResources returns a paginated list of resources. ListResources(ctx context.Context, req proto.ListResourcesRequest) (*types.ListResourcesResponse, error) - // ListWindowsDesktops returns a paginated list of windows desktops.. + // ListWindowsDesktops returns a paginated list of windows desktops. ListWindowsDesktops(ctx context.Context, req types.ListWindowsDesktopsRequest) (*types.ListWindowsDesktopsResponse, error) } diff --git a/lib/services/local/desktops_test.go b/lib/services/local/desktops_test.go index 76b27f0a18f7e..3500b8b53b73f 100644 --- a/lib/services/local/desktops_test.go +++ b/lib/services/local/desktops_test.go @@ -31,6 +31,7 @@ import ( ) func TestListWindowsDesktops(t *testing.T) { + t.Parallel() ctx := context.Background() liteBackend, err := lite.NewWithConfig(ctx, lite.Config{ @@ -53,9 +54,7 @@ func TestListWindowsDesktops(t *testing.T) { // With label. testLabel := map[string]string{"env": "test"} - d1, err := types.NewWindowsDesktopV3("apple", testLabel, types.WindowsDesktopSpecV3{ - Addr: "_", - }) + d1, err := types.NewWindowsDesktopV3("apple", testLabel, types.WindowsDesktopSpecV3{Addr: "_"}) require.NoError(t, err) require.NoError(t, service.CreateWindowsDesktop(ctx, d1)) @@ -78,117 +77,137 @@ func TestListWindowsDesktops(t *testing.T) { cmpopts.IgnoreFields(types.Metadata{}, "ID"), )) - t.Run("fetch first, middle, last", func(t *testing.T) { - t.Parallel() - // First - resp, err := service.ListWindowsDesktops(ctx, types.ListWindowsDesktopsRequest{ - Limit: 1, - }) - require.NoError(t, err) - require.Len(t, resp.Desktops, 1) - require.Equal(t, out.Desktops[0], resp.Desktops[0]) - require.Equal(t, backend.GetPaginationKey(out.Desktops[1]), resp.NextKey) - - // Middle - resp, err = service.ListWindowsDesktops(ctx, types.ListWindowsDesktopsRequest{ - Limit: 1, - StartKey: resp.NextKey, - }) - require.NoError(t, err) - require.Len(t, resp.Desktops, 1) - require.Equal(t, out.Desktops[1], resp.Desktops[0]) - require.Equal(t, backend.GetPaginationKey(out.Desktops[2]), resp.NextKey) - - // Last - resp, err = service.ListWindowsDesktops(ctx, types.ListWindowsDesktopsRequest{ - Limit: 1, - StartKey: resp.NextKey, - }) - require.NoError(t, err) - require.Len(t, resp.Desktops, 1) - require.Equal(t, out.Desktops[2], resp.Desktops[0]) - require.Empty(t, resp.NextKey) + // Test pagination. + + // First fetch. + resp, err := service.ListWindowsDesktops(ctx, types.ListWindowsDesktopsRequest{ + Limit: 1, + }) + require.NoError(t, err) + require.Len(t, resp.Desktops, 1) + require.Equal(t, out.Desktops[0], resp.Desktops[0]) + require.Equal(t, backend.GetPaginationKey(out.Desktops[1]), resp.NextKey) + + // Middle fetch. + resp, err = service.ListWindowsDesktops(ctx, types.ListWindowsDesktopsRequest{ + Limit: 1, + StartKey: resp.NextKey, + }) + require.NoError(t, err) + require.Len(t, resp.Desktops, 1) + require.Equal(t, out.Desktops[1], resp.Desktops[0]) + require.Equal(t, backend.GetPaginationKey(out.Desktops[2]), resp.NextKey) + + // Last fetch. + resp, err = service.ListWindowsDesktops(ctx, types.ListWindowsDesktopsRequest{ + Limit: 1, + StartKey: resp.NextKey, }) + require.NoError(t, err) + require.Len(t, resp.Desktops, 1) + require.Equal(t, out.Desktops[2], resp.Desktops[0]) + require.Empty(t, resp.NextKey) +} + +func TestListWindowsDesktops_Filters(t *testing.T) { + t.Parallel() + ctx := context.Background() + + liteBackend, err := lite.NewWithConfig(ctx, lite.Config{ + Path: t.TempDir(), + Clock: clockwork.NewFakeClock(), + }) + require.NoError(t, err) + + service := NewWindowsDesktopService(liteBackend) + + // Upsert some windows desktops. - t.Run("filter", func(t *testing.T) { - t.Parallel() - tests := []struct { - name string - filter types.ListWindowsDesktopsRequest - wantErr bool - expectedLen int - }{ - { - name: "NOK non-matching host id and name", - filter: types.ListWindowsDesktopsRequest{ - Limit: 10, - WindowsDesktopFilter: types.WindowsDesktopFilter{ - HostID: "no-match", - Name: "no-match", - }, + // With label. + testLabel := map[string]string{"env": "test"} + d1, err := types.NewWindowsDesktopV3("banana", testLabel, types.WindowsDesktopSpecV3{Addr: "_", HostID: "test-host-id"}) + require.NoError(t, err) + require.NoError(t, service.CreateWindowsDesktop(ctx, d1)) + + d2, err := types.NewWindowsDesktopV3("banana", testLabel, types.WindowsDesktopSpecV3{Addr: "_"}) + require.NoError(t, err) + require.NoError(t, service.CreateWindowsDesktop(ctx, d2)) + + // Without labels. + d3, err := types.NewWindowsDesktopV3("carrot", nil, types.WindowsDesktopSpecV3{Addr: "_", HostID: "test-host-id"}) + require.NoError(t, err) + require.NoError(t, service.CreateWindowsDesktop(ctx, d3)) + + tests := []struct { + name string + filter types.ListWindowsDesktopsRequest + wantErr bool + }{ + { + name: "NOK non-matching host id and name", + filter: types.ListWindowsDesktopsRequest{ + Limit: 10, + WindowsDesktopFilter: types.WindowsDesktopFilter{ + HostID: "no-match", + Name: "no-match", }, - wantErr: true, }, - { - name: "NOK invalid limit", - filter: types.ListWindowsDesktopsRequest{}, - wantErr: true, + wantErr: true, + }, + { + name: "NOK invalid limit", + filter: types.ListWindowsDesktopsRequest{}, + wantErr: true, + }, + { + name: "matching host id", + filter: types.ListWindowsDesktopsRequest{ + Limit: 5, + WindowsDesktopFilter: types.WindowsDesktopFilter{HostID: "test-host-id"}, }, - { - name: "matching host id", - filter: types.ListWindowsDesktopsRequest{ - Limit: 1, - WindowsDesktopFilter: types.WindowsDesktopFilter{HostID: "test-host-id"}, - }, - expectedLen: 1, + }, + { + name: "matching name", + filter: types.ListWindowsDesktopsRequest{ + Limit: 5, + WindowsDesktopFilter: types.WindowsDesktopFilter{Name: "banana"}, }, - { - name: "matching name", - filter: types.ListWindowsDesktopsRequest{ - Limit: 1, - WindowsDesktopFilter: types.WindowsDesktopFilter{Name: "banana"}, - }, - expectedLen: 1, + }, + { + name: "with search", + filter: types.ListWindowsDesktopsRequest{ + Limit: 5, + SearchKeywords: []string{"env", "test"}, }, - { - name: "with search", - filter: types.ListWindowsDesktopsRequest{ - Limit: 10, - SearchKeywords: []string{"env", "test"}, - }, - expectedLen: 2, + }, + { + name: "with labels", + filter: types.ListWindowsDesktopsRequest{ + Limit: 5, + Labels: testLabel, }, - { - name: "with labels", - filter: types.ListWindowsDesktopsRequest{ - Limit: 10, - Labels: testLabel, - }, - expectedLen: 2, - }, - { - name: "with predicate", - filter: types.ListWindowsDesktopsRequest{ - Limit: 10, - PredicateExpression: `labels.env == "test"`, - }, - expectedLen: 2, + }, + { + name: "with predicate", + filter: types.ListWindowsDesktopsRequest{ + Limit: 5, + PredicateExpression: `labels.env == "test"`, }, - } - - for _, tc := range tests { - tc := tc - t.Run(tc.name, func(t *testing.T) { - t.Parallel() - resp, err := service.ListWindowsDesktops(ctx, tc.filter) - - if tc.wantErr { - require.Error(t, err) - } else { - require.NoError(t, err) - require.Len(t, resp.Desktops, tc.expectedLen) - } - }) - } - }) + }, + } + + for _, tc := range tests { + tc := tc + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + resp, err := service.ListWindowsDesktops(ctx, tc.filter) + + if tc.wantErr { + require.Error(t, err) + } else { + require.NoError(t, err) + require.Len(t, resp.Desktops, 2) + } + }) + } }