-
Notifications
You must be signed in to change notification settings - Fork 825
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
test(amplify-appsync-simulator): add resolver tests
Added tests for unit and pipeline resolvers
- Loading branch information
Showing
4 changed files
with
385 additions
and
9 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
202 changes: 202 additions & 0 deletions
202
packages/amplify-appsync-simulator/src/__tests__/resolvers/pipeline-resolver.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,202 @@ | ||
import { AppSyncPipelineResolver } from '../../resolvers/pipeline-resolver'; | ||
import { AmplifyAppSyncSimulator } from '../..'; | ||
import { RESOLVER_KIND, AppSyncSimulatorPipelineResolverConfig } from '../../type-definition'; | ||
describe('Pipeline Resolvers', () => { | ||
const getFunction = jest.fn(); | ||
const getMappingTemplate = jest.fn(); | ||
const simulatorContext: AmplifyAppSyncSimulator = ({ | ||
getFunction, | ||
getMappingTemplate, | ||
} as any) as AmplifyAppSyncSimulator; | ||
let baseConfig; | ||
beforeEach(() => { | ||
jest.resetAllMocks(); | ||
getFunction.mockReturnValue({ resolve: () => 'foo' }); | ||
getMappingTemplate.mockReturnValue('TEMPLATE'); | ||
baseConfig = { | ||
fieldName: 'fn1', | ||
typeName: 'Query', | ||
kind: RESOLVER_KIND.PIPELINE, | ||
functions: ['fn1', 'fn2'], | ||
}; | ||
}); | ||
it('should initialize when the request and response mapping templates are inline templates', () => { | ||
const config: AppSyncSimulatorPipelineResolverConfig = { | ||
...baseConfig, | ||
requestMappingTemplate: 'request', | ||
responseMappingTemplate: 'response', | ||
}; | ||
expect(() => new AppSyncPipelineResolver(config, simulatorContext)).not.toThrow(); | ||
expect(getMappingTemplate).not.toHaveBeenCalled(); | ||
}); | ||
|
||
it('should work when the request and response mapping are external template', () => { | ||
const config: AppSyncSimulatorPipelineResolverConfig = { | ||
...baseConfig, | ||
requestMappingTemplateLocation: 'resolvers/request', | ||
responseMappingTemplateLocation: 'resolvers/response', | ||
}; | ||
expect(() => new AppSyncPipelineResolver(config, simulatorContext)).not.toThrow(); | ||
expect(getMappingTemplate).toHaveBeenCalledTimes(2); | ||
}); | ||
|
||
it('should throw error when request templates are missing', () => { | ||
getMappingTemplate.mockImplementation(() => { | ||
throw new Error('Missing template'); | ||
}); | ||
expect(() => new AppSyncPipelineResolver(baseConfig, simulatorContext)).toThrowError('Missing request mapping template'); | ||
expect(getMappingTemplate).toHaveBeenCalled(); | ||
}); | ||
|
||
describe('resolve', () => { | ||
let resolver: AppSyncPipelineResolver; | ||
const baseConfig: AppSyncSimulatorPipelineResolverConfig = { | ||
fieldName: 'fn1', | ||
typeName: 'Query', | ||
kind: RESOLVER_KIND.PIPELINE, | ||
functions: ['fn1', 'fn2'], | ||
requestMappingTemplateLocation: 'request', | ||
responseMappingTemplateLocation: 'response', | ||
}; | ||
let templates; | ||
let fnImpl; | ||
beforeEach(() => { | ||
fnImpl = { | ||
fn1: { | ||
resolve: jest.fn().mockImplementation((source, args, stash, prevResult, context, info) => { | ||
return { | ||
result: 'FN1-RESULT', | ||
stash: { ...stash, exeSeq: [...(stash.exeSeq || []), 'fn1'] }, | ||
}; | ||
}), | ||
}, | ||
fn2: { | ||
resolve: jest.fn().mockImplementation((source, args, stash, prevResult, context, info) => { | ||
return { | ||
result: 'FN2-RESULT', | ||
stash: { ...stash, exeSeq: [...(stash.exeSeq || []), 'fn2'] }, | ||
}; | ||
}), | ||
}, | ||
}; | ||
getFunction.mockImplementation(fnName => fnImpl[fnName]); | ||
templates = { | ||
request: { | ||
render: jest.fn().mockImplementation(({ stash }) => ({ | ||
result: 'REQUEST_TEMPLATE_RESULT', | ||
errors: [], | ||
stash: { ...stash, exeSeq: [...(stash.exeSeq || []), 'REQUEST-MAPPING-TEMPLATE'] }, | ||
})), | ||
}, | ||
response: { | ||
render: jest.fn().mockImplementation(({ stash }) => ({ | ||
result: 'RESPONSE_TEMPLATE_RESULT', | ||
errors: [], | ||
stash: { ...stash, exeSeq: [...stash.exeSeq, 'fn2'] }, | ||
})), | ||
}, | ||
}; | ||
|
||
getMappingTemplate.mockImplementation(templateName => templates[templateName]); | ||
resolver = new AppSyncPipelineResolver(baseConfig, simulatorContext); | ||
}); | ||
|
||
it('should render requestMapping template', async () => { | ||
const source = 'SOURCE'; | ||
const args = { arg1: 'val' }; | ||
const context = { | ||
appsyncErrors: [], | ||
}; | ||
const info = {}; | ||
const result = await resolver.resolve(source, args, context, info); | ||
expect(result).toEqual('RESPONSE_TEMPLATE_RESULT'); | ||
expect(templates['request'].render).toHaveBeenCalledWith( | ||
{ | ||
source, | ||
arguments: args, | ||
stash: {}, | ||
}, | ||
context, | ||
info, | ||
); | ||
|
||
expect(getMappingTemplate).toHaveBeenCalledTimes(4); // 2 times in constructor and 2 times for resolving | ||
expect(getFunction).toHaveBeenCalled(); | ||
}); | ||
|
||
it('should pass stash and prevResult between functions and templates', async () => { | ||
const source = 'SOURCE'; | ||
const args = { arg1: 'val' }; | ||
const context = { | ||
appsyncErrors: [], | ||
}; | ||
const info = {}; | ||
const result = await resolver.resolve(source, args, context, info); | ||
expect(result).toEqual('RESPONSE_TEMPLATE_RESULT'); | ||
expect(fnImpl.fn1.resolve).toHaveBeenLastCalledWith( | ||
source, | ||
args, | ||
{ exeSeq: ['REQUEST-MAPPING-TEMPLATE'] }, | ||
'REQUEST_TEMPLATE_RESULT', | ||
context, | ||
info, | ||
); | ||
|
||
expect(fnImpl.fn2.resolve).toHaveBeenLastCalledWith( | ||
source, | ||
args, | ||
{ exeSeq: ['REQUEST-MAPPING-TEMPLATE', 'fn1'] }, | ||
'FN1-RESULT', | ||
context, | ||
info, | ||
); | ||
|
||
expect(templates['response'].render).toHaveBeenCalledWith( | ||
{ | ||
source, | ||
arguments: args, | ||
prevResult: 'FN2-RESULT', | ||
stash: { exeSeq: ['REQUEST-MAPPING-TEMPLATE', 'fn1', 'fn2'] }, | ||
}, | ||
context, | ||
info, | ||
); | ||
}); | ||
|
||
it('should not call response mapping template when #return is called', async () => { | ||
templates.request.render.mockReturnValue({ isReturn: true, result: 'REQUEST_TEMPLATE_RESULT', templateErrors: [] }); | ||
const source = 'SOURCE'; | ||
const args = { arg1: 'val' }; | ||
const context = { | ||
appsyncErrors: [], | ||
}; | ||
const info = {}; | ||
const result = await resolver.resolve(source, args, context, info); | ||
expect(result).toEqual('REQUEST_TEMPLATE_RESULT'); | ||
}); | ||
|
||
it('should merge template errors', async () => { | ||
templates.request.render.mockReturnValue({ | ||
isReturn: false, | ||
stash: {}, | ||
result: 'REQUEST_TEMPLATE_RESULT', | ||
errors: ['REQUEST_TEMPLATE_ERROR'], | ||
}); | ||
templates.response.render.mockReturnValue({ | ||
isReturn: false, | ||
result: 'RESPONSE_TEMPLATE_RESULT', | ||
errors: ['RESPONSE_TEMPLATE_ERROR'], | ||
}); | ||
|
||
const source = 'SOURCE'; | ||
const args = { arg1: 'val' }; | ||
const context = { | ||
appsyncErrors: [], | ||
}; | ||
const info = {}; | ||
const result = await resolver.resolve(source, args, context, info); | ||
expect(result).toEqual('RESPONSE_TEMPLATE_RESULT'); | ||
expect(context.appsyncErrors).toEqual(['REQUEST_TEMPLATE_ERROR', 'RESPONSE_TEMPLATE_ERROR']); | ||
}); | ||
}); | ||
}); |
176 changes: 176 additions & 0 deletions
176
packages/amplify-appsync-simulator/src/__tests__/resolvers/unit-resolver.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,176 @@ | ||
import { AppSyncUnitResolver } from '../../resolvers/unit-resolver'; | ||
import { AmplifyAppSyncSimulator } from '../..'; | ||
import { RESOLVER_KIND, AppSyncSimulatorUnitResolverConfig } from '../../type-definition'; | ||
|
||
describe('Unit resolver', () => { | ||
const getDataLoader = jest.fn(); | ||
const getMappingTemplate = jest.fn(); | ||
const simulatorContext: AmplifyAppSyncSimulator = ({ | ||
getDataLoader, | ||
getMappingTemplate, | ||
} as any) as AmplifyAppSyncSimulator; | ||
let baseConfig; | ||
|
||
beforeEach(() => { | ||
jest.resetAllMocks(); | ||
getDataLoader.mockReturnValue({ | ||
load: () => { | ||
return 'DATA'; | ||
}, | ||
}); | ||
getMappingTemplate.mockReturnValue('TEMPLATE'); | ||
baseConfig = { | ||
fieldName: 'getPost', | ||
typeName: 'Query', | ||
kind: RESOLVER_KIND.UNIT, | ||
dataSourceName: 'TodoTable', | ||
}; | ||
}); | ||
|
||
it('should initialize when the request and response mapping templates are inline templates', () => { | ||
const config: AppSyncSimulatorUnitResolverConfig = { | ||
...baseConfig, | ||
requestMappingTemplate: 'request', | ||
responseMappingTemplate: 'response', | ||
}; | ||
expect(() => new AppSyncUnitResolver(config, simulatorContext)).not.toThrow(); | ||
expect(getMappingTemplate).not.toHaveBeenCalled(); | ||
}); | ||
|
||
it('should work when the request and response mapping are external template', () => { | ||
const config: AppSyncSimulatorUnitResolverConfig = { | ||
...baseConfig, | ||
requestMappingTemplateLocation: 'resolvers/request', | ||
responseMappingTemplateLocation: 'resolvers/response', | ||
}; | ||
expect(() => new AppSyncUnitResolver(config, simulatorContext)).not.toThrow(); | ||
expect(getMappingTemplate).toHaveBeenCalledTimes(2); | ||
}); | ||
|
||
it('should throw error when request templates are missing', () => { | ||
getMappingTemplate.mockImplementation(() => { | ||
throw new Error('Missing template'); | ||
}); | ||
expect(() => new AppSyncUnitResolver(baseConfig, simulatorContext)).toThrowError('Missing request mapping template'); | ||
expect(getMappingTemplate).toHaveBeenCalled(); | ||
}); | ||
|
||
describe('resolve', () => { | ||
let templates; | ||
let resolver; | ||
|
||
const info = {}; | ||
const DATA_FROM_DATA_SOURCE = 'DATA FROM DATA SOURCE'; | ||
const REQUEST_TEMPLATE_RESULT = { | ||
version: '2017-02-29', | ||
result: 'REQUEST_TEMPLATE_RESULT', | ||
}; | ||
const RESPONSE_TEMPLATE_RESULT = { | ||
version: '2017-02-29', | ||
data: 'RESPONSE_TEMPLATE_RESULT', | ||
}; | ||
let dataFetcher; | ||
|
||
const source = 'SOURCE'; | ||
const args = { key: 'value' }; | ||
const context = { | ||
appsyncErrors: [], | ||
}; | ||
|
||
beforeEach(() => { | ||
context.appsyncErrors = []; | ||
templates = { | ||
request: { | ||
render: jest.fn().mockImplementation(() => ({ | ||
result: REQUEST_TEMPLATE_RESULT, | ||
errors: [], | ||
})), | ||
}, | ||
response: { | ||
render: jest.fn().mockImplementation(() => ({ | ||
result: RESPONSE_TEMPLATE_RESULT, | ||
errors: [], | ||
})), | ||
}, | ||
}; | ||
|
||
dataFetcher = jest.fn().mockResolvedValue(DATA_FROM_DATA_SOURCE); | ||
|
||
getDataLoader.mockReturnValue({ | ||
load: dataFetcher, | ||
}); | ||
getMappingTemplate.mockImplementation(templateName => { | ||
return templates[templateName]; | ||
}); | ||
|
||
resolver = new AppSyncUnitResolver( | ||
{ ...baseConfig, requestMappingTemplateLocation: 'request', responseMappingTemplateLocation: 'response' }, | ||
simulatorContext, | ||
); | ||
}); | ||
|
||
it('should resolve', async () => { | ||
const result = await resolver.resolve(source, args, context, info); | ||
expect(result).toEqual(RESPONSE_TEMPLATE_RESULT); | ||
expect(templates.request.render).toHaveBeenCalledWith({ source, arguments: args }, context, info); | ||
expect(dataFetcher).toHaveBeenCalledWith(REQUEST_TEMPLATE_RESULT); | ||
expect(getDataLoader).toBeCalledWith('TodoTable'); | ||
expect(templates.response.render).toHaveBeenCalledWith({ source, arguments: args, result: DATA_FROM_DATA_SOURCE }, context, info); | ||
}); | ||
|
||
it('should not call the response mapping template with template version 2017-02-29 and data fetcher throws error', async () => { | ||
dataFetcher.mockImplementation(() => { | ||
throw new Error('Some request template error'); | ||
}); | ||
|
||
await expect(() => resolver.resolve(source, args, context, info)).rejects.toThrowError('Some request template error'); | ||
expect(templates.request.render).toHaveBeenCalledWith({ source, arguments: args }, context, info); | ||
expect(dataFetcher).toHaveBeenCalledWith(REQUEST_TEMPLATE_RESULT); | ||
expect(getDataLoader).toBeCalledWith('TodoTable'); | ||
expect(templates.response.render).not.toHaveBeenCalled(); | ||
}); | ||
|
||
it('should render response mapping when data fetcher throws error and template version is 2018-05-29', async () => { | ||
REQUEST_TEMPLATE_RESULT.version = '2018-05-29'; | ||
const error = new Error('Some request template error'); | ||
dataFetcher.mockImplementation(() => { | ||
throw error; | ||
}); | ||
const result = await resolver.resolve(source, args, context, info); | ||
expect(result).toEqual(RESPONSE_TEMPLATE_RESULT); | ||
expect(templates.request.render).toHaveBeenCalledWith({ source, arguments: args }, context, info); | ||
expect(dataFetcher).toHaveBeenCalledWith(REQUEST_TEMPLATE_RESULT); | ||
expect(getDataLoader).toBeCalledWith('TodoTable'); | ||
expect(templates.response.render).toHaveBeenCalledWith({ source, arguments: args, error: error, result: null }, context, info); | ||
}); | ||
|
||
it('should not render response mapping template when #return is used in request mapping template', async () => { | ||
REQUEST_TEMPLATE_RESULT.version = '2018-05-29'; | ||
templates.request.render.mockReturnValue({ | ||
...REQUEST_TEMPLATE_RESULT, | ||
errors: [], | ||
isReturn: true, | ||
}); | ||
|
||
const result = await resolver.resolve(source, args, context, info); | ||
expect(result).toEqual(REQUEST_TEMPLATE_RESULT.result); | ||
expect(templates.request.render).toHaveBeenCalledWith({ source, arguments: args }, context, info); | ||
expect(dataFetcher).not.toHaveBeenCalledWith(REQUEST_TEMPLATE_RESULT); | ||
expect(templates.response.render).not.toHaveBeenCalled(); | ||
}); | ||
|
||
it('should collect all the errors in context object', async () => { | ||
templates.request.render.mockReturnValue({ | ||
...REQUEST_TEMPLATE_RESULT, | ||
errors: ['request error'], | ||
}); | ||
templates.response.render.mockReturnValue({ | ||
...REQUEST_TEMPLATE_RESULT, | ||
errors: ['response error'], | ||
}); | ||
const result = await resolver.resolve(source, args, context, info); | ||
expect(result).toEqual(REQUEST_TEMPLATE_RESULT.result); | ||
expect(context.appsyncErrors).toEqual(['request error', 'response error']); | ||
}); | ||
}); | ||
}); |
Oops, something went wrong.