text: 'use strict'
  
  // Tests for header objects passed to `.reply()`, including header objects
  // containing lambdas.
  
  const { IncomingMessage } = require('http')
  const { expect } = require('chai')
  const sinon = require('sinon')
  const fakeTimers = require('@sinonjs/fake-timers')
  const nock = require('..')
  const got = require('./got_client')
  
  require('./setup')
  
  describe('`reply()` headers', () => {
    describe('using parameter value', () => {
      it('as array', async () => {
        const scope = nock('http://example.test')
          .get('/')
          .reply(200, 'Hello World!', [
            'X-My-Header',
            'My Header value',
            'X-Other-Header',
            'My Other Value',
          ])
  
        const { headers, rawHeaders } = await got('http://example.test/')
  
        expect(headers).to.deep.equal({
          'x-my-header': 'My Header value',
          'x-other-header': 'My Other Value',
        })
        expect(rawHeaders).to.deep.equal([
          'X-My-Header',
          'My Header value',
          'X-Other-Header',
          'My Other Value',
        ])
        scope.done()
      })
  
      it('given an invalid array, raises the expected error', async () => {
        const scope = nock('http://example.test').get('/')
  
        expect(() =>
          scope.reply(200, 'Hello World!', ['one', 'two', 'three'])
        ).to.throw(
          Error,
          'Raw headers must be provided as an array with an even number of items. [fieldName, value, ...]'
        )
      })
  
      // https://nodejs.org/api/http.html#http_message_headers
      it('folds duplicate headers the same as Node', async () => {
        const replyHeaders = [
          'Content-Type',
          'text/html; charset=utf-8',
          'set-cookie',
          ['set-cookie1=foo', 'set-cookie2=bar'],
          'set-cookie',
          'set-cookie3=baz',
          'CONTENT-TYPE',
          'text/xml',
          'cookie',
          'cookie1=foo; cookie2=bar',
          'cookie',
          'cookie3=baz',
          'x-custom',
          'custom1',
          'X-Custom',
          ['custom2', 'custom3'],
        ]
        const scope = nock('http://example.test')
          .get('/')
          .reply(200, 'Hello World!', replyHeaders)
  
        const { headers, rawHeaders } = await got('http://example.test/')
  
        expect(headers).to.deep.equal({
          'content-type': 'text/html; charset=utf-8',
          'set-cookie': ['set-cookie1=foo', 'set-cookie2=bar', 'set-cookie3=baz'],
          cookie: 'cookie1=foo; cookie2=bar; cookie3=baz',
          'x-custom': 'custom1, custom2, custom3',
        })
        expect(rawHeaders).to.deep.equal(replyHeaders)
  
        scope.done()
      })
  
      it('as object', async () => {
        const scope = nock('http://example.test')
          .get('/')
          .reply(200, 'Hello World!', { 'X-My-Headers': 'My Header value' })
  
        const { headers, rawHeaders } = await got('http://example.test/')
  
        expect(headers).to.deep.equal({ 'x-my-headers': 'My Header value' })
        expect(rawHeaders).to.deep.equal(['X-My-Headers', 'My Header value'])
        scope.done()
      })
  
      it('as Map', async () => {
        const replyHeaders = new Map([
          ['X-My-Header', 'My Header value'],
          ['X-Other-Header', 'My Other Value'],
        ])
        const scope = nock('http://example.test')
          .get('/')
          .reply(200, 'Hello World!', replyHeaders)
  
        const { headers, rawHeaders } = await got('http://example.test/')
  
        expect(headers).to.deep.equal({
          'x-my-header': 'My Header value',
          'x-other-header': 'My Other Value',
        })
        expect(rawHeaders).to.deep.equal([
          'X-My-Header',
          'My Header value',
          'X-Other-Header',
          'My Other Value',
        ])
        scope.done()
      })
  
      it('given invalid data types, raises the expected error', async () => {
        const scope = nock('http://example.test').get('/')
  
        expect(() => scope.reply(200, 'Hello World!', 'foo: bar')).to.throw(
          Error,
          'Headers must be provided as an array of raw values, a Map, or a plain Object. foo: bar'
        )
  
        expect(() => scope.reply(200, 'Hello World!', false)).to.throw(
          Error,
          'Headers must be provided as an array of raw values, a Map, or a plain Object. false'
        )
      })
    })
  
    describe('using synchronous reply function', () => {
      it('as array', async () => {
        const scope = nock('http://example.test')
          .get('/')
          .reply(() => [
            200,
            'Hello World!',
            [
              'X-My-Header',
              'My Header value',
              'X-Other-Header',
              'My Other Value',
            ],
          ])
  
        const { headers, rawHeaders } = await got('http://example.test/')
  
        expect(headers).to.deep.equal({
          'x-my-header': 'My Header value',
          'x-other-header': 'My Other Value',
        })
        expect(rawHeaders).to.deep.equal([
          'X-My-Header',
          'My Header value',
          'X-Other-Header',
          'My Other Value',
        ])
        scope.done()
      })
  
      it('as object', async () => {
        const scope = nock('http://example.test')
          .get('/')
          .reply(() => [
            200,
            'Hello World!',
            { 'X-My-Headers': 'My Header value' },
          ])
  
        const { headers, rawHeaders } = await got('http://example.test/')
  
        expect(headers).to.deep.equal({ 'x-my-headers': 'My Header value' })
        expect(rawHeaders).to.deep.equal(['X-My-Headers', 'My Header value'])
        scope.done()
      })
  
      it('as Map', async () => {
        const replyHeaders = new Map([
          ['X-My-Header', 'My Header value'],
          ['X-Other-Header', 'My Other Value'],
        ])
        const scope = nock('http://example.test')
          .get('/')
          .reply(() => [200, 'Hello World!', replyHeaders])
  
        const { headers, rawHeaders } = await got('http://example.test/')
  
        expect(headers).to.deep.equal({
          'x-my-header': 'My Header value',
          'x-other-header': 'My Other Value',
        })
        expect(rawHeaders).to.deep.equal([
          'X-My-Header',
          'My Header value',
          'X-Other-Header',
          'My Other Value',
        ])
        scope.done()
      })
    })
  
    describe('using functions', () => {
      it('sends the result back in the response', async () => {
        const scope = nock('http://example.test')
          .get('/')
          .reply(200, 'boo!', {
            'X-My-Headers': () => 'yo!',
          })
  
        const { headers, rawHeaders } = await got('http://example.test/')
  
        expect(headers).to.deep.equal({ 'x-my-headers': 'yo!' })
        expect(rawHeaders).to.deep.equal(['X-My-Headers', 'yo!'])
        scope.done()
      })
  
      it('receives the correct arguments', async () => {
        const myHeaderFnCalled = sinon.spy()
  
        const { ClientRequest: OverriddenClientRequest } = require('http')
        const scope = nock('http://example.test')
          .post('/')
          .reply(200, 'boo!', {
            'X-My-Headers': (req, res, body) => {
              myHeaderFnCalled()
              expect(req).to.be.an.instanceof(OverriddenClientRequest)
              expect(res).to.be.an.instanceof(IncomingMessage)
              expect(body).to.be.an.instanceof(Buffer)
              expect(Buffer.from('boo!').equals(body)).to.be.true()
              return 'gotcha'
            },
          })
  
        await got.post('http://example.test/')
  
        expect(myHeaderFnCalled).to.have.been.called()
        scope.done()
      })
  
      it('is evaluated exactly once', async () => {
        const myHeaderFnCalled = sinon.spy()
  
        const scope = nock('http://example.test')
          .get('/')
          .reply(200, 'boo!', {
            'X-My-Headers': () => {
              myHeaderFnCalled()
              return 'heya'
            },
          })
  
        await got('http://example.test/')
  
        expect(myHeaderFnCalled).to.have.been.calledOnce()
        scope.done()
      })
  
      it('when keys are duplicated, is evaluated once per input field name, in correct order', async () => {
        const replyHeaders = [
          'X-MY-HEADER',
          () => 'one',
          'x-my-header',
          () => 'two',
        ]
  
        const scope = nock('http://example.test')
          .get('/')
          .reply(200, 'Hello World!', replyHeaders)
  
        const { headers, rawHeaders } = await got('http://example.test/')
  
        expect(headers).to.deep.equal({ 'x-my-header': 'one, two' })
        expect(rawHeaders).to.deep.equal([
          'X-MY-HEADER',
          'one',
          'x-my-header',
          'two',
        ])
  
        scope.done()
      })
  
      it('is re-evaluated for a subsequent request', async () => {
        let counter = 0
        const scope = nock('http://example.test')
          .get('/')
          .times(2)
          .reply(200, 'boo!', {
            'X-My-Headers': () => `${++counter}`,
          })
  
        const { headers, rawHeaders } = await got('http://example.test/')
        expect(headers).to.deep.equal({ 'x-my-headers': '1' })
        expect(rawHeaders).to.deep.equal(['X-My-Headers', '1'])
  
        expect(counter).to.equal(1)
  
        const { headers: headers2, rawHeaders: rawHeaders2 } = await got(
          'http://example.test/'
        )
        expect(headers2).to.deep.equal({ 'x-my-headers': '2' })
        expect(rawHeaders2).to.deep.equal(['X-My-Headers', '2'])
  
        expect(counter).to.equal(2)
  
        scope.done()
      })
    })
  })
  
  describe('`replyContentLength()`', () => {
    it('sends explicit content-length header with response', async () => {
      const response = { hello: 'world' }
  
      const scope = nock('http://example.test')
        .replyContentLength()
        .get('/')
        .reply(200, response)
  
      const { headers } = await got('http://example.test/')
  
      expect(headers['content-length']).to.equal(
        `${JSON.stringify(response).length}`
      )
      scope.done()
    })
  })
  
  describe('`replyDate()`', () => {
    it('sends explicit date header with response', async () => {
      const date = new Date()
  
      const scope = nock('http://example.test')
        .replyDate(date)
        .get('/')
        .reply()
  
      const { headers } = await got('http://example.test/')
  
      expect(headers.date).to.equal(date.toUTCString())
      scope.done()
    })
  
    describe('with mock timers', () => {
      let clock
      beforeEach(() => {
        clock = fakeTimers.install()
      })
      afterEach(() => {
        if (clock) {
          clock.uninstall()
          clock = undefined
        }
      })
  
      it('sends date header with response', async () => {
        const scope = nock('http://example.test')
          .replyDate()
          .get('/')
          .reply()
  
        const req = got('http://example.test/')
        clock.tick()
        const { headers } = await req
        const date = new Date()
        expect(headers).to.include({ date: date.toUTCString() })
  
        scope.done()
      })
    })
  })
  