Skip to content

Commit 8ff0737

Browse files
authored
feat(tests): SAM JSON-schema regression tests #2601
Problem: Currently we have no way of testing whether or not any schema changes in the [goformation](https://github.com/awslabs/goformation) project causes regressions in this project. Solution Add an easy way to verify that certain definitions are available in the JSON schema provided by the goformation project. It only deals with verifying definitions, properties and references. closes #2597
1 parent d849ae2 commit 8ff0737

File tree

3 files changed

+167
-0
lines changed

3 files changed

+167
-0
lines changed
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
/*!
2+
* Copyright 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
import {
7+
getCITestSchemas,
8+
JSONObject,
9+
unmarshal,
10+
assertDefinitionProperty,
11+
assertProperty,
12+
assertRef,
13+
assertDefinition,
14+
} from '../../test/shared/schema/testUtils'
15+
16+
describe('Sam Schema Regression', function () {
17+
let samSchema: JSONObject
18+
19+
before(async function () {
20+
;({ samSchema } = await getCITestSchemas())
21+
})
22+
23+
it('has Policy Templates', function () {
24+
const samPolicyTemplate = 'AWS::Serverless::Function.SAMPolicyTemplate'
25+
assertDefinitionProperty(samSchema, samPolicyTemplate, 'S3WritePolicy')
26+
assertDefinitionProperty(samSchema, samPolicyTemplate, 'DynamoDBWritePolicy')
27+
assertDefinitionProperty(samSchema, samPolicyTemplate, 'SSMParameterReadPolicy')
28+
assertDefinitionProperty(samSchema, samPolicyTemplate, 'AWSSecretsManagerGetSecretValuePolicy')
29+
})
30+
31+
it('has property Domain in AWS::Serverless::Api', function () {
32+
const domainLocation = unmarshal(
33+
samSchema,
34+
'definitions',
35+
'AWS::Serverless::Api',
36+
'properties',
37+
'Properties',
38+
'properties'
39+
)
40+
assertProperty(domainLocation, 'Domain')
41+
assertRef(unmarshal(domainLocation, 'Domain'), 'Api.DomainConfiguration')
42+
43+
assertDefinition(samSchema, 'AWS::Serverless::Api.DomainConfiguration')
44+
assertDefinition(samSchema, 'AWS::Serverless::Api.MutualTlsAuthentication')
45+
assertDefinition(samSchema, 'AWS::Serverless::Api.Route53Configuration')
46+
})
47+
48+
it('has Property Version in AWS::Serverless::Function.IAMPolicyDocument and AWS::Serverless::StateMachine.IAMPolicyDocument', function () {
49+
assertDefinitionProperty(samSchema, 'AWS::Serverless::Function.IAMPolicyDocument', 'Version')
50+
assertDefinitionProperty(samSchema, 'AWS::Serverless::StateMachine.IAMPolicyDocument', 'Version')
51+
})
52+
53+
it('has Property RequestModel and RequestParameters in AWS::Serverless::Function.ApiEvent', function () {
54+
assertDefinitionProperty(samSchema, 'AWS::Serverless::Function.ApiEvent', 'RequestModel')
55+
assertDefinitionProperty(samSchema, 'AWS::Serverless::Function.ApiEvent', 'RequestParameters')
56+
assertDefinition(samSchema, 'AWS::Serverless::Function.RequestModel')
57+
assertDefinition(samSchema, 'AWS::Serverless::Function.RequestParameter')
58+
})
59+
})

src/shared/extensions/git.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -364,6 +364,7 @@ export class GitExtension {
364364

365365
return execFileAsync(api.git.path, ['cat-file', type, hash], {
366366
cwd: tmpDir,
367+
maxBuffer: 1024 * 1024 * 5,
367368
}).then(({ stdout }) => stdout)
368369
},
369370
}))
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
/*!
2+
* Copyright 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
import * as assert from 'assert'
7+
import * as path from 'path'
8+
import { GitExtension } from '../../../shared/extensions/git'
9+
10+
export type JSONValue = string | boolean | number | null | JSONValue[] | JSONObject
11+
12+
export interface JSONObject {
13+
[key: string]: JSONValue
14+
}
15+
16+
export interface TestSchemas {
17+
samSchema: JSONObject
18+
cfnSchema: JSONObject
19+
}
20+
21+
export async function getCITestSchemas(): Promise<TestSchemas> {
22+
const fetchUrl = 'https://github.com/awslabs/goformation'
23+
const repo = await GitExtension.instance.listAllRemoteFiles({
24+
fetchUrl,
25+
})
26+
27+
const samFilePath = path.join('schema', 'sam.schema.json')
28+
const samSchemaFile = await repo.files.find(f => f.name === samFilePath)?.read()
29+
if (!samSchemaFile) {
30+
throw new Error(`Unable to find the sam schema file in path ${samFilePath} in repository ${fetchUrl}`)
31+
}
32+
33+
const cfnFilePath = path.join('schema', 'cloudformation.schema.json')
34+
const cfnSchemaFile = await repo.files.find(f => f.name === cfnFilePath)?.read()
35+
if (!cfnSchemaFile) {
36+
throw new Error(
37+
`Unable to find the cloudformation schema file in path ${cfnFilePath} in repository ${fetchUrl}`
38+
)
39+
}
40+
41+
repo.dispose()
42+
43+
const samSchema = JSON.parse(samSchemaFile)
44+
const cfnSchema = JSON.parse(cfnSchemaFile)
45+
return {
46+
samSchema,
47+
cfnSchema,
48+
}
49+
}
50+
51+
/**
52+
* Assert whether or not name exists under definitionName in the JSON schema
53+
* @param schema The JSON schema
54+
* @param definitionName The name of the definition to use
55+
* @param name The name of the property to look for
56+
*/
57+
export function assertDefinitionProperty(schema: JSONObject, definitionName: string, name: string): void | never {
58+
const definitionProperties = unmarshal(schema, 'definitions', definitionName, 'properties')
59+
assertProperty(definitionProperties, name)
60+
}
61+
62+
/**
63+
* Assert whether name exists at an arbitary location in the JSON schema
64+
* @param arbitrarySchemaLocation An arbitary location in the JSON schema
65+
* @param name The name of the property to look for
66+
*/
67+
export function assertProperty(arbitrarySchemaLocation: JSONObject, name: string): void | never {
68+
assert.ok(name in arbitrarySchemaLocation, `Property ${name} was not found in the "Properties" object`)
69+
}
70+
71+
/**
72+
* Assert whether a reference exists at definitionLocation to referenceName in the JSON Schema
73+
* @param definitionLocation A location in the JSON schema
74+
* @param referenceName A name of a reference to look for
75+
*/
76+
export function assertRef(definitionLocation: JSONObject, referenceName: string): void | never {
77+
const definitionRef = definitionLocation['$ref']
78+
if (definitionRef !== `#/definitions/AWS::Serverless::${referenceName}`) {
79+
assert.fail(`The reference for ${definitionRef} did not point to ${referenceName}`)
80+
}
81+
}
82+
83+
/**
84+
* Assert that definitionName is in the JSON schemas definitions
85+
* @param schema The JSON schema to use
86+
* @param definitionName The name of the definition to check
87+
*/
88+
export function assertDefinition(schema: JSONObject, definitionName: string): void | never {
89+
if (!(definitionName in (schema['definitions'] as JSONObject))) {
90+
assert.fail(`Definition for ${definitionName} not found`)
91+
}
92+
}
93+
94+
/**
95+
* Traverse through the initial JSON object, visiting all of the properties.
96+
* Only suitable for accessing JSON objects.
97+
* @param initialObject the object you want to start the traversal at
98+
* @param properties the properties you want to visit and traverse into
99+
* @returns The location in initialObject after visiting all of properties
100+
*/
101+
export function unmarshal(initialObject: JSONObject, ...properties: string[]) {
102+
let processedObject = initialObject
103+
for (const propertyName of properties) {
104+
processedObject = processedObject[propertyName] as JSONObject
105+
}
106+
return processedObject
107+
}

0 commit comments

Comments
 (0)