rpgeeganage/ifto

View on GitHub
lib/spy_modules/spy_http.ts

Summary

Maintainability
A
0 mins
Test Coverage
import { SizeRestrictedLog, getId } from '../util';
import { BaseModule, Entries } from './base_module';

const http = require('http');
const https = require('https');

export class SpyHttp extends BaseModule {
  /**
   * Logs from http call
   *
   * @static
   * @type {SizeRestrictedLog}
   * @memberof SpyHttp
   */
  static logs: SizeRestrictedLog;

  /**
   * Original Request method from http request
   *
   * @static
   * @memberof SpyHttp
   */
  static originalRequest?: (...args: any[]) => {};

  /**
   * Original Request method from https request
   *
   * @static
   * @memberof SpyHttp
   */
  static originalRequestSecure?: (...args: any[]) => {};

  /**
   * Original Get method from http request
   *
   * @static
   * @memberof SpyHttp
   */
  static originalGet?: (...args: any[]) => {};

  /**
   * Get instance of Spy
   *
   * @static
   * @param {number} logSize
   * @returns
   * @memberof SpyHttp
   */
  static init(logs: SizeRestrictedLog) {
    SpyHttp.logs = logs;

    SpyHttp.stop();

    SpyHttp.originalRequest = undefined;
    SpyHttp.originalRequestSecure = undefined;
    SpyHttp.originalGet = undefined;
  }

  /**
   * Start logging
   *
   * @memberof SpyHttp
   */
  static start() {
    SpyHttp.stop();

    SpyHttp.originalRequest = http.request;
    SpyHttp.originalRequestSecure = https.request;
    SpyHttp.originalGet = http.get;

    http.request = SpyHttp.proxyRequest;
    https.request = SpyHttp.proxyHttpsRequest;
    http.get = SpyHttp.proxyGet;
  }

  /**
   * Stop logging
   *
   * @memberof SpyHttp
   */
  static stop() {
    if (SpyHttp.originalRequest) {
      http.request = SpyHttp.originalRequest;
    }

    if (SpyHttp.originalRequestSecure) {
      https.request = SpyHttp.originalRequestSecure;
    }

    if (SpyHttp.originalGet) {
      http.get = SpyHttp.originalGet;
    }
  }

  /**
   * Get entries
   *
   * @returns {Entries}
   * @memberof SpyHttp
   */
  static getEntries(): Entries {
    return {
      module: 'http',
      remark: SpyHttp.getRemark(),
      log: SpyHttp.logs
    };
  }

  /**
   * Get remark string
   *
   * @returns
   * @memberof SpyHttp
   */
  static getRemark() {
    return 'Possible unfinished HTTP requests';
  }

  /**
   * Proxy request method
   *
   * function request(options: RequestOptions | string | URL, callback?: (res: IncomingMessage) => void): ClientRequest;
   * function request(url: string | URL, options: RequestOptions, callback?: (res: IncomingMessage) => void): ClientRequest;
   * @private
   * @param {*} arg1
   * @param {*} arg2
   * @param {(a: any) => {}} cb
   * @returns
   * @memberof SpyHttp
   */
  static proxyRequest(...args: any[]) {
    if (!SpyHttp.originalRequest) {
      throw new Error(
        'Unable locate the preserved request method from http module'
      );
    }

    const id = SpyHttp.addLogEntry(args[0]);

    const clientRequest = SpyHttp.originalRequest(
      ...SpyHttp.getMockedRequestArgs(id, ...args)
    );

    return SpyHttp.handleClientRequest(id, clientRequest);
  }

  /**
   * Proxy https request method
   * @private
   * @param {*} arg1
   * @param {*} arg2
   * @param {(a: any) => {}} cb
   * @returns
   * @memberof SpyHttp
   */
  static proxyHttpsRequest(...args: any[]) {
    if (!SpyHttp.originalRequestSecure) {
      throw new Error(
        'Unable locate the preserved request method from https module'
      );
    }

    const clientRequest = SpyHttp.originalRequestSecure(
      ...SpyHttp.getMockedRequestArgs(null, ...args)
    );

    return SpyHttp.handleClientRequest(null, clientRequest);
  }

  /**
   * Proxy request method
   *
   * function request(options: RequestOptions | string | URL, callback?: (res: IncomingMessage) => void): ClientRequest;
   * function request(url: string | URL, options: RequestOptions, callback?: (res: IncomingMessage) => void): ClientRequest;
   * @private
   * @param {*} arg1
   * @param {*} arg2
   * @param {(a: any) => {}} cb
   * @returns
   * @memberof SpyHttp
   */
  static proxyGet(...args: any[]) {
    if (!SpyHttp.originalGet) {
      throw new Error(
        'Unable locate the preserved Get method from http module'
      );
    }

    const id = SpyHttp.addLogEntry(args[0]);

    const clientRequest = SpyHttp.originalGet(
      ...SpyHttp.getMockedRequestArgs(id, ...args)
    );

    return SpyHttp.handleClientRequest(id, clientRequest);
  }

  /**
   * Get Modified arguments with fake cb
   *
   * @static
   * @param {...any[]} args
   * @returns
   * @memberof SpyHttp
   */
  static getMockedRequestArgs(id: any, ...args: any[]) {
    if (args.length === 1) {
      return args;
    }

    if (typeof args[args.length - 1] !== 'function') {
      return args;
    }

    const cb = args[args.length - 1];

    const fakeCb = (err: any, res: any, body: any) => {
      const currentId = id;
      SpyHttp.logs.remove(currentId);
      return cb(err, res, body);
    };

    args[args.length - 1] = fakeCb;

    return args;
  }

  /**
   * Handles http stream
   *
   * @static
   * @param {*} urlOrOptions
   * @param {*} clientRequest
   * @returns
   * @memberof SpyHttp
   */
  static handleClientRequest(id: any, clientRequest: any) {
    clientRequest.on('response', () => {
      const currentId = id;
      SpyHttp.logs.remove(currentId);
    });

    clientRequest.on('error', (error: any) => {
      const currentId = id;
      SpyHttp.logs.remove(currentId);
    });

    return clientRequest;
  }

  /**
   * Add a new log entry and return the id of the log
   *
   * @static
   * @param {*} urlOrOptions
   * @returns
   * @memberof SpyHttp
   */
  static addLogEntry(urlOrOptions: any) {
    const id = getId();
    if (typeof urlOrOptions === 'string') {
      SpyHttp.logs.add(id, urlOrOptions);
    } else if (urlOrOptions.href) {
      SpyHttp.logs.add(id, urlOrOptions.href);
    } else {
      const url = `${urlOrOptions.protocol}//${urlOrOptions.hostname}${
        urlOrOptions.port ? ':' + urlOrOptions.port : ''
      }${urlOrOptions.path}`;
      SpyHttp.logs.add(id, url);
    }

    return id;
  }
}