Skip to content

Commit 123064f

Browse files
committed
feat: add AddEvaluationSuiteForAllCatalogs to register suites for all loaded catalogs
Adds a new method to EvaluationOrchestrator that registers evaluation suites for every reference catalog loaded via AddReferenceCatalogs. This eliminates the need for plugins to re-parse catalog files to extract IDs and loop over them individually. Fixes #181 Signed-off-by: Vinaya Damle <vinayada1@users.noreply.github.com>
1 parent 96b9e91 commit 123064f

File tree

3 files changed

+101
-0
lines changed

3 files changed

+101
-0
lines changed

pluginkit/evaluation_orchestrator.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,20 @@ func (v *EvaluationOrchestrator) AddEvaluationSuite(catalogId string, loader Dat
102102
return BAD_CATALOG(v.PluginName, fmt.Sprintf("no reference catalog found with id '%s'", catalogId), "aos40")
103103
}
104104

105+
// AddEvaluationSuiteForAllCatalogs registers the provided steps for every
106+
// reference catalog that has been loaded via AddReferenceCatalogs.
107+
// This allows plugin developers to define their step implementations once and
108+
// have them automatically applied to all catalog versions.
109+
func (v *EvaluationOrchestrator) AddEvaluationSuiteForAllCatalogs(loader DataLoader, steps map[string][]gemara.AssessmentStep) error {
110+
if len(v.referenceCatalogs) == 0 {
111+
return BAD_CATALOG(v.PluginName, "no reference catalogs loaded", "aac10")
112+
}
113+
for _, catalog := range v.referenceCatalogs {
114+
v.addEvaluationSuite(catalog, loader, steps)
115+
}
116+
return nil
117+
}
118+
105119
func (v *EvaluationOrchestrator) addEvaluationSuite(catalog *gemara.ControlCatalog, loader DataLoader, steps map[string][]gemara.AssessmentStep) {
106120
importedControls := getImportedControls(catalog, v.referenceCatalogs)
107121
catalog.Controls = append(catalog.Controls, importedControls...)

pluginkit/evaluation_orchestrator_test.go

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,86 @@ func TestEvaluationOrchestrator_AddEvaluationSuite(t *testing.T) {
116116
})
117117
}
118118

119+
func TestEvaluationOrchestrator_AddEvaluationSuiteForAllCatalogs(t *testing.T) {
120+
t.Run("Error Without Reference Catalogs", func(t *testing.T) {
121+
orchestrator := &EvaluationOrchestrator{}
122+
steps := map[string][]gemara.AssessmentStep{
123+
"test-requirement": {step_Pass},
124+
}
125+
126+
err := orchestrator.AddEvaluationSuiteForAllCatalogs(nil, steps)
127+
if err == nil {
128+
t.Error("Expected error when no reference catalogs are loaded")
129+
}
130+
if !strings.Contains(err.Error(), "no reference catalogs loaded") {
131+
t.Errorf("Expected 'no reference catalogs loaded' error, got: %v", err)
132+
}
133+
})
134+
135+
t.Run("Registers Suite For Each Catalog", func(t *testing.T) {
136+
orchestrator := &EvaluationOrchestrator{}
137+
catalog1 := getTestCatalogWithID("CCC.ObjStor")
138+
catalog2 := getTestCatalogWithID("CCC.ObjStor-v2")
139+
orchestrator.referenceCatalogs = map[string]*gemara.ControlCatalog{
140+
catalog1.Metadata.Id: catalog1,
141+
catalog2.Metadata.Id: catalog2,
142+
}
143+
144+
steps := map[string][]gemara.AssessmentStep{
145+
"CCC.Core.C01.TR01": {step_Pass},
146+
}
147+
148+
err := orchestrator.AddEvaluationSuiteForAllCatalogs(nil, steps)
149+
if err != nil {
150+
t.Fatalf("Unexpected error: %v", err)
151+
}
152+
153+
if len(orchestrator.possibleSuites) != 2 {
154+
t.Errorf("Expected 2 suites (one per catalog), got %d",
155+
len(orchestrator.possibleSuites))
156+
}
157+
158+
// Verify each catalog got its own suite
159+
suiteIDs := make(map[string]bool)
160+
for _, suite := range orchestrator.possibleSuites {
161+
suiteIDs[suite.CatalogId] = true
162+
}
163+
if !suiteIDs["CCC.ObjStor"] || !suiteIDs["CCC.ObjStor-v2"] {
164+
t.Errorf("Expected suites for both catalogs, got: %v", suiteIDs)
165+
}
166+
})
167+
168+
t.Run("Uses Provided Loader", func(t *testing.T) {
169+
orchestrator := &EvaluationOrchestrator{}
170+
catalog := getTestCatalogWithRequirements()
171+
orchestrator.referenceCatalogs = map[string]*gemara.ControlCatalog{
172+
catalog.Metadata.Id: catalog,
173+
}
174+
175+
testLoader := func(cfg *config.Config) (interface{}, error) {
176+
return "suite-specific-payload", nil
177+
}
178+
steps := map[string][]gemara.AssessmentStep{
179+
"CCC.Core.C01.TR01": {step_Pass},
180+
}
181+
182+
err := orchestrator.AddEvaluationSuiteForAllCatalogs(testLoader, steps)
183+
if err != nil {
184+
t.Fatalf("Unexpected error: %v", err)
185+
}
186+
187+
if len(orchestrator.possibleSuites) != 1 {
188+
t.Errorf("Expected 1 suite for single catalog, got %d", len(orchestrator.possibleSuites))
189+
}
190+
191+
for _, suite := range orchestrator.possibleSuites {
192+
if suite.loader == nil {
193+
t.Error("Expected suite loader to be set")
194+
}
195+
}
196+
})
197+
}
198+
119199
func TestEvaluationOrchestrator_Integration(t *testing.T) {
120200
t.Run("Basic Setup", func(t *testing.T) {
121201
orchestrator := &EvaluationOrchestrator{

pluginkit/test_data.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,13 @@ func getTestCatalogWithNoRequirements() *gemara.ControlCatalog {
3636
}
3737
}
3838

39+
// getTestCatalogWithID returns a valid catalog with the given ID for tests that need multiple distinct catalogs.
40+
func getTestCatalogWithID(id string) *gemara.ControlCatalog {
41+
catalog := getTestCatalogWithRequirements()
42+
catalog.Metadata.Id = id
43+
return catalog
44+
}
45+
3946
// getTestCatalogWithRequirements returns a catalog with controls and assessment requirements for tests that need a valid catalog.
4047
func getTestCatalogWithRequirements() *gemara.ControlCatalog {
4148
return &gemara.ControlCatalog{

0 commit comments

Comments
 (0)