Skip to content

Commit 384021d

Browse files
committed
create a re-usable job definition in schema.json + include validations for release, no-op, approval, lock and unlock job types
1 parent ba29b49 commit 384021d

File tree

2 files changed

+758
-821
lines changed

2 files changed

+758
-821
lines changed

pkg/parser/jsonschema_test.go

Lines changed: 259 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package parser
22

33
import (
4+
"strings"
45
"testing"
56

67
"github.com/CircleCI-Public/circleci-yaml-language-server/pkg/expect"
@@ -72,3 +73,261 @@ test:
7273

7374
expect.DiagnosticList(t, diagnostics).To.Include(expected)
7475
}
76+
77+
func Test_JobDefinitionTypes(t *testing.T) {
78+
testCases := []struct {
79+
name string
80+
yaml string
81+
expectError bool
82+
expectErrorContains string
83+
}{
84+
// Build type tests
85+
{
86+
name: "build type - valid with docker and steps",
87+
yaml: `
88+
version: 2.1
89+
jobs:
90+
my-build-job:
91+
type: build
92+
docker:
93+
- image: cimg/base:2023.01
94+
steps:
95+
- checkout
96+
`,
97+
expectError: false,
98+
},
99+
{
100+
name: "build type - valid with explicit type",
101+
yaml: `
102+
version: 2.1
103+
jobs:
104+
my-build-job:
105+
type: build
106+
docker:
107+
- image: cimg/base:2023.01
108+
steps:
109+
- checkout
110+
- run: echo "build job with explicit type"
111+
`,
112+
expectError: false,
113+
},
114+
{
115+
name: "build type - missing steps (should error)",
116+
yaml: `
117+
version: 2.1
118+
jobs:
119+
my-build-job:
120+
type: build
121+
docker:
122+
- image: cimg/base:2023.01
123+
`,
124+
expectError: true,
125+
expectErrorContains: "steps",
126+
},
127+
128+
// Release type tests
129+
{
130+
name: "release type - valid with plan_name",
131+
yaml: `
132+
version: 2.1
133+
jobs:
134+
my-release-job:
135+
type: release
136+
plan_name: my-release-plan
137+
`,
138+
expectError: false,
139+
},
140+
{
141+
name: "release type - valid with additional properties",
142+
yaml: `
143+
version: 2.1
144+
jobs:
145+
my-release-job:
146+
type: release
147+
plan_name: my-plan
148+
some_other_property: allowed
149+
`,
150+
expectError: false,
151+
},
152+
{
153+
name: "release type - missing plan_name (should error)",
154+
yaml: `
155+
version: 2.1
156+
jobs:
157+
my-release-job:
158+
type: release
159+
`,
160+
expectError: true,
161+
expectErrorContains: "plan_name",
162+
},
163+
164+
// Lock type tests
165+
{
166+
name: "lock type - valid with key",
167+
yaml: `
168+
version: 2.1
169+
jobs:
170+
my-lock-job:
171+
type: lock
172+
key: my-lock-key
173+
`,
174+
expectError: false,
175+
},
176+
{
177+
name: "lock type - valid with additional properties",
178+
yaml: `
179+
version: 2.1
180+
jobs:
181+
my-lock-job:
182+
type: lock
183+
key: my-key
184+
some_other_property: allowed
185+
`,
186+
expectError: false,
187+
},
188+
{
189+
name: "lock type - missing key (should error)",
190+
yaml: `
191+
version: 2.1
192+
jobs:
193+
my-lock-job:
194+
type: lock
195+
`,
196+
expectError: true,
197+
expectErrorContains: "key",
198+
},
199+
200+
// Unlock type tests
201+
{
202+
name: "unlock type - valid with key",
203+
yaml: `
204+
version: 2.1
205+
jobs:
206+
my-unlock-job:
207+
type: unlock
208+
key: my-lock-key
209+
`,
210+
expectError: false,
211+
},
212+
{
213+
name: "unlock type - valid with additional properties",
214+
yaml: `
215+
version: 2.1
216+
jobs:
217+
my-unlock-job:
218+
type: unlock
219+
key: my-key
220+
some_other_property: allowed
221+
`,
222+
expectError: false,
223+
},
224+
{
225+
name: "unlock type - missing key (should error)",
226+
yaml: `
227+
version: 2.1
228+
jobs:
229+
my-unlock-job:
230+
type: unlock
231+
`,
232+
expectError: true,
233+
expectErrorContains: "key",
234+
},
235+
236+
// Approval type tests
237+
{
238+
name: "approval type - valid minimal",
239+
yaml: `
240+
version: 2.1
241+
jobs:
242+
my-approval-job:
243+
type: approval
244+
`,
245+
expectError: false,
246+
},
247+
{
248+
name: "approval type - valid with steps (ignored)",
249+
yaml: `
250+
version: 2.1
251+
jobs:
252+
my-approval-job:
253+
type: approval
254+
steps:
255+
- run: echo "This will be ignored"
256+
`,
257+
expectError: false,
258+
},
259+
260+
// No-op type tests
261+
{
262+
name: "no-op type - valid minimal",
263+
yaml: `
264+
version: 2.1
265+
jobs:
266+
my-noop-job:
267+
type: no-op
268+
`,
269+
expectError: false,
270+
},
271+
{
272+
name: "no-op type - valid with steps (ignored)",
273+
yaml: `
274+
version: 2.1
275+
jobs:
276+
my-noop-job:
277+
type: no-op
278+
steps:
279+
- run: echo "This will be ignored"
280+
`,
281+
expectError: false,
282+
},
283+
}
284+
285+
for _, tc := range testCases {
286+
t.Run(tc.name, func(t *testing.T) {
287+
context := testHelpers.GetDefaultLsContext()
288+
yamlDocument, _ := ParseFromContent([]byte(tc.yaml), context, uri.File(""), protocol.Position{})
289+
290+
if tc.expectError {
291+
// For error cases, also run JSON schema validation
292+
validator := JSONSchemaValidator{
293+
Doc: yamlDocument,
294+
}
295+
296+
schemaPath := "../../schema.json"
297+
err := validator.LoadJsonSchema(schemaPath)
298+
if err != nil {
299+
t.Logf("Warning: Could not load schema: %v", err)
300+
t.SkipNow()
301+
}
302+
303+
diagnostics := validator.ValidateWithJSONSchema(yamlDocument.RootNode, yamlDocument.Content)
304+
305+
// Log all diagnostics for debugging
306+
if len(diagnostics) > 0 {
307+
t.Logf("Found %d diagnostic(s):", len(diagnostics))
308+
for _, d := range diagnostics {
309+
t.Logf(" - %s", d.Message)
310+
}
311+
} else {
312+
t.Logf("No diagnostics found")
313+
}
314+
315+
assert.NotEmpty(t, diagnostics, "Expected validation errors but got none")
316+
if tc.expectErrorContains != "" {
317+
found := false
318+
for _, d := range diagnostics {
319+
if strings.Contains(strings.ToLower(d.Message), strings.ToLower(tc.expectErrorContains)) {
320+
found = true
321+
break
322+
}
323+
}
324+
assert.True(t, found, "Expected error message to contain '%s'", tc.expectErrorContains)
325+
}
326+
} else {
327+
// For non-error cases, just check that parsing succeeded
328+
diagnostics := yamlDocument.Diagnostics
329+
assert.Empty(t, diagnostics, "Expected no errors but got: %v", diagnostics)
330+
}
331+
})
332+
}
333+
}

0 commit comments

Comments
 (0)