Skip to content

Commit 3873e33

Browse files
authored
Restore core/framework coverage gate with validation tests (#7)
1 parent ae0b59e commit 3873e33

File tree

2 files changed

+128
-0
lines changed

2 files changed

+128
-0
lines changed

core/framework/framework.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,10 @@ func Load(idOrFile string) (*Framework, error) {
7474
if err != nil {
7575
return nil, fmt.Errorf("load framework %s: %w", idOrFile, err)
7676
}
77+
return parseFramework(idOrFile, raw)
78+
}
79+
80+
func parseFramework(idOrFile string, raw []byte) (*Framework, error) {
7781
var f Framework
7882
if err := yaml.Unmarshal(raw, &f); err != nil {
7983
return nil, err

core/framework/framework_test.go

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,3 +72,127 @@ func TestFrameworkCopiesStayInSync(t *testing.T) {
7272
require.Equalf(t, string(repoRaw), string(coreRaw), "framework copy mismatch for %s", entry.Name())
7373
}
7474
}
75+
76+
func TestParseFrameworkBranches(t *testing.T) {
77+
_, err := parseFramework("bad-yaml", []byte("framework: ["))
78+
require.Error(t, err)
79+
80+
_, err = parseFramework("missing-id", []byte(`
81+
framework:
82+
version: "1"
83+
title: Missing ID
84+
controls:
85+
- id: c1
86+
title: Control
87+
required_record_types: [decision]
88+
required_fields: [record_id]
89+
minimum_frequency: continuous
90+
`))
91+
require.ErrorContains(t, err, "missing id")
92+
93+
_, err = parseFramework("missing-controls", []byte(`
94+
framework:
95+
id: test
96+
version: "1"
97+
title: Missing Controls
98+
controls: []
99+
`))
100+
require.ErrorContains(t, err, "has no controls")
101+
}
102+
103+
func TestValidateControlsErrors(t *testing.T) {
104+
cases := []struct {
105+
name string
106+
in []Control
107+
needle string
108+
}{
109+
{
110+
name: "missing id",
111+
in: []Control{{
112+
Title: "Control",
113+
RequiredRecordTypes: []string{"decision"},
114+
MinimumFrequency: "continuous",
115+
RequiredFields: []string{"record_id"},
116+
}},
117+
needle: "missing id",
118+
},
119+
{
120+
name: "missing title",
121+
in: []Control{{
122+
ID: "c1",
123+
RequiredRecordTypes: []string{"decision"},
124+
MinimumFrequency: "continuous",
125+
RequiredFields: []string{"record_id"},
126+
}},
127+
needle: "missing title",
128+
},
129+
{
130+
name: "missing required_record_types",
131+
in: []Control{{
132+
ID: "c1",
133+
Title: "Control",
134+
MinimumFrequency: "continuous",
135+
RequiredFields: []string{"record_id"},
136+
}},
137+
needle: "missing required_record_types",
138+
},
139+
{
140+
name: "missing minimum_frequency",
141+
in: []Control{{
142+
ID: "c1",
143+
Title: "Control",
144+
RequiredRecordTypes: []string{"decision"},
145+
RequiredFields: []string{"record_id"},
146+
}},
147+
needle: "missing minimum_frequency",
148+
},
149+
{
150+
name: "blank required_record_types entry",
151+
in: []Control{{
152+
ID: "c1",
153+
Title: "Control",
154+
RequiredRecordTypes: []string{"decision", " "},
155+
MinimumFrequency: "continuous",
156+
RequiredFields: []string{"record_id"},
157+
}},
158+
needle: "blank required_record_types entry",
159+
},
160+
{
161+
name: "blank required_fields entry",
162+
in: []Control{{
163+
ID: "c1",
164+
Title: "Control",
165+
RequiredRecordTypes: []string{"decision"},
166+
MinimumFrequency: "continuous",
167+
RequiredFields: []string{"record_id", ""},
168+
}},
169+
needle: "blank required_fields entry",
170+
},
171+
{
172+
name: "invalid child control",
173+
in: []Control{{
174+
ID: "c1",
175+
Title: "Control",
176+
RequiredRecordTypes: []string{"decision"},
177+
MinimumFrequency: "continuous",
178+
RequiredFields: []string{"record_id"},
179+
Children: []Control{{
180+
ID: "child",
181+
Title: "Child",
182+
MinimumFrequency: "continuous",
183+
RequiredFields: []string{"record_id"},
184+
}},
185+
}},
186+
needle: "missing required_record_types",
187+
},
188+
}
189+
190+
for _, tc := range cases {
191+
tc := tc
192+
t.Run(tc.name, func(t *testing.T) {
193+
err := validateControls(tc.in, "controls")
194+
require.Error(t, err)
195+
require.ErrorContains(t, err, tc.needle)
196+
})
197+
}
198+
}

0 commit comments

Comments
 (0)