-
Notifications
You must be signed in to change notification settings - Fork 221
Expand file tree
/
Copy pathschema.test.js
More file actions
76 lines (69 loc) · 3.7 KB
/
Copy pathschema.test.js
File metadata and controls
76 lines (69 loc) · 3.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
/* eslint-disable no-undef */
const fs = require('fs')
const path = require('path')
// The generated schemas declare JSON Schema draft 2020-12, where OpenAPI 3.0's
// `nullable` keyword does not exist. The build consumes GitHub's OpenAPI 3.1
// description, which models null-ability as `type: [X, 'null']` unions instead,
// so no `nullable` keyword may survive in the output.
describe('dereferenced schemas', () => {
const files = ['settings.json', 'suborgs.json', 'repos.json']
files.forEach(file => {
describe(file, () => {
const schema = JSON.parse(fs.readFileSync(path.join(__dirname, '../../schema/dereferenced', file), 'utf8'))
it('contains no OpenAPI `nullable` keywords', () => {
const found = []
const walk = (node, at) => {
if (Array.isArray(node)) {
node.forEach((v, i) => walk(v, `${at}/${i}`))
} else if (node && typeof node === 'object') {
if ('nullable' in node) {
found.push(at)
}
Object.entries(node).forEach(([k, v]) => walk(v, `${at}/${k}`))
}
}
walk(schema, '')
expect(found).toEqual([])
})
})
})
it('allows null for required-but-nullable branch protection fields', () => {
const schema = JSON.parse(fs.readFileSync(path.join(__dirname, '../../schema/dereferenced/settings.json'), 'utf8'))
const protection = schema.properties.branches.items.properties.protection
const protectionObject = protection.anyOf.find(variant => variant.type === 'object')
expect(protectionObject.properties.required_status_checks.type).toContain('null')
expect(protectionObject.properties.enforce_admins.type).toContain('null')
expect(protectionObject.properties.restrictions.type).toContain('null')
})
// Suborg and repo override files feed the rulesets plugin at repo scope
// (childPluginsList in lib/settings.js), so their schemas validate rulesets
// against the repo-level API shape, not the org-level one.
it('validates per-repo rulesets in the suborg and repo schemas', () => {
;['suborgs.json', 'repos.json'].forEach(file => {
const schema = JSON.parse(fs.readFileSync(path.join(__dirname, '../../schema/dereferenced', file), 'utf8'))
const ruleset = schema.properties.rulesets.items
// `required` is an unordered set in JSON Schema
expect(ruleset.required).toHaveLength(2)
expect(ruleset.required).toEqual(expect.arrayContaining(['name', 'enforcement']))
// org-only condition targeting must not leak into the repo-level shape
expect(JSON.stringify(ruleset.properties.conditions)).not.toContain('repository_name')
const actorTypes = ruleset.properties.bypass_actors.items.properties.actor_type.enum
expect(actorTypes).toContain('User')
})
})
// An empty `protection` value is safe-settings' own delete-branch-protection
// semantic (see isEmpty in lib/plugins/branches.js, and the plugin tests for
// null/{}/[]/false); the GitHub API's PUT body it is validated against does
// not model that, so the schema allows the empty values explicitly.
it('allows the empty protection values that delete branch protection', () => {
files.forEach(file => {
const schema = JSON.parse(fs.readFileSync(path.join(__dirname, '../../schema/dereferenced', file), 'utf8'))
const protection = schema.properties.branches.items.properties.protection
// enum ordering is not semantically meaningful, so assert membership
const emptyVariant = protection.anyOf.find(variant => Array.isArray(variant.enum))
expect(emptyVariant).toBeDefined()
expect(emptyVariant.enum).toHaveLength(4)
expect(emptyVariant.enum).toEqual(expect.arrayContaining([null, {}, [], false]))
})
})
})