Repository: clouditor/clouditor
Pull request #988 (2023-02-27 20:37:54)

Add parent controls to controls_in_scope at ToE creation
This PR 
- adds the controls (e.g., OPS-01) to the controls_in_scope, 
- updates the testdata mock control variables,
- adds a MockControlsInScope,
- cuts off the seconds from timestamp for less test errors  (Test_GetCertificate()) and
- fixes the ListControls(). The  case that only the catalog id is given was missing.

codecov[bot]: # [Codecov](https://codecov.io/gh/clouditor/clouditor/pull/988?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=clouditor) Report
> Merging [#988](https://codecov.io/gh/clouditor/clouditor/pull/988?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=clouditor) (4c7b6e2) into [main](https://codecov.io/gh/clouditor/clouditor/commit/75f7e3685ac58b0b1919dbade6cc450e27b8e04e?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=clouditor) (75f7e36) will **increase** coverage by `0.04%`.
> The diff coverage is `100.00%`.

```diff
@@            Coverage Diff             @@
##             main     #988      +/-   ##
==========================================
+ Coverage   81.92%   81.96%   +0.04%     
==========================================
  Files          89       89              
  Lines        7573     7590      +17     
==========================================
+ Hits         6204     6221      +17     
  Misses       1050     1050              
  Partials      319      319              
```

| Flag | Coverage Δ | |
|---|---|---|
| unittests | `81.96% <100.00%> (+0.04%)` | :arrow_up: |

Flags with carried forward coverage won't be shown. [Click here](https://docs.codecov.io/docs/carryforward-flags?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=clouditor#carryforward-flags-in-the-pull-request-comment) to find out more.

| [Impacted Files](https://codecov.io/gh/clouditor/clouditor/pull/988?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=clouditor) | Coverage Δ | |
|---|---|---|
| [internal/testutil/orchestratortest/orchestrator.go](https://codecov.io/gh/clouditor/clouditor/pull/988?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=clouditor#diff-aW50ZXJuYWwvdGVzdHV0aWwvb3JjaGVzdHJhdG9ydGVzdC9vcmNoZXN0cmF0b3IuZ28=) | `100.00% <100.00%> (ø)` | |
| [service/orchestrator/toe.go](https://codecov.io/gh/clouditor/clouditor/pull/988?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=clouditor#diff-c2VydmljZS9vcmNoZXN0cmF0b3IvdG9lLmdv) | `80.83% <100.00%> (+1.10%)` | :arrow_up: |

Help us with your feedback. Take ten seconds to tell us [how you rate us](https://about.codecov.io/nps?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=clouditor). Have a feature suggestion? [Share it here.](https://app.codecov.io/gh/feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=clouditor)

anatheka: I'm not sure if that is the best solution for getting the parent controls. We can also use a hash map, but I think since there are not so many controls we can do it that way. 

Also, we could consider querying the controls for the corresponding assurance levels directly via the database. But for now, I would leave it as is.

Patch:
diff --git a/service/orchestrator/catalogs.go b/service/orchestrator/catalogs.go
index dc0f531f..8ae36d71 100644
--- a/service/orchestrator/catalogs.go
+++ b/service/orchestrator/catalogs.go
@@ -186,6 +186,9 @@ func (srv *Service) ListControls(_ context.Context, req *orchestrator.ListContro
 	if req.CategoryName != "" && req.CatalogId != "" {
 		res.Controls, res.NextPageToken, err = service.PaginateStorage[*orchestrator.Control](req, srv.storage,
 			service.DefaultPaginationOpts, "category_name = ? AND category_catalog_id = ?", req.CategoryName, req.CatalogId)
+	} else if req.CatalogId != "" {
+		res.Controls, res.NextPageToken, err = service.PaginateStorage[*orchestrator.Control](req, srv.storage,
+			service.DefaultPaginationOpts, "category_catalog_id = ?", req.CatalogId)
 	} else {
 		res.Controls, res.NextPageToken, err = service.PaginateStorage[*orchestrator.Control](req, srv.storage,
 			service.DefaultPaginationOpts)
diff --git a/service/orchestrator/toe.go b/service/orchestrator/toe.go
index 9ed463c7..263bd32c 100644
--- a/service/orchestrator/toe.go
+++ b/service/orchestrator/toe.go
@@ -29,6 +29,7 @@ import (
 	"context"
 	"errors"
 	"fmt"
+	"sort"
 
 	"clouditor.io/clouditor/api"
 	"clouditor.io/clouditor/api/orchestrator"
@@ -395,7 +396,7 @@ func getControls(controls []*orchestrator.Control, levels []string, level string
 		}
 	}
 
-	// Add all needed controls based on the assurance level and return
+	// Add all needed controls based on the assurance level
 	switch level {
 	case levels[0]:
 		c = append(c, low...)
@@ -408,7 +409,19 @@ func getControls(controls []*orchestrator.Control, levels []string, level string
 		c = append(c, high...)
 	}
 
-	return c, nil
+	// Add parent controls to the sub-control included in the list. That results in duplicates that we can remove later.
+	for i := range c {
+		for j := range controls {
+			if c[i].GetParentControlId() == controls[j].GetId() {
+				c = append(c, controls[j])
+			}
+		}
+	}
+
+	// Deduplicate controls
+	dedupControls := deduplicate(c)
+
+	return dedupControls, nil
 }
 
 // informToeHooks informs the registered hook function either of a event change for the Target of Evaluation or Control Monitoring Status
@@ -425,3 +438,20 @@ func (s *Service) informToeHooks(ctx context.Context, event *orchestrator.Target
 		}
 	}
 }
+
+// deduplicate removes all duplicates
+func deduplicate(result []*orchestrator.Control) []*orchestrator.Control {
+	// Sort slice by ID
+	sort.Slice(result, func(i, j int) bool {
+		return result[i].GetId() > result[j].GetId()
+	})
+
+	// Compare elements and delete if it is a duplicate
+	for i := len(result) - 1; i > 0; i-- {
+		if result[i] == result[i-1] {
+			result = append(result[:i], result[i+1:]...)
+		}
+	}
+
+	return result
+}


Test patch:
diff --git a/internal/testdata/testdata.go b/internal/testdata/testdata.go
index 2accc55c..5435ea6e 100644
--- a/internal/testdata/testdata.go
+++ b/internal/testdata/testdata.go
@@ -28,14 +28,17 @@ const (
 	MockCatalogDescription        = "This is a mock catalog"
 	MockCategoryName              = "Mock Category Name"
 	MockCategoryDescription       = "This is a mock category"
-	MockControlID1                = "Cont1234"
-	MockControlID2                = "Cont2.B"
-	MockControlID3                = "Cont3.S"
-	MockControlID4                = "Cont4.H"
+	MockControlID1                = "Cont1"
+	MockControlID2                = "Cont2"
+	MockControlID3                = "Cont3"
+	MockControlID4                = "Cont4"
 	MockControlID5                = "Cont5"
 	MockControlName               = "Mock Control Name"
 	MockControlDescription        = "This is a mock control"
-	MockSubControlID              = "Cont1234.1"
+	MockSubControlID11            = "Cont1.1"
+	MockSubControlID21            = "Cont2.1"
+	MockSubControlID31            = "Cont3.1"
+	MockSubControlID32            = "Cont3.2"
 	MockSubControlName            = "Mock Sub-Control Name"
 	MockSubControlDescription     = "This is a mock sub-control"
 	MockAnotherControlID          = "Cont4567"
diff --git a/internal/testutil/orchestratortest/orchestrator.go b/internal/testutil/orchestratortest/orchestrator.go
index 38deab09..1ba1a5d6 100644
--- a/internal/testutil/orchestratortest/orchestrator.go
+++ b/internal/testutil/orchestratortest/orchestrator.go
@@ -6,6 +6,7 @@ import (
 	"clouditor.io/clouditor/api/assessment"
 	"clouditor.io/clouditor/api/orchestrator"
 	"clouditor.io/clouditor/internal/testdata"
+	"clouditor.io/clouditor/internal/util"
 	"google.golang.org/protobuf/types/known/structpb"
 	"google.golang.org/protobuf/types/known/timestamppb"
 )
@@ -65,7 +66,7 @@ func NewCatalog() *orchestrator.Catalog {
 							}}}},
 				}},
 				Controls: []*orchestrator.Control{{
-					Id:                testdata.MockSubControlID,
+					Id:                testdata.MockSubControlID11,
 					Name:              testdata.MockSubControlName,
 					Description:       testdata.MockSubControlDescription,
 					Metrics:           []*assessment.Metric{},
@@ -102,7 +103,7 @@ func NewTargetOfEvaluation(assuranceLevel string) *orchestrator.TargetOfEvaluati
 		CategoryCatalogId: testdata.MockCatalogID,
 		Name:              testdata.MockControlName,
 	}, {
-		Id:                testdata.MockSubControlID,
+		Id:                testdata.MockSubControlID11,
 		CategoryName:      testdata.MockCategoryName,
 		CategoryCatalogId: testdata.MockCatalogID,
 		Name:              testdata.MockSubControlName,
@@ -208,6 +209,117 @@ var (
 		CategoryName:      testdata.MockCategoryName,
 		CategoryCatalogId: testdata.MockCatalogID,
 		Description:       testdata.MockControlDescription,
+		Controls: []*orchestrator.Control{
+			{
+				Id:                             testdata.MockSubControlID11,
+				Name:                           testdata.MockControlName,
+				CategoryName:                   testdata.MockCategoryName,
+				CategoryCatalogId:              testdata.MockCatalogID,
+				Description:                    testdata.MockControlDescription,
+				AssuranceLevel:                 &testdata.AssuranceLevelBasic,
+				ParentControlId:                util.Ref(testdata.MockControlID1),
+				ParentControlCategoryName:      util.Ref(testdata.MockCategoryName),
+				ParentControlCategoryCatalogId: util.Ref(testdata.MockCatalogID),
+				Metrics: []*assessment.Metric{{
+					Id:          testdata.MockMetricID,
+					Name:        testdata.MockMetricName,
+					Description: testdata.MockMetricDescription,
+					Scale:       assessment.Metric_ORDINAL,
+					Range: &assessment.Range{
+						Range: &assessment.Range_AllowedValues{
+							AllowedValues: &assessment.AllowedValues{
+								Values: []*structpb.Value{
+									structpb.NewBoolValue(false),
+									structpb.NewBoolValue(true),
+								},
+							},
+						},
+					},
+				}}},
+		}}
+	MockControl2 = &orchestrator.Control{
+		Id:                testdata.MockControlID2,
+		Name:              testdata.MockControlName,
+		CategoryName:      testdata.MockCategoryName,
+		CategoryCatalogId: testdata.MockCatalogID,
+		Description:       testdata.MockControlDescription,
+		Controls: []*orchestrator.Control{
+			{
+				Id:                             testdata.MockSubControlID21,
+				Name:                           testdata.MockControlName,
+				CategoryName:                   testdata.MockCategoryName,
+				CategoryCatalogId:              testdata.MockCatalogID,
+				Description:                    testdata.MockControlDescription,
+				AssuranceLevel:                 &testdata.AssuranceLevelBasic,
+				ParentControlId:                util.Ref(testdata.MockControlID2),
+				ParentControlCategoryName:      util.Ref(testdata.MockCategoryName),
+				ParentControlCategoryCatalogId: util.Ref(testdata.MockCatalogID),
+				Metrics: []*assessment.Metric{{
+					Id:          testdata.MockMetricID,
+					Name:        testdata.MockMetricName,
+					Description: testdata.MockMetricDescription,
+					Scale:       assessment.Metric_ORDINAL,
+					Range: &assessment.Range{
+						Range: &assessment.Range_AllowedValues{
+							AllowedValues: &assessment.AllowedValues{
+								Values: []*structpb.Value{
+									structpb.NewBoolValue(false),
+									structpb.NewBoolValue(true),
+								},
+							},
+						},
+					},
+				}}},
+		}}
+	MockControl3 = &orchestrator.Control{
+		Id:                testdata.MockControlID3,
+		Name:              testdata.MockControlName,
+		CategoryName:      testdata.MockCategoryName,
+		CategoryCatalogId: testdata.MockCatalogID,
+		Description:       testdata.MockControlDescription,
+		Controls: []*orchestrator.Control{
+			{
+				Id:                             testdata.MockSubControlID31,
+				Name:                           testdata.MockControlName,
+				CategoryName:                   testdata.MockCategoryName,
+				CategoryCatalogId:              testdata.MockCatalogID,
+				Description:                    testdata.MockControlDescription,
+				AssuranceLevel:                 &testdata.AssuranceLevelSubstantial,
+				ParentControlId:                util.Ref(testdata.MockControlID3),
+				ParentControlCategoryName:      util.Ref(testdata.MockCategoryName),
+				ParentControlCategoryCatalogId: util.Ref(testdata.MockCatalogID),
+			}},
+	}
+	MockControl4 = &orchestrator.Control{
+		Id:                testdata.MockControlID4,
+		Name:              testdata.MockControlName,
+		CategoryName:      testdata.MockCategoryName,
+		CategoryCatalogId: testdata.MockCatalogID,
+		Description:       testdata.MockControlDescription,
+		AssuranceLevel:    &testdata.AssuranceLevelHigh,
+		Metrics: []*assessment.Metric{{
+			Id:          testdata.MockMetricID,
+			Name:        testdata.MockMetricName,
+			Description: testdata.MockMetricDescription,
+			Scale:       assessment.Metric_ORDINAL,
+			Range: &assessment.Range{
+				Range: &assessment.Range_AllowedValues{
+					AllowedValues: &assessment.AllowedValues{
+						Values: []*structpb.Value{
+							structpb.NewBoolValue(false),
+							structpb.NewBoolValue(true),
+						},
+					},
+				},
+			},
+		}},
+	}
+	MockControl5 = &orchestrator.Control{
+		Id:                testdata.MockControlID5,
+		Name:              testdata.MockControlName,
+		CategoryName:      testdata.MockCategoryName,
+		CategoryCatalogId: testdata.MockCatalogID,
+		Description:       testdata.MockControlDescription,
 		AssuranceLevel:    nil,
 		Metrics: []*assessment.Metric{{
 			Id:          testdata.MockMetricID,
@@ -226,13 +338,58 @@ var (
 			},
 		}},
 	}
-	MockControl2 = &orchestrator.Control{
+	MockControls = []*orchestrator.Control{MockControl1, MockControl2, MockControl3, MockControl4, MockControl5}
+
+	MockControlsInScope1 = &orchestrator.Control{
+		Id:                testdata.MockControlID1,
+		Name:              testdata.MockControlName,
+		CategoryName:      testdata.MockCategoryName,
+		CategoryCatalogId: testdata.MockCatalogID,
+		Description:       testdata.MockControlDescription,
+	}
+	MockControlsInScopeSubControl1 = &orchestrator.Control{
+		Id:                             testdata.MockSubControlID11,
+		Name:                           testdata.MockControlName,
+		CategoryName:                   testdata.MockCategoryName,
+		CategoryCatalogId:              testdata.MockCatalogID,
+		Description:                    testdata.MockControlDescription,
+		AssuranceLevel:                 &testdata.AssuranceLevelBasic,
+		ParentControlId:                util.Ref(testdata.MockControlID1),
+		ParentControlCategoryName:      util.Ref(testdata.MockCategoryName),
+		ParentControlCategoryCatalogId: util.Ref(testdata.MockCatalogID),
+		Metrics: []*assessment.Metric{{
+			Id:          testdata.MockMetricID,
+			Name:        testdata.MockMetricName,
+			Description: testdata.MockMetricDescription,
+			Scale:       assessment.Metric_ORDINAL,
+			Range: &assessment.Range{
+				Range: &assessment.Range_AllowedValues{
+					AllowedValues: &assessment.AllowedValues{
+						Values: []*structpb.Value{
+							structpb.NewBoolValue(false),
+							structpb.NewBoolValue(true),
+						},
+					},
+				},
+			},
+		}}}
+	MockControlsInScope2 = &orchestrator.Control{
 		Id:                testdata.MockControlID2,
 		Name:              testdata.MockControlName,
 		CategoryName:      testdata.MockCategoryName,
 		CategoryCatalogId: testdata.MockCatalogID,
 		Description:       testdata.MockControlDescription,
-		AssuranceLevel:    &testdata.AssuranceLevelBasic,
+	}
+	MockControlsInScopeSubControl2 = &orchestrator.Control{
+		Id:                             testdata.MockSubControlID21,
+		Name:                           testdata.MockControlName,
+		CategoryName:                   testdata.MockCategoryName,
+		CategoryCatalogId:              testdata.MockCatalogID,
+		Description:                    testdata.MockControlDescription,
+		AssuranceLevel:                 &testdata.AssuranceLevelBasic,
+		ParentControlId:                util.Ref(testdata.MockControlID2),
+		ParentControlCategoryName:      util.Ref(testdata.MockCategoryName),
+		ParentControlCategoryCatalogId: util.Ref(testdata.MockCatalogID),
 		Metrics: []*assessment.Metric{{
 			Id:          testdata.MockMetricID,
 			Name:        testdata.MockMetricName,
@@ -250,15 +407,25 @@ var (
 			},
 		}},
 	}
-	MockControl3 = &orchestrator.Control{
+	MockControlsInScope3 = &orchestrator.Control{
 		Id:                testdata.MockControlID3,
 		Name:              testdata.MockControlName,
 		CategoryName:      testdata.MockCategoryName,
 		CategoryCatalogId: testdata.MockCatalogID,
 		Description:       testdata.MockControlDescription,
-		AssuranceLevel:    &testdata.AssuranceLevelSubstantial,
 	}
-	MockControl4 = &orchestrator.Control{
+	MockControlsInScopeSubControl3 = &orchestrator.Control{
+		Id:                             testdata.MockSubControlID31,
+		Name:                           testdata.MockControlName,
+		CategoryName:                   testdata.MockCategoryName,
+		CategoryCatalogId:              testdata.MockCatalogID,
+		Description:                    testdata.MockControlDescription,
+		AssuranceLevel:                 &testdata.AssuranceLevelSubstantial,
+		ParentControlId:                util.Ref(testdata.MockControlID3),
+		ParentControlCategoryName:      util.Ref(testdata.MockCategoryName),
+		ParentControlCategoryCatalogId: util.Ref(testdata.MockCatalogID),
+	}
+	MockControlsInScope4 = &orchestrator.Control{
 		Id:                testdata.MockControlID4,
 		Name:              testdata.MockControlName,
 		CategoryName:      testdata.MockCategoryName,
@@ -282,7 +449,7 @@ var (
 			},
 		}},
 	}
-	MockControl5 = &orchestrator.Control{
+	MockControlsInScope5 = &orchestrator.Control{
 		Id:                testdata.MockControlID5,
 		Name:              testdata.MockControlName,
 		CategoryName:      testdata.MockCategoryName,
@@ -306,5 +473,16 @@ var (
 			},
 		}},
 	}
-	MockControls = []*orchestrator.Control{MockControl1, MockControl2, MockControl3, MockControl4, MockControl5}
+	MockControlsInScopeSubControl4 = &orchestrator.Control{
+		Id:                             testdata.MockSubControlID32,
+		Name:                           testdata.MockControlName,
+		CategoryName:                   testdata.MockCategoryName,
+		CategoryCatalogId:              testdata.MockCatalogID,
+		Description:                    testdata.MockControlDescription,
+		AssuranceLevel:                 &testdata.AssuranceLevelSubstantial,
+		ParentControlId:                util.Ref(testdata.MockControlID3),
+		ParentControlCategoryName:      util.Ref(testdata.MockCategoryName),
+		ParentControlCategoryCatalogId: util.Ref(testdata.MockCatalogID),
+	}
+	MockControlsInScope = []*orchestrator.Control{MockControlsInScope1, MockControlsInScopeSubControl1, MockControlsInScope2, MockControlsInScopeSubControl2, MockControlsInScope3, MockControlsInScopeSubControl3, MockControlsInScope4, MockControlsInScope5, MockControlsInScopeSubControl4}
 )
diff --git a/service/orchestrator/catalogs_test.go b/service/orchestrator/catalogs_test.go
index c6a3fee4..743271f4 100644
--- a/service/orchestrator/catalogs_test.go
+++ b/service/orchestrator/catalogs_test.go
@@ -444,7 +444,7 @@ func TestService_GetControl(t *testing.T) {
 							}}}},
 				}},
 				Controls: []*orchestrator.Control{{
-					Id:                             testdata.MockSubControlID,
+					Id:                             testdata.MockSubControlID11,
 					Name:                           testdata.MockSubControlName,
 					Description:                    testdata.MockSubControlDescription,
 					Metrics:                        []*assessment.Metric{},
diff --git a/service/orchestrator/orchestrator_test.go b/service/orchestrator/orchestrator_test.go
index cbdac646..a8282501 100644
--- a/service/orchestrator/orchestrator_test.go
+++ b/service/orchestrator/orchestrator_test.go
@@ -384,9 +384,11 @@ func Test_GetCertificate(t *testing.T) {
 
 			if tt.res != nil {
 				assert.NotEmpty(t, res.Id)
-				// Compare timestamp. We have to cut off the microseconds, otherwise an error is returned.
-				tt.res.States[0].Timestamp = strings.Split(tt.res.States[0].GetTimestamp(), ".")[0]
-				res.States[0].Timestamp = strings.Split(res.States[0].GetTimestamp(), ".")[0]
+				// Compare timestamp. We have to cut off the microseconds and seconds, otherwise an error can be returned.
+				t1 := strings.Split(tt.res.States[0].GetTimestamp(), ".")[0]
+				tt.res.States[0].Timestamp = t1[:len(t1)-3]
+				t2 := strings.Split(res.States[0].GetTimestamp(), ".")[0]
+				res.States[0].Timestamp = t2[:len(t2)-3]
 				assert.True(t, proto.Equal(tt.res, res), "Want: %v\nGot : %v", tt.res, res)
 			}
 		})
diff --git a/service/orchestrator/toe_test.go b/service/orchestrator/toe_test.go
index e9d88ea5..0c05f349 100644
--- a/service/orchestrator/toe_test.go
+++ b/service/orchestrator/toe_test.go
@@ -29,6 +29,7 @@ import (
 	"context"
 	"reflect"
 	"runtime"
+	"sort"
 	"sync"
 	"testing"
 
@@ -626,7 +627,7 @@ func TestService_ListControlsInScope(t *testing.T) {
 						MonitoringStatus:                 orchestrator.MonitoringStatus_MONITORING_STATUS_UNSPECIFIED,
 					},
 					{
-						ControlId:                        testdata.MockSubControlID,
+						ControlId:                        testdata.MockSubControlID11,
 						ControlCategoryName:              testdata.MockCategoryName,
 						ControlCategoryCatalogId:         testdata.MockCatalogID,
 						TargetOfEvaluationCloudServiceId: testdata.MockCloudServiceID,
@@ -679,7 +680,7 @@ func TestService_ListControlsInScope(t *testing.T) {
 						MonitoringStatus:                 orchestrator.MonitoringStatus_MONITORING_STATUS_AUTOMATICALLY_MONITORED,
 					},
 					{
-						ControlId:                        testdata.MockSubControlID,
+						ControlId:                        testdata.MockSubControlID11,
 						ControlCategoryName:              testdata.MockCategoryName,
 						ControlCategoryCatalogId:         testdata.MockCatalogID,
 						TargetOfEvaluationCloudServiceId: testdata.MockCloudServiceID,
@@ -1114,7 +1115,7 @@ func Test_getControls(t *testing.T) {
 		{
 			name: "List of assurance levels unspecified",
 			args: args{
-				controls:        orchestratortest.MockControls,
+				controls:        orchestratortest.MockControlsInScope,
 				assuranceLevels: []string{},
 			},
 			want: []*orchestrator.Control{},
@@ -1125,7 +1126,7 @@ func Test_getControls(t *testing.T) {
 		{
 			name: "Assurance level unspecified",
 			args: args{
-				controls:        orchestratortest.MockControls,
+				controls:        orchestratortest.MockControlsInScope,
 				level:           "",
 				assuranceLevels: []string{testdata.AssuranceLevelBasic, testdata.AssuranceLevelSubstantial, testdata.AssuranceLevelHigh},
 			},
@@ -1137,31 +1138,31 @@ func Test_getControls(t *testing.T) {
 		{
 			name: "Happy path with assurance level basic",
 			args: args{
-				controls:        orchestratortest.MockControls,
+				controls:        orchestratortest.MockControlsInScope,
 				level:           testdata.AssuranceLevelBasic,
 				assuranceLevels: []string{testdata.AssuranceLevelBasic, testdata.AssuranceLevelSubstantial, testdata.AssuranceLevelHigh},
 			},
-			want:    []*orchestrator.Control{orchestratortest.MockControl2},
+			want:    []*orchestrator.Control{orchestratortest.MockControlsInScope1, orchestratortest.MockControlsInScopeSubControl1, orchestratortest.MockControlsInScope2, orchestratortest.MockControlsInScopeSubControl2},
 			wantErr: assert.NoError,
 		},
 		{
 			name: "Happy path with assurance level substantial",
 			args: args{
-				controls:        orchestratortest.MockControls,
+				controls:        orchestratortest.MockControlsInScope,
 				level:           testdata.AssuranceLevelSubstantial,
 				assuranceLevels: []string{testdata.AssuranceLevelBasic, testdata.AssuranceLevelSubstantial, testdata.AssuranceLevelHigh},
 			},
-			want:    []*orchestrator.Control{orchestratortest.MockControl2, orchestratortest.MockControl3},
+			want:    []*orchestrator.Control{orchestratortest.MockControlsInScope1, orchestratortest.MockControlsInScopeSubControl1, orchestratortest.MockControlsInScope2, orchestratortest.MockControlsInScopeSubControl2, orchestratortest.MockControlsInScope3, orchestratortest.MockControlsInScopeSubControl3, orchestratortest.MockControlsInScopeSubControl4},
 			wantErr: assert.NoError,
 		},
 		{
 			name: "Happy path with assurance level high",
 			args: args{
-				controls:        orchestratortest.MockControls,
+				controls:        orchestratortest.MockControlsInScope,
 				level:           testdata.AssuranceLevelHigh,
 				assuranceLevels: []string{testdata.AssuranceLevelBasic, testdata.AssuranceLevelSubstantial, testdata.AssuranceLevelHigh},
 			},
-			want:    []*orchestrator.Control{orchestratortest.MockControl2, orchestratortest.MockControl3, orchestratortest.MockControl4},
+			want:    []*orchestrator.Control{orchestratortest.MockControlsInScope1, orchestratortest.MockControlsInScopeSubControl1, orchestratortest.MockControlsInScope2, orchestratortest.MockControlsInScopeSubControl2, orchestratortest.MockControlsInScope3, orchestratortest.MockControlsInScopeSubControl3, orchestratortest.MockControlsInScope4, orchestratortest.MockControlsInScopeSubControl4},
 			wantErr: assert.NoError,
 		},
 	}
@@ -1169,6 +1170,14 @@ func Test_getControls(t *testing.T) {
 		t.Run(tt.name, func(t *testing.T) {
 			got, err := getControls(tt.args.controls, tt.args.assuranceLevels, tt.args.level)
 
+			// Sort slices
+			sort.Slice(tt.want, func(i, j int) bool {
+				return tt.want[i].GetId() > tt.want[j].GetId()
+			})
+			sort.Slice(got, func(i, j int) bool {
+				return got[i].GetId() > got[j].GetId()
+			})
+
 			assert.Equal(t, tt.want, got)
 			tt.wantErr(t, err)
 		})