import { test } from '@japa/runner'
import sinon from 'sinon'
import {
  HttpProject,
  ProjectRequest,
  IResponse,
  Response,
  IRequestLog,
  FlowResponseDataEnum,
  FlowSourceEnum,
  ReadDataSourceStep,
  EqualAssertion,
  DeleteCookieStep,
  ReadDataStep,
  SetVariableStep,
  FlowRequestDataEnum,
  InMemoryCookieJar,
  AmfNamespace as ns,
  createLogger,
  ProjectRequestRunner,
  Environment,
  IRunResult,
  RequestAuthorization,
  IBasicAuthorization,
} from '../../../../src/index.js'

const logger = createLogger()

test.group('Base runs', () => {
  test('runs a request from a folder', async ({ assert, httpConfig }) => {
    const project = new HttpProject()
    const folder = project.addFolder()
    const request = ProjectRequest.fromHttpRequest(
      {
        url: `http://localhost:${httpConfig.httpPort}/v1/get`,
        method: 'GET',
        headers: 'x-test: true',
      },
      project
    )
    folder.addRequest(request)
    const runner = new ProjectRequestRunner(project)
    runner.logger = logger
    const result = await runner.run({ parent: folder.key })
    assert.typeOf(result, 'object', 'returns an object')
    assert.typeOf(result.variables, 'object', 'has the variables object')
    const { items } = result

    assert.typeOf(items, 'array', 'returns an array')
    assert.lengthOf(items, 1, 'has a single result')
    const [report] = items
    assert.typeOf(report, 'object', 'the report is an object')
    assert.equal(report.key, request.key, 'the report has the key')
    assert.isUndefined(report.error, 'the report has no error')
    assert.isUndefined(report.errorMessage, 'the report has no errorMessage')

    const log = report.log as IRequestLog
    assert.typeOf(log, 'object', 'has the log')
  })

  test('runs requests from a folder', async ({ assert, httpConfig }) => {
    const project = new HttpProject()
    const folder = project.addFolder()
    const request1 = ProjectRequest.fromHttpRequest(
      {
        url: `http://localhost:${httpConfig.httpPort}/v1/get`,
        method: 'GET',
        headers: 'x-test: true',
      },
      project
    )
    project.addRequest(request1, {
      parent: folder.key,
    })
    const request2 = ProjectRequest.fromHttpRequest(
      {
        url: `http://localhost:${httpConfig.httpPort}/v1/get`,
        method: 'GET',
        headers: 'x-test: false',
      },
      project
    )
    project.addRequest(request2, {
      parent: folder.key,
    })
    const runner = new ProjectRequestRunner(project)
    runner.logger = logger
    const result = await runner.run({ parent: folder.key })
    const { items } = result
    assert.typeOf(items, 'array', 'returns an array')
    assert.lengthOf(items, 2, 'has both results')
    const [report1, report2] = items
    assert.typeOf(report1, 'object', 'the report1 is an object')
    assert.typeOf(report2, 'object', 'the report1 is an object')
    assert.equal(report1.key, request1.key, 'the report1 has the key')
    assert.equal(report2.key, request2.key, 'the report1 has the key')
    assert.isUndefined(report1.error, 'the report2 has no error')
    assert.isUndefined(report2.error, 'the report2 has no error')

    const log1 = report1.log as IRequestLog
    const payload1 = await Response.readPayloadAsString(log1.response as IResponse)
    const body1 = JSON.parse(payload1 as string)
    assert.equal(body1.headers['x-test'], 'true')

    const log2 = report2.log as IRequestLog
    const payload2 = await Response.readPayloadAsString(log2.response as IResponse)
    const body2 = JSON.parse(payload2 as string)
    assert.equal(body2.headers['x-test'], 'false')
  })

  test('runs a request from a folder only', async ({ assert, httpConfig }) => {
    const project = new HttpProject()
    const folder1 = project.addFolder()
    const request1 = ProjectRequest.fromHttpRequest(
      {
        url: `http://localhost:${httpConfig.httpPort}/v1/get`,
        method: 'GET',
        headers: 'x-test: true',
      },
      project
    )
    project.addRequest(request1, {
      parent: folder1.key,
    })
    const folder2 = project.addFolder()
    const request2 = ProjectRequest.fromHttpRequest(
      {
        url: `http://localhost:${httpConfig.httpPort}/v1/get`,
        method: 'GET',
        headers: 'x-test: true',
      },
      project
    )
    project.addRequest(request2, {
      parent: folder2.key,
    })
    const runner = new ProjectRequestRunner(project)
    runner.logger = logger
    const result = await runner.run({ parent: folder1.key })
    const { items } = result
    assert.lengthOf(items, 1, 'has a single result')
    const [report] = items
    assert.typeOf(report, 'object', 'the report is an object')
    assert.equal(report.key, request1.key, 'the report has the key')
    assert.isUndefined(report.error, 'the report has no error')
    assert.isUndefined(report.errorMessage, 'the report has no errorMessage')

    const log = report.log as IRequestLog
    assert.typeOf(log, 'object', 'has the log')
  })

  test('returns empty array when the project has no requests', async ({ assert }) => {
    const project = new HttpProject()
    const runner = new ProjectRequestRunner(project)
    runner.logger = logger
    const result = await runner.run()
    const { items } = result

    assert.lengthOf(items, 0, 'has no results')
  })

  test('returns empty array when the folder has no requests', async ({ assert }) => {
    const project = new HttpProject()
    const folder = project.addFolder()
    const runner = new ProjectRequestRunner(project)
    runner.logger = logger
    const result = await runner.run({ parent: folder.key })
    const { items } = result
    assert.lengthOf(items, 0, 'has no results')
  })

  test('throws when folder not found', async ({ assert }) => {
    const project = new HttpProject()
    const runner = new ProjectRequestRunner(project)
    runner.logger = logger
    assert.rejects(async () => {
      await runner.run({ parent: 'test' })
    }, `The parent folder not found: test.`)
  })

  test('runs selected requests only', async ({ assert, httpConfig }) => {
    const project = new HttpProject()
    const r1 = project.addRequest(`http://localhost:${httpConfig.httpPort}/v1/get`)
    const r2 = project.addRequest(`http://localhost:${httpConfig.httpPort}/v1/get`)
    r2.info.name = 'included request'
    project.addRequest(`http://localhost:${httpConfig.httpPort}/v1/get`)
    const runner = new ProjectRequestRunner(project)
    runner.logger = logger
    const result = await runner.run({ requests: [r1.key, 'included request'] })
    const { items } = result

    assert.typeOf(items, 'array', 'returns an array')
    assert.lengthOf(items, 2, 'has both results')
  })
})

test.group('Events', () => {
  test('dispatches the lifecycle events', async ({ assert, httpConfig }) => {
    const project = new HttpProject()
    const r1 = ProjectRequest.fromHttpRequest(
      {
        url: `http://localhost:${httpConfig.httpPort}/v1/get`,
        method: 'GET',
        headers: 'x-test: true',
      },
      project
    )
    const request = project.addRequest(r1)
    const runner = new ProjectRequestRunner(project)
    runner.logger = logger
    const requestSpy = sinon.spy()
    const responseSpy = sinon.spy()
    runner.on('request', requestSpy)
    runner.on('response', responseSpy)

    await runner.run()

    assert.equal(requestSpy.callCount, 1)
    assert.equal(responseSpy.callCount, 1)

    const requestKey = requestSpy.args[0][0]
    const requestObj = requestSpy.args[0][1]
    assert.equal(requestKey, request.key, 'the request event has the key')
    assert.deepEqual(requestObj, r1.expects.toJSON(), 'the request event has the HTTP request object')

    const responseKey = responseSpy.args[0][0]
    const responseObj = responseSpy.args[0][1]
    assert.equal(responseKey, request.key, 'the response event has the key')
    assert.typeOf(responseObj, 'object', 'the response event has the log')
    assert.typeOf(responseObj.request, 'object', 'the response event has the log.request')
    assert.typeOf(responseObj.response, 'object', 'the response event has the log.response')
  })
})

test.group('Variables in environments', () => {
  test('applies variables from the project in a folder request', async ({ assert, httpConfig }) => {
    const project = new HttpProject()
    const env = project.addEnvironment('default')
    env.addVariable('httpPort', httpConfig.httpPort)
    const folder = project.addFolder()
    const request = ProjectRequest.fromHttpRequest(
      {
        url: `http://localhost:{httpPort}/v1/get`,
        method: 'GET',
        headers: 'x-test: true',
      },
      project
    )
    project.addRequest(request, {
      parent: folder.key,
    })
    const runner = new ProjectRequestRunner(project)
    runner.logger = logger
    const result = await runner.run({ parent: folder.key })
    const { items } = result
    assert.lengthOf(items, 1, 'has a single result')
    const [report] = items
    assert.typeOf(report, 'object', 'the report is an object')
    assert.equal(report.key, request.key, 'the report has the key')
    assert.isUndefined(report.error, 'the report has no error')
    assert.isUndefined(report.errorMessage, 'the report has no errorMessage')

    const log = report.log as IRequestLog
    assert.typeOf(log, 'object', 'has the log')
    assert.typeOf(log.response as IResponse, 'object', 'has the log.response')
  })

  test('applies variables from a folder', async ({ assert, httpConfig }) => {
    const project = new HttpProject()
    const folder = project.addFolder()
    const env = folder.addEnvironment('default')
    env.addVariable('httpPort', httpConfig.httpPort)
    const request = ProjectRequest.fromHttpRequest(
      {
        url: `http://localhost:{httpPort}/v1/get`,
        method: 'GET',
        headers: 'x-test: true',
      },
      project
    )
    project.addRequest(request, {
      parent: folder.key,
    })
    const runner = new ProjectRequestRunner(project)
    runner.logger = logger
    const result = await runner.run({ parent: folder.key })
    const { items } = result
    assert.lengthOf(items, 1, 'has a single result')
    const [report] = items
    assert.typeOf(report, 'object', 'the report is an object')
    assert.equal(report.key, request.key, 'the report has the key')
    assert.isUndefined(report.error, 'the report has no error')
    assert.isUndefined(report.errorMessage, 'the report has no errorMessage')

    const log = report.log as IRequestLog
    assert.typeOf(log, 'object', 'has the log')
    assert.typeOf(log.response as IResponse, 'object', 'has the log.response')
  })

  test('overrides variables from a parent', async ({ assert, httpConfig }) => {
    const project = new HttpProject()
    const projectEnv = project.addEnvironment('default')
    projectEnv.addVariable('httpPort', 1234567)
    const folder = project.addFolder()
    const env = folder.addEnvironment('default')
    env.addVariable('httpPort', httpConfig.httpPort)
    const request = ProjectRequest.fromHttpRequest(
      {
        url: `http://localhost:{httpPort}/v1/get`,
        method: 'GET',
        headers: 'x-test: true',
      },
      project
    )
    project.addRequest(request, {
      parent: folder.key,
    })
    const runner = new ProjectRequestRunner(project)
    runner.logger = logger
    const result = await runner.run({ parent: folder.key })
    const { items } = result
    assert.lengthOf(items, 1, 'has a single result')
    const [report] = items
    assert.typeOf(report, 'object', 'the report is an object')
    assert.equal(report.key, request.key, 'the report has the key')
    assert.isUndefined(report.error, 'the report has no error')
    assert.isUndefined(report.errorMessage, 'the report has no errorMessage')

    const log = report.log as IRequestLog
    assert.typeOf(log, 'object', 'has the log')
    assert.typeOf(log.response as IResponse, 'object', 'has the log.response')
  })

  test('respects the encapsulated flag', async ({ assert, httpConfig }) => {
    const project = new HttpProject()

    const projectEnv = project.addEnvironment('default')
    projectEnv.addVariable('notIncluded', 'test1')

    const folder = project.addFolder()
    const env = folder.addEnvironment('default')
    env.encapsulated = true
    env.addVariable('included', 'test2')

    const request = ProjectRequest.fromHttpRequest(
      {
        url: `http://localhost:${httpConfig.httpPort}/v1/get`,
        method: 'GET',
        headers: 'x-included: {included}\nx-not-included: {notIncluded}',
      },
      project
    )
    project.addRequest(request, {
      parent: folder.key,
    })
    const runner = new ProjectRequestRunner(project)
    runner.logger = logger
    const result = await runner.run({ parent: folder.key })
    const { items } = result
    const [report] = items
    const log = report.log as IRequestLog
    const payload = await Response.readPayloadAsString(log.response as IResponse)
    const body = JSON.parse(payload as string)
    assert.equal(body.headers['x-included'], 'test2')
    assert.equal(body.headers['x-not-included'], 'undefined')
  })

  test('uses the passed environment instead of the project ones', async ({ assert, httpConfig }) => {
    const project = new HttpProject()
    const env = project.addEnvironment('default')
    env.addVariable('httpPort', '1342') // this would fail the request when used
    const folder = project.addFolder()
    const request = ProjectRequest.fromHttpRequest(
      {
        url: `http://localhost:{httpPort}/v1/get`,
        method: 'GET',
        headers: 'x-test: true',
      },
      project
    )
    folder.addRequest(request)

    const masterEnvironment = Environment.fromName('master')
    masterEnvironment.addVariable('httpPort', httpConfig.httpPort)

    const runner = new ProjectRequestRunner(project, {
      environment: masterEnvironment,
    })
    runner.logger = logger
    runner.on('error', () => {
      //
    })
    const result = await runner.run({ parent: folder.key })
    const { items } = result
    assert.lengthOf(items, 1, 'has a single result')
    const [report] = items
    assert.typeOf(report, 'object', 'the report is an object')
    assert.equal(report.key, request.key, 'the report has the key')

    assert.isUndefined(report.error, 'the report has no error')
    assert.isUndefined(report.errorMessage, 'the report has no errorMessage')

    const log = report.log as IRequestLog
    assert.typeOf(log, 'object', 'has the log')
    assert.typeOf(log.response as IResponse, 'object', 'has the log.response')
    assert.equal(log.response!.status, 200, 'has 200 status code')
  })

  test('shares the generated environment between requests', async ({ assert, httpConfig }) => {
    const project = new HttpProject()
    const r1 = ProjectRequest.fromHttpRequest(
      {
        url: `http://localhost:${httpConfig.httpPort}/v1/response/static`,
        method: 'GET',
      },
      project
    )
    r1.flows = [
      {
        trigger: 'response',
        condition: {
          // source: FlowSourceEnum.response,
          // data: FlowResponseDataEnum.status,
          // operator: AssertionEnum.equal,
          // value: '200',
          steps: [
            new ReadDataStep(FlowSourceEnum.response, '', FlowResponseDataEnum.status).toJSON(),
            new EqualAssertion('200', true, ns.w3.xmlSchema.number).toJSON(),
          ],
        },
        actions: [
          {
            steps: [
              new ReadDataStep(FlowSourceEnum.response, 'data.token', FlowRequestDataEnum.body).toJSON(),
              new SetVariableStep('token').toJSON(),
            ],
          },
        ],
      },
    ]
    const r2 = ProjectRequest.fromHttpRequest(
      {
        url: `http://localhost:${httpConfig.httpPort}/v1/get`,
        method: 'GET',
        headers: 'Authorization: Bearer {token}',
      },
      project
    )
    project.addRequest(r1)
    project.addRequest(r2)

    const runner = new ProjectRequestRunner(project)
    runner.logger = logger
    runner.on('error', () => {
      //
    })
    const result = await runner.run()
    const { items } = result
    assert.lengthOf(items, 2, 'has both logs')
    const [, l2] = items
    const log2 = l2.log as IRequestLog
    const rsp2 = new Response(log2.response as IResponse)
    const body2 = (await rsp2.readPayloadAsString()) as string
    const p2 = JSON.parse(body2)
    assert.equal(p2.headers.authorization, 'Bearer sJxlgNgHi8')
  })
})

test.group('cookies', (group) => {
  let cookies: InMemoryCookieJar

  group.setup(() => {
    cookies = new InMemoryCookieJar()
    return (): void => cookies.clear()
  })

  group.each.teardown(() => {
    cookies.clear()
  })

  test('stores response cookies in the store', async ({ assert, httpConfig }) => {
    const project = new HttpProject()
    const r1 = ProjectRequest.fromHttpRequest(
      {
        url: `http://localhost:${httpConfig.httpPort}/v1/cookie`,
        method: 'GET',
      },
      project
    )
    project.addRequest(r1)

    const runner = new ProjectRequestRunner(project, {
      cookies,
    })
    runner.logger = logger
    runner.on('error', () => {
      //
    })

    const result = await runner.run()
    const { items } = result
    assert.lengthOf(items, 1, 'has a response')

    const stored = await cookies.listCookies(`http://localhost:${httpConfig.httpPort}/v1/cookie`)
    assert.lengthOf(stored, 3, 'has all cookies')
  })

  test('uses stored cookies with the request', async ({ assert, httpConfig }) => {
    const project = new HttpProject()
    const r1 = ProjectRequest.fromHttpRequest(
      {
        url: `http://localhost:${httpConfig.httpPort}/v1/cookie`,
        method: 'GET',
      },
      project
    )
    project.addRequest(r1)
    const r2 = ProjectRequest.fromHttpRequest(
      {
        url: `http://localhost:${httpConfig.httpPort}/v1/get`,
        method: 'GET',
      },
      project
    )
    project.addRequest(r2)

    const runner = new ProjectRequestRunner(project, { cookies })
    runner.logger = logger
    runner.on('error', () => {
      //
    })

    const result = await runner.run()
    const { items } = result
    assert.lengthOf(items, 2, 'has both responses')

    const [, l2] = items
    const log2 = l2.log as IRequestLog
    const rsp2 = new Response(log2.response as IResponse)
    const body2 = (await rsp2.readPayloadAsString()) as string
    const p2 = JSON.parse(body2)
    assert.equal(p2.headers.cookie, 'c1=v1; c2=v2; c3=v3')
  })

  test('uses the store with http flows', async ({ assert, httpConfig }) => {
    const project = new HttpProject()
    const r1 = ProjectRequest.fromHttpRequest(
      {
        url: `http://localhost:${httpConfig.httpPort}/v1/cookie`,
        method: 'GET',
      },
      project
    )
    r1.flows = [
      {
        trigger: 'response',
        // actions are executed after cookies are stored.
        actions: [
          {
            steps: [DeleteCookieStep.byName('c2').toJSON()],
          },
        ],
      },
    ]
    project.addRequest(r1)

    const runner = new ProjectRequestRunner(project, { cookies })
    runner.logger = logger
    runner.on('error', () => {
      //
    })

    const result = await runner.run()
    const { items } = result
    assert.lengthOf(items, 1, 'has the response')

    const stored = await cookies.listCookies(`http://localhost:${httpConfig.httpPort}/v1/cookie`)
    assert.lengthOf(stored, 2, 'has the remaining cookies')
    assert.equal(stored[0].name, 'c1')
    assert.equal(stored[1].name, 'c3')
  })
})

test.group('Base URI', () => {
  test("applies the base URI to the request from server's uri to the variable", async ({ assert, httpConfig }) => {
    const project = new HttpProject()
    const env = project.addEnvironment('default')
    env.addServer(`http://localhost:${httpConfig.httpPort}/v1`)
    const request = ProjectRequest.fromHttpRequest(
      {
        url: `{baseUri}/get`,
        method: 'GET',
        headers: 'x-test: true',
      },
      project
    )
    project.addRequest(request)
    const runner = new ProjectRequestRunner(project)
    runner.logger = logger
    const result = await runner.run()

    const { items } = result
    assert.lengthOf(items, 1, 'has a single result')
    const [report] = items
    assert.typeOf(report, 'object', 'the report is an object')
    assert.equal(report.key, request.key, 'the report has the key')
    assert.isUndefined(report.error, 'the report has no error')
    assert.isUndefined(report.errorMessage, 'the report has no errorMessage')

    const log = report.log as IRequestLog
    assert.typeOf(log, 'object', 'has the log')
    assert.typeOf(log.response as IResponse, 'object', 'has the log.response')
  })

  test("applies the base URI to the request from server's uri without a variable", async ({ assert, httpConfig }) => {
    const project = new HttpProject()
    const env = project.addEnvironment('default')
    env.addServer(`http://localhost:${httpConfig.httpPort}/v1`)
    const request = ProjectRequest.fromHttpRequest(
      {
        url: `/get`,
        method: 'GET',
        headers: 'x-test: true',
      },
      project
    )
    project.addRequest(request)
    const runner = new ProjectRequestRunner(project)
    runner.logger = logger
    const result = await runner.run()
    const { items } = result
    assert.lengthOf(items, 1, 'has a single result')
    const [report] = items
    assert.typeOf(report, 'object', 'the report is an object')
    assert.equal(report.key, request.key, 'the report has the key')
    assert.isUndefined(report.error, 'the report has no error')
    assert.isUndefined(report.errorMessage, 'the report has no errorMessage')

    const log = report.log as IRequestLog
    assert.typeOf(log, 'object', 'has the log')
    assert.typeOf(log.response as IResponse, 'object', 'has the log.response')
  })
})

test.group('[Symbol.asyncIterator]', () => {
  test('executes requests and yields the run result', async ({ assert, httpConfig }) => {
    const project = new HttpProject()
    const request1 = ProjectRequest.fromHttpRequest(
      {
        url: `http://localhost:${httpConfig.httpPort}/v1/get`,
        method: 'GET',
        headers: 'x-request1: true',
      },
      project
    )
    project.addRequest(request1)
    const request2 = ProjectRequest.fromHttpRequest(
      {
        url: `http://localhost:${httpConfig.httpPort}/v1/get`,
        method: 'GET',
        headers: 'x-request2: true',
      },
      project
    )
    const folder = project.addFolder('f1')
    folder.addRequest(request2)
    const runner = new ProjectRequestRunner(project)
    runner.logger = logger
    const items: IRunResult[] = []
    for await (const runResult of runner) {
      items.push(runResult)
    }
    assert.lengthOf(items, 2, 'has both requests')
    const [r1, r2] = items
    assert.equal(r1.key, request1.key, 'the report #1 has the key')
    assert.isUndefined(r1.error, 'the report #1 has no error')
    assert.isUndefined(r1.errorMessage, 'the report #1 has no errorMessage')
    assert.isUndefined(r1.parent, 'the report #1 has no parent')
    const l1 = r1.log as IRequestLog
    assert.typeOf(l1, 'object', 'has the log #1')
    assert.equal(l1.requestId as string, request1.key, 'the log #1 has the requestId')

    assert.equal(r2.key, request2.key, 'the report #2 has the key')
    assert.isUndefined(r2.error, 'the report #2 has no error')
    assert.isUndefined(r2.errorMessage, 'the report #2 has no errorMessage')
    assert.equal(r2.parent, folder.key, 'the report #2 has the parent')
    const l2 = r2.log as IRequestLog
    assert.typeOf(l2, 'object', 'has the log #2')
    assert.equal(l2.requestId as string, request2.key, 'the log #2 has the requestId')
  })
})

test.group('assertions', () => {
  test('produces the assertions report', async ({ assert, httpConfig }) => {
    const project = new HttpProject()
    const request = ProjectRequest.fromHttpRequest(
      {
        url: `http://localhost:${httpConfig.httpPort}/v1/get`,
        method: 'GET',
        headers: 'x-test: true',
      },
      project
    )
    project.addRequest(request)
    request.assertions = [
      {
        description: 'Returns 200 status code',
        steps: [
          new ReadDataSourceStep(FlowSourceEnum.response, FlowResponseDataEnum.status).toJSON(),
          new EqualAssertion('200', true, ns.aml.vocabularies.shapes.number).toJSON(),
        ],
      },
    ]
    const runner = new ProjectRequestRunner(project)
    runner.logger = logger
    const result = await runner.run()
    const log = result.items[0].log as IRequestLog
    assert.typeOf(log.assertions, 'array', 'has the assertions')
    assert.lengthOf(log.assertions!, 1, 'has all assertions')
    assert.isTrue(log.assertions![0].ok, 'the assertion has the value')
  })
})

test.group('authorization', () => {
  test('performs authorization defined on the request', async ({ assert, httpConfig }) => {
    const project = new HttpProject()
    const r1 = ProjectRequest.fromHttpRequest(
      {
        url: `http://localhost:${httpConfig.httpPort}/v1/get`,
        method: 'GET',
      },
      project
    )
    const a1 = RequestAuthorization.fromTypedConfig(
      'basic',
      {
        username: 'a',
        password: 'b',
      } as IBasicAuthorization,
      true
    )
    r1.authorization = [a1]
    project.addRequest(r1)

    const runner = new ProjectRequestRunner(project)
    runner.logger = logger
    const result = await runner.run()
    const { items } = result
    const [report] = items
    const log = report.log as IRequestLog
    const payload = await Response.readPayloadAsString(log.response as IResponse)
    const body = JSON.parse(payload as string)
    assert.equal(body.headers['authorization'], 'Basic YTpi')
  })

  test('performs authorization from the selected environment', async ({ assert, httpConfig }) => {
    const project = new HttpProject()
    const e1 = project.addEnvironment('e1')
    const e2 = project.addEnvironment('e2')
    const a1 = RequestAuthorization.fromTypedConfig(
      'basic',
      {
        username: 'a',
        password: 'b',
      } as IBasicAuthorization,
      true
    )
    const a2 = RequestAuthorization.fromTypedConfig(
      'basic',
      {
        username: 'c',
        password: 'd',
      } as IBasicAuthorization,
      true
    )
    e1.authorization = [a1]
    e2.authorization = [a2]
    const r1 = ProjectRequest.fromHttpRequest(
      {
        url: `http://localhost:${httpConfig.httpPort}/v1/get`,
        method: 'GET',
      },
      project
    )
    r1.environment = e1.key

    project.addRequest(r1)

    const runner = new ProjectRequestRunner(project)
    runner.logger = logger
    const result = await runner.run()
    const { items } = result
    const [report] = items
    const log = report.log as IRequestLog
    const payload = await Response.readPayloadAsString(log.response as IResponse)
    const body = JSON.parse(payload as string)
    // if it wasn't "selected" then the first picked environment would be e2 (tested in the text test)
    assert.equal(body.headers['authorization'], 'Basic YTpi')
  })

  test('performs authorization from the inherited environment', async ({ assert, httpConfig }) => {
    const project = new HttpProject()
    const e1 = project.addEnvironment('e1')
    const e2 = project.addEnvironment('e2')
    const a1 = RequestAuthorization.fromTypedConfig(
      'basic',
      {
        username: 'a',
        password: 'b',
      } as IBasicAuthorization,
      true
    )
    const a2 = RequestAuthorization.fromTypedConfig(
      'basic',
      {
        username: 'c',
        password: 'd',
      } as IBasicAuthorization,
      true
    )
    e1.authorization = [a1]
    e2.authorization = [a2]
    const r1 = ProjectRequest.fromHttpRequest(
      {
        url: `http://localhost:${httpConfig.httpPort}/v1/get`,
        method: 'GET',
      },
      project
    )
    project.addRequest(r1)

    const runner = new ProjectRequestRunner(project)
    runner.logger = logger
    const result = await runner.run()
    const { items } = result
    const [report] = items
    const log = report.log as IRequestLog
    const payload = await Response.readPayloadAsString(log.response as IResponse)
    const body = JSON.parse(payload as string)
    assert.equal(body.headers['authorization'], 'Basic Yzpk')
  })
})
