Skip to content

Commit 538824d

Browse files
author
Marco Franceschi
committed
chore: Updated unit tests for RulesEngine and Evaluators
1 parent 659e5b0 commit 538824d

File tree

9 files changed

+247
-131
lines changed

9 files changed

+247
-131
lines changed

src/rules-engine/evaluators/js-evaluator.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ export default class JsEvaluator implements RuleEvaluator<JsRule> {
4343
typename: data.resource?.__typename, // eslint-disable-line no-underscore-dangle
4444
rule: ruleMetadata,
4545
} as RuleFinding
46-
46+
this.findings.push(finding)
4747
return finding
4848
}
4949

src/rules-engine/evaluators/json-evaluator.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,6 @@ export default class JsonEvaluator implements RuleEvaluator<JsonRule> {
4848
rule: ruleMetadata,
4949
} as RuleFinding
5050
this.findings.push(finding)
51-
5251
return finding
5352
}
5453

src/rules-engine/types.ts

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -90,17 +90,9 @@ export interface Engine {
9090
*/
9191
processRule: (rule: Rule, data: any) => Promise<RuleFinding[]>
9292

93-
/**
94-
* Process findings before convert them in mutations
95-
* @param findings resulted findings during rules execution
96-
* @returns {ProcessedFindings} processed findings object
97-
*/
98-
// processFindings: (findings: RuleFinding[]) => ProcessedFindings
99-
10093
/**
10194
* Transforms RuleFinding array into a mutation array for GraphQL
102-
* @param processedFindings resulted findings for automated rules
103-
* @param manualFindings resulted findings for manual rules
95+
* @param findings resulted findings during rules execution
10496
* @returns {Entity[]} Array of generated mutations
10597
*/
10698
prepareMutations: (findings: RuleFinding[]) => Entity[]

tests/evaluators/js-evaluator.test.ts

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
import cuid from 'cuid'
2+
import { Result } from '../../src'
3+
import JsEvaluator from '../../src/rules-engine/evaluators/js-evaluator'
4+
import { Severity } from '../../src/rules-engine/types'
5+
6+
const providerName = 'azure'
7+
const entityName = 'CIS'
8+
const jsRule = {
9+
id: cuid(),
10+
description: 'none',
11+
title: 'Mocked Automated Rule',
12+
rationale: "raison d'être",
13+
audit: 'evaluate schemaA',
14+
remediation: 'fix the schemaA',
15+
references: [],
16+
gql: `{
17+
querySchemaA {
18+
id
19+
__typename
20+
value
21+
}
22+
}`,
23+
resource: 'querySchemaA[*]',
24+
check: jest.fn(),
25+
severity: Severity.HIGH,
26+
}
27+
28+
export default {
29+
jsRule,
30+
}
31+
32+
describe('JsEvaluator', () => {
33+
let evaluator
34+
beforeEach(() => {
35+
evaluator = new JsEvaluator(providerName, entityName)
36+
})
37+
38+
it('should accept all rules that have a check field', () => {
39+
expect(evaluator.canEvaluate({} as never)).toBe(false)
40+
expect(evaluator.canEvaluate({ checks: 1 } as never)).toBe(false)
41+
42+
// we could improve these, but following tests will pass
43+
expect(evaluator.canEvaluate({ check: 1 } as never)).toBe(true)
44+
expect(evaluator.canEvaluate({ check: 0 } as never)).toBe(true)
45+
expect(evaluator.canEvaluate({ check: () => 1 } as never)).toBe(true)
46+
})
47+
48+
it('should call check with data', () => {
49+
const spy = jest.fn()
50+
const data = 'asdf'
51+
evaluator.evaluateSingleResource({ check: spy } as never, data as never)
52+
53+
expect(spy).toHaveBeenCalledWith(data)
54+
})
55+
56+
it('should return fail if rule is true', async () => {
57+
const spy = jest.fn()
58+
spy.mockReturnValue(false)
59+
const failedRule = await evaluator.evaluateSingleResource(
60+
{ check: spy } as never,
61+
{ resource: { id: cuid() } } as never
62+
)
63+
expect(failedRule.result).toEqual(Result.FAIL)
64+
spy.mockReturnValue(true)
65+
const passedRule = await evaluator.evaluateSingleResource(
66+
{ check: spy } as never,
67+
{ resource: { id: cuid() } } as never
68+
)
69+
expect(passedRule.result).toEqual(Result.PASS)
70+
})
71+
72+
it('should return a processed rules mutations array', async () => {
73+
const resourceId = cuid()
74+
const resourceType = 'schemaA'
75+
await evaluator.evaluateSingleResource(jsRule, {
76+
resource: {
77+
id: resourceId,
78+
__typename: resourceType,
79+
value: 'automated',
80+
},
81+
})
82+
const mutations = evaluator.prepareMutations()
83+
const [atuomatedMutation] = mutations
84+
85+
expect(mutations.length).toBe(1)
86+
expect(atuomatedMutation).toBeDefined()
87+
expect(atuomatedMutation.data instanceof Object).toBeTruthy()
88+
expect(atuomatedMutation.data.filter.id.eq).toBe(resourceId)
89+
expect(atuomatedMutation.name).toBe(`${providerName}${entityName}Findings`)
90+
expect(atuomatedMutation.mutation).toContain(`update${resourceType}`)
91+
})
92+
})

tests/json-evaluator.test.ts renamed to tests/evaluators/json-evaluator.test.ts

Lines changed: 71 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,79 @@
11
import cuid from 'cuid'
2-
import { Result } from '../src'
3-
import JsonEvaluator from '../src/rules-engine/evaluators/json-evaluator'
2+
import { Result } from '../../src'
3+
import JsonEvaluator from '../../src/rules-engine/evaluators/json-evaluator'
4+
import { Severity } from '../../src/rules-engine/types'
5+
6+
const providerName = 'aws'
7+
const entityName = 'CIS'
8+
const ruleMetadata = {
9+
id: cuid(),
10+
description: 'none',
11+
title: 'Mocked Automated Rule',
12+
rationale: "raison d'être",
13+
audit: 'evaluate schemaA',
14+
remediation: 'fix the schemaA',
15+
references: [],
16+
severity: Severity.HIGH,
17+
}
18+
const jsonRule = {
19+
...ruleMetadata,
20+
gql: `{
21+
querySchemaA {
22+
id
23+
__typename
24+
value
25+
}
26+
}`,
27+
resource: 'querySchemaA[*]',
28+
conditions: {
29+
path: '@.value',
30+
equal: false,
31+
},
32+
}
33+
34+
const compositeRule = {
35+
...ruleMetadata,
36+
queries: [
37+
{
38+
gql: `{
39+
querySchemaA {
40+
id
41+
__typename
42+
value
43+
}
44+
}`,
45+
resource: 'querySchemaA[*]',
46+
conditions: {
47+
path: '@.value',
48+
equal: false,
49+
},
50+
},
51+
{
52+
gql: `{
53+
querySchemaB {
54+
id
55+
__typename
56+
value
57+
}
58+
}`,
59+
resource: 'querySchemaB[*]',
60+
conditions: {
61+
path: '@.value',
62+
equal: true,
63+
},
64+
},
65+
],
66+
}
67+
68+
export default {
69+
jsonRule,
70+
compositeRule,
71+
}
472

573
describe('JsonEvaluator', () => {
674
let evaluator
775
beforeEach(() => {
8-
evaluator = new JsonEvaluator()
76+
evaluator = new JsonEvaluator(providerName, entityName)
977
})
1078

1179
test('should accept all rules that have a conditions field', () => {

tests/manual-evaluator.test.ts renamed to tests/evaluators/manual-evaluator.test.ts

Lines changed: 35 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,32 @@
11
import cuid from 'cuid'
2-
import { Result } from '../src'
3-
import ManualEvaluator from '../src/rules-engine/evaluators/manual-evaluator'
2+
import { Result } from '../../src'
3+
import ManualEvaluator from '../../src/rules-engine/evaluators/manual-evaluator'
4+
import { Severity } from '../../src/rules-engine/types'
5+
6+
const providerName = 'gcp'
7+
const entityName = 'CIS'
8+
const manualRule = {
9+
id: cuid(),
10+
description: 'none',
11+
title: 'Mocked Manual Rule',
12+
rationale: 'Ikigai',
13+
audit: 'evaluate schemaA',
14+
remediation: 'fix the schemaA',
15+
references: [],
16+
severity: Severity.HIGH,
17+
}
18+
export default {
19+
manualRule,
20+
}
421

522
describe('ManualEvaluator', () => {
623
let evaluator
7-
beforeAll(() => {
8-
evaluator = new ManualEvaluator()
24+
beforeEach(() => {
25+
evaluator = new ManualEvaluator(providerName, entityName)
926
})
1027

1128
it('should pass when it does not contain gql, conditions, check, and resource fields', () => {
12-
expect(
13-
evaluator.canEvaluate({
14-
id: '',
15-
description: '',
16-
rationale: '',
17-
autid: '',
18-
remediation: '',
19-
references: [],
20-
severity: '',
21-
} as never)
22-
).toBe(true)
29+
expect(evaluator.canEvaluate(manualRule)).toBe(true)
2330
})
2431

2532
it('should fail when it contains gql, conditions, check, or resource fields', () => {
@@ -50,4 +57,17 @@ describe('ManualEvaluator', () => {
5057
)
5158
expect(finding.id.includes('manual')).toEqual(true)
5259
})
60+
61+
it('should return a manual mutations array', async () => {
62+
await evaluator.evaluateSingleResource(manualRule)
63+
const mutations = evaluator.prepareMutations()
64+
const [manualMutation] = mutations
65+
66+
expect(mutations.length).toBe(1)
67+
expect(manualMutation.name).toBe(`${providerName}${entityName}Findings`)
68+
expect(manualMutation.data.length).toBe(1)
69+
expect(manualMutation.mutation).toContain(
70+
`add${providerName}${entityName}Findings`
71+
)
72+
})
5373
})

tests/js-evaluator.test.ts

Lines changed: 0 additions & 43 deletions
This file was deleted.

tests/operators/date.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import JsonEvaluator from '../../src/rules-engine/evaluators/json-evaluator'
55
describe('Date Operators', () => {
66
let evaluator
77
beforeEach(() => {
8-
evaluator = new JsonEvaluator()
8+
evaluator = new JsonEvaluator('aws', 'CIS')
99
})
1010

1111
test('should process dates', async () => {

0 commit comments

Comments
 (0)