Is this even possible?
Hello friends. I'm in the process of building an application using AWS AppSync + DynamoDB and I'm starting to have quite a large pile of resolver mapping templates, all which are written using the Apache Velocity Template Language (VTL).
The concern I'm starting to have is that these vtl files are quite critical to the application (since they define how data is retrieved) and a bug in one of the could wreak havoc. So like any critical part of a system... I would like to write some automated unit tests for them. But I haven't found much about others doing this.
Thanks in advance!
It took me a while to figure this out myself, but I found a good way to write unit tests for my VTL request and response templates. I used the amplify-appsync-simulator
npm package's VelocityTemplate class. The only caveat I have seen so far is that you need to use $context
in your VTL, the abbreviated $ctx
is not recognized by the simulators VTL renderer. Check it out:
My VTL:
#set( $timeNow = $util.time.nowEpochMilliSeconds() )
{
"version" : "2018-05-29",
"operation" : "PutItem",
"key": {
"pk" : { "S" : "game#${util.autoId()}" },
"sk" : { "S" : "meta#${timeNow}" }
},
"attributeValues" : {
"players": { "L" : [
{ "M" : {
## num and color added at start game
"player": $util.dynamodb.toDynamoDBJson($context.args.input.player)
}}
]},
"createdBy": { "S": "${context.identity.sub}"},
"gsipk": { "S": "${context.args.input.status}"},
"gsisk": { "S": "${context.args.input.level}#${context.args.input.maxPlayers}"},
"gsipk2": {"S": "game"},
"turns": { "L": [] },
"nextPlayerNum": { "N": 1 },
"createdAt": { "N": ${timeNow} },
"updatedAt": { "N": ${timeNow} }
}
}
My test:
import { AmplifyAppSyncSimulator } from 'amplify-appsync-simulator'
import { VelocityTemplate } from "amplify-appsync-simulator/lib/velocity"
import { readFileSync } from 'fs'
import path from 'path';
const vtl = readFileSync(path.join(__dirname, '..', 'addGame-request-mapping-template.vtl'))
const template = {
content: vtl
}
const velocity = new VelocityTemplate(template, new AmplifyAppSyncSimulator)
describe('valid user and request', () => {
// This is the graphql input
const validContext = {
arguments: {
input: {
player: 'player#1234',
maxPlayers: 4,
status: 'new',
level: 7
}
},
source: {}
}
// This is a logged in user with a JWT
const requestContext = {
requestAuthorizationMode: 'OPENID_CONNECT',
jwt: {
sub: 'abcd1234'
}
}
const info = {
fieldNodes: []
}
it('works', () => {
const result = velocity.render(validContext, requestContext, info)
expect(result).toEqual({
result: {
version: "2018-05-29",
operation: "PutItem",
key: {
pk: { S: expect.stringMatching(/^game#[a-f0-9-]*$/) },
sk: { S: expect.stringMatching(/^meta#[0-9]*$/)}
},
attributeValues: {
players: {
L: [
{ M: { player: { S: validContext.arguments.input.player }}}
]
},
createdBy: { S: requestContext.jwt.sub },
gsipk: { S: validContext.arguments.input.status },
gsisk: { S: `${validContext.arguments.input.level}#${validContext.arguments.input.maxPlayers}`},
gsipk2: { S: 'game' },
turns: { L: [] },
nextPlayerNum: { N: 1 },
createdAt: { N: expect.any(Number) },
updatedAt: { N: expect.any(Number) }
}
},
stash: {},
errors: [],
isReturn: false
})
})
})
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With