import path from 'path';
import * as harUtils from '../har';
import * as render from '../render';
import * as models from '../../models';
import { AUTH_BASIC } from '../constants';
import { globalBeforeEach } from '../../__jest__/before-each';

describe('exportHar()', () => {
  beforeEach(globalBeforeEach);
  it('exports single requests', async () => {
    const wrk = await models.workspace.create({
      _id: 'wrk_1',
      name: 'Workspace',
    });
    const req1 = await models.request.create({
      _id: 'req_1',
      name: 'Request 1',
      parentId: wrk._id,

      url: 'https://httpstat.us/200',
      method: 'POST',
      body: {
        mimeType: 'application/json',
        text: '{}',
      },
      headers: [
        { name: 'Content-Type', value: 'application/json' },
        { name: 'Accept', value: 'application/json', disabled: false },
        { name: 'X-Disabled', value: 'X-Disabled', disabled: true },
      ],
    });
    await models.response.create({
      parentId: req1._id,

      statusCode: 200,
      statusMessage: 'OK',
      elapsedTime: 999,
      headers: [{ name: 'Content-Type', value: 'application/json' }],
      contentType: 'application/json',
      bodyPath: path.join(__dirname, '../__fixtures__/har/test-response.json'),
      bodyCompression: null,
    });

    const exportRequests = [{ requestId: req1._id, environmentId: 'n/a' }];
    const harExport = await harUtils.exportHar(exportRequests);

    expect(harExport).toMatchObject({
      log: {
        version: '1.2',
        creator: {
          name: 'Insomnia REST Client',
        },
        entries: [
          {
            startedDateTime: expect.any(String),
            time: 999,
            request: {
              method: 'POST',
              url: 'https://httpstat.us/200',
              httpVersion: 'HTTP/1.1',
              cookies: [],
              headers: [
                { name: 'Content-Type', value: 'application/json' },
                { name: 'Accept', value: 'application/json' },
              ],
              queryString: [],
              postData: {
                mimeType: 'application/json',
                params: [],
                text: '{}',
              },
              headersSize: -1,
              bodySize: -1,
            },
            response: {
              status: 200,
              statusText: 'OK',
              httpVersion: 'HTTP/1.1',
              cookies: [],
              headers: [{ name: 'Content-Type', value: 'application/json' }],
              content: {
                size: 15,
                mimeType: 'application/json',
                text: '{"key":"value"}',
              },
              redirectURL: '',
              headersSize: -1,
              bodySize: -1,
            },
            cache: {},
            timings: {
              blocked: -1,
              dns: -1,
              connect: -1,
              send: 0,
              wait: 999,
              receive: 0,
              ssl: -1,
            },
            comment: req1.name,
          },
        ],
      },
    });
  });

  it('exports multiple requests', async () => {
    const workspace = await models.workspace.create({
      _id: 'wrk_1',
      name: 'Workspace',
    });

    const baseReq = await models.request.create({
      _id: 'req_0',
      type: models.request.type,
      name: 'Request',
      parentId: workspace._id,

      url: 'http://localhost',
      method: 'GET',
      body: {},
      headers: [{ name: 'X-Environment', value: '{{ envvalue }}' }],
    });
    const req1 = await models.request.duplicate(baseReq);
    req1._id = 'req_1';
    req1.name = 'Request 1';
    req1.headers.push({ name: 'X-Request', value: '1' });
    await models.request.create(req1);
    const req2 = await models.request.duplicate(baseReq);
    req2._id = 'req_2';
    req2.name = 'Request 2';
    req2.headers.push({ name: 'X-Request', value: '2' });
    await models.request.create(req2);
    const req3 = await models.request.duplicate(baseReq);
    req3._id = 'req_3';
    req3.name = 'Request 3';
    req3.headers.push({ name: 'X-Request', value: '3' });
    await models.request.create(req3);

    await models.response.create({
      _id: 'res_1',
      parentId: req1._id,
      statusCode: 204,
    });
    await models.response.create({
      _id: 'res_2',
      parentId: req2._id,
      statusCode: 404,
    });
    await models.response.create({
      _id: 'res_3',
      parentId: req3._id,
      statusCode: 500,
    });

    const envBase = await models.environment.getOrCreateForWorkspace(workspace);
    await models.environment.update(envBase, {
      data: {
        envvalue: '',
      },
    });
    const envPublic = await models.environment.create({
      _id: 'env_1',
      name: 'Public',
      parentId: envBase._id,
    });
    await models.environment.update(envPublic, {
      data: {
        envvalue: 'public',
      },
    });
    const envPrivate = await models.environment.create({
      _id: 'env_2',
      name: 'Private',
      isPrivate: true,
      parentId: envBase._id,
    });
    await models.environment.update(envPrivate, {
      data: {
        envvalue: 'private',
      },
    });

    const exportRequests = [
      { requestId: req1._id, environmentId: 'n/a' },
      { requestId: req2._id, environmentId: envPublic._id },
      { requestId: req3._id, environmentId: envPrivate._id },
    ];
    const harExport = await harUtils.exportHar(exportRequests);

    expect(harExport).toMatchObject({
      log: {
        version: '1.2',
        creator: {
          name: 'Insomnia REST Client',
        },
        entries: [
          {
            request: {
              headers: [{ name: 'X-Environment', value: '' }, { name: 'X-Request', value: '1' }],
            },
            response: {
              status: 204,
            },
            comment: req1.name,
          },
          {
            request: {
              headers: [
                { name: 'X-Environment', value: 'public' },
                { name: 'X-Request', value: '2' },
              ],
            },
            response: {
              status: 404,
            },
            comment: req2.name,
          },
          {
            request: {
              headers: [
                { name: 'X-Environment', value: 'private' },
                { name: 'X-Request', value: '3' },
              ],
            },
            response: {
              status: 500,
            },
            comment: req3.name,
          },
        ],
      },
    });
  });
});

describe('exportHarResponse()', () => {
  beforeEach(globalBeforeEach);
  it('exports a default har response for an empty response', async () => {
    const notFoundResponse = null;

    const harResponse = await harUtils.exportHarResponse(notFoundResponse);

    expect(harResponse).toMatchObject({
      status: 0,
      statusText: '',
      httpVersion: 'HTTP/1.1',
      content: {
        size: 0,
        mimeType: '',
      },
    });
  });
  it('exports a valid har response for a non empty response', async () => {
    const response = Object.assign(models.response.init(), {
      _id: 'res_123',
      type: models.response.type,
      parentId: 'req_123',
      modified: 0,
      created: 0,

      statusCode: 200,
      statusMessage: 'OK',
      headers: [
        { name: 'Content-Type', value: 'application/json' },
        { name: 'Content-Length', value: '2' },
        { name: 'Set-Cookie', value: 'sessionid=12345; HttpOnly; Path=/' },
      ],
      contentType: 'application/json',
      bodyPath: path.join(__dirname, '../__fixtures__/har/test-response.json'),
    });

    const harResponse = await harUtils.exportHarResponse(response);

    expect(harResponse).toMatchObject({
      status: 200,
      statusText: 'OK',
      httpVersion: 'HTTP/1.1',
      cookies: [
        {
          name: 'sessionid',
          value: '12345',
          path: '/',
          httpOnly: true,
        },
      ],
      headers: [
        { name: 'Content-Type', value: 'application/json' },
        { name: 'Content-Length', value: '2' },
        { name: 'Set-Cookie', value: 'sessionid=12345; HttpOnly; Path=/' },
      ],
      content: {
        size: 15,
        mimeType: 'application/json',
        text: '{"key":"value"}',
      },
      redirectURL: '',
      headersSize: -1,
      bodySize: -1,
    });
  });
});

describe('exportHarWithRequest()', () => {
  beforeEach(globalBeforeEach);
  it('renders does it correctly', async () => {
    const workspace = await models.workspace.create();
    const cookies = [
      {
        creation: new Date('2016-10-05T04:40:49.505Z'),
        key: 'foo',
        value: 'barrrrr',
        expires: new Date('2096-10-12T04:40:49.000Z'),
        domain: 'google.com',
        path: '/',
        hostOnly: true,
        lastAccessed: new Date('2096-10-05T04:40:49.505Z'),
      },
    ];

    const cookieJar = await models.cookieJar.getOrCreateForParentId(workspace._id);
    await models.cookieJar.update(cookieJar, {
      parentId: workspace._id,
      cookies,
    });

    const request = Object.assign(models.request.init(), {
      _id: 'req_123',
      parentId: workspace._id,
      headers: [{ name: 'Content-Type', value: 'application/json' }],
      parameters: [{ name: 'foo bar', value: 'hello&world' }],
      method: 'POST',
      body: {
        text: 'foo bar',
      },
      url: 'http://google.com',
      authentication: {
        type: AUTH_BASIC,
        username: 'user',
        password: 'pass',
      },
    });

    const renderedRequest = await render.getRenderedRequest(request);
    const har = await harUtils.exportHarWithRequest(renderedRequest);

    expect(har.cookies.length).toBe(1);
    expect(har).toEqual({
      bodySize: -1,
      cookies: [
        {
          domain: 'google.com',
          expires: '2096-10-12T04:40:49.000Z',
          name: 'foo',
          path: '/',
          value: 'barrrrr',
        },
      ],
      headers: [
        { name: 'Content-Type', value: 'application/json' },
        { name: 'Authorization', value: 'Basic dXNlcjpwYXNz' },
      ],
      headersSize: -1,
      httpVersion: 'HTTP/1.1',
      method: 'POST',
      postData: {
        mimeType: '',
        params: [],
        text: 'foo bar',
      },
      queryString: [{ name: 'foo bar', value: 'hello&world' }],
      url: 'http://google.com/',
      settingEncodeUrl: true,
    });
  });
});
