/*
 * Copyright (C) 2019, 2020 StApps
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */
import {
  SCNotFoundErrorResponse,
  SCRequestBodyTooLargeErrorResponse,
  SCSyntaxErrorResponse,
  SCUnsupportedMediaTypeErrorResponse,
} from '@openstapps/core';
import {expect} from 'chai';
import {DEFAULT_TIMEOUT} from '../src/common.js';
import {DEFAULT_TEST_TIMEOUT} from './common.js';
import {testApp} from './tests-setup.js';
import sinon from 'sinon';
import mockedEnv from 'mocked-env';
import {backendConfig} from '../src/config.js';

describe('App', async function () {
  // increase timeout for the suite
  this.timeout(DEFAULT_TEST_TIMEOUT);
  const sandbox = sinon.createSandbox();

  afterEach(function () {
    sandbox.restore();
  });

  it('should exit process if there is 20 seconds of pause after a request during the integration test', async function () {
    const clock = sandbox.useFakeTimers();
    const processExitStub = sandbox.stub(process, 'exit');
    // fake NODE_ENV as integration test
    // @ts-expect-error type definitions are not working for some reason
    const restore = mockedEnv({
      NODE_ENV: 'integration-test',
    });
    await testApp.post('/');
    // fake timeout of default timeout
    clock.tick(DEFAULT_TIMEOUT);

    expect(processExitStub.called).to.be.true;

    // restore env variables
    restore();
    // terminate faking of the clock (setTimeout etc.)
    clock.restore();
  });

  it('should provide request body too large error in case of a body larger than the max size', async function () {
    sandbox.stub(backendConfig.backend, 'maxRequestBodySize').value('3');

    const {status} = await testApp
      .post('/')
      .set('Content-Type', 'application/json')
      .send({some: 'data larger than 1 byte'});

    expect(status).to.equal(new SCRequestBodyTooLargeErrorResponse().statusCode);
  });

  it('should implement CORS', async function () {
    // @ts-expect error
    const {headers} = await testApp.options('/');

    expect(headers['access-control-allow-origin']).to.be.equal('*');
  });

  it('should provide unsupported media type error in case of a non-json body', async function () {
    const responseNoType = await testApp.post('/non-existing-route').send();

    const responseOtherType = await testApp
      .post('/non-existing-route')
      .set('Content-Type', 'application/foo')
      .send();

    expect(responseNoType.status).to.equal(new SCUnsupportedMediaTypeErrorResponse().statusCode);
    expect(responseOtherType.status).to.equal(new SCUnsupportedMediaTypeErrorResponse().statusCode);
  });

  it('should provide syntax error if not able to parse the expected JSON body', async function () {
    const {status} = await testApp
      .post('/non-existing-route')
      .set('Content-Type', 'application/json')
      .send('this is not a JSON');

    expect(status).to.equal(new SCSyntaxErrorResponse('Any message').statusCode);
  });

  it('should provide route not found error for non-registered routes', async function () {
    const {status} = await testApp
      .post('/non-existing-route')
      .set('Content-Type', 'application/json')
      .send({});

    expect(status).to.equal(new SCNotFoundErrorResponse().statusCode);
  });
});
