UNPKG

graphql-validity

Version:

Make business logic validation easy on the graphql side without adding any declarations or modifications to the existing graphql schema.

621 lines (518 loc) 19.5 kB
var chai = require("chai"); var chaiAsPromised = require("chai-as-promised"); chai.use(chaiAsPromised); var expect = chai.expect; import { wrapResolvers, Processed } from '../src/schema-wrapper'; import { FieldValidationDefinitions } from ".."; describe('schema-wrapper', () => { describe('wrapResolvers', () => { it('should create proxy for field resolver', () => { const resolve = function () { return true; }; const field = { resolve }; wrapResolvers(field); expect(field.resolve).to.not.equal(resolve); }); it('should create proxy for field resolvers on a given object type', () => { const resolve = function () { return true; }; const field = { resolve }; const type = new GraphQLObjectType([field], 'Test'); wrapResolvers(type); expect(field.resolve).to.not.equal(resolve); }); it('should create proxy for field resolvers on a given schema', () => { const resolve = function () { return true; }; const field = { resolve }; const type = new GraphQLObjectType([field], 'Test'); const typesMap = { 'Test': type }; const schema = new GraphQLSchema(typesMap); wrapResolvers(schema); expect(field.resolve).to.not.equal(resolve); }); it('should set Processed to true', () => { const resolve = function () { return true; }; const field = { resolve }; wrapResolvers(field); expect((field as any)[Processed]).to.be.true; }); it('resolve should return result if validity is not set up', async () => { const resolve = function () { return true; }; const field = { resolve }; wrapResolvers(field); const result = await field.resolve(); expect(result).to.be.true; }); it('resolve should return result if validity is set up', async () => { FieldValidationDefinitions['$'] = [() => {return new Error('test1')}]; const resolve = function (...args: any[]) { return true; }; const field = { resolve, name: 'Test' }; const type = new GraphQLObjectType([field], 'Test'); const typesMap = { 'Test': type }; const schema = new GraphQLSchema(typesMap); wrapResolvers(schema); const validity = { ___validationResults: [], ___globalValidationResultsCaptured: false, ___profilingData: [] }; const result = await field.resolve( { parentType: 'Test', rootValue: { __graphQLValidity: validity } }); expect(result).to.be.true; }); it('resolve should perform validation if validity is set up', async () => { FieldValidationDefinitions['$'] = [() => {return new Error('test1')}]; const resolve = function (...args: any[]) { return true; }; const field = { resolve, name: 'Test' }; const type = new GraphQLObjectType([field], 'Test'); const typesMap = { 'Test': type }; const schema = new GraphQLSchema(typesMap); wrapResolvers(schema); const validity = { ___validationResults: [], ___globalValidationResultsCaptured: false, ___profilingData: [] }; await field.resolve( { parentType: 'Test', rootValue: { __graphQLValidity: validity } }); expect(validity.___validationResults.length).to.equal(1); expect((validity.___validationResults[0] as any).message).to.equal('test1'); }); it('resolve should perform profiling if validity is set up and profiling enabled', async () => { FieldValidationDefinitions['$'] = [() => {return new Error('test1')}]; const resolve = function (...args: any[]) { return true; }; const field = { resolve, name: 'Test' }; const type = new GraphQLObjectType([field], 'Test'); const typesMap = { 'Test': type }; const schema = new GraphQLSchema(typesMap); wrapResolvers(schema, { wrapErrors: false, enableProfiling: true, unhandledErrorWrapper: (err: Error) => {return err} }); const validity = { ___validationResults: [], ___globalValidationResultsCaptured: false, ___profilingData: [] }; await field.resolve( { parentType: 'Test', rootValue: { __graphQLValidity: validity } }); expect(validity.___profilingData.length).to.equal(1); }); it('resolve should perform profiling if validity is set up and profiling enabled and validator returns promise', async () => { FieldValidationDefinitions['$'] = [() => { return new Promise((resolve) => { resolve([new Error('test1')]); }); }]; const resolve = function (...args: any[]) { return true; }; const field = { resolve, name: 'Test' }; const type = new GraphQLObjectType([field], 'Test'); const typesMap = { 'Test': type }; const schema = new GraphQLSchema(typesMap); wrapResolvers(schema, { wrapErrors: false, enableProfiling: true, profilingResultHandler: () => {} }); const validity = { ___validationResults: [], ___globalValidationResultsCaptured: false, ___profilingData: [] }; await field.resolve( { parentType: 'Test', rootValue: { __graphQLValidity: validity } }); expect(validity.___profilingData.length).to.equal(1); }); it('resolve should perform profiling if validity is set up and profiling enabled and resolver returns promise', async () => { FieldValidationDefinitions['$'] = [() => { return []; }]; const resolve = function (...args: any[]) { return new Promise((resolve) => { resolve([new Error('test1')]); }); }; const field = { resolve, name: 'Test' }; const type = new GraphQLObjectType([field], 'Test'); const typesMap = { 'Test': type }; const schema = new GraphQLSchema(typesMap); wrapResolvers(schema, { wrapErrors: false, enableProfiling: true, profilingResultHandler: () => {} }); const validity = { ___validationResults: [], ___globalValidationResultsCaptured: false, ___profilingData: [] }; await field.resolve( { parentType: 'Test', rootValue: { __graphQLValidity: validity } }); expect(validity.___profilingData.length).to.equal(1); }); it('resolve should perform profiling if validity is set up and profiling enabled and both resolver and validation returns promise', async () => { FieldValidationDefinitions['$'] = [() => { return new Promise(( resolve, reject ) => resolve([])) }]; const resolve = function (...args: any[]) { return new Promise((resolve) => { resolve([new Error('test1')]); }); }; const field = { resolve, name: 'Test' }; const type = new GraphQLObjectType([field], 'Test'); const typesMap = { 'Test': type }; const schema = new GraphQLSchema(typesMap); wrapResolvers(schema, { wrapErrors: false, enableProfiling: true, unhandledErrorWrapper: (err: Error) => {return err} }); const validity = { ___validationResults: [], ___globalValidationResultsCaptured: false, ___profilingData: [] }; await field.resolve( { parentType: 'Test', rootValue: { __graphQLValidity: validity } }); expect(validity.___profilingData.length).to.equal(1); }); it('resolve should not throw exception during profiling failure', async () => { FieldValidationDefinitions['$'] = [() => {return new Error('test2')}]; const resolve = function (...args: any[]) { return true; }; const field = { resolve, name: 'Test' }; const type = new GraphQLObjectType([field], 'Test'); const typesMap = { 'Test': type }; const schema = new GraphQLSchema(typesMap); wrapResolvers(schema, { wrapErrors: false, enableProfiling: true, unhandledErrorWrapper: (err: Error) => {return err} }); const validity = {}; expect(field.resolve).to.not.throw(( { parentType: 'Test', rootValue: { __graphQLValidity: validity } })); }); it('resolve should not throw during validator exception being returned', async () => { FieldValidationDefinitions['$'] = [() => {return new Error('test2')}]; const resolve = function (...args: any[]) { return true; }; const field = { resolve, name: 'Test' }; const type = new GraphQLObjectType([field], 'Test'); const typesMap = { 'Test': type }; const schema = new GraphQLSchema(typesMap); wrapResolvers(schema, { wrapErrors: false, enableProfiling: true, unhandledErrorWrapper: (err: Error) => {return err} }); const validity = {}; expect(field.resolve).to.not.throw(( { parentType: 'Test', rootValue: { __graphQLValidity: validity } })); }); it('resolve should throw during validator promise rejection', async () => { FieldValidationDefinitions['$'] = [() => { return new Promise((resolve, reject) => reject('test2')); }]; const resolve = function (...args: any[]) { return true; }; const field = { resolve, name: 'Test' }; const type = new GraphQLObjectType([field], 'Test'); const typesMap = { 'Test': type }; const schema = new GraphQLSchema(typesMap); wrapResolvers(schema, { wrapErrors: false, enableProfiling: true, unhandledErrorWrapper: (err: Error) => {return err} }); const validity = {}; expect(field.resolve( { parentType: 'Test', rootValue: { __graphQLValidity: validity } })).to.eventually.be.rejected; } ); it('resolve should return result if validity is set up to return promise', async () => { FieldValidationDefinitions['$'] = [() => { return new Promise((resolve) => { resolve([new Error('test1')]); }); }]; const resolve = function (...args: any[]) { return true; }; const field = { resolve, name: 'Test' }; const type = new GraphQLObjectType([field], 'Test'); const typesMap = { 'Test': type }; const schema = new GraphQLSchema(typesMap); wrapResolvers(schema); const validity = { ___validationResults: [], ___globalValidationResultsCaptured: false, ___profilingData: [] }; const result = await field.resolve( { parentType: 'Test', rootValue: { __graphQLValidity: validity } }); expect(result).to.be.true; }); it('resolve should perform validation if validity is set up to return promise', async () => { FieldValidationDefinitions['$'] = [() => { return new Promise((resolve) => { resolve([new Error('test2')]); }); }]; const resolve = function (...args: any[]) { return true; }; const field = { resolve, name: 'Test' }; const type = new GraphQLObjectType([field], 'Test'); const typesMap = { 'Test': type }; const schema = new GraphQLSchema(typesMap); wrapResolvers(schema); const validity = { ___validationResults: [], ___globalValidationResultsCaptured: false, ___profilingData: [] }; await field.resolve( { parentType: 'Test', rootValue: { __graphQLValidity: validity } }); expect(validity.___validationResults.length).to.equal(1); expect((validity.___validationResults[0] as any).message).to.equal('test2'); }); it('shouldn\'t replace resolver for type not implementing getFields', async () => { FieldValidationDefinitions['$'] = [() => { return new Promise((resolve) => { resolve([new Error('test2')]); }); }]; const resolve = function (...args: any[]) { return true; }; const field = { resolve, name: 'Test' }; const field2 = { resolve: function (...args: any[]) { return true; }, name: 'Test2' }; const type = new GraphQLObjectTypeWithoutFields([field, field2], 'Test'); const typesMap = { 'Test': type }; const schema = new GraphQLSchema(typesMap); wrapResolvers(schema); expect(type.fields[0].resolve).to.equal(resolve); }); it('shouldn\'t replace resolvers for functions that aren\'t related to types', async () => { FieldValidationDefinitions['$'] = [() => { return new Promise((resolve) => { resolve([new Error('test2')]); }); }]; const resolve = function (...args: any[]) { return true; }; const resolve2 = function (...args: any[]) { return true; }; const field = { resolve, name: 'Test' }; const field2 = { resolve: resolve2, name: 'Test2' }; const type = new GraphQLObjectTypeFieldAsObject( new FieldsType2(field, field2), 'Test' ); const typesMap = { 'Test': type }; const schema = new GraphQLSchema(typesMap); wrapResolvers(schema); expect(field.resolve).to.not.equal(resolve); expect(field2.resolve).to.not.equal(resolve2); }); it('shouldn\'t replace resolvers for parent schema object', async () => { FieldValidationDefinitions['$'] = [() => { return new Promise((resolve) => { resolve([new Error('test2')]); }); }]; const resolve = function (...args: any[]) { return true; }; const resolve2 = function (...args: any[]) { return true; }; const field = { resolve, name: 'Test' }; const field2 = { resolve: resolve2, name: 'Test2' }; const type = new GraphQLObjectType2( [field, field2], 'Test33' ); const type2 = new GraphQLObjectType2( [field, field2], 'Test44' ); const typesMap = { 'Test33': type, 'Test44': type2 }; const schema = new GraphQLSchema(typesMap); wrapResolvers(schema); expect(field.resolve).to.not.equal(resolve); expect(field2.resolve).to.not.equal(resolve2); }); }); }); class GraphQLObjectTypeWithoutFields { constructor(public fields: any[], public name: string) { } } class GraphQLObjectTypeFieldAsObject { constructor(private fields: any, public name: string) { } getFields() { return this.fields; } } class GraphQLObjectType { constructor(public fields: any[], public name: string) { } getFields() { return this.fields; } } class GraphQLObjectType2 extends GraphQLObjectType { constructor(public fields: any[], public name: string) { super(fields, name); } getFields() { return this.fields; } } class GraphQLSchema2 { constructor(public types: any) { } getTypeMap() { return this.types; } } class GraphQLSchema extends GraphQLSchema2 { constructor(public types: any) { super(types); } getTypeMap() { return this.types; } } class FieldsType1 { constructor(public field: any) { } } class FieldsType2 extends FieldsType1 { constructor(public field1: any, field2: any) { super(field2); } }