import { test } from '@japa/runner'
import sinon from 'sinon'
import {
  HttpProjectKind,
  HttpProject,
  IHttpProject,
  IProjectFolderIteratorResult,
  Certificate,
  Environment,
  LicenseKind,
  ProjectFolderKind,
  ProjectRequest,
  ProjectSchema,
  ProviderKind,
  ServerKind,
  ThingKind,
} from '../../../src/index.js'
import { ArcLegacyProject, ARCSavedRequest, LegacyMock } from '../../../src/legacy.js'

const generator = new LegacyMock()

test.group('Default project initialization', () => {
  test('initializes a default project', ({ assert }) => {
    const result = new HttpProject()
    assert.equal(result.kind, HttpProjectKind, 'sets the kind property')
    assert.typeOf(result.definitions, 'object', 'sets the definitions property')
    assert.deepEqual(result.definitions.environments, [], 'sets the definitions.environments property')
    assert.deepEqual(result.definitions.folders, [], 'sets the definitions.folders property')
    assert.deepEqual(result.definitions.requests, [], 'sets the definitions.requests property')
    assert.deepEqual(result.definitions.schemas, [], 'sets the definitions.schemas property')
    assert.deepEqual(result.definitions.certificates, [], 'sets the definitions.certificates property')
    assert.deepEqual(result.items, [], 'sets the items property')
    const { info } = result
    assert.typeOf(info, 'EventTarget', 'sets the default info property')
    assert.equal(info.kind, ThingKind, 'sets the info.kind property')
    assert.equal(info.name, '', 'sets the info.name property')
    assert.isUndefined(result.license, 'has no license property')
    assert.isUndefined(result.provider, 'has no provider property')
  })

  test('sets initEnvironments', ({ assert }) => {
    const result = new HttpProject(undefined, [
      {
        key: 'a',
        kind: 'Core#Environment',
        info: {
          kind: ThingKind,
          name: 'test',
        },
        variables: [],
        server: {
          kind: ServerKind,
          uri: 'https://api.com',
        },
      },
    ])
    const envs = result.getEnvironments()
    assert.typeOf(envs, 'array')
    assert.lengthOf(envs, 1)
    assert.equal(envs[0].info.name, 'test')
  })

  test('passed in constructor environments override project environments', ({ assert }) => {
    const schema = new HttpProject({
      definitions: {
        environments: [
          {
            key: 'b',
            kind: 'Core#Environment',
            info: {
              kind: ThingKind,
              name: 'test b',
            },
            variables: [],
            server: {
              kind: ServerKind,
              uri: 'https://domain.com',
            },
          },
        ],
      },
      info: {
        kind: ThingKind,
        name: 'Project',
      },
      items: [],
      key: 'abc',
      kind: HttpProjectKind,
    }).toJSON()
    const result = new HttpProject(schema, [
      {
        key: 'a',
        kind: 'Core#Environment',
        info: {
          kind: ThingKind,
          name: 'test',
        },
        variables: [],
        server: {
          kind: ServerKind,
          uri: 'https://api.com',
        },
      },
    ])
    const envs = result.getEnvironments()
    assert.typeOf(envs, 'array')
    assert.lengthOf(envs, 1)
    assert.equal(envs[0].info.name, 'test')
  })
})

test.group('From schema initialization', (group) => {
  let base: IHttpProject
  group.each.setup(() => {
    base = {
      kind: HttpProjectKind,
      key: 'abc',
      definitions: {},
      items: [],
      info: {
        kind: ThingKind,
        name: '',
      },
    }
  })

  test('sets the info', ({ assert }) => {
    const init: IHttpProject = {
      ...base,
      ...{
        info: {
          kind: ThingKind,
          name: 'Test project',
          description: 'Project description',
          version: '1.2.3',
        },
      },
    }
    const project = new HttpProject(init)
    const { info } = project
    assert.equal(info.kind, ThingKind, 'sets the info.kind property')
    assert.equal(info.name, 'Test project', 'sets the info.name property')
    assert.equal(info.description, 'Project description', 'sets the info.description property')
    assert.equal(info.version, '1.2.3', 'sets the info.version property')
  })

  test('sets the provider', ({ assert }) => {
    const init: IHttpProject = {
      ...base,
      ...{
        provider: {
          kind: ProviderKind,
          name: 'Test Provider',
          email: 'a@b.c',
          url: 'https://test.com',
        },
      },
    }
    const project = new HttpProject(init)
    const { provider } = project
    assert.typeOf(provider, 'object', 'has the provider')
    assert.equal(provider!.kind, ProviderKind, 'sets the provider.kind property')
    assert.equal(provider!.name, 'Test Provider', 'sets the provider.name property')
    assert.equal(provider!.email, 'a@b.c', 'sets the provider.email property')
    assert.equal(provider!.url, 'https://test.com', 'sets the provider.url property')
  })

  test('sets the license', ({ assert }) => {
    const init: IHttpProject = {
      ...base,
      ...{
        license: {
          kind: LicenseKind,
          name: 'Test License',
          content: 'test content',
          url: 'https://test.com',
        },
      },
    }
    const project = new HttpProject(init)
    const { license } = project
    assert.typeOf(license, 'object', 'has the license')
    assert.equal(license!.kind, LicenseKind, 'sets the license.kind property')
    assert.equal(license!.name, 'Test License', 'sets the license.name property')
    assert.equal(license!.content, 'test content', 'sets the license.content property')
    assert.equal(license!.url, 'https://test.com', 'sets the license.url property')
  })

  test('sets the items', ({ assert }) => {
    const init: IHttpProject = {
      ...base,
      ...{
        items: [
          {
            kind: ProjectFolderKind,
            key: '123456',
          },
        ],
      },
    }
    const project = new HttpProject(init)
    const { items } = project
    assert.typeOf(items, 'array', 'has the items')
    assert.lengthOf(items, 1, 'has a single item')
    const [item] = items
    assert.equal(item.kind, ProjectFolderKind, 'sets the item.kind property')
    assert.equal(item.key, '123456', 'sets the item.key property')
  })

  test('sets the definitions.folders', ({ assert }) => {
    const init: IHttpProject = {
      ...base,
      ...{
        definitions: {
          folders: [
            {
              key: '123456',
              kind: ProjectFolderKind,
              created: 1234567,
              updated: 98765,
              info: {
                kind: ThingKind,
                name: 'test',
              },
              items: [],
            },
          ],
        },
      },
    }
    const project = new HttpProject(init)
    const { definitions } = project
    assert.typeOf(definitions.folders, 'array', 'has the items')
    assert.lengthOf(definitions.folders, 1, 'has a single item')
    const [item] = definitions.folders
    assert.equal(item.kind, ProjectFolderKind, 'sets the item.kind property')
    assert.equal(item.key, '123456', 'sets the item.key property')
  })

  test('sets the definitions.requests', ({ assert }) => {
    const request = ProjectRequest.fromUrl('https://api.com', new HttpProject())
    const init: IHttpProject = {
      ...base,
      ...{
        definitions: {
          requests: [request.toJSON()],
        },
      },
    }
    const project = new HttpProject(init)
    const { definitions } = project
    assert.typeOf(definitions.requests, 'array', 'has the items')
    assert.lengthOf(definitions.requests, 1, 'has a single item')
    const [item] = definitions.requests
    assert.equal(item.key, request.key, 'sets the request instance')
  })

  test('sets the definitions.certificates', ({ assert }) => {
    const cert = Certificate.fromP12('value')
    const init: IHttpProject = {
      ...base,
      ...{
        definitions: {
          certificates: [cert.toJSON()],
        },
      },
    }
    const project = new HttpProject(init)
    const { definitions } = project
    assert.typeOf(definitions.certificates, 'array', 'has the definitions')
    assert.lengthOf(definitions.certificates, 1, 'has a single definition')
    const [item] = definitions.certificates
    assert.equal(item.key, cert.key, 'sets the request instance')
  })
})

test.group('From JSON string initialization', () => {
  test('restores project data from JSON string', ({ assert }) => {
    const orig = new HttpProject()
    orig.info.name = 'a project'
    orig.addFolder('folder')
    orig.addRequest('https://api.com')
    const str = JSON.stringify(orig)
    const result = new HttpProject(str)

    assert.equal(result.key, orig.key, 'restores the key')
    assert.equal(result.info.name, orig.info.name, 'restores the info object')
    assert.ok(result.findFolder('folder'), 'restores a folder')
    assert.ok(result.findRequest('https://api.com'), 'restores a request')
  })
})

test.group('HttpProject.fromName()', () => {
  test('creates an empty project with a name', ({ assert }) => {
    const project = HttpProject.fromName('Test project')
    assert.equal(project.kind, HttpProjectKind, 'sets the kind property')
    assert.deepEqual(project.definitions.environments, [], 'sets the definitions.environments property')
    assert.deepEqual(project.definitions.folders, [], 'sets the definitions.folders property')
    assert.deepEqual(project.definitions.requests, [], 'sets the definitions.requests property')
    assert.deepEqual(project.definitions.schemas, [], 'sets the definitions.schemas property')
    assert.deepEqual(project.definitions.certificates, [], 'sets the definitions.certificates property')
    assert.deepEqual(project.items, [], 'sets the items property')
    const { info } = project
    assert.typeOf(info, 'EventTarget', 'sets the default info property')
    assert.equal(info.kind, ThingKind, 'sets the info.kind property')
    assert.equal(info.name, 'Test project', 'sets the info.name property')
    assert.isUndefined(project.license, 'has no license property')
    assert.isUndefined(project.provider, 'has no provider property')
  })
})

test.group('HttpProject.fromLegacy()', () => {
  test('sets the name', async ({ assert }) => {
    const init: ArcLegacyProject = {
      name: 'abc',
    }
    const result = await HttpProject.fromLegacy(init, [])
    assert.equal(result.info.name, 'abc')
  })

  test('sets the description', async ({ assert }) => {
    const init: ArcLegacyProject = {
      name: 'abc',
      description: 'test',
    }
    const result = await HttpProject.fromLegacy(init, [])
    assert.equal(result.info.description, 'test')
  })

  test('adds the requests', async ({ assert }) => {
    const init: ArcLegacyProject = {
      name: 'abc',
      description: 'test',
      requests: ['1'],
    }
    const requests: ARCSavedRequest[] = [
      {
        method: 'PUT',
        name: 'r1',
        url: 'https://',
        _id: '1',
      },
    ]
    const result = await HttpProject.fromLegacy(init, requests)
    const projectRequests = result.listRequests()
    assert.lengthOf(projectRequests, 1, 'has a single request')
    assert.equal(projectRequests[0].info.name, 'r1')
  })

  test('ignores missing requests', async ({ assert }) => {
    const init: ArcLegacyProject = {
      name: 'abc',
      description: 'test',
      requests: ['1', '2'],
    }
    const requests: ARCSavedRequest[] = [
      {
        method: 'PUT',
        name: 'r1',
        url: 'https://',
        _id: '1',
      },
      {
        method: 'DELETE',
        name: 'r3',
        url: 'https://api.com',
        _id: '3',
      },
    ]
    const result = await HttpProject.fromLegacy(init, requests)
    const projectRequests = result.listRequests()
    assert.lengthOf(projectRequests, 1, 'has a single request')
    assert.equal(projectRequests[0].info.name, 'r1')
  })
})

test.group('HttpProject.fromInitOptions()', () => {
  test('sets the name', ({ assert }) => {
    const result = HttpProject.fromInitOptions({
      name: 'abc',
    })
    assert.equal(result.info.name, 'abc')
  })
})

test.group('toJSON()', (group) => {
  let project: HttpProject
  group.each.setup(() => {
    project = new HttpProject()
  })

  test('sets the kind', ({ assert }) => {
    const result = project.toJSON()
    assert.equal(result.kind, HttpProjectKind)
  })

  test('sets the key', ({ assert }) => {
    const result = project.toJSON()
    assert.equal(result.key, project.key)
  })

  test('sets the info object', ({ assert }) => {
    project.info.name = 'a'
    const result = project.toJSON()
    assert.equal(result.info.name, 'a')
  })

  test('sets the empty definitions', ({ assert }) => {
    const result = project.toJSON()
    assert.deepEqual(result.definitions, {})
  })

  test('sets the empty items', ({ assert }) => {
    const result = project.toJSON()
    assert.deepEqual(result.items, [])
  })

  test('does not set empty provider', ({ assert }) => {
    const result = project.toJSON()
    assert.isUndefined(result.provider)
  })

  test('does not set empty license', ({ assert }) => {
    const result = project.toJSON()
    assert.isUndefined(result.license)
  })

  test('serializes the items', ({ assert }) => {
    const f1 = project.addFolder('f1')
    const result = project.toJSON()
    assert.lengthOf(result.items, 1)
    assert.equal(result.items[0].key, f1.key)
  })

  test('serializes the definitions.environments', ({ assert }) => {
    const e1 = project.addEnvironment('e1')
    const result = project.toJSON()
    assert.lengthOf(result.definitions.environments!, 1)
    assert.equal(result.definitions.environments![0].key, e1.key)
  })

  test('serializes the definitions.requests', ({ assert }) => {
    const r1 = project.addRequest('r1')
    const result = project.toJSON()
    assert.lengthOf(result.definitions.requests!, 1)
    assert.equal(result.definitions.requests![0].key, r1.key)
  })

  test('serializes the definitions.folders', ({ assert }) => {
    const f1 = project.addFolder('f1')
    const result = project.toJSON()
    assert.lengthOf(result.definitions.folders!, 1)
    assert.equal(result.definitions.folders![0].key, f1.key)
  })

  test('serializes the definitions.certificates', ({ assert }) => {
    const c1 = Certificate.fromP12('value')
    project.addCertificate(c1)
    const result = project.toJSON()
    assert.lengthOf(result.definitions.certificates!, 1)
    assert.equal(result.definitions.certificates![0].key, c1.key)
  })
})

test.group('addFolder()', () => {
  test('returns a key of the inserted folder', ({ assert }) => {
    const project = new HttpProject()
    const created = project.addFolder('A folder')
    assert.typeOf(created, 'object', 'returns an object')
  })

  test('adds the folder to the items array', ({ assert }) => {
    const project = new HttpProject()
    const created = project.addFolder('A folder')
    const { items } = project
    assert.lengthOf(items, 1, 'items has a single object')
    const [item] = items

    assert.equal(item.key, created.key, 'has the key')
    assert.equal(item.kind, ProjectFolderKind, 'has the kind')
  })

  test('adds the definition', ({ assert }) => {
    const project = new HttpProject()
    const created = project.addFolder('A folder')
    const { definitions } = project
    assert.lengthOf(definitions.folders, 1, 'has the definition')
    const folder = definitions.folders[0]

    assert.equal(folder.kind, ProjectFolderKind, 'has the kind')
    assert.equal(folder.key, created.key, 'has the key')

    const { info } = folder
    assert.typeOf(info, 'EventTarget', 'has the info object')
    assert.equal(info.kind, ThingKind, 'has the info.kind property')
    assert.equal(info.name, 'A folder', 'has the info.name property')

    assert.typeOf(folder.created, 'number', 'has the created property')
    assert.typeOf(folder.updated, 'number', 'has the updated property')
    assert.equal(folder.created, folder.updated, 'the updated and created properties equal')

    assert.deepEqual(folder.items, [], 'has empty items')
  })

  test('adds multiple folders', ({ assert }) => {
    const project = new HttpProject()
    const created1 = project.addFolder('f1')
    const created2 = project.addFolder('f2')
    const created3 = project.addFolder('f3')
    assert.lengthOf(project.items, 3, 'has all items')
    assert.equal(project.items[0].key, created1.key, 'has item #1')
    assert.equal(project.items[1].key, created2.key, 'has item #2')
    assert.equal(project.items[2].key, created3.key, 'has item #3')
  })

  test('adds a folder of a folder', ({ assert }) => {
    const project = new HttpProject()
    const created1 = project.addFolder('f1')
    const folder = project.findFolder(created1.key)!
    const created2 = folder.addFolder('inception')
    assert.lengthOf(project.items, 1, 'project has a single item')
    assert.lengthOf(project.definitions.folders, 2, 'project has a two definitions')

    const theFolder = project.definitions.folders[1]
    assert.equal(theFolder.key, created2.key, 'adds the sub folder definition to the project')
    assert.equal(theFolder.info.name, 'inception', 'sub-folder has the name')
  })

  test('ignores adding when the folder already exists', ({ assert }) => {
    const project = new HttpProject()
    const created1 = project.addFolder('f1')
    const created2 = project.addFolder('f1', { skipExisting: true })
    assert.deepEqual(created1, created2)
  })

  test('adds a folder on a specific position', ({ assert }) => {
    const project = new HttpProject()
    project.addFolder('f1')
    project.addFolder('f2')
    project.addFolder('f3')
    const inserted = project.addFolder('f4', { index: 1 })
    assert.equal(project.items[1].key, inserted.key)
  })
})

test.group('findFolder()', () => {
  test('returns undefine when no definitions', ({ assert }) => {
    const project = new HttpProject()
    const result = project.findFolder('abc')
    assert.isUndefined(result)
  })

  test('finds the folder by the name', ({ assert }) => {
    const project = new HttpProject()
    const created = project.addFolder('abc')
    const result = project.findFolder('abc')
    assert.deepEqual(result, created)
  })

  test('finds the folder by the key', ({ assert }) => {
    const project = new HttpProject()
    const created = project.addFolder('abc')
    const result = project.findFolder(created.key)
    assert.deepEqual(result, created)
  })

  test('returns undefined for the keyOnly option', ({ assert }) => {
    const project = new HttpProject()
    project.addFolder('abc')
    const result = project.findFolder('abc', { keyOnly: true })
    assert.isUndefined(result)
  })

  test('returns a folder only', ({ assert }) => {
    const project = new HttpProject()
    const request = ProjectRequest.fromName('abc', project)
    project.addRequest(request)
    const created = project.addFolder('abc')
    const result = project.findFolder('abc')
    assert.deepEqual(result, created)
  })
})

test.group('removeFolder()', () => {
  test('removes the folder from the project', ({ assert }) => {
    const name = 'abc'
    const project = new HttpProject()
    const created = project.addFolder(name)
    project.removeFolder(created.key)
    const result = project.findFolder(name)
    assert.isUndefined(result)
  })

  test('removes the folder from the project items', ({ assert }) => {
    const name = 'abc'
    const project = new HttpProject()
    const created = project.addFolder(name)
    assert.lengthOf(project.items, 1, 'has an item')
    project.removeFolder(created.key)
    project.findFolder(name)
    assert.deepEqual(project.items, [], 'the items is empty')
  })

  test('removes the folder from the definitions', ({ assert }) => {
    const name = 'abc'
    const project = new HttpProject()
    const created = project.addFolder(name)
    assert.lengthOf(project.definitions.folders, 1, 'has a definition')
    project.removeFolder(created.key)
    project.findFolder(name)
    assert.deepEqual(project.definitions.folders, [], 'the definitions is empty')
  })

  test('removes the folder from a parent folder', ({ assert }) => {
    const name = 'abc'
    const project = new HttpProject()
    const parent = project.addFolder('parent')
    const created = project.addFolder(name, { parent: parent.key })
    project.removeFolder(created.key)
    const result = project.findFolder(name)
    assert.isUndefined(result)
  })

  test('removes the folder from the parent folder items', ({ assert }) => {
    const name = 'abc'
    const project = new HttpProject()
    const parent = project.addFolder('parent')
    const created = project.addFolder(name, { parent: parent.key })
    assert.lengthOf(parent.items, 1, 'has an item')
    project.removeFolder(created.key)
    project.findFolder(name)
    assert.deepEqual(parent.items, [], 'the items is empty')
  })

  test('removes the folder from the definitions', ({ assert }) => {
    const name = 'abc'
    const project = new HttpProject()
    const parent = project.addFolder('parent')
    const created = project.addFolder(name, { parent: parent.key })
    assert.lengthOf(project.definitions.folders, 2, 'has 2 definitions')
    project.removeFolder(created.key)
    project.findFolder(name)
    assert.lengthOf(project.definitions.folders, 1, 'has 1 definition')
  })

  test('throws an error when folder is not found', ({ assert }) => {
    const project = new HttpProject()
    assert.throws(() => {
      project.removeFolder('hello')
    })
  })

  test('does not throw with the safe option', ({ assert }) => {
    const project = new HttpProject()
    assert.doesNotThrow(() => {
      project.removeFolder('hello', { safe: true })
    })
  })

  test('calls detachedCallback() on the folder', ({ assert }) => {
    const project = new HttpProject()
    const created = project.addFolder('test')
    const spy = sinon.spy(created, 'detachedCallback')
    project.removeFolder(created.key)
    assert.isTrue(spy.calledOnce)
  })

  test('removes requests from the folder', ({ assert }) => {
    const project = new HttpProject()
    const parent = project.addFolder('f1')
    parent.addRequest('r1')
    parent.addRequest('r2')
    parent.remove()

    assert.deepEqual(project.items, [])
    assert.deepEqual(project.definitions.folders, [])
  })

  test('removes folders from the folder', ({ assert }) => {
    const project = new HttpProject()
    const parent = project.addFolder('f1')
    parent.addFolder('f2')
    parent.addFolder('f3')
    parent.remove()

    assert.deepEqual(project.items, [])
    assert.deepEqual(project.definitions.folders, [])
  })

  test('deeply removes folders and request from the folder', ({ assert }) => {
    const project = new HttpProject()
    const parent = project.addFolder('f1')
    parent.addFolder('f2')
    const f3 = parent.addFolder('f3')
    f3.addRequest('r1')
    f3.addRequest('r2')
    parent.remove()
    const otherFolder = project.addFolder('f4')
    const otherRequest = project.addRequest('r3')

    assert.lengthOf(project.items, 2, 'has the remaining items')
    assert.deepEqual(project.definitions.folders, [otherFolder])
    assert.deepEqual(project.definitions.requests, [otherRequest])
  })
})

test.group('findParent()', () => {
  test('finds a parent for a root level folder', ({ assert }) => {
    const project = new HttpProject()
    const created = project.addFolder('test')
    const parent = project.findParent(created.key)
    assert.deepEqual(parent, project)
  })

  test('finds a parent for a root level request', ({ assert }) => {
    const project = new HttpProject()
    const request = ProjectRequest.fromName('request', project)
    const created = project.addRequest(request)
    const parent = project.findParent(created.key)
    assert.deepEqual(parent, project)
  })

  test('finds a parent for a sub-folder', ({ assert }) => {
    const project = new HttpProject()
    const folder = project.addFolder('parent')
    const created = project.addFolder('test', { parent: folder.key })
    const parent = project.findParent(created.key)
    assert.deepEqual(parent, folder)
  })

  test('finds a parent for a root level request', ({ assert }) => {
    const project = new HttpProject()
    const folder = project.addFolder('parent')
    const request = ProjectRequest.fromName('request', project)
    const created = project.addRequest(request, { parent: folder.key })
    const parent = project.findParent(created.key)
    assert.deepEqual(parent, folder)
  })

  test('returns undefined when not found', ({ assert }) => {
    const project = new HttpProject()
    const folder = project.addFolder('parent')
    project.addFolder('test', { parent: folder.key })
    const parent = project.findParent('other')
    assert.isUndefined(parent)
  })
})

test.group('moveFolder()', (group) => {
  let project: HttpProject
  group.each.setup(() => {
    project = new HttpProject()
    const folder1 = project.addFolder('folder1')
    const folder2 = project.addFolder('folder2')
    project.addFolder('folder3', { parent: folder2.key })
    const request1 = ProjectRequest.fromName('request1', project)
    const request2 = ProjectRequest.fromName('request2', project)
    const request3 = ProjectRequest.fromName('request3', project)
    const request4 = ProjectRequest.fromName('request4', project)
    project.addRequest(request1)
    folder1.addRequest(request2)
    folder1.addRequest(request3)
    folder1.addRequest(request4)
  })

  test('moves a folder to an index position inside the project root', ({ assert }) => {
    const moved = project.findFolder('folder1')!
    const indexBefore = project.items.findIndex((i) => i.key === moved.key)
    assert.equal(indexBefore, 0, 'the item is on position 0 initially')
    project.moveFolder(moved.key, { index: 1 })
    const index = project.items.findIndex((i) => i.key === moved.key)
    assert.equal(index, 1, 'is moved to a new position')
  })

  test('moves a folder to the end inside the project root', ({ assert }) => {
    const moved = project.findFolder('folder1')!
    const indexBefore = project.items.findIndex((i) => i.key === moved.key)
    assert.equal(indexBefore, 0, 'the item is on position 0 initially')
    project.moveFolder(moved.key)
    const index = project.items.findIndex((i) => i.key === moved.key)
    assert.equal(index, 2, 'is moved to the end')
  })

  test('moves a folder to another folder', ({ assert }) => {
    const oldParent = project.findFolder('folder2')!
    const moved = project.findFolder('folder3')!
    const target = project.findFolder('folder1')!
    assert.isTrue(
      oldParent.items.some((i) => i.key === moved.key),
      'the old parent has the item'
    )
    assert.isFalse(
      target.items.some((i) => i.key === moved.key),
      "the target doesn't have the moved item"
    )
    project.moveFolder(moved.key, { parent: target.key })
    assert.isFalse(
      oldParent.items.some((i) => i.key === moved.key),
      'the item is removed from the old parent'
    )
    assert.equal(target.items[target.items.length - 1].key, moved.key, 'the item is inserted at the end of the target')
  })

  test('moves a folder to another folder into a position', ({ assert }) => {
    const moved = project.findFolder('folder3')!
    const target = project.findFolder('folder1')!
    project.moveFolder(moved.key, { parent: target.key, index: 1 })
    assert.equal(target.items[1].key, moved.key, 'the item is inserted at the position')
  })

  test('moves a folder to the project root', ({ assert }) => {
    const moved = project.findFolder('folder3')!
    project.moveFolder(moved.key)
    assert.equal(project.items[project.items.length - 1].key, moved.key)
  })

  test('moves a folder to the project root at position', ({ assert }) => {
    const moved = project.findFolder('folder3')!
    project.moveFolder(moved.key, { index: 1 })
    assert.equal(project.items[1].key, moved.key)
  })

  test('throws when moving into a position that is out of bounds', ({ assert }) => {
    const moved = project.findFolder('folder3')!
    assert.throws(
      () => {
        project.moveFolder(moved.key, { index: 5 })
      },
      RangeError,
      'Index out of bounds. Maximum index is 3.'
    )
  })

  test('throws when the folder is not found', ({ assert }) => {
    assert.throws(
      () => {
        project.moveFolder('unknown')
      },
      Error,
      'Unable to locate the folder unknown'
    )
  })

  test('throws when the parent is not found', ({ assert }) => {
    const moved = project.findFolder('folder3')!
    assert.throws(
      () => {
        project.moveFolder(moved.key, { parent: 'unknown' })
      },
      Error,
      'Unable to locate the new parent folder unknown'
    )
  })

  test('throws when moving a parent to its child', ({ assert }) => {
    const parent = project.findFolder('folder2')!
    const child = project.findFolder('folder3')!
    assert.throws(
      () => {
        project.moveFolder(parent.key, { parent: child.key })
      },
      RangeError,
      'Unable to move a folder to its child.'
    )
  })
})

test.group('hasChild()', (group) => {
  let project: HttpProject
  group.each.setup(() => {
    project = new HttpProject()
    const folder1 = project.addFolder('folder1')
    const folder2 = project.addFolder('folder2', { parent: folder1.key })
    const folder3 = project.addFolder('folder3', { parent: folder2.key })
    project.addFolder('folder4')
    const request1 = ProjectRequest.fromName('request1', project)
    const request2 = ProjectRequest.fromName('request2', project)
    const request3 = ProjectRequest.fromName('request3', project)
    const request4 = ProjectRequest.fromName('request4', project)
    project.addRequest(request1)
    folder1.addRequest(request2)
    folder1.addRequest(request3)
    folder3.addRequest(request4)
  })

  test('returns true when the project has a child request (direct)', ({ assert }) => {
    const request = project.findRequest('request1')!
    const result = project.hasChild(request.key)
    assert.isTrue(result)
  })

  test('returns true when the project has a child request (indirect)', ({ assert }) => {
    const request = project.findRequest('request4')!
    const result = project.hasChild(request.key)
    assert.isTrue(result)
  })

  test('returns true when the project has a child folder (direct)', ({ assert }) => {
    const folder = project.findFolder('folder1')!
    const result = project.hasChild(folder.key)
    assert.isTrue(result)
  })

  test('returns true when the project has a child folder (indirect)', ({ assert }) => {
    const folder = project.findFolder('folder3')!
    const result = project.hasChild(folder.key)
    assert.isTrue(result)
  })

  test('returns true when a folder has a child request (direct)', ({ assert }) => {
    const parent = project.findFolder('folder1')!
    const request = project.findRequest('request2')!
    const result = project.hasChild(request.key, parent.key)
    assert.isTrue(result)
  })

  test('returns true when a folder has a child request (indirect)', ({ assert }) => {
    const parent = project.findFolder('folder1')!
    const request = project.findRequest('request4')!
    const result = project.hasChild(request.key, parent.key)
    assert.isTrue(result)
  })

  test('returns true when a folder has a child folder (direct)', ({ assert }) => {
    const parent = project.findFolder('folder1')!
    const folder = project.findFolder('folder2')!
    const result = project.hasChild(folder.key, parent.key)
    assert.isTrue(result)
  })

  test('returns true when a folder has a child request (indirect)', ({ assert }) => {
    const parent = project.findFolder('folder1')!
    const folder = project.findFolder('folder3')!
    const result = project.hasChild(folder.key, parent.key)
    assert.isTrue(result)
  })

  test('returns false when the folder has no a folder as a child', ({ assert }) => {
    const parent = project.findFolder('folder1')!
    const folder = project.findFolder('folder4')!
    const result = project.hasChild(folder.key, parent.key)
    assert.isFalse(result)
  })

  test('returns false when the project has no folder as a child', ({ assert }) => {
    const result = project.hasChild('unknown')
    assert.isFalse(result)
  })

  test('returns false when a folder has no request as a child', ({ assert }) => {
    const parent = project.findFolder('folder1')!
    const request = project.findRequest('request1')!
    const result = project.hasChild(request.key, parent.key)
    assert.isFalse(result)
  })
})

test.group('addRequest()', () => {
  test('adds an instance of the request', ({ assert }) => {
    const project = new HttpProject()
    const request = ProjectRequest.fromName('test', project)
    const created = project.addRequest(request)
    assert.deepEqual(created, request)

    assert.lengthOf(project.definitions.requests, 1, 'has one definition')
    assert.deepEqual(project.definitions.requests[0], created, "inserts the definition into project's definitions")
    assert.equal(project.items[0].key, created.key, 'the project has the item')

    assert.equal(created.getParent()!.kind, HttpProjectKind, 'the request has the parent as the project')
  })

  test('adds the request from the schema', ({ assert }) => {
    const project = new HttpProject()
    const request = ProjectRequest.fromName('test', project)
    const schema = request.toJSON()
    const created = project.addRequest(schema)

    assert.deepEqual(created, request)

    assert.lengthOf(project.definitions.requests, 1, 'has one definition')
    assert.deepEqual(project.definitions.requests[0], created, "inserts the definition into project's definitions")
    assert.equal(project.items[0].key, created.key, 'the project has the item')

    assert.equal(created.getParent()!.kind, HttpProjectKind, 'the request has the parent as the project')
  })

  test('adds the request to a folder', ({ assert }) => {
    const project = new HttpProject()
    const folder = project.addFolder('a folder')

    const request = ProjectRequest.fromName('test', project)
    const created = project.addRequest(request, { parent: folder.key })

    assert.lengthOf(project.definitions.requests, 1, 'has the request definition')
    assert.deepEqual(project.definitions.requests[0], created, "inserts the definition into project's definitions")
    assert.lengthOf(folder.items, 1, 'the folder has a single item')
    assert.equal(folder.items[0].key, created.key, 'the folder has the item')

    assert.deepEqual(created.getParent(), folder, 'the request has the parent as the folder')
  })

  test('adds the key if missing', ({ assert }) => {
    const project = new HttpProject()
    const request = ProjectRequest.fromName('test', project)
    const schema = request.toJSON()
    // @ts-expect-error Used in testing
    delete schema.key
    const created = project.addRequest(schema)

    assert.typeOf(created.key, 'string', 'has a new key')
  })

  test('calls the attachedCallback() on the request', ({ assert }) => {
    const project = new HttpProject()
    const request = ProjectRequest.fromName('test', project)
    const spy = sinon.spy(request, 'attachedCallback')
    project.addRequest(request)

    assert.isTrue(spy.calledOnce)
  })

  test('throws when parent folder not found', ({ assert }) => {
    const project = new HttpProject()
    const request = ProjectRequest.fromName('test', project)

    assert.throws(
      () => {
        project.addRequest(request, { parent: 'unknown' })
      },
      Error,
      'Unable to find the parent folder unknown.'
    )
  })

  test('throws when index out of bounds', ({ assert }) => {
    const project = new HttpProject()
    const request = ProjectRequest.fromName('test', project)

    assert.throws(
      () => {
        project.addRequest(request, { index: 1 })
      },
      RangeError,
      'Index out of bounds. Maximum index is 0.'
    )
  })
})

test.group('findRequest()', () => {
  test('returns undefined when no definitions', ({ assert }) => {
    const project = new HttpProject()
    const result = project.findRequest('abc')
    assert.isUndefined(result)
  })

  test('finds the request by the name', ({ assert }) => {
    const project = new HttpProject()
    const created = ProjectRequest.fromName('abc', project)
    project.addRequest(created)
    const result = project.findRequest('abc')
    assert.deepEqual(result, created)
  })

  test('finds the request by the key', ({ assert }) => {
    const project = new HttpProject()
    const created = ProjectRequest.fromName('abc', project)
    project.addRequest(created)
    const result = project.findRequest(created.key)
    assert.deepEqual(result, created)
  })

  test('returns undefined for the keyOnly option', ({ assert }) => {
    const project = new HttpProject()
    project.addRequest(ProjectRequest.fromName('abc', project))
    const result = project.findRequest('abc', { keyOnly: true })
    assert.isUndefined(result)
  })

  test('returns a request only', ({ assert }) => {
    const project = new HttpProject()
    project.addFolder('abc')
    const request = ProjectRequest.fromName('abc', project)
    project.addRequest(request)
    const result = project.findRequest('abc')
    assert.deepEqual(result, request)
  })
})

test.group('removeRequest()', () => {
  test('removes the request from the project', ({ assert }) => {
    const name = 'abc'
    const project = new HttpProject()
    const created = ProjectRequest.fromName(name, project)
    project.addRequest(created)
    project.removeRequest(created.key)
    const result = project.findRequest(name)
    assert.isUndefined(result)
  })

  test('removes the request from the project items', ({ assert }) => {
    const project = new HttpProject()
    const created = ProjectRequest.fromName('test', project)
    project.addRequest(created)
    assert.lengthOf(project.items, 1, 'has an item')
    project.removeRequest(created.key)
    assert.deepEqual(project.items, [], 'the items is empty')
  })

  test('removes the request from the definitions', ({ assert }) => {
    const project = new HttpProject()
    const created = ProjectRequest.fromName('test', project)
    project.addRequest(created)
    assert.lengthOf(project.definitions.requests, 1, 'has a definition')
    project.removeRequest(created.key)
    assert.deepEqual(project.definitions.requests, [], 'the definitions is empty')
  })

  test('removes the request from a parent folder', ({ assert }) => {
    const name = 'abc'
    const project = new HttpProject()
    const parent = project.addFolder('parent')
    const created = ProjectRequest.fromName(name, project)
    project.addRequest(created, { parent: parent.key })
    project.removeRequest(created.key)
    const result = project.findRequest(name)
    assert.isUndefined(result)
  })

  test('removes the request from the parent folder items', ({ assert }) => {
    const name = 'abc'
    const project = new HttpProject()
    const parent = project.addFolder('parent')
    const created = ProjectRequest.fromName(name, project)
    project.addRequest(created, { parent: parent.key })
    assert.lengthOf(parent.items, 1, 'has an item')
    project.removeRequest(created.key)
    assert.deepEqual(parent.items, [], 'the items is empty')
  })

  test('removes the request from the definitions', ({ assert }) => {
    const name = 'abc'
    const project = new HttpProject()
    const parent = project.addFolder('parent')
    const created = ProjectRequest.fromName(name, project)
    project.addRequest(created, { parent: parent.key })
    assert.lengthOf(project.definitions.requests, 1, 'has 1 definition')
    project.removeRequest(created.key)
    assert.lengthOf(project.definitions.requests, 0, 'has 0 definitions')
  })

  test('throws an error when the request is not found', ({ assert }) => {
    const project = new HttpProject()
    assert.throws(() => {
      project.removeRequest('hello')
    })
  })

  test('does not throw with the safe option', ({ assert }) => {
    const project = new HttpProject()
    assert.doesNotThrow(() => {
      project.removeRequest('hello', { safe: true })
    })
  })

  test('calls detachedCallback() on the request', ({ assert }) => {
    const project = new HttpProject()
    const created = ProjectRequest.fromName('test', project)
    project.addRequest(created)
    const spy = sinon.spy(created, 'detachedCallback')
    project.removeRequest(created.key)
    assert.isTrue(spy.calledOnce)
  })
})

test.group('moveRequest()', (group) => {
  let project: HttpProject
  group.each.setup(() => {
    project = new HttpProject()
    const folder1 = project.addFolder('folder1')
    const folder2 = project.addFolder('folder2')
    project.addFolder('folder3', { parent: folder2.key })
    const request1 = ProjectRequest.fromName('request1', project)
    const request2 = ProjectRequest.fromName('request2', project)
    const request3 = ProjectRequest.fromName('request3', project)
    const request4 = ProjectRequest.fromName('request4', project)
    project.addRequest(request1)
    folder1.addRequest(request2)
    folder1.addRequest(request3)
    folder1.addRequest(request4)
  })

  test('moves a request to the end of project root from a folder', ({ assert }) => {
    const oldParent = project.findFolder('folder1')!
    const moved = project.findRequest('request2')!
    assert.isTrue(
      oldParent.items.some((i) => i.key === moved.key),
      'the old parent has the request'
    )
    project.moveRequest(moved.key)
    assert.isFalse(
      oldParent.items.some((i) => i.key === moved.key),
      'removes the request from the old parent'
    )
    assert.equal(project.items[project.items.length - 1].key, moved.key, 'project has the request')
  })

  test('moves a request to the specific position of the project root from a folder', ({ assert }) => {
    const moved = project.findRequest('request2')!
    project.moveRequest(moved.key, { index: 0 })
    assert.equal(project.items[0].key, moved.key)
  })

  test('moves a request from the project root to a folder at the end', ({ assert }) => {
    const moved = project.findRequest('request1')!
    const parent = project.findFolder('folder2')!
    project.moveRequest(moved.key, { parent: parent.key })
    assert.isFalse(
      project.items.some((i) => i.key === moved.key),
      "removes the request from the project's root"
    )
    assert.equal(parent.items[parent.items.length - 1].key, moved.key, 'the parent has the request')
  })

  test('moves a request from the project root to a folder at a position', ({ assert }) => {
    const moved = project.findRequest('request1')!
    const parent = project.findFolder('folder1')!
    project.moveRequest(moved.key, { parent: parent.key, index: 1 })
    assert.isFalse(
      project.items.some((i) => i.key === moved.key),
      "removes the request from the project's root"
    )
    assert.equal(parent.items[1].key, moved.key, 'the parent has the request')
  })

  test('moves a request from a folder to a folder at the end', ({ assert }) => {
    const moved = project.findRequest('request1')!
    const oldParent = project.findFolder('folder1')!
    const parent = project.findFolder('folder2')!
    project.moveRequest(moved.key, { parent: parent.key })
    assert.isFalse(
      oldParent.items.some((i) => i.key === moved.key),
      'removes the request from the old parent'
    )
    assert.equal(parent.items[parent.items.length - 1].key, moved.key, 'the parent has the request')
  })

  test('moves a request from a folder root to a folder at a position', ({ assert }) => {
    const moved = project.findRequest('request1')!
    const parent = project.findFolder('folder2')!
    project.moveRequest(moved.key, { parent: parent.key, index: 0 })
    assert.equal(parent.items[0].key, moved.key, 'the parent has the request')
  })

  test('calls the detachedCallback() on the request', ({ assert }) => {
    const moved = project.findRequest('request2')!
    const spy = sinon.spy(moved, 'detachedCallback')
    project.moveRequest(moved.key)
    assert.isTrue(spy.calledOnce)
  })

  test('calls the attachedCallback() on the request', ({ assert }) => {
    const moved = project.findRequest('request2')!
    const spy = sinon.spy(moved, 'attachedCallback')
    project.moveRequest(moved.key)
    assert.isTrue(spy.calledOnce)
  })

  test('throws when moving into a position that is out of bounds', ({ assert }) => {
    const moved = project.findRequest('request2')!
    const parent = project.findFolder('folder3')!
    assert.throws(
      () => {
        project.moveRequest(moved.key, { parent: parent.key, index: 5 })
      },
      RangeError,
      'Index out of bounds. Maximum index is 0.'
    )
  })

  test('throws when the request is not found', ({ assert }) => {
    assert.throws(
      () => {
        project.moveRequest('unknown')
      },
      Error,
      'Unable to locate the request unknown'
    )
  })

  test('throws when the parent is not found', ({ assert }) => {
    const moved = project.findRequest('request2')!
    assert.throws(
      () => {
        project.moveRequest(moved.key, { parent: 'unknown' })
      },
      Error,
      'Unable to locate the new parent folder unknown'
    )
  })
})

test.group('addLegacyRequest()', () => {
  test('adds a legacy history request', async ({ assert }) => {
    const project = new HttpProject()
    const request = generator.http.history()
    const created = await project.addLegacyRequest(request)
    assert.ok(created, 'returns the created request')
    assert.lengthOf(project.definitions.requests, 1, 'has one definition')

    assert.deepEqual(project.definitions.requests[0], created, "inserts the definition into project's definitions")
    assert.equal(project.items[0].key, created.key, 'the project has the item')

    assert.equal(created.getParent()!.kind, HttpProjectKind, 'the request has the parent as the project')

    assert.equal(created.expects.url, request.url, 'has the URL')
    assert.equal(created.info.name, 'Unnamed request', 'has the default name')
  })

  test('adds a legacy saved request', async ({ assert }) => {
    const project = new HttpProject()
    const request = generator.http.saved()
    const created = await project.addLegacyRequest(request)
    assert.ok(created, 'returns the created request')
    assert.lengthOf(project.definitions.requests, 1, 'has one definition')

    assert.deepEqual(project.definitions.requests[0], created, "inserts the definition into project's definitions")
    assert.equal(project.items[0].key, created.key, 'the project has the item')

    assert.equal(created.getParent()!.kind, HttpProjectKind, 'the request has the parent as the project')

    assert.equal(created.expects.url, request.url, 'has the URL')
    assert.equal(created.info.name, request.name, 'has the name')
  })
})

test.group('listFolderItems()', () => {
  test('returns empty array when no items', ({ assert }) => {
    const project = new HttpProject()
    const result = project.listFolderItems()
    assert.deepEqual(result, [])
  })

  test('ignores request items', ({ assert }) => {
    const project = new HttpProject()
    const request = ProjectRequest.fromName('name', project)
    project.addRequest(request)
    const result = project.listFolderItems()
    assert.deepEqual(result, [])
  })

  test('returns folders', ({ assert }) => {
    const project = new HttpProject()
    const request = ProjectRequest.fromName('name', project)
    project.addRequest(request)
    const folder = project.addFolder('a folder')
    const result = project.listFolderItems()
    assert.lengthOf(result, 1, 'has a single result')
    assert.equal(result[0].key, folder.key)
  })
})

test.group('listRequestItems()', () => {
  test('returns empty array when no items', ({ assert }) => {
    const project = new HttpProject()
    const result = project.listRequestItems()
    assert.deepEqual(result, [])
  })

  test('ignores folder items', ({ assert }) => {
    const project = new HttpProject()
    project.addFolder('a folder')
    const result = project.listRequestItems()
    assert.deepEqual(result, [])
  })

  test('returns requests', ({ assert }) => {
    const project = new HttpProject()
    const request = ProjectRequest.fromName('name', project)
    project.addRequest(request)
    project.addFolder('a folder')
    const result = project.listRequestItems()
    assert.lengthOf(result, 1, 'has a single result')
    assert.equal(result[0].key, request.key)
  })
})

test.group('listFolders()', () => {
  test('lists folders from the project', ({ assert }) => {
    const project = new HttpProject()
    const f1 = project.addFolder('f1')
    const f2 = project.addFolder('f2')
    f2.addFolder('f3')
    project.addRequest(ProjectRequest.fromName('r1', project))
    const result = project.listFolders()
    assert.lengthOf(result, 2, 'has both folders')
    assert.equal(result[0].key, f1.key)
    assert.equal(result[1].key, f2.key)
  })

  test('lists folders from a folder', ({ assert }) => {
    const project = new HttpProject()
    project.addFolder('f1')
    const f2 = project.addFolder('f2')
    const f3 = f2.addFolder('f3')
    f2.addRequest(ProjectRequest.fromName('r1', project))
    const result = project.listFolders({ folder: f2.key })
    assert.lengthOf(result, 1, 'has a single folder')
    assert.equal(result[0].key, f3.key)
  })

  test('returns empty list when no items', ({ assert }) => {
    const project = new HttpProject()
    const f1 = project.addFolder('f1')
    const result = project.listFolders({ folder: f1.key })
    assert.deepEqual(result, [])
  })

  test('throws when parent folder not found', ({ assert }) => {
    const project = new HttpProject()
    assert.throws(
      () => {
        project.listFolders({ folder: 'unknown' })
      },
      Error,
      'Unable to find the folder unknown.'
    )
  })
})

test.group('listRequests()', () => {
  test('lists requests from the project', ({ assert }) => {
    const project = new HttpProject()
    const folder = project.addFolder('f1')
    const request = ProjectRequest.fromName('r1', project)
    project.addRequest(request)
    project.addRequest(ProjectRequest.fromName('r2', project), { parent: folder.key })
    const result = project.listRequests()
    assert.lengthOf(result, 1, 'has a single request')
    assert.equal(result[0].key, request.key)
  })

  test('lists requests from a folder', ({ assert }) => {
    const project = new HttpProject()
    const folder = project.addFolder('f1')
    const request = ProjectRequest.fromName('r1', project)
    project.addRequest(ProjectRequest.fromName('r2', project))
    project.addRequest(request, { parent: folder.key })
    const result = project.listRequests(folder.key)
    assert.lengthOf(result, 1, 'has a single folder')
    assert.equal(result[0].key, request.key)
  })

  test('returns empty list when no items', ({ assert }) => {
    const project = new HttpProject()
    const f1 = project.addFolder('f1')
    const result = project.listRequests(f1.key)
    assert.deepEqual(result, [])
  })

  test('throws when parent folder not found', ({ assert }) => {
    const project = new HttpProject()
    assert.throws(
      () => {
        project.listRequests('unknown')
      },
      Error,
      'Unable to find the folder unknown.'
    )
  })
})

test.group('listDefinitions()', () => {
  test('returns all definitions for a project root', ({ assert }) => {
    const project = new HttpProject()
    const folder = project.addFolder('f1')
    const request = ProjectRequest.fromName('name', project)
    project.addRequest(request)
    const result = project.listDefinitions()
    assert.lengthOf(result, 2, 'has both definitions')
    assert.equal(result[0].key, folder.key, 'has the folder')
    assert.equal(result[1].key, request.key, 'has the request')
  })

  test('returns only the project definitions', ({ assert }) => {
    const project = new HttpProject()
    const folder = project.addFolder('f1')
    const request = ProjectRequest.fromName('name', project)
    project.addRequest(request)

    project.addFolder('other', { parent: folder.key })
    project.addRequest(ProjectRequest.fromName('other', project), { parent: folder.key })

    const result = project.listDefinitions()
    assert.lengthOf(result, 2, 'has both definitions')
    assert.equal(result[0].key, folder.key, 'has the folder')
    assert.equal(result[1].key, request.key, 'has the request')
  })

  test('returns all definitions for a folder', ({ assert }) => {
    const project = new HttpProject()
    const folder = project.addFolder('f1')
    const request = ProjectRequest.fromName('name', project)
    project.addRequest(request, { parent: folder.key })
    const sub = folder.addFolder('sub')
    sub.addFolder('sub-sub')

    const result = project.listDefinitions(folder.key)
    assert.lengthOf(result, 2, 'has both definitions')
    assert.equal(result[0].key, request.key, 'has the request')
    assert.equal(result[1].key, sub.key, 'has the folder')
  })
})

test.group('getParent()', () => {
  test('always returns undefined', ({ assert }) => {
    const project = new HttpProject()
    const result = project.getParent()
    assert.isUndefined(result)
  })
})

test.group('getProject()', () => {
  test('always returns the project', ({ assert }) => {
    const project = new HttpProject()
    const result = project.getProject()
    assert.isTrue(result === project)
  })
})

test.group('attachedCallback()', () => {
  // for code coverage
  test('is called', () => {
    const project = new HttpProject()
    project.attachedCallback()
  })
})

test.group('detachedCallback()', () => {
  // for code coverage
  test('is called', () => {
    const project = new HttpProject()
    project.detachedCallback()
  })
})

test.group('addEnvironment()', (group) => {
  let project: HttpProject
  group.each.setup(() => {
    project = HttpProject.fromName('test')
  })

  test('adds environment by name', ({ assert }) => {
    const created = project.addEnvironment('test')
    assert.deepEqual(project.definitions.environments, [created])
  })

  test('adds environment from an instance', ({ assert }) => {
    const env = Environment.fromName('test')
    project.addEnvironment(env)
    assert.deepEqual(project.definitions.environments, [env])
  })

  test('adds environment from a schema', ({ assert }) => {
    const env = Environment.fromName('test')
    project.addEnvironment(env.toJSON())
    assert.deepEqual(project.definitions.environments, [env])
  })

  test('creates environment array when missing', ({ assert }) => {
    // @ts-expect-error Used in testing
    delete project.definitions.environments
    const created = project.addEnvironment('test')
    assert.deepEqual(project.definitions.environments, [created])
  })

  test('adds missing keys', ({ assert }) => {
    const env = Environment.fromName('test')
    // @ts-expect-error Used in testing
    delete env.key
    project.addEnvironment(env)
    const envs = project.getEnvironments()
    assert.typeOf(envs[0].key, 'string')
  })

  test('adds the environment to the list of project items', ({ assert }) => {
    const e1 = project.addEnvironment('e1')
    assert.lengthOf(project.items, 1)
    assert.equal(project.items[0].key, e1.key)
  })

  test('adds the environment to a folder', ({ assert }) => {
    const f1 = project.addFolder('f1')
    const e1 = project.addEnvironment('e1', { parent: f1.key })
    assert.deepEqual(project.definitions.environments, [e1], 'has the env definition on the project')
    assert.lengthOf(project.items, 1, 'project has only folder item')
    assert.deepEqual(f1.items[0].key, e1.key)
  })
})

test.group('readEnvironments()', (group) => {
  let project: HttpProject
  group.each.setup(() => {
    project = new HttpProject()
  })

  test('reads environments from the project without subfolders without the name', async ({ assert }) => {
    project.addEnvironment('a')
    project.addEnvironment('b')

    const envs = project.readEnvironments()
    assert.lengthOf(envs, 2, 'has both environments')
  })

  test('reads environments from the project without subfolders with the name', async ({ assert }) => {
    project.addEnvironment('a')
    project.addEnvironment('b')

    const envs = project.readEnvironments({ nameOrKey: 'b' })
    assert.lengthOf(envs, 1, 'has the project environment')
    assert.equal(envs[0].info.name, 'b', 'has the first environment')
  })

  test('reads environments from a folder', async ({ assert }) => {
    project.addEnvironment('a')
    const f = project.addFolder('folder')
    f.addEnvironment('b')
    f.addEnvironment('c')
    const envs = project.readEnvironments({ parent: f.key })
    assert.lengthOf(envs, 3, 'has all environments')
    assert.equal(envs[0].info.name, 'a', 'has the first environment')
    assert.equal(envs[1].info.name, 'c', 'has the second environment')
    assert.equal(envs[2].info.name, 'b', 'has the third environment')
  })

  test('skips folders without environments', async ({ assert }) => {
    project.addEnvironment('a')
    const f1 = project.addFolder('folder 1')
    const f2 = f1.addFolder('folder 2')
    f2.addEnvironment('b')

    const envs = project.readEnvironments({ parent: f2.key })
    assert.lengthOf(envs, 2, 'has all environments')
    assert.equal(envs[0].info.name, 'a', 'has the first environment')
    assert.equal(envs[1].info.name, 'b', 'has the second environment')
  })

  test('stops reading when "encapsulated is set"', async ({ assert }) => {
    project.addEnvironment('a')
    const f1 = project.addFolder('folder 1')
    const f2 = f1.addFolder('folder 2')
    f1.addEnvironment('b')
    const env = f2.addEnvironment('c')
    env.encapsulated = true

    const envs = project.readEnvironments({ parent: f2.key })
    assert.lengthOf(envs, 1, 'has a single environment')
    assert.equal(envs[0].info.name, 'c', 'has the folder environment')
  })

  test('returns empty list when unknown parent', async ({ assert }) => {
    project.addEnvironment('a')
    const f = project.addFolder('folder 1')
    f.addEnvironment('b')
    const envs = project.readEnvironments({ parent: 'some' })
    assert.lengthOf(envs, 0)
  })

  test('lists all applicable environments to a request in a folder', ({ assert }) => {
    project.addEnvironment('e1')
    const f1 = project.addFolder('f1')
    f1.addEnvironment('e2')
    const f2 = f1.addFolder('f2')
    f2.addEnvironment('e3')
    const r1 = f2.addRequest('r1')
    const result = r1.readEnvironments()
    assert.lengthOf(result, 3, 'has all environments')
    assert.equal(result[0].info.name, 'e1', 'has the first environment')
    assert.equal(result[1].info.name, 'e2', 'has the second environment')
    assert.equal(result[2].info.name, 'e3', 'has the third environment')
  })

  test('lists all applicable environments to a request in a folder with encapsulate', ({ assert }) => {
    project.addEnvironment('e1')
    const f1 = project.addFolder('f1')
    const e2 = f1.addEnvironment('e2')
    e2.encapsulated = true
    const f2 = f1.addFolder('f2')
    f2.addEnvironment('e3')
    const r1 = f2.addRequest('r1')
    const result = r1.readEnvironments()
    assert.lengthOf(result, 2, 'has all environments')
    assert.equal(result[0].info.name, 'e2', 'has the second environment')
    assert.equal(result[1].info.name, 'e3', 'has the third environment')
  })
})

test.group('listEnvironments()', (group) => {
  let project: HttpProject
  group.each.setup(() => {
    project = new HttpProject()
  })

  test('returns environments defined in the project only', ({ assert }) => {
    const f1 = project.addFolder('f1')
    f1.addEnvironment('e1')
    const e2 = project.addEnvironment('e2')

    const result = project.listEnvironments()
    assert.deepEqual(result, [e2])
  })

  test('returns environments defined in a folder', ({ assert }) => {
    const f1 = project.addFolder('f1')
    const e1 = f1.addEnvironment('e1')
    project.addEnvironment('e2')

    const result = project.listEnvironments({ parent: f1.key })
    assert.deepEqual(result, [e1])
  })
})

test.group('getEnvironments()', () => {
  test('returns class initialization environments', ({ assert }) => {
    const result = new HttpProject(undefined, [
      {
        key: 'a',
        kind: 'Core#Environment',
        info: {
          kind: ThingKind,
          name: 'test',
        },
        variables: [],
        server: {
          kind: ServerKind,
          uri: 'https://api.com',
        },
      },
    ])
    const envs = result.getEnvironments()
    assert.typeOf(envs, 'array')
    assert.lengthOf(envs, 1)
    assert.equal(envs[0].info.name, 'test')
  })

  test('returns environments when no initialization environments', ({ assert }) => {
    const project = new HttpProject()
    const f1 = project.addFolder('f1')
    f1.addEnvironment('e1')
    const e2 = project.addEnvironment('e2')

    const result = project.getEnvironments()
    assert.deepEqual(result, [e2])
  })
})

test.group('findEnvironment()', () => {
  test('returns the environment from the project root', ({ assert }) => {
    const project = new HttpProject()
    project.addEnvironment('e1')
    const env = project.addEnvironment('e2')
    const result = project.findEnvironment(env.key)
    assert.deepEqual(result, env)
  })

  test('returns the environment from a folder', ({ assert }) => {
    const project = new HttpProject()
    project.addEnvironment('e1')
    const f1 = project.addFolder('f1')
    const env = f1.addEnvironment('e2')
    const result = project.findEnvironment(env.key)
    assert.deepEqual(result, env)
  })
})

test.group('removeEnvironment()', (group) => {
  let project: HttpProject
  group.each.setup(() => {
    project = new HttpProject()
  })

  test('removes the environment from the definitions when in project root', ({ assert }) => {
    const e1 = project.addEnvironment('e1')
    project.removeEnvironment(e1.key)
    assert.deepEqual(project.definitions.environments, [])
  })

  test('removes the environment from the definitions when in a folder', ({ assert }) => {
    const f1 = project.addFolder('f1')
    const e1 = f1.addEnvironment('e1')
    project.removeEnvironment(e1.key, { parent: f1.key })
    assert.deepEqual(project.definitions.environments, [])
  })

  test('does nothing when the environment does not belong to the folder', ({ assert }) => {
    const f1 = project.addFolder('f1')
    const f2 = f1.addFolder('f2')
    const e1 = f2.addEnvironment('e1')
    project.removeEnvironment(e1.key, { parent: f1.key })
    assert.isNotEmpty(project.definitions.environments)
  })

  test('returns the removed environment', ({ assert }) => {
    const e1 = project.addEnvironment('e1')
    const result = project.removeEnvironment(e1.key)
    assert.deepEqual(result, e1)
  })

  test('removes the environment from the project items', ({ assert }) => {
    const e1 = project.addEnvironment('e1')
    project.removeEnvironment(e1.key)
    assert.deepEqual(project.items, [])
  })

  test('removes the environment from the folder items', ({ assert }) => {
    const f1 = project.addFolder('f1')
    const e1 = f1.addEnvironment('e1')
    project.removeEnvironment(e1.key, { parent: f1.key })
    assert.deepEqual(f1.items, [])
  })
})

test.group('clone()', (group) => {
  let project: HttpProject
  group.each.setup(() => {
    project = HttpProject.fromName('a project')
  })

  test('clones the project', ({ assert }) => {
    project.addRequest('https://domain.com')
    project.addFolder('test')
    const copy = project.clone()

    assert.equal(copy.kind, HttpProjectKind)
    assert.equal(copy.info.name, 'a project')

    assert.lengthOf(copy.listFolders(), 1, 'has the folder')
    assert.lengthOf(copy.listRequests(), 1, 'has the requests')

    project.info.name = 'updated'
    assert.equal(copy.info.name, 'a project')
  })

  test('recreates the project key', ({ assert }) => {
    const copy = project.clone()

    assert.typeOf(copy.key, 'string')
    assert.notEqual(copy.key, project.key)
  })

  test('recreates folder keys', ({ assert }) => {
    const f = project.addFolder('test')
    const copy = project.clone()

    const [folder] = copy.listFolders()
    assert.typeOf(folder.key, 'string')
    assert.notEqual(folder.key, f.key)
  })

  test('recreates requests keys', ({ assert }) => {
    const r = project.addRequest('https://domain.com')
    const copy = project.clone()

    const [request] = copy.listRequests()
    assert.typeOf(request.key, 'string')
    assert.notEqual(request.key, r.key)
  })

  test('updates the keys in folder items', ({ assert }) => {
    const f = project.addFolder('f1')
    const r = f.addRequest('https://domain.com')
    r.info.name = 'r1'
    const copy = project.clone()

    const request = copy.findRequest('r1')!
    const folder = copy.findFolder('f1')!
    assert.ok(request, 'has copied request')
    assert.ok(folder, 'has copied folder')

    assert.lengthOf(folder.items, 1, 'folder has the item')
    assert.equal(folder.items[0].key, request.key, 'the item has the reference key')
  })

  test('updates keys for environments', ({ assert }) => {
    const env = project.addEnvironment('test')
    const copy = project.clone()

    assert.typeOf(copy.definitions.environments[0].key, 'string')
    assert.notEqual(copy.definitions.environments[0].key, env.key)
  })

  test('updates keys for schemas', ({ assert }) => {
    const schema = ProjectSchema.fromName('s1')
    project.definitions.schemas.push(schema)
    const copy = project.clone()

    assert.typeOf(copy.definitions.schemas[0].key, 'string')
    assert.notEqual(copy.definitions.schemas[0].key, schema.key)
  })

  test('updates keys for certificates', ({ assert }) => {
    const cert = Certificate.fromP12('value')
    project.definitions.certificates.push(cert)
    const copy = project.clone()

    assert.typeOf(copy.definitions.certificates[0].key, 'string')
    assert.notEqual(copy.definitions.certificates[0].key, cert.key)
  })

  test('does not update keys when configured', ({ assert }) => {
    const f = project.addFolder('test')
    const r = project.addRequest('https://domain.com')
    const env = project.addEnvironment('test')
    const schema = ProjectSchema.fromName('s1')
    project.definitions.schemas.push(schema)
    const cert = Certificate.fromP12('value')
    project.definitions.certificates.push(cert)
    const copy = project.clone({ withoutRevalidate: true })

    assert.equal(copy.key, project.key)
    assert.equal(copy.definitions.schemas[0].key, schema.key)
    assert.equal(copy.definitions.certificates[0].key, cert.key)
    const [folder] = copy.listFolders()
    assert.equal(folder.key, f.key)
    const [request] = copy.listRequests()
    assert.equal(request.key, r.key)
    assert.equal(copy.definitions.environments[0].key, env.key)
  })
})

test.group('HttpProject.clone()', () => {
  test('clones a project', ({ assert }) => {
    const project = HttpProject.fromName('a project')
    const copy = HttpProject.clone(project.toJSON())
    assert.equal(copy.info.name, 'a project')
  })
})

test.group('toString()', () => {
  test('serializes the project', ({ assert }) => {
    const project = HttpProject.fromName('a project')
    const str = project.toString()
    assert.typeOf(str, 'string', 'produces a string')
    const obj = JSON.parse(str)
    assert.equal(obj.info.name, 'a project')
  })
})

test.group('addSchema()', () => {
  test('adds an instance of the schema', ({ assert }) => {
    const project = new HttpProject()
    const schema = ProjectSchema.fromName('test')
    const created = project.addSchema(schema)
    assert.deepEqual(created, schema)

    assert.lengthOf(project.definitions.schemas, 1, 'has one schema')
    assert.equal(project.definitions.schemas[0].key, created.key, 'the project has the schema')
  })

  test('adds by a schema', ({ assert }) => {
    const project = new HttpProject()
    const schema = ProjectSchema.fromName('test')
    const created = project.addSchema(schema.toJSON())
    assert.deepEqual(created, schema)

    assert.lengthOf(project.definitions.schemas, 1, 'has one schema')
    assert.equal(project.definitions.schemas[0].key, created.key, 'the project has the schema')
  })

  test('adds by name', ({ assert }) => {
    const project = new HttpProject()
    const created = project.addSchema('test schema')
    assert.equal(created.name, 'test schema')

    assert.lengthOf(project.definitions.schemas, 1, 'has one schema')
    assert.equal(project.definitions.schemas[0].key, created.key, 'the project has the schema')
  })

  test('inserts schema at position', ({ assert }) => {
    const project = new HttpProject()
    project.addSchema('schema 1')
    project.addSchema('schema 2')
    const key = project.addSchema('schema 3', { index: 1 }).key
    assert.equal(project.definitions.schemas[1].key, key)
  })
})

test.group('listSchemas()', () => {
  test('returns empty array when no schemas', ({ assert }) => {
    const project = new HttpProject()
    // @ts-expect-error Used in testing
    delete project.definitions.schemas
    const result = project.listSchemas()
    assert.deepEqual(result, [])
  })

  test('returns created schemas', ({ assert }) => {
    const project = new HttpProject()
    project.addSchema('s1')
    project.addSchema('s2')
    const result = project.listSchemas()
    assert.lengthOf(result, 2)
  })
})

test.group('requestIterator()', () => {
  test('iterates over requests in the project', ({ assert }) => {
    const project = new HttpProject()
    const r1 = project.addRequest('r1')
    const r2 = project.addRequest('r2')
    const r3 = project.addRequest('r3')

    const result: ProjectRequest[] = []

    for (const request of project.requestIterator()) {
      result.push(request)
    }

    assert.deepEqual(result, [r1, r2, r3])
  })

  test('iterates over requests in a folder (parent option)', ({ assert }) => {
    const project = new HttpProject()
    const f1 = project.addFolder('f1')
    const r1 = f1.addRequest('r1')
    const r2 = f1.addRequest('r2')
    const r3 = f1.addRequest('r3')

    const result: ProjectRequest[] = []

    for (const request of project.requestIterator({ parent: f1.key })) {
      result.push(request)
    }

    assert.deepEqual(result, [r1, r2, r3])
  })

  test('iterates over requests in the project and a folder (recursive)', ({ assert }) => {
    const project = new HttpProject()
    const r1 = project.addRequest('r1')
    const f1 = project.addFolder('f1')
    const r2 = f1.addRequest('r2')
    const r3 = f1.addRequest('r3')
    const result: ProjectRequest[] = []
    for (const request of project.requestIterator({ recursive: true })) {
      result.push(request)
    }
    assert.deepEqual(result, [r1, r2, r3])
  })

  test('respects the recursive order', ({ assert }) => {
    const project = new HttpProject()
    const r1 = project.addRequest('r1')
    const f1 = project.addFolder('f1')
    const r2 = f1.addRequest('r2')
    const r3 = project.addRequest('r3')
    const result: ProjectRequest[] = []
    for (const request of project.requestIterator({ recursive: true })) {
      result.push(request)
    }
    assert.deepEqual(result, [r1, r2, r3])
  })

  test('iterates over a deep structure', ({ assert }) => {
    const project = new HttpProject()
    const r1 = project.addRequest('r1')
    const f1 = project.addFolder('f1')
    const f2 = f1.addFolder('f2')
    const r2 = f1.addRequest('r2')
    const r3 = f2.addRequest('r3')
    const result: ProjectRequest[] = []
    for (const request of project.requestIterator({ recursive: true })) {
      result.push(request)
    }
    assert.deepEqual(result, [r1, r3, r2])
  })

  test('respects the ignore option with a request name', ({ assert }) => {
    const project = new HttpProject()
    const r1 = project.addRequest('r1')
    r1.info.name = 'r1name'
    const r2 = project.addRequest('r2')
    const r3 = project.addRequest('r3')
    const result: ProjectRequest[] = []
    for (const request of project.requestIterator({ ignore: ['r1name'] })) {
      result.push(request)
    }
    assert.deepEqual(result, [r2, r3])
  })

  test('respects the ignore option with a request key', ({ assert }) => {
    const project = new HttpProject()
    const r1 = project.addRequest('r1')
    const r2 = project.addRequest('r2')
    const r3 = project.addRequest('r3')
    const result: ProjectRequest[] = []
    for (const request of project.requestIterator({ ignore: [r1.key] })) {
      result.push(request)
    }
    assert.deepEqual(result, [r2, r3])
  })

  test('ignores entire folders with the key', ({ assert }) => {
    const project = new HttpProject()
    const r1 = project.addRequest('r1')
    const f1 = project.addFolder('f1')
    f1.addRequest('r2')
    f1.addRequest('r3')
    const result: ProjectRequest[] = []
    for (const request of project.requestIterator({ ignore: [f1.key], recursive: true })) {
      result.push(request)
    }
    assert.deepEqual(result, [r1])
  })

  test('ignores entire folders with the name', ({ assert }) => {
    const project = new HttpProject()
    const r1 = project.addRequest('r1')
    const f1 = project.addFolder('f1')
    f1.addRequest('r2')
    f1.addRequest('r3')
    const result: ProjectRequest[] = []
    for (const request of project.requestIterator({ ignore: ['f1'], recursive: true })) {
      result.push(request)
    }
    assert.deepEqual(result, [r1])
  })

  test('includes only requested items from the project root by name', ({ assert }) => {
    const project = new HttpProject()
    const r1 = project.addRequest('r1')
    r1.info.name = 'r1name'
    project.addRequest('r2')
    project.addRequest('r3')
    const result: ProjectRequest[] = []
    for (const request of project.requestIterator({ requests: ['r1name'] })) {
      result.push(request)
    }
    assert.deepEqual(result, [r1])
  })

  test('includes only requested items from the project root by key', ({ assert }) => {
    const project = new HttpProject()
    const r1 = project.addRequest('r1')
    project.addRequest('r2')
    project.addRequest('r3')
    const result: ProjectRequest[] = []
    for (const request of project.requestIterator({ requests: [r1.key] })) {
      result.push(request)
    }
    assert.deepEqual(result, [r1])
  })

  test('includes only requested items recursive', ({ assert }) => {
    const project = new HttpProject()
    const r1 = project.addRequest('r1')
    const f1 = project.addFolder('f1')
    const r2 = f1.addRequest('r2')
    project.addRequest('r3')
    const result: ProjectRequest[] = []
    for (const request of project.requestIterator({ requests: [r1.key, r2.key], recursive: true })) {
      result.push(request)
    }
    assert.deepEqual(result, [r1, r2])
  })

  test('recursively iterates the project object', ({ assert }) => {
    const project = new HttpProject()
    const r1 = project.addRequest('r1')
    const f1 = project.addFolder('f1')
    const r2 = f1.addRequest('r2')
    const r3 = project.addRequest('r3')
    const result: ProjectRequest[] = []
    for (const request of project) {
      result.push(request)
    }
    assert.deepEqual(result, [r1, r2, r3])
  })
})

test.group('[Symbol.iterator]', () => {
  test('iterates recursively', ({ assert }) => {
    const project = new HttpProject()
    const r1 = project.addRequest('r1')
    const f1 = project.addFolder('f1')
    const r2 = f1.addRequest('r2')
    const r3 = project.addRequest('r3')
    const result: ProjectRequest[] = []
    for (const request of project) {
      result.push(request)
    }
    assert.deepEqual(result, [r1, r2, r3])
  })
})

test.group('folderIterator()', () => {
  test('iterates over folders in the project', ({ assert }) => {
    const project = new HttpProject()
    const f1 = project.addFolder('f1')
    const f2 = project.addFolder('f2')
    const f3 = project.addFolder('f3')

    const result: IProjectFolderIteratorResult[] = []

    for (const folder of project.folderIterator()) {
      result.push(folder)
    }

    assert.deepEqual(result[0].folder, f1)
    assert.deepEqual(result[1].folder, f2)
    assert.deepEqual(result[2].folder, f3)

    assert.isUndefined(result[0].parent)
    assert.isUndefined(result[1].parent)
    assert.isUndefined(result[2].parent)

    assert.equal(result[0].indent, 0)
    assert.equal(result[1].indent, 0)
    assert.equal(result[2].indent, 0)
  })

  test('iterates over folders in a folder', ({ assert }) => {
    const project = new HttpProject()
    const f1 = project.addFolder('f1')
    const f2 = f1.addFolder('f2')
    const f3 = f1.addFolder('f3')

    const result: IProjectFolderIteratorResult[] = []

    for (const folder of project.folderIterator({ parent: f1.key })) {
      result.push(folder)
    }

    assert.deepEqual(result[0].folder, f2)
    assert.deepEqual(result[1].folder, f3)

    assert.equal(result[0].parent, f1.key)
    assert.equal(result[1].parent, f1.key)

    assert.equal(result[0].indent, 0, 'has no relative indent')
    assert.equal(result[1].indent, 0, 'has no relative indent')
  })

  test('iterates over folders recursively', ({ assert }) => {
    const project = new HttpProject()
    const f1 = project.addFolder('f1')
    const f2 = f1.addFolder('f2')
    const f3 = f2.addFolder('f3')

    const result: IProjectFolderIteratorResult[] = []

    for (const folder of project.folderIterator({ recursive: true })) {
      result.push(folder)
    }

    assert.deepEqual(result[0].folder, f1)
    assert.deepEqual(result[1].folder, f2)
    assert.deepEqual(result[2].folder, f3)

    assert.isUndefined(result[0].parent)
    assert.equal(result[1].parent, f1.key)
    assert.equal(result[2].parent, f2.key)

    assert.equal(result[0].indent, 0, 'has no relative indent')
    assert.equal(result[1].indent, 1, 'has relative indent')
    assert.equal(result[2].indent, 2, 'has relative indent')
  })

  test('preserves the order', ({ assert }) => {
    const project = new HttpProject()
    const f1 = project.addFolder('f1')
    const f2 = f1.addFolder('f2')
    const f3 = project.addFolder('f3')
    const f4 = f1.addFolder('f4')

    const result: IProjectFolderIteratorResult[] = []

    for (const folder of project.folderIterator({ recursive: true })) {
      result.push(folder)
    }

    assert.deepEqual(result[0].folder, f1, '#1 is f1')
    assert.deepEqual(result[1].folder, f2, '#2 is f2')
    assert.deepEqual(result[2].folder, f4, '#3 is f4')
    assert.deepEqual(result[3].folder, f3, '#4 is f3')
  })

  test('ignores folders', ({ assert }) => {
    const project = new HttpProject()
    const f1 = project.addFolder('f1')
    const f2 = project.addFolder('f2')
    const f3 = project.addFolder('f3')

    const result: IProjectFolderIteratorResult[] = []

    for (const folder of project.folderIterator({ ignore: [f2.key, f3.key] })) {
      result.push(folder)
    }

    assert.lengthOf(result, 1, 'has only one element')

    assert.deepEqual(result[0].folder, f1)
  })

  test('ignores folders with recursive', ({ assert }) => {
    const project = new HttpProject()
    const f1 = project.addFolder('f1')
    const f2 = project.addFolder('f2')
    const f3 = f2.addFolder('f3')

    const result: IProjectFolderIteratorResult[] = []

    for (const folder of project.folderIterator({ ignore: [f3.key], recursive: true })) {
      result.push(folder)
    }

    assert.lengthOf(result, 2, 'has only one element')

    assert.deepEqual(result[0].folder, f1)
    assert.deepEqual(result[1].folder, f2)
  })

  test('ignores sub-folders with recursive', ({ assert }) => {
    const project = new HttpProject()
    const f1 = project.addFolder('f1')
    const f2 = project.addFolder('f2')
    f1.addFolder('f3')

    const result: IProjectFolderIteratorResult[] = []

    for (const folder of project.folderIterator({ ignore: [f1.key], recursive: true })) {
      result.push(folder)
    }
    assert.lengthOf(result, 1, 'has only one element')
    assert.deepEqual(result[0].folder, f2)
  })

  test('ignores requests', ({ assert }) => {
    const project = new HttpProject()
    const f1 = project.addFolder('f1')
    project.addRequest('r1')

    const result: IProjectFolderIteratorResult[] = []

    for (const folder of project.folderIterator()) {
      result.push(folder)
    }
    assert.lengthOf(result, 1, 'has only one element')
    assert.deepEqual(result[0].folder, f1)
  })
})

test.group('addCertificate()', () => {
  test('adds a certificate instance', ({ assert }) => {
    const cert = Certificate.fromP12('value')
    const project = new HttpProject()
    const created = project.addCertificate(cert)
    assert.deepEqual(created, cert)

    assert.lengthOf(project.definitions.certificates, 1, 'has one certificate')
    assert.equal(project.definitions.certificates[0].key, created.key, 'the project has the certificate')
  })

  test('adds by the schema', ({ assert }) => {
    const project = new HttpProject()
    const cert = Certificate.fromP12('value')
    const created = project.addCertificate(cert.toJSON())
    assert.deepEqual(created, cert)

    assert.lengthOf(project.definitions.certificates, 1, 'has one certificate')
    assert.equal(project.definitions.certificates[0].key, created.key, 'the project has the certificate')
  })
})

test.group('removeCertificate()', (group) => {
  let project: HttpProject
  let cert: Certificate
  group.each.setup(() => {
    cert = Certificate.fromP12('value')
    project = new HttpProject()
    project.addCertificate(cert)
  })

  test('removes the certificate from the project', ({ assert }) => {
    project.removeCertificate(cert.key)
    assert.lengthOf(project.definitions.certificates, 0, 'has no certificates')
  })

  test('returns the removed certificate', ({ assert }) => {
    const result = project.removeCertificate(cert.key)
    assert.deepEqual(result, cert)
  })

  test('returns undefined when no certificate', ({ assert }) => {
    const result = project.removeCertificate('other')
    assert.isUndefined(result)
  })

  test('removes only the requested certificate', ({ assert }) => {
    const c2 = Certificate.fromP12('value')
    project.addCertificate(c2)
    project.removeCertificate(cert.key)
    assert.deepEqual(project.definitions.certificates, [c2])
  })
})

test.group('findCertificate()', (group) => {
  let project: HttpProject
  let cert: Certificate
  group.each.setup(() => {
    cert = Certificate.fromP12('value')
    project = new HttpProject()
    project.addCertificate(cert)
  })

  test('returns the certificate', ({ assert }) => {
    const result = project.findCertificate(cert.key)
    assert.deepEqual(result, cert)
  })

  test('returns undefined when no certificate', ({ assert }) => {
    const result = project.findCertificate('other')
    assert.isUndefined(result)
  })
})
