text: 'use strict'
  
  const { test } = require('tap')
  const Fastify = require('fastify')
  const Swagger = require('swagger-parser')
  const yaml = require('js-yaml')
  const fastifySwagger = require('../../../index')
  const {
    swaggerOption,
    schemaBody,
    schemaConsumes,
    schemaExtension,
    schemaHeaders,
    schemaHeadersParams,
    schemaParams,
    schemaQuerystring,
    schemaSecurity
  } = require('../../../examples/options')
  
  test('swagger should return valid swagger object', t => {
    t.plan(3)
    const fastify = Fastify()
  
    fastify.register(fastifySwagger, swaggerOption)
  
    fastify.get('/', () => {})
    fastify.post('/', () => {})
    fastify.get('/example', schemaQuerystring, () => {})
    fastify.post('/example', schemaBody, () => {})
    fastify.get('/parameters/:id', schemaParams, () => {})
    fastify.get('/headers', schemaHeaders, () => {})
    fastify.get('/headers/:id', schemaHeadersParams, () => {})
    fastify.get('/security', schemaSecurity, () => {})
  
    fastify.ready(err => {
      t.error(err)
  
      const swaggerObject = fastify.swagger()
      t.equal(typeof swaggerObject, 'object')
  
      Swagger.validate(swaggerObject)
        .then(function (api) {
          t.pass('valid swagger object')
        })
        .catch(function (err) {
          t.fail(err)
        })
    })
  })
  
  test('swagger should return a valid swagger yaml', t => {
    t.plan(3)
    const fastify = Fastify()
  
    fastify.register(fastifySwagger, swaggerOption)
  
    fastify.get('/', () => {})
    fastify.post('/', () => {})
    fastify.get('/example', schemaQuerystring, () => {})
    fastify.post('/example', schemaBody, () => {})
    fastify.get('/parameters/:id', schemaParams, () => {})
    fastify.get('/headers', schemaHeaders, () => {})
    fastify.get('/headers/:id', schemaHeadersParams, () => {})
    fastify.get('/security', schemaSecurity, () => {})
  
    fastify.ready(err => {
      t.error(err)
  
      const swaggerYaml = fastify.swagger({ yaml: true })
      t.equal(typeof swaggerYaml, 'string')
  
      try {
        yaml.load(swaggerYaml)
        t.pass('valid swagger yaml')
      } catch (err) {
        t.fail(err)
      }
    })
  })
  
  test('route options - deprecated', t => {
    t.plan(3)
    const fastify = Fastify()
  
    fastify.register(fastifySwagger, swaggerOption)
  
    const opts = {
      schema: {
        deprecated: true,
        body: {
          type: 'object',
          properties: {
            hello: { type: 'string' },
            obj: {
              type: 'object',
              properties: {
                some: { type: 'string' }
              }
            }
          }
        }
      }
    }
  
    fastify.get('/', opts, () => {})
  
    fastify.ready(err => {
      t.error(err)
      const swaggerObject = fastify.swagger()
  
      Swagger.validate(swaggerObject)
        .then(function (api) {
          t.pass('valid swagger object')
          t.ok(swaggerObject.paths['/'])
        })
        .catch(function (err) {
          t.fail(err)
        })
    })
  })
  
  test('route options - meta', t => {
    t.plan(8)
    const fastify = Fastify()
  
    fastify.register(fastifySwagger, swaggerOption)
  
    const opts = {
      schema: {
        operationId: 'doSomething',
        summary: 'Route summary',
        tags: ['tag'],
        description: 'Route description',
        produces: ['application/octet-stream'],
        consumes: ['application/x-www-form-urlencoded']
      }
    }
  
    fastify.get('/', opts, () => {})
  
    fastify.ready(err => {
      t.error(err)
      const swaggerObject = fastify.swagger()
  
      Swagger.validate(swaggerObject)
        .then(function (api) {
          const definedPath = api.paths['/'].get
          t.ok(definedPath)
          t.equal(opts.schema.operationId, definedPath.operationId)
          t.equal(opts.schema.summary, definedPath.summary)
          t.same(opts.schema.tags, definedPath.tags)
          t.equal(opts.schema.description, definedPath.description)
          t.same(opts.schema.produces, definedPath.produces)
          t.same(opts.schema.consumes, definedPath.consumes)
        })
        .catch(function (err) {
          t.fail(err)
        })
    })
  })
  
  test('route options - consumes', t => {
    t.plan(3)
    const fastify = Fastify()
    fastify.register(fastifySwagger, swaggerOption)
    fastify.get('/', schemaConsumes, () => {})
  
    fastify.ready(err => {
      t.error(err)
      const swaggerObject = fastify.swagger()
  
      Swagger.validate(swaggerObject)
        .then(function (api) {
          const definedPath = api.paths['/'].get
          t.ok(definedPath)
          t.same(definedPath.parameters, [{
            in: 'formData',
            name: 'hello',
            description: 'hello',
            required: true,
            type: 'string'
          }])
        })
        .catch(function (err) {
          t.fail(err)
        })
    })
  })
  
  test('route options - extension', t => {
    t.plan(5)
    const fastify = Fastify()
    fastify.register(fastifySwagger, { swagger: { 'x-ternal': true } })
    fastify.get('/', schemaExtension, () => {})
  
    fastify.ready(err => {
      t.error(err)
      const swaggerObject = fastify.swagger()
  
      Swagger.validate(swaggerObject)
        .then(function (api) {
          t.ok(api['x-ternal'])
          t.same(api['x-ternal'], true)
  
          const definedPath = api.paths['/'].get
          t.ok(definedPath)
          t.same(definedPath['x-tension'], true)
        })
        .catch(function (err) {
          t.fail(err)
        })
    })
  })
  
  test('route options - querystring', t => {
    t.plan(3)
  
    const opts = {
      schema: {
        querystring: {
          type: 'object',
          properties: {
            hello: { type: 'string' },
            world: { type: 'string', description: 'world description' }
          },
          required: ['hello']
        }
      }
    }
  
    const fastify = Fastify()
    fastify.register(fastifySwagger, swaggerOption)
    fastify.get('/', opts, () => {})
  
    fastify.ready(err => {
      t.error(err)
      const swaggerObject = fastify.swagger()
  
      Swagger.validate(swaggerObject)
        .then(function (api) {
          const definedPath = api.paths['/'].get
          t.ok(definedPath)
          t.same(definedPath.parameters, [
            {
              in: 'query',
              name: 'hello',
              type: 'string',
              required: true
            },
            {
              in: 'query',
              name: 'world',
              type: 'string',
              required: false,
              description: 'world description'
            }
          ])
        })
        .catch(function (err) {
          t.fail(err)
        })
    })
  })
  
  test('swagger json output should not omit enum part in params config', t => {
    t.plan(3)
    const opts = {
      schema: {
        params: {
          type: 'object',
          properties: {
            enumKey: { type: 'string', enum: ['enum1', 'enum2'] }
          }
        }
      }
    }
  
    const fastify = Fastify()
    fastify.register(fastifySwagger, swaggerOption)
    fastify.get('/test/:enumKey', opts, () => {})
  
    fastify.ready(err => {
      t.error(err)
      const swaggerObject = fastify.swagger()
      Swagger.validate(swaggerObject)
        .then((api) => {
          const definedPath = api.paths['/test/{enumKey}'].get
          t.ok(definedPath)
          t.same(definedPath.parameters, [{
            in: 'path',
            name: 'enumKey',
            type: 'string',
            enum: ['enum1', 'enum2'],
            required: true
          }])
        })
        .catch(err => {
          t.error(err)
        })
    })
  })
  
  test('custom verbs should not be interpreted as path params', t => {
    t.plan(3)
    const opts = {
      schema: {
        params: {
          type: 'object',
          properties: {
            id: { type: 'string' }
          }
        }
      }
    }
  
    const fastify = Fastify()
    fastify.register(fastifySwagger, swaggerOption)
    fastify.get('/resource/:id/sub-resource::watch', opts, () => {})
  
    fastify.ready(err => {
      t.error(err)
      const swaggerObject = fastify.swagger()
  
      Swagger.validate(swaggerObject)
        .then((api) => {
          const definedPath = api.paths['/resource/{id}/sub-resource:watch'].get
          t.ok(definedPath)
          t.same(definedPath.parameters, [{
            in: 'path',
            name: 'id',
            type: 'string',
            required: true
          }])
        })
        .catch(err => {
          console.log(err)
          t.error(err)
        })
    })
  })
  
  test('swagger json output should not omit consume in querystring schema', async (t) => {
    t.plan(1)
    const fastify = Fastify()
  
    fastify.register(fastifySwagger, swaggerOption)
  
    const schemaQuerystring = {
      schema: {
        querystring: {
          type: 'object',
          properties: {
            hello: {
              type: 'object',
              'x-consume': 'application/json',
              required: ['bar'],
              properties: {
                bar: { type: 'string' },
                baz: { type: 'string' }
              }
            }
          }
        }
      }
    }
  
    fastify.get('/', schemaQuerystring, () => {})
  
    await fastify.ready()
  
    try {
      fastify.swagger()
      t.fail('error was not thrown')
    } catch (err) {
      if (err.message.startsWith('Complex serialization is not supported by Swagger')) {
        t.pass('error was thrown')
      } else {
        t.error(err)
      }
    }
  })
  
  test('swagger should not support Links', t => {
    t.plan(2)
    const fastify = Fastify()
  
    fastify.register(fastifySwagger, swaggerOption)
  
    fastify.get('/user/:id', {
      schema: {
        params: {
          type: 'object',
          properties: {
            id: {
              type: 'string',
              description: 'the user identifier, as userId'
            }
          },
          required: ['id']
        },
        response: {
          200: {
            type: 'object',
            properties: {
              uuid: {
                type: 'string',
                format: 'uuid'
              }
            }
          }
        }
      },
      links: {
        200: {
          address: {
            operationId: 'getUserAddress',
            parameters: {
              id: '$request.path.id'
            }
          }
        }
      }
    }, () => {})
  
    fastify.get('/user/:id/address', {
      schema: {
        operationId: 'getUserAddress',
        params: {
          type: 'object',
          properties: {
            id: {
              type: 'string',
              description: 'the user identifier, as userId'
            }
          },
          required: ['id']
        },
        response: {
          200: {
            type: 'string'
          }
        }
      }
    }, () => {})
  
    fastify.ready(err => {
      t.error(err)
  
      t.throws(() => fastify.swagger(), new Error('Swagger (Open API v2) does not support Links. Upgrade to OpenAPI v3 (see fastify-swagger readme)'))
    })
  })
  
  test('security headers ignored when declared in security and securityScheme', t => {
    t.plan(7)
    const fastify = Fastify()
  
    fastify.register(fastifySwagger, swaggerOption)
  
    fastify.get('/address1/:id', {
      schema: {
        params: {
          type: 'object',
          properties: {
            id: { type: 'string' }
          }
        },
        headers: {
          type: 'object',
          properties: {
            apiKey: {
              type: 'string',
              description: 'api token'
            },
            somethingElse: {
              type: 'string',
              description: 'common field'
            }
          }
        }
      }
    }, () => {})
  
    fastify.get('/address2/:id', {
      schema: {
        params: {
          type: 'object',
          properties: {
            id: { type: 'string' }
          }
        },
        headers: {
          type: 'object',
          properties: {
            authKey: {
              type: 'string',
              description: 'auth token'
            },
            somethingElse: {
              type: 'string',
              description: 'common field'
            }
          }
        }
      }
    }, () => {})
  
    fastify.ready(err => {
      t.error(err)
  
      const swaggerObject = fastify.swagger()
      t.equal(typeof swaggerObject, 'object')
  
      Swagger.validate(swaggerObject)
        .then(function (api) {
          t.pass('valid swagger object')
          t.ok(api.paths['/address1/{id}'].get.parameters.find(({ name }) => (name === 'id')))
          t.ok(api.paths['/address2/{id}'].get.parameters.find(({ name }) => (name === 'id')))
          t.notOk(api.paths['/address1/{id}'].get.parameters.find(({ name }) => (name === 'apiKey')))
          t.ok(api.paths['/address2/{id}'].get.parameters.find(({ name }) => (name === 'authKey')))
        })
        .catch(function (err) {
          t.error(err)
        })
    })
  })
  
  test('security querystrings ignored when declared in security and securityScheme', t => {
    t.plan(7)
    const fastify = Fastify()
  
    fastify.register(fastifySwagger, {
      swagger: {
        securityDefinitions: {
          apiKey: {
            type: 'apiKey',
            name: 'apiKey',
            in: 'query'
          }
        },
        security: [{
          apiKey: []
        }]
      }
    })
  
    fastify.get('/address1/:id', {
      schema: {
        params: {
          type: 'object',
          properties: {
            id: { type: 'string' }
          }
        },
        querystring: {
          type: 'object',
          properties: {
            apiKey: {
              type: 'string',
              description: 'api token'
            },
            somethingElse: {
              type: 'string',
              description: 'common field'
            }
          }
        }
      }
    }, () => {})
  
    fastify.get('/address2/:id', {
      schema: {
        params: {
          type: 'object',
          properties: {
            id: { type: 'string' }
          }
        },
        querystring: {
          type: 'object',
          properties: {
            authKey: {
              type: 'string',
              description: 'auth token'
            },
            somethingElse: {
              type: 'string',
              description: 'common field'
            }
          }
        }
      }
    }, () => {})
  
    fastify.ready(err => {
      t.error(err)
  
      const swaggerObject = fastify.swagger()
      t.equal(typeof swaggerObject, 'object')
  
      Swagger.validate(swaggerObject)
        .then(function (api) {
          t.pass('valid swagger object')
          t.ok(api.paths['/address1/{id}'].get.parameters.find(({ name }) => (name === 'somethingElse')))
          t.ok(api.paths['/address2/{id}'].get.parameters.find(({ name }) => (name === 'somethingElse')))
          t.notOk(api.paths['/address1/{id}'].get.parameters.find(({ name }) => (name === 'apiKey')))
          t.ok(api.paths['/address2/{id}'].get.parameters.find(({ name }) => (name === 'authKey')))
        })
        .catch(function (err) {
          t.error(err)
        })
    })
  })
  