/* eslint-disable max-len */
import { test } from '@japa/runner'
import {
  FlowResponseDataEnum,
  FlowSourceEnum,
  EqualAssertion,
  AmfNamespace as ns,
  ISentRequest,
  IncludesAssertion,
  IResponse,
  AsLowerCaseStep,
  AsNumberStep,
  AsUpperCaseStep,
  GreaterThanAssertion,
  IHttpAssertion,
  IsEmptyAssertion,
  IsNotEmptyAssertion,
  LengthOfAssertion,
  LessThanAssertion,
  NotEqualAssertion,
  NotIncludesAssertion,
  NotOkAssertion,
  NotToBeAssertion,
  OkAssertion,
  PayloadSerializer,
  ReadDataSourceStep,
  ReadValueStep,
  ToBeAssertion,
  ToBeTypes,
} from '../../../../src/index.js'
import { HttpAssertionRunner } from '../../../../src/runtime/http-runner/HttpAssertionRunner.js'
import driveList from '../../../resources/http/drive-list.js'

const request: ISentRequest = Object.freeze({
  startTime: 0,
  endTime: 0,
  url: 'https://api.com?a=b',
  method: 'GET',
})

test.group('response headers', (group) => {
  let response: IResponse
  let factory: HttpAssertionRunner

  group.each.setup(() => {
    response = {
      loadingTime: 0,
      status: 200,
      headers: 'content-type: text/plain',
    }
    factory = new HttpAssertionRunner()
  })

  test('asserts headers are set', async ({ assert }) => {
    const assertions: IHttpAssertion[] = [
      {
        steps: [
          new ReadDataSourceStep(FlowSourceEnum.response, FlowResponseDataEnum.headers).toJSON(),
          new OkAssertion().toJSON(),
        ],
      },
    ]
    const result = await factory.run(request, response, assertions)
    assert.lengthOf(result, 1, 'has one result')
    const [report] = result
    assert.isTrue(report.ok)
  })

  test('throws when headers are not set', async ({ assert }) => {
    delete response.headers
    const assertions: IHttpAssertion[] = [
      {
        steps: [
          new ReadDataSourceStep(FlowSourceEnum.response, FlowResponseDataEnum.headers).toJSON(),
          new OkAssertion().toJSON(),
        ],
      },
    ]
    const result = await factory.run(request, response, assertions)
    assert.lengthOf(result, 1, 'has one result')
    const [report] = result
    assert.isFalse(report.ok, 'has an error')
  })

  test('asserts header is set', async ({ assert }) => {
    const assertions: IHttpAssertion[] = [
      {
        steps: [
          new ReadDataSourceStep(FlowSourceEnum.response, FlowResponseDataEnum.headers).toJSON(),
          new ReadValueStep('content-type').toJSON(),
          new OkAssertion().toJSON(),
        ],
      },
    ]
    const result = await factory.run(request, response, assertions)
    assert.lengthOf(result, 1, 'has one result')
    const [report] = result
    assert.isTrue(report.ok)
  })

  test('throws when header is not set', async ({ assert }) => {
    const assertions: IHttpAssertion[] = [
      {
        steps: [
          new ReadDataSourceStep(FlowSourceEnum.response, FlowResponseDataEnum.headers).toJSON(),
          new ReadValueStep('x-content-type').toJSON(),
          new OkAssertion().toJSON(),
        ],
      },
    ]
    const result = await factory.run(request, response, assertions)
    assert.lengthOf(result, 1, 'has one result')
    const [report] = result
    assert.isFalse(report.ok)
  })

  test('asserts header value equal', async ({ assert }) => {
    const assertions: IHttpAssertion[] = [
      {
        steps: [
          new ReadDataSourceStep(FlowSourceEnum.response, FlowResponseDataEnum.headers).toJSON(),
          new ReadValueStep('content-type').toJSON(),
          new EqualAssertion('text/plain').toJSON(),
        ],
      },
    ]
    const result = await factory.run(request, response, assertions)
    assert.lengthOf(result, 1, 'has one result')
    const [report] = result
    assert.isTrue(report.ok)
  })

  test('throws when header value is different', async ({ assert }) => {
    const assertions: IHttpAssertion[] = [
      {
        steps: [
          new ReadDataSourceStep(FlowSourceEnum.response, FlowResponseDataEnum.headers).toJSON(),
          new ReadValueStep('content-type').toJSON(),
          new EqualAssertion('application/json').toJSON(),
        ],
      },
    ]
    const result = await factory.run(request, response, assertions)
    assert.lengthOf(result, 1, 'has one result')
    const [report] = result

    assert.isFalse(report.ok)
    assert.equal(report.error!.actual, 'text/plain')
    assert.equal(report.error!.expected, 'application/json')
    assert.equal(report.error!.operator, 'strictEqual')
  })

  test('asserts header value not equal', async ({ assert }) => {
    const assertions: IHttpAssertion[] = [
      {
        steps: [
          new ReadDataSourceStep(FlowSourceEnum.response, FlowResponseDataEnum.headers).toJSON(),
          new ReadValueStep('content-type').toJSON(),
          new NotEqualAssertion('text/other').toJSON(),
        ],
      },
    ]
    const result = await factory.run(request, response, assertions)
    assert.lengthOf(result, 1, 'has one result')
    const [report] = result
    assert.isTrue(report.ok)
  })

  test('throws when header value equals while it should not equal', async ({ assert }) => {
    const assertions: IHttpAssertion[] = [
      {
        steps: [
          new ReadDataSourceStep(FlowSourceEnum.response, FlowResponseDataEnum.headers).toJSON(),
          new ReadValueStep('content-type').toJSON(),
          new NotEqualAssertion('text/plain').toJSON(),
        ],
      },
    ]
    const result = await factory.run(request, response, assertions)
    assert.lengthOf(result, 1, 'has one result')
    const [report] = result
    assert.isFalse(report.ok)

    assert.equal(report.error!.actual, 'text/plain')
    assert.equal(report.error!.expected, 'text/plain')
    assert.equal(report.error!.operator, 'notStrictEqual')
  })

  test('asserts headers include value', async ({ assert }) => {
    response.headers += '\nx-retry: 200'
    const assertions: IHttpAssertion[] = [
      {
        steps: [
          new ReadDataSourceStep(FlowSourceEnum.response, FlowResponseDataEnum.headers).toJSON(),
          new IncludesAssertion('content-type: text/plain').toJSON(),
        ],
      },
    ]
    const result = await factory.run(request, response, assertions)
    assert.lengthOf(result, 1, 'has one result')
    const [report] = result
    assert.isTrue(report.ok)
  })

  test('throws when headers include value is missing', async ({ assert }) => {
    const assertions: IHttpAssertion[] = [
      {
        steps: [
          new ReadDataSourceStep(FlowSourceEnum.response, FlowResponseDataEnum.headers).toJSON(),
          new IncludesAssertion('something').toJSON(),
        ],
      },
    ]
    const result = await factory.run(request, response, assertions)
    assert.lengthOf(result, 1, 'has one result')
    const [report] = result
    assert.isFalse(report.ok)
    assert.equal(report.error!.message, "expected 'content-type: text/plain' to include 'something'")
  })

  test('asserts headers not include value', async ({ assert }) => {
    const assertions: IHttpAssertion[] = [
      {
        steps: [
          new ReadDataSourceStep(FlowSourceEnum.response, FlowResponseDataEnum.headers).toJSON(),
          new NotIncludesAssertion('something').toJSON(),
        ],
      },
    ]
    const result = await factory.run(request, response, assertions)
    assert.lengthOf(result, 1, 'has one result')
    const [report] = result
    assert.isTrue(report.ok)
  })

  test('throws when headers not include value is set', async ({ assert }) => {
    const assertions: IHttpAssertion[] = [
      {
        steps: [
          new ReadDataSourceStep(FlowSourceEnum.response, FlowResponseDataEnum.headers).toJSON(),
          new NotIncludesAssertion('content-type').toJSON(),
        ],
      },
    ]
    const result = await factory.run(request, response, assertions)
    assert.lengthOf(result, 1, 'has one result')
    const [report] = result
    assert.isFalse(report.ok)
    assert.equal(report.error!.message, "expected 'content-type: text/plain' to not include 'content-type'")
  })

  test('assets header value as number is strict equal', async ({ assert }) => {
    response.headers += '\nx-retry: 200'
    const assertions: IHttpAssertion[] = [
      {
        steps: [
          new ReadDataSourceStep(FlowSourceEnum.response, FlowResponseDataEnum.headers).toJSON(),
          new ReadValueStep('x-retry').toJSON(),
          new AsNumberStep().toJSON(),
          new EqualAssertion('200', true, ns.aml.vocabularies.shapes.number).toJSON(),
        ],
      },
    ]
    const result = await factory.run(request, response, assertions)
    assert.lengthOf(result, 1, 'has one result')
    const [report] = result
    assert.isTrue(report.ok)
  })

  test('throws when header value not as number is not strict equal', async ({ assert }) => {
    response.headers += '\nx-retry: 200'
    const assertions: IHttpAssertion[] = [
      {
        steps: [
          new ReadDataSourceStep(FlowSourceEnum.response, FlowResponseDataEnum.headers).toJSON(),
          new ReadValueStep('x-retry').toJSON(),
          new EqualAssertion('200', true, ns.aml.vocabularies.shapes.number).toJSON(),
        ],
      },
    ]
    const result = await factory.run(request, response, assertions)
    assert.lengthOf(result, 1, 'has one result')
    const [report] = result
    assert.isFalse(report.ok)
    assert.equal(report.error!.message, "expected '200' to equal 200")
  })

  test('assets header value equal with lowercase transformation', async ({ assert }) => {
    response.headers += '\nx-case: aBcD'
    const assertions: IHttpAssertion[] = [
      {
        steps: [
          new ReadDataSourceStep(FlowSourceEnum.response, FlowResponseDataEnum.headers).toJSON(),
          new ReadValueStep('x-case').toJSON(),
          new AsLowerCaseStep().toJSON(),
          new EqualAssertion('abcd').toJSON(),
        ],
      },
    ]
    const result = await factory.run(request, response, assertions)
    assert.lengthOf(result, 1, 'has one result')
    const [report] = result
    assert.isTrue(report.ok)
  })

  test('assets header value equal with uppercase transformation', async ({ assert }) => {
    response.headers += '\nx-case: aBcD'
    const assertions: IHttpAssertion[] = [
      {
        steps: [
          new ReadDataSourceStep(FlowSourceEnum.response, FlowResponseDataEnum.headers).toJSON(),
          new ReadValueStep('x-case').toJSON(),
          new AsUpperCaseStep().toJSON(),
          new EqualAssertion('ABCD').toJSON(),
        ],
      },
    ]
    const result = await factory.run(request, response, assertions)
    assert.lengthOf(result, 1, 'has one result')
    const [report] = result
    assert.isTrue(report.ok)
  })

  test('assets header value as number is greater than', async ({ assert }) => {
    response.headers += '\nx-retry: 200'
    const assertions: IHttpAssertion[] = [
      {
        steps: [
          new ReadDataSourceStep(FlowSourceEnum.response, FlowResponseDataEnum.headers).toJSON(),
          new ReadValueStep('x-retry').toJSON(),
          new AsNumberStep().toJSON(),
          new GreaterThanAssertion(100).toJSON(),
        ],
      },
    ]
    const result = await factory.run(request, response, assertions)
    assert.lengthOf(result, 1, 'has one result')
    const [report] = result
    assert.isTrue(report.ok)
  })

  test('throws when header value as number is not greater than', async ({ assert }) => {
    response.headers += '\nx-retry: 200'
    const assertions: IHttpAssertion[] = [
      {
        steps: [
          new ReadDataSourceStep(FlowSourceEnum.response, FlowResponseDataEnum.headers).toJSON(),
          new ReadValueStep('x-retry').toJSON(),
          new AsNumberStep().toJSON(),
          new GreaterThanAssertion(200).toJSON(),
        ],
      },
    ]
    const result = await factory.run(request, response, assertions)
    assert.lengthOf(result, 1, 'has one result')
    const [report] = result
    assert.isFalse(report.ok)
    assert.equal(report.error!.message, 'expected 200 to be above 200')
  })

  test('assets header value as number is greater than or equal', async ({ assert }) => {
    response.headers += '\nx-retry: 200'
    const assertions: IHttpAssertion[] = [
      {
        steps: [
          new ReadDataSourceStep(FlowSourceEnum.response, FlowResponseDataEnum.headers).toJSON(),
          new ReadValueStep('x-retry').toJSON(),
          new AsNumberStep().toJSON(),
          new GreaterThanAssertion(200, true).toJSON(),
        ],
      },
    ]
    const result = await factory.run(request, response, assertions)
    assert.lengthOf(result, 1, 'has one result')
    const [report] = result
    assert.isTrue(report.ok)
  })

  test('throws when header value as number is not greater than or equal', async ({ assert }) => {
    response.headers += '\nx-retry: 200'
    const assertions: IHttpAssertion[] = [
      {
        steps: [
          new ReadDataSourceStep(FlowSourceEnum.response, FlowResponseDataEnum.headers).toJSON(),
          new ReadValueStep('x-retry').toJSON(),
          new AsNumberStep().toJSON(),
          new GreaterThanAssertion(201, true).toJSON(),
        ],
      },
    ]
    const result = await factory.run(request, response, assertions)
    assert.lengthOf(result, 1, 'has one result')
    const [report] = result
    assert.isFalse(report.ok)
    assert.equal(report.error!.message, 'expected 200 to be at least 201')
    assert.equal(report.error!.actual, 200)
    assert.equal(report.error!.expected, 201)
    assert.equal(report.error!.operator, 'strictEqual')
  })
})

test.group('response status', (group) => {
  let response: IResponse
  let factory: HttpAssertionRunner

  group.each.setup(() => {
    response = {
      loadingTime: 0,
      status: 200,
      headers: 'content-type: text/plain',
    }
    factory = new HttpAssertionRunner()
  })

  test('asserts a specific value (strict)', async ({ assert }) => {
    const assertions: IHttpAssertion[] = [
      {
        steps: [
          new ReadDataSourceStep(FlowSourceEnum.response, FlowResponseDataEnum.status).toJSON(),
          new EqualAssertion('200', true, ns.aml.vocabularies.shapes.number).toJSON(),
        ],
      },
    ]
    const result = await factory.run(request, response, assertions)
    assert.lengthOf(result, 1, 'has one result')
    const [report] = result
    assert.isTrue(report.ok)
  })

  test('asserts a specific value (not strict)', async ({ assert }) => {
    const assertions: IHttpAssertion[] = [
      {
        steps: [
          new ReadDataSourceStep(FlowSourceEnum.response, FlowResponseDataEnum.status).toJSON(),
          new EqualAssertion('200').toJSON(),
        ],
      },
    ]
    const result = await factory.run(request, response, assertions)
    assert.lengthOf(result, 1, 'has one result')
    const [report] = result
    assert.isTrue(report.ok)
  })

  test('asserts is greater', async ({ assert }) => {
    const assertions: IHttpAssertion[] = [
      {
        steps: [
          new ReadDataSourceStep(FlowSourceEnum.response, FlowResponseDataEnum.status).toJSON(),
          new GreaterThanAssertion(199).toJSON(),
        ],
      },
    ]
    const result = await factory.run(request, response, assertions)
    assert.lengthOf(result, 1, 'has one result')
    const [report] = result
    assert.isTrue(report.ok)
  })

  test('asserts is greater or equal', async ({ assert }) => {
    const assertions: IHttpAssertion[] = [
      {
        steps: [
          new ReadDataSourceStep(FlowSourceEnum.response, FlowResponseDataEnum.status).toJSON(),
          new GreaterThanAssertion(200, true).toJSON(),
        ],
      },
    ]
    const result = await factory.run(request, response, assertions)
    assert.lengthOf(result, 1, 'has one result')
    const [report] = result
    assert.isTrue(report.ok)
  })

  test('asserts is less', async ({ assert }) => {
    const assertions: IHttpAssertion[] = [
      {
        steps: [
          new ReadDataSourceStep(FlowSourceEnum.response, FlowResponseDataEnum.status).toJSON(),
          new LessThanAssertion(299).toJSON(),
        ],
      },
    ]
    const result = await factory.run(request, response, assertions)
    assert.lengthOf(result, 1, 'has one result')
    const [report] = result
    assert.isTrue(report.ok)
  })

  test('asserts is greater or equal', async ({ assert }) => {
    const assertions: IHttpAssertion[] = [
      {
        steps: [
          new ReadDataSourceStep(FlowSourceEnum.response, FlowResponseDataEnum.status).toJSON(),
          new LessThanAssertion(200, true).toJSON(),
        ],
      },
    ]
    const result = await factory.run(request, response, assertions)
    assert.lengthOf(result, 1, 'has one result')
    const [report] = result
    assert.isTrue(report.ok)
  })

  test('asserts within a range (exclusive)', async ({ assert }) => {
    const assertions: IHttpAssertion[] = [
      {
        steps: [
          new ReadDataSourceStep(FlowSourceEnum.response, FlowResponseDataEnum.status).toJSON(),
          new GreaterThanAssertion(199).toJSON(),
          new LessThanAssertion(299).toJSON(),
        ],
      },
    ]
    const result = await factory.run(request, response, assertions)
    assert.lengthOf(result, 1, 'has one result')
    const [report] = result
    assert.isTrue(report.ok)
  })

  test('asserts within a range (inclusive)', async ({ assert }) => {
    const assertions: IHttpAssertion[] = [
      {
        steps: [
          new ReadDataSourceStep(FlowSourceEnum.response, FlowResponseDataEnum.status).toJSON(),
          new GreaterThanAssertion(200, true).toJSON(),
          new LessThanAssertion(200, true).toJSON(),
        ],
      },
    ]
    const result = await factory.run(request, response, assertions)
    assert.lengthOf(result, 1, 'has one result')
    const [report] = result
    assert.isTrue(report.ok)
  })

  test('throws when value does not equal (strict)', async ({ assert }) => {
    const assertions: IHttpAssertion[] = [
      {
        steps: [
          new ReadDataSourceStep(FlowSourceEnum.response, FlowResponseDataEnum.status).toJSON(),
          new EqualAssertion('204', true, ns.aml.vocabularies.shapes.number).toJSON(),
        ],
      },
    ]
    const result = await factory.run(request, response, assertions)
    assert.lengthOf(result, 1, 'has one result')
    const [report] = result
    assert.isFalse(report.ok)
    assert.equal(report.error!.message, 'expected 200 to equal 204')
    assert.equal(report.error!.actual, 200)
    assert.equal(report.error!.expected, 204)
    assert.equal(report.error!.operator, 'strictEqual')
  })

  test('throws when value does not equal (not strict)', async ({ assert }) => {
    const assertions: IHttpAssertion[] = [
      {
        steps: [
          new ReadDataSourceStep(FlowSourceEnum.response, FlowResponseDataEnum.status).toJSON(),
          new EqualAssertion('204').toJSON(),
        ],
      },
    ]
    const result = await factory.run(request, response, assertions)
    assert.lengthOf(result, 1, 'has one result')
    const [report] = result
    assert.isFalse(report.ok)
    assert.equal(report.error!.actual, 200)
    assert.equal(report.error!.expected, 204)
    assert.equal(report.error!.operator, 'strictEqual')
  })
})

test.group('response status text', (group) => {
  let response: IResponse
  let factory: HttpAssertionRunner

  group.each.setup(() => {
    response = {
      loadingTime: 0,
      status: 200,
      headers: 'content-type: text/plain',
      statusText: 'OK',
    }
    factory = new HttpAssertionRunner()
  })

  test('asserts a specific value', async ({ assert }) => {
    const assertions: IHttpAssertion[] = [
      {
        steps: [
          new ReadDataSourceStep(FlowSourceEnum.response, FlowResponseDataEnum.statusText).toJSON(),
          new EqualAssertion('OK', true).toJSON(),
        ],
      },
    ]
    const result = await factory.run(request, response, assertions)
    assert.lengthOf(result, 1, 'has one result')
    const [report] = result
    assert.isTrue(report.ok)
  })

  test('throws when value does not equal (strict)', async ({ assert }) => {
    const assertions: IHttpAssertion[] = [
      {
        steps: [
          new ReadDataSourceStep(FlowSourceEnum.response, FlowResponseDataEnum.statusText).toJSON(),
          new EqualAssertion('Created').toJSON(),
        ],
      },
    ]
    const result = await factory.run(request, response, assertions)
    assert.lengthOf(result, 1, 'has one result')
    const [report] = result
    assert.isFalse(report.ok)
    assert.equal(report.error!.message, "expected 'OK' to equal 'Created'")
    assert.equal(report.error!.actual, 'OK')
    assert.equal(report.error!.expected, 'Created')
    assert.equal(report.error!.operator, 'strictEqual')
  })
})

test.group('JSON response body', (group) => {
  let response: IResponse
  let factory: HttpAssertionRunner

  const body = {
    test: true,
    number: 1,
    deep: {
      value: 'yes',
      deeper: [{ kind: 'a1' }],
    },

    cursor: 'a cursor string',
    items: [
      {
        kind: 'product',
        name: 'My product',
        price: 14.45,
        unit: 'ml',
        available: false,
        category: '',
        lowestPrice: 0,
      },
    ],
  }

  group.each.setup(async () => {
    const payload = await PayloadSerializer.serialize(JSON.stringify(body))
    response = {
      loadingTime: 0,
      status: 200,
      headers: 'content-type: application/json',
      statusText: 'OK',
      payload,
    }
    factory = new HttpAssertionRunner()
  })

  test('asserts has a body', async ({ assert }) => {
    const assertions: IHttpAssertion[] = [
      {
        steps: [
          new ReadDataSourceStep(FlowSourceEnum.response, FlowResponseDataEnum.body).toJSON(),
          new OkAssertion().toJSON(),
        ],
      },
    ]
    const result = await factory.run(request, response, assertions)
    assert.lengthOf(result, 1, 'has one result')
    const [report] = result
    assert.isTrue(report.ok)
  })

  test('throws when has a body for not-ok', async ({ assert }) => {
    const assertions: IHttpAssertion[] = [
      {
        steps: [
          new ReadDataSourceStep(FlowSourceEnum.response, FlowResponseDataEnum.body).toJSON(),
          new NotOkAssertion().toJSON(),
        ],
      },
    ]
    const result = await factory.run(request, response, assertions)
    assert.lengthOf(result, 1, 'has one result')
    const [report] = result
    assert.isFalse(report.ok)
    assert.equal(report.error!.message, `expected '${JSON.stringify(body)}' to be falsy`)
    assert.equal(report.error!.actual, JSON.stringify(body))
    assert.isUndefined(report.error!.expected)
    assert.equal(report.error!.operator, 'strictEqual')
  })

  test('asserts body includes', async ({ assert }) => {
    const assertions: IHttpAssertion[] = [
      {
        steps: [
          new ReadDataSourceStep(FlowSourceEnum.response, FlowResponseDataEnum.body).toJSON(),
          new IncludesAssertion('"value":"yes"').toJSON(),
        ],
      },
    ]
    const result = await factory.run(request, response, assertions)
    assert.lengthOf(result, 1, 'has one result')
    const [report] = result
    assert.isTrue(report.ok)
  })

  test('throws body includes', async ({ assert }) => {
    const assertions: IHttpAssertion[] = [
      {
        steps: [
          new ReadDataSourceStep(FlowSourceEnum.response, FlowResponseDataEnum.body).toJSON(),
          new IncludesAssertion('something').toJSON(),
        ],
      },
    ]
    const result = await factory.run(request, response, assertions)
    assert.lengthOf(result, 1, 'has one result')
    const [report] = result
    assert.isFalse(report.ok)
    assert.equal(report.error!.message, `expected '${JSON.stringify(body)}' to include 'something'`)
    assert.equal(report.error!.actual, JSON.stringify(body))
  })

  test('asserts body not includes', async ({ assert }) => {
    const assertions: IHttpAssertion[] = [
      {
        steps: [
          new ReadDataSourceStep(FlowSourceEnum.response, FlowResponseDataEnum.body).toJSON(),
          new NotIncludesAssertion('something').toJSON(),
        ],
      },
    ]
    const result = await factory.run(request, response, assertions)
    assert.lengthOf(result, 1, 'has one result')
    const [report] = result
    assert.isTrue(report.ok)
  })

  test('asserts body value equals (string, non-strict)', async ({ assert }) => {
    const assertions: IHttpAssertion[] = [
      {
        steps: [
          new ReadDataSourceStep(FlowSourceEnum.response, FlowResponseDataEnum.body).toJSON(),
          new ReadValueStep('deep.value').toJSON(),
          new EqualAssertion('yes').toJSON(),
        ],
      },
    ]
    const result = await factory.run(request, response, assertions)
    assert.lengthOf(result, 1, 'has one result')
    const [report] = result
    assert.isTrue(report.ok)
  })

  test('asserts body value equals (boolean, non-strict)', async ({ assert }) => {
    const assertions: IHttpAssertion[] = [
      {
        steps: [
          new ReadDataSourceStep(FlowSourceEnum.response, FlowResponseDataEnum.body).toJSON(),
          new ReadValueStep('test').toJSON(),
          new EqualAssertion('true').toJSON(),
        ],
      },
    ]
    const result = await factory.run(request, response, assertions)
    assert.lengthOf(result, 1, 'has one result')
    const [report] = result
    assert.isTrue(report.ok)
  })

  test('asserts body value equals (boolean, strict)', async ({ assert }) => {
    const assertions: IHttpAssertion[] = [
      {
        steps: [
          new ReadDataSourceStep(FlowSourceEnum.response, FlowResponseDataEnum.body).toJSON(),
          new ReadValueStep('test').toJSON(),
          new EqualAssertion('true', true, ns.aml.vocabularies.shapes.boolean).toJSON(),
        ],
      },
    ]
    const result = await factory.run(request, response, assertions)
    assert.lengthOf(result, 1, 'has one result')
    const [report] = result
    assert.isTrue(report.ok)
  })

  test('asserts body value equals (number, non-strict)', async ({ assert }) => {
    const assertions: IHttpAssertion[] = [
      {
        steps: [
          new ReadDataSourceStep(FlowSourceEnum.response, FlowResponseDataEnum.body).toJSON(),
          new ReadValueStep('number').toJSON(),
          new EqualAssertion('1').toJSON(),
        ],
      },
    ]
    const result = await factory.run(request, response, assertions)
    assert.lengthOf(result, 1, 'has one result')
    const [report] = result
    assert.isTrue(report.ok)
  })

  test('asserts body value equals (number, strict)', async ({ assert }) => {
    const assertions: IHttpAssertion[] = [
      {
        steps: [
          new ReadDataSourceStep(FlowSourceEnum.response, FlowResponseDataEnum.body).toJSON(),
          new ReadValueStep('number').toJSON(),
          new EqualAssertion('1', true, ns.aml.vocabularies.shapes.number).toJSON(),
        ],
      },
    ]
    const result = await factory.run(request, response, assertions)
    assert.lengthOf(result, 1, 'has one result')
    const [report] = result
    assert.isTrue(report.ok)
  })

  test('asserts body includes under a path (string value)', async ({ assert }) => {
    const assertions: IHttpAssertion[] = [
      {
        steps: [
          new ReadDataSourceStep(FlowSourceEnum.response, FlowResponseDataEnum.body).toJSON(),
          new ReadValueStep('cursor').toJSON(),
          new IncludesAssertion('cursor').toJSON(),
        ],
      },
    ]
    const result = await factory.run(request, response, assertions)
    assert.lengthOf(result, 1, 'has one result')
    const [report] = result
    assert.isTrue(report.ok)
  })

  test('asserts body includes under a path (number value)', async ({ assert }) => {
    const assertions: IHttpAssertion[] = [
      {
        steps: [
          new ReadDataSourceStep(FlowSourceEnum.response, FlowResponseDataEnum.body).toJSON(),
          new ReadValueStep('items[0].price').toJSON(),
          new IncludesAssertion('14').toJSON(),
        ],
      },
    ]
    const result = await factory.run(request, response, assertions)
    assert.lengthOf(result, 1, 'has one result')
    const [report] = result
    assert.isTrue(report.ok)
  })

  test('asserts body includes under a path (object value)', async ({ assert }) => {
    const assertions: IHttpAssertion[] = [
      {
        steps: [
          new ReadDataSourceStep(FlowSourceEnum.response, FlowResponseDataEnum.body).toJSON(),
          new ReadValueStep('deep').toJSON(),
          new IncludesAssertion('yes').toJSON(),
        ],
      },
    ]
    const result = await factory.run(request, response, assertions)
    assert.lengthOf(result, 1, 'has one result')
    const [report] = result
    assert.isTrue(report.ok)
  })

  test('asserts body not includes under a path (string value)', async ({ assert }) => {
    const assertions: IHttpAssertion[] = [
      {
        steps: [
          new ReadDataSourceStep(FlowSourceEnum.response, FlowResponseDataEnum.body).toJSON(),
          new ReadValueStep('cursor').toJSON(),
          new NotIncludesAssertion('something').toJSON(),
        ],
      },
    ]
    const result = await factory.run(request, response, assertions)
    assert.lengthOf(result, 1, 'has one result')
    const [report] = result
    assert.isTrue(report.ok)
  })

  test('asserts body not includes under a path (object value)', async ({ assert }) => {
    const assertions: IHttpAssertion[] = [
      {
        steps: [
          new ReadDataSourceStep(FlowSourceEnum.response, FlowResponseDataEnum.body).toJSON(),
          new ReadValueStep('deep').toJSON(),
          new NotIncludesAssertion('something').toJSON(),
        ],
      },
    ]
    const result = await factory.run(request, response, assertions)
    assert.lengthOf(result, 1, 'has one result')
    const [report] = result
    assert.isTrue(report.ok)
  })

  test('asserts body value is greater under a path', async ({ assert }) => {
    const assertions: IHttpAssertion[] = [
      {
        steps: [
          new ReadDataSourceStep(FlowSourceEnum.response, FlowResponseDataEnum.body).toJSON(),
          new ReadValueStep('number').toJSON(),
          new GreaterThanAssertion(0).toJSON(),
        ],
      },
    ]
    const result = await factory.run(request, response, assertions)
    assert.lengthOf(result, 1, 'has one result')
    const [report] = result
    assert.isTrue(report.ok)
  })

  test('asserts body value is less under a path', async ({ assert }) => {
    const assertions: IHttpAssertion[] = [
      {
        steps: [
          new ReadDataSourceStep(FlowSourceEnum.response, FlowResponseDataEnum.body).toJSON(),
          new ReadValueStep('number').toJSON(),
          new LessThanAssertion(100).toJSON(),
        ],
      },
    ]
    const result = await factory.run(request, response, assertions)
    assert.lengthOf(result, 1, 'has one result')
    const [report] = result
    assert.isTrue(report.ok)
  })

  test('asserts value is ok under a path (string value)', async ({ assert }) => {
    const assertions: IHttpAssertion[] = [
      {
        steps: [
          new ReadDataSourceStep(FlowSourceEnum.response, FlowResponseDataEnum.body).toJSON(),
          new ReadValueStep('cursor').toJSON(),
          new OkAssertion().toJSON(),
        ],
      },
    ]
    const result = await factory.run(request, response, assertions)
    assert.lengthOf(result, 1, 'has one result')
    const [report] = result
    assert.isTrue(report.ok)
  })

  test('asserts value is ok under a path (number value)', async ({ assert }) => {
    const assertions: IHttpAssertion[] = [
      {
        steps: [
          new ReadDataSourceStep(FlowSourceEnum.response, FlowResponseDataEnum.body).toJSON(),
          new ReadValueStep('number').toJSON(),
          new OkAssertion().toJSON(),
        ],
      },
    ]
    const result = await factory.run(request, response, assertions)
    assert.lengthOf(result, 1, 'has one result')
    const [report] = result
    assert.isTrue(report.ok)
  })

  test('asserts value is ok under a path (object value)', async ({ assert }) => {
    const assertions: IHttpAssertion[] = [
      {
        steps: [
          new ReadDataSourceStep(FlowSourceEnum.response, FlowResponseDataEnum.body).toJSON(),
          new ReadValueStep('deep').toJSON(),
          new OkAssertion().toJSON(),
        ],
      },
    ]
    const result = await factory.run(request, response, assertions)
    assert.lengthOf(result, 1, 'has one result')
    const [report] = result
    assert.isTrue(report.ok)
  })

  test('asserts value is not ok under a path (string value)', async ({ assert }) => {
    const assertions: IHttpAssertion[] = [
      {
        steps: [
          new ReadDataSourceStep(FlowSourceEnum.response, FlowResponseDataEnum.body).toJSON(),
          new ReadValueStep('items[0].category').toJSON(),
          new NotOkAssertion().toJSON(),
        ],
      },
    ]
    const result = await factory.run(request, response, assertions)
    assert.lengthOf(result, 1, 'has one result')
    const [report] = result
    assert.isTrue(report.ok)
  })

  test('asserts value is not ok under a path (number value)', async ({ assert }) => {
    const assertions: IHttpAssertion[] = [
      {
        steps: [
          new ReadDataSourceStep(FlowSourceEnum.response, FlowResponseDataEnum.body).toJSON(),
          new ReadValueStep('items[0].lowestPrice').toJSON(),
          new NotOkAssertion().toJSON(),
        ],
      },
    ]
    const result = await factory.run(request, response, assertions)
    assert.lengthOf(result, 1, 'has one result')
    const [report] = result
    assert.isTrue(report.ok)
  })

  test('asserts value is not ok under a path (object value)', async ({ assert }) => {
    const assertions: IHttpAssertion[] = [
      {
        steps: [
          new ReadDataSourceStep(FlowSourceEnum.response, FlowResponseDataEnum.body).toJSON(),
          new ReadValueStep('items[0].something').toJSON(),
          new NotOkAssertion().toJSON(),
        ],
      },
    ]
    const result = await factory.run(request, response, assertions)
    assert.lengthOf(result, 1, 'has one result')
    const [report] = result
    assert.isTrue(report.ok)
  })
})

test.group('real life scenarios', (group) => {
  let response: IResponse
  let factory: HttpAssertionRunner

  group.each.setup(async () => {
    response = {
      loadingTime: 0,
      status: 200,
      headers: 'content-type: application/json',
      statusText: 'OK',
    }
    factory = new HttpAssertionRunner()
  })

  test('runs a series of assertions (all passed)', async ({ assert }) => {
    response.headers =
      'cache-control: no-cache, no-store, max-age=0, must-revalidate\ncontent-encoding: gzip\ncontent-length: 5506\ncontent-type: application/json; charset=UTF-8\ndate: Mon, 30 Jan 2023 03: 03: 21 GMT\nexpires: Mon, 01 Jan 1990 00: 00: 00 GMT\npragma: no-cache\nserver: ESF\nvary: Origin, X-Origin'
    response.payload = await PayloadSerializer.serialize(JSON.stringify(driveList))
    const assertions: IHttpAssertion[] = [
      {
        description: 'Returns expected status code',
        steps: [
          new ReadDataSourceStep(FlowSourceEnum.response, FlowResponseDataEnum.status).toJSON(),
          new EqualAssertion('200', true, ns.aml.vocabularies.shapes.number).toJSON(),
        ],
      },
      {
        description: 'Sets the "cache-control" header',
        steps: [
          new ReadDataSourceStep(FlowSourceEnum.response, FlowResponseDataEnum.headers).toJSON(),
          new ReadValueStep('cache-control'),
          new IncludesAssertion('no-cache, no-store, max-age=0, must-revalidate').toJSON(),
        ],
      },
      {
        description: 'Response is gzipped',
        steps: [
          new ReadDataSourceStep(FlowSourceEnum.response, FlowResponseDataEnum.headers).toJSON(),
          new ReadValueStep('content-encoding').toJSON(),
          new EqualAssertion('gzip').toJSON(),
        ],
      },
      {
        description: 'Is a JSON response',
        steps: [
          new ReadDataSourceStep(FlowSourceEnum.response, FlowResponseDataEnum.headers).toJSON(),
          new ReadValueStep('content-type').toJSON(),
          new IncludesAssertion('application/json').toJSON(),
        ],
      },
      {
        description: 'Has the nextPageToken property',
        steps: [
          new ReadDataSourceStep(FlowSourceEnum.response, FlowResponseDataEnum.body).toJSON(),
          new ReadValueStep('nextPageToken').toJSON(),
          new OkAssertion().toJSON(),
          new ToBeAssertion(ToBeTypes.string).toJSON(),
          new IsNotEmptyAssertion().toJSON(),
        ],
      },
      {
        description: 'Has the kind property',
        steps: [
          new ReadDataSourceStep(FlowSourceEnum.response, FlowResponseDataEnum.body).toJSON(),
          new ReadValueStep('kind').toJSON(),
          new EqualAssertion('drive#fileList').toJSON(),
        ],
      },
      {
        description: 'Has the incompleteSearch property set to false',
        steps: [
          new ReadDataSourceStep(FlowSourceEnum.response, FlowResponseDataEnum.body).toJSON(),
          new ReadValueStep('incompleteSearch').toJSON(),
          new EqualAssertion('false', true, ns.aml.vocabularies.shapes.boolean).toJSON(),
        ],
      },
      {
        description: 'Has the files property',
        steps: [
          new ReadDataSourceStep(FlowSourceEnum.response, FlowResponseDataEnum.body).toJSON(),
          new ReadValueStep('files').toJSON(),
          new OkAssertion().toJSON(),
          new ToBeAssertion(ToBeTypes.array).toJSON(),
          new LengthOfAssertion(94).toJSON(),
        ],
      },
      {
        description: 'Has the "kind" property on an item',
        steps: [
          new ReadDataSourceStep(FlowSourceEnum.response, FlowResponseDataEnum.body).toJSON(),
          new ReadValueStep('files[0].kind').toJSON(),
          new EqualAssertion('drive#file').toJSON(),
        ],
      },
      {
        description: 'Has the "mimeType" property on an item',
        steps: [
          new ReadDataSourceStep(FlowSourceEnum.response, FlowResponseDataEnum.body).toJSON(),
          new ReadValueStep('files[0].mimeType').toJSON(),
          new EqualAssertion('application/vnd.google-apps.presentation').toJSON(),
        ],
      },
      {
        description: 'Has the "id" property on an item',
        steps: [
          new ReadDataSourceStep(FlowSourceEnum.response, FlowResponseDataEnum.body).toJSON(),
          new ReadValueStep('files[0].id').toJSON(),
          new OkAssertion().toJSON(),
          new ToBeAssertion(ToBeTypes.string).toJSON(),
          new IsNotEmptyAssertion().toJSON(),
        ],
      },
      {
        description: 'Has the "name" property on an item',
        steps: [
          new ReadDataSourceStep(FlowSourceEnum.response, FlowResponseDataEnum.body).toJSON(),
          new ReadValueStep('files[0].id').toJSON(),
          new OkAssertion().toJSON(),
          new IsNotEmptyAssertion().toJSON(),
        ],
      },
      {
        description: 'Has all items per page',
        steps: [
          new ReadDataSourceStep(FlowSourceEnum.response, FlowResponseDataEnum.body).toJSON(),
          new ReadValueStep('files').toJSON(),
          new LengthOfAssertion(94).toJSON(),
        ],
      },
    ]
    const result = await factory.run(request, response, assertions)
    assert.lengthOf(result, 13, 'has all results')
    assert.isFalse(
      result.some((i) => !i.ok),
      'all passed results'
    )
  })

  test('runs a series of assertions (all failed)', async ({ assert }) => {
    response.headers =
      'cache-control: max-age=0, must-revalidate\ncontent-length: 5506\ncontent-type: application/json; charset=UTF-8\ndate: Mon, 30 Jan 2023 03: 03: 21 GMT\nexpires: Mon, 01 Jan 1990 00: 00: 00 GMT\npragma: no-cache\nserver: ESF\nvary: Origin, X-Origin'
    response.payload = await PayloadSerializer.serialize(JSON.stringify(driveList))
    const assertions: IHttpAssertion[] = [
      {
        description: 'Returns expected status code',
        steps: [
          new ReadDataSourceStep(FlowSourceEnum.response, FlowResponseDataEnum.status).toJSON(),
          new EqualAssertion('204', true, ns.aml.vocabularies.shapes.number).toJSON(),
        ],
      },
      {
        description: 'Sets the "cache-control" header',
        steps: [
          new ReadDataSourceStep(FlowSourceEnum.response, FlowResponseDataEnum.headers).toJSON(),
          new ReadValueStep('cache-control'),
          new IncludesAssertion('no-cache, no-store').toJSON(),
        ],
      },
      {
        description: 'Response is gzipped',
        steps: [
          new ReadDataSourceStep(FlowSourceEnum.response, FlowResponseDataEnum.headers).toJSON(),
          new ReadValueStep('content-encoding').toJSON(),
          new EqualAssertion('gzip').toJSON(),
        ],
      },
      {
        description: 'Is a XML response',
        steps: [
          new ReadDataSourceStep(FlowSourceEnum.response, FlowResponseDataEnum.headers).toJSON(),
          new ReadValueStep('content-type').toJSON(),
          new IncludesAssertion('application/xml').toJSON(),
        ],
      },
      {
        description: 'Has the nextPageToken property that is not a string',
        steps: [
          new ReadDataSourceStep(FlowSourceEnum.response, FlowResponseDataEnum.body).toJSON(),
          new ReadValueStep('nextPageToken').toJSON(),
          new NotToBeAssertion(ToBeTypes.string),
        ],
      },
      {
        description: 'Has the nextPageToken property that is empty',
        steps: [
          new ReadDataSourceStep(FlowSourceEnum.response, FlowResponseDataEnum.body).toJSON(),
          new ReadValueStep('nextPageToken').toJSON(),
          new IsEmptyAssertion(),
        ],
      },
      {
        description: 'Has the kind property',
        steps: [
          new ReadDataSourceStep(FlowSourceEnum.response, FlowResponseDataEnum.body).toJSON(),
          new ReadValueStep('kind').toJSON(),
          new EqualAssertion('drive#fake').toJSON(),
        ],
      },
      {
        description: 'Has the incompleteSearch property set to true',
        steps: [
          new ReadDataSourceStep(FlowSourceEnum.response, FlowResponseDataEnum.body).toJSON(),
          new ReadValueStep('incompleteSearch').toJSON(),
          new EqualAssertion('true', true, ns.aml.vocabularies.shapes.boolean).toJSON(),
        ],
      },
      {
        description: 'Has the data property',
        steps: [
          new ReadDataSourceStep(FlowSourceEnum.response, FlowResponseDataEnum.body).toJSON(),
          new ReadValueStep('data').toJSON(),
          new OkAssertion().toJSON(),
          new ToBeAssertion(ToBeTypes.array).toJSON(),
        ],
      },
      {
        description: 'Has the files property that is not an array',
        steps: [
          new ReadDataSourceStep(FlowSourceEnum.response, FlowResponseDataEnum.body).toJSON(),
          new ReadValueStep('files').toJSON(),
          new NotToBeAssertion(ToBeTypes.object).toJSON(),
        ],
      },
      {
        description: 'has 30 items',
        steps: [
          new ReadDataSourceStep(FlowSourceEnum.response, FlowResponseDataEnum.body).toJSON(),
          new ReadValueStep('files').toJSON(),
          new LengthOfAssertion(30).toJSON(),
        ],
      },
      {
        description: 'Has the "kind" property on an item',
        steps: [
          new ReadDataSourceStep(FlowSourceEnum.response, FlowResponseDataEnum.body).toJSON(),
          new ReadValueStep('files[0].kind').toJSON(),
          new EqualAssertion('drive#object').toJSON(),
        ],
      },
      {
        description: 'Has the "mimeType" property on an item',
        steps: [
          new ReadDataSourceStep(FlowSourceEnum.response, FlowResponseDataEnum.body).toJSON(),
          new ReadValueStep('files[0].mimeType').toJSON(),
          new EqualAssertion('application/vnd.google-apps.spreadsheet').toJSON(),
        ],
      },
      {
        description: 'Has the "id" property that is not a string',
        steps: [
          new ReadDataSourceStep(FlowSourceEnum.response, FlowResponseDataEnum.body).toJSON(),
          new ReadValueStep('files[0].id').toJSON(),
          new NotToBeAssertion(ToBeTypes.string),
        ],
      },
      {
        description: 'Has the "id" property that is empty',
        steps: [
          new ReadDataSourceStep(FlowSourceEnum.response, FlowResponseDataEnum.body).toJSON(),
          new ReadValueStep('files[0].id').toJSON(),
          new IsEmptyAssertion(),
        ],
      },
    ]
    const result = await factory.run(request, response, assertions)
    assert.lengthOf(result, 15, 'has all results')
    assert.isFalse(
      result.some((i) => !!i.ok),
      'all failed results'
    )
  })
})
