diff --git a/cmd/fleetctl/fleetctl/gitops_test.go b/cmd/fleetctl/fleetctl/gitops_test.go index e6c401e323f..3336b02ff96 100644 --- a/cmd/fleetctl/fleetctl/gitops_test.go +++ b/cmd/fleetctl/fleetctl/gitops_test.go @@ -2159,7 +2159,7 @@ func TestGitOpsFullGlobal(t *testing.T) { // App config ds.AppConfigFunc = func(ctx context.Context) (*fleet.AppConfig, error) { - return &fleet.AppConfig{MDM: fleet.MDM{EnabledAndConfigured: true}}, nil + return &fleet.AppConfig{MDM: fleet.MDM{EnabledAndConfigured: true, WindowsEnabledAndConfigured: true}}, nil } ds.SaveAppConfigFunc = func(ctx context.Context, config *fleet.AppConfig) error { savedAppConfig = config diff --git a/server/service/integration_core_test.go b/server/service/integration_core_test.go index 8328a7225ca..376c5eeff1d 100644 --- a/server/service/integration_core_test.go +++ b/server/service/integration_core_test.go @@ -6159,10 +6159,35 @@ func (s *integrationTestSuite) TestListHostsByLabel() { ), ) + // The device_mapping is built with GROUP_CONCAT, whose element order is not + // guaranteed, so normalize the ordering before comparing the two responses. + normalizeDeviceMapping := func(resp *listHostsResponse) { + for i := range resp.Hosts { + dm := resp.Hosts[i].DeviceMapping + if dm == nil { + continue + } + var mappings []fleet.HostDeviceMapping + require.NoError(t, json.Unmarshal(*dm, &mappings)) + sort.Slice(mappings, func(a, b int) bool { + if mappings[a].Email != mappings[b].Email { + return mappings[a].Email < mappings[b].Email + } + return mappings[a].Source < mappings[b].Source + }) + normalized, err := json.Marshal(mappings) + require.NoError(t, err) + raw := json.RawMessage(normalized) + resp.Hosts[i].DeviceMapping = &raw + } + } + // Now do the actual API calls that we will compare. var hostsResp, labelsResp listHostsResponse s.DoJSON("GET", "/api/latest/fleet/hosts", nil, http.StatusOK, &hostsResp, "device_mapping", "true") s.DoJSON("GET", fmt.Sprintf("/api/latest/fleet/labels/%d/hosts", labelID), nil, http.StatusOK, &labelsResp, "device_mapping", "true") + normalizeDeviceMapping(&hostsResp) + normalizeDeviceMapping(&labelsResp) // Converting to formatted JSON for easier diffs hostsJson, _ := json.MarshalIndent(hostsResp, "", " ") @@ -6172,6 +6197,8 @@ func (s *integrationTestSuite) TestListHostsByLabel() { // Do request with include_device_status, since it's an additional feature s.DoJSON("GET", "/api/latest/fleet/hosts", nil, http.StatusOK, &hostsResp, "device_mapping", "true", "include_device_status", "true") s.DoJSON("GET", fmt.Sprintf("/api/latest/fleet/labels/%d/hosts", labelID), nil, http.StatusOK, &labelsResp, "device_mapping", "true", "include_device_status", "true") + normalizeDeviceMapping(&hostsResp) + normalizeDeviceMapping(&labelsResp) // Converting to formatted JSON for easier diffs hostsJson, _ = json.MarshalIndent(hostsResp, "", " ") @@ -10664,14 +10691,19 @@ func (s *integrationTestSuite) TestHostsReportDownload() { res = s.DoRaw("GET", "/api/latest/fleet/hosts/report", nil, http.StatusOK, "format", "csv", "columns", "id,hostname,device_mapping") rawCSV, err := io.ReadAll(res.Body) require.NoError(t, err) - require.Contains(t, string(rawCSV), `"a@b.c,b@b.c"`) // inside quotes because it contains a comma + // the cell is wrapped in quotes because it contains a comma; the order of the + // emails within it is not guaranteed (GROUP_CONCAT), so accept either order. + require.True(t, + strings.Contains(string(rawCSV), `"a@b.c,b@b.c"`) || strings.Contains(string(rawCSV), `"b@b.c,a@b.c"`), + string(rawCSV)) rows, err = csv.NewReader(bytes.NewReader(rawCSV)).ReadAll() res.Body.Close() require.NoError(t, err) require.Len(t, rows, len(hosts)+1) for _, row := range rows[1:] { if row[0] == fmt.Sprint(hosts[2].ID) { - require.Equal(t, "a@b.c,b@b.c", row[2], row) + // the order of the emails is not guaranteed + require.ElementsMatch(t, []string{"a@b.c", "b@b.c"}, strings.Split(row[2], ","), row) } else { require.Equal(t, "", row[2], row) }