text: // Handlers are meant to be async outside tests
  /* eslint-disable require-await */
  const test = require('ava')
  const execa = require('execa')
  const getPort = require('get-port')
  const waitPort = require('wait-port')
  
  const fs = require('../src/lib/fs')
  
  const callCli = require('./utils/call-cli')
  const cliPath = require('./utils/cli-path')
  const { withDevServer } = require('./utils/dev-server')
  const got = require('./utils/got')
  const { handleQuestions, answerWithValue, CONFIRM } = require('./utils/handle-questions')
  const { withMockApi } = require('./utils/mock-api')
  const { killProcess } = require('./utils/process')
  const { withSiteBuilder } = require('./utils/site-builder')
  
  test('should return function response when invoked', async (t) => {
    await withSiteBuilder('site-with-ping-function', async (builder) => {
      builder.withNetlifyToml({ config: { functions: { directory: 'functions' } } }).withFunction({
        path: 'ping.js',
        handler: async () => ({
          statusCode: 200,
          body: 'ping',
        }),
      })
  
      await builder.buildAsync()
  
      await withDevServer({ cwd: builder.directory }, async (server) => {
        const stdout = await callCli(['functions:invoke', 'ping', '--identity', `--port=${server.port}`], {
          cwd: builder.directory,
        })
        t.is(stdout, 'ping')
      })
    })
  })
  
  test('should create a new function directory when none is found', async (t) => {
    const siteInfo = {
      admin_url: 'https://app.netlify.com/sites/site-name/overview',
      ssl_url: 'https://site-name.netlify.app/',
      id: 'site_id',
      name: 'site-name',
      build_settings: { repo_url: 'https://github.com/owner/repo' },
    }
  
    const routes = [
      {
        path: 'accounts',
        response: [{ slug: 'test-account' }],
      },
      { path: 'sites/site_id/service-instances', response: [] },
      { path: 'sites/site_id', response: siteInfo },
      {
        path: 'sites',
        response: [siteInfo],
      },
      { path: 'sites/site_id', method: 'patch', response: {} },
    ]
  
    await withSiteBuilder('site-with-no-functions-dir', async (builder) => {
      await builder.buildAsync()
  
      const createFunctionQuestions = [
        {
          question: 'Enter the path, relative to your site',
          answer: answerWithValue('test/functions'),
        },
        {
          question: 'Pick a template',
          answer: answerWithValue(CONFIRM),
        },
        {
          question: 'name your function',
          answer: answerWithValue(CONFIRM),
        },
      ]
  
      await withMockApi(routes, async ({ apiUrl }) => {
        const childProcess = execa(cliPath, ['functions:create'], {
          env: {
            NETLIFY_API_URL: apiUrl,
            NETLIFY_SITE_ID: 'site_id',
            NETLIFY_AUTH_TOKEN: 'fake-token',
          },
          cwd: builder.directory,
        })
  
        handleQuestions(childProcess, createFunctionQuestions)
  
        await childProcess
  
        t.is(await fs.fileExistsAsync(`${builder.directory}/test/functions/hello-world/hello-world.js`), true)
      })
    })
  })
  
  test('should not create a new function directory when one is found', async (t) => {
    const siteInfo = {
      admin_url: 'https://app.netlify.com/sites/site-name/overview',
      ssl_url: 'https://site-name.netlify.app/',
      id: 'site_id',
      name: 'site-name',
      build_settings: { repo_url: 'https://github.com/owner/repo' },
    }
  
    const routes = [
      {
        path: 'accounts',
        response: [{ slug: 'test-account' }],
      },
      { path: 'sites/site_id/service-instances', response: [] },
      { path: 'sites/site_id', response: siteInfo },
      {
        path: 'sites',
        response: [siteInfo],
      },
      { path: 'sites/site_id', method: 'patch', response: {} },
    ]
  
    await withSiteBuilder('site-with-functions-dir', async (builder) => {
      builder.withNetlifyToml({ config: { build: { functions: 'functions' } } })
  
      await builder.buildAsync()
  
      const createFunctionQuestions = [
        {
          question: 'Pick a template',
          answer: answerWithValue(CONFIRM),
        },
        {
          question: 'name your function',
          answer: answerWithValue(CONFIRM),
        },
      ]
  
      await withMockApi(routes, async ({ apiUrl }) => {
        const childProcess = execa(cliPath, ['functions:create'], {
          env: {
            NETLIFY_API_URL: apiUrl,
            NETLIFY_SITE_ID: 'site_id',
            NETLIFY_AUTH_TOKEN: 'fake-token',
          },
          cwd: builder.directory,
        })
  
        handleQuestions(childProcess, createFunctionQuestions)
  
        await childProcess
  
        t.is(await fs.fileExistsAsync(`${builder.directory}/functions/hello-world/hello-world.js`), true)
      })
    })
  })
  
  const DEFAULT_PORT = 9999
  const SERVE_TIMEOUT = 180000
  
  const withFunctionsServer = async ({ builder, args = [], port = DEFAULT_PORT }, testHandler) => {
    let ps
    try {
      ps = execa(cliPath, ['functions:serve', ...args], {
        cwd: builder.directory,
      })
  
      ps.stdout.on('data', (data) => console.log(data.toString()))
      ps.stderr.on('data', (data) => console.log(data.toString()))
  
      const open = await waitPort({
        port,
        output: 'silent',
        timeout: SERVE_TIMEOUT,
      })
      if (!open) {
        throw new Error('Timed out waiting for functions server')
      }
      return await testHandler()
    } finally {
      await killProcess(ps)
    }
  }
  
  test('should serve functions on default port', async (t) => {
    await withSiteBuilder('site-with-ping-function', async (builder) => {
      await builder
        .withNetlifyToml({ config: { functions: { directory: 'functions' } } })
        .withFunction({
          path: 'ping.js',
          handler: async () => ({
            statusCode: 200,
            body: 'ping',
          }),
        })
        .buildAsync()
  
      await withFunctionsServer({ builder }, async () => {
        const response = await got(`http://localhost:9999/.netlify/functions/ping`, { retry: 1 }).text()
        t.is(response, 'ping')
      })
    })
  })
  
  test('should serve functions on custom port', async (t) => {
    await withSiteBuilder('site-with-ping-function', async (builder) => {
      await builder
        .withNetlifyToml({ config: { functions: { directory: 'functions' } } })
        .withFunction({
          path: 'ping.js',
          handler: async () => ({
            statusCode: 200,
            body: 'ping',
          }),
        })
        .buildAsync()
  
      const port = await getPort()
      await withFunctionsServer({ builder, args: ['--port', port], port }, async () => {
        const response = await got(`http://localhost:${port}/.netlify/functions/ping`).text()
        t.is(response, 'ping')
      })
    })
  })
  
  test('should use settings from netlify.toml dev', async (t) => {
    await withSiteBuilder('site-with-ping-function', async (builder) => {
      const port = await getPort()
      await builder
        .withNetlifyToml({
          config: { functions: { directory: 'functions' }, dev: { functions: 'other', functionsPort: port } },
        })
        .withFunction({
          pathPrefix: 'other',
          path: 'ping.js',
          handler: async () => ({
            statusCode: 200,
            body: 'ping',
          }),
        })
        .buildAsync()
  
      await withFunctionsServer({ builder, port }, async () => {
        const response = await got(`http://localhost:${port}/.netlify/functions/ping`).text()
        t.is(response, 'ping')
      })
    })
  })
  
  /* eslint-enable require-await */
  