Source: steps.js

"use strict";
/**
 * Steps to manage proxy.
 *
 * These methods are mixed with glacejs [Steps](https://glacejs.github.io/glace-core/Steps.html)
 * class and available via its instance [$](https://glacejs.github.io/glace-core/global.html#$)
 * in tests.
 *
 * @mixin ProxySteps
 * @prop {string} webUrl - Web application URL.
 * @prop {GlobalProxy} globalProxy - Global proxy instance.
 * @prop {HttpProxy} httpProxy - HTTP proxy instance.
 */

var url = require("url");
var util = require("util");

var _ = require("lodash");
var expect = require("chai").expect;
var U = require("glace-utils");

require("./fixtures");
var GlobalProxy = require("./globalProxy");
var HttpProxy = require("./httpProxy");

var LOG = U.logger;

var ProxySteps = {
    __GlobalProxy: GlobalProxy,
    __HttpProxy: HttpProxy,

    registerProxy: function (opts) {
        /**
         * Helper to register proxy classes.
         * 
         * @memberOf ProxySteps
         * @method registerProxy
         * @instance
         * @arg {object} [opts] - Options.
         * @arg {object} [opts.GlobalProxy] - Global proxy class.
         * @arg {object} [opts.HttpProxy] - HTTP proxy class.
         * @example
         *
         * $.registerProxy({ GlobalProxy: MyGlobalProxy, HttpProxy: MyHttpProxy });
         */

        opts = U.defVal(opts, {});
        this.__GlobalProxy = U.defVal(opts.GlobalProxy, this.__GlobalProxy);
        this.__HttpProxy = U.defVal(opts.HttpProxy, this.__HttpProxy);
    },

    startHttpProxy: async function (opts) {
        /**
         * Step to start HTTP proxy. Step recall will be skipped if proxy wasn't
         *  stopped before.
         *
         * @async
         * @memberOf ProxySteps
         * @method startHttpProxy
         * @instance
         * @arg {object} [opts] - Step options.
         * @arg {string} [opts.webUrl] - Web application url which will
         *  be proxied. Default value will be requested from `config.web.url`
         *  if it is specified.
         * @arg {boolean} [opts.useCache] - Flag to use proxy cache for
         *  responses. Default value will be requested from `config.useCache`
         *  if it is specified.
         * @arg {number} [opts.timeout] - Proxy timeout to break connection.
         *  Default value will be requested from `config.timeout.proxy`.
         * @arg {number} [opts.reconnect] - Number of proxy reconnects on failure.
         * @arg {number} [opts.port] - Proxy port. Default value will be
         *  requested from `config.proxyPort`.
         * @arg {boolean} [opts.check=true] - Flag to check that proxy was launched.
         * @return {Promise<string>} Proxied URL.
         * @return {Promise<boolean>} `false` if step was skipped
         * @throws {AssertionError} If proxy was not launched.
         * @example
         *
         * var proxiedUrl = await $.startHttpProxy();
         * var proxiedUrl = await $.startHttpProxy({ webUrl: "http://example.com", });
         * var proxiedUrl = await $.startHttpProxy({ webUrl: "http://example.com", port: 8080 });
         */

        if (this._isProxyStarted) {
            LOG.warn("Step to start proxy was passed already");
            return false;
        };

        opts = U.defVal(opts, {});
        var webUrl = U.defVal(opts.webUrl, CONF.web && CONF.web.url);
        var useCache = U.defVal(opts.useCache, CONF.cache.use);
        var timeout = U.defVal(opts.timeout, CONF.proxy.timeout);
        var reconnect = U.defVal(opts.reconnect, CONF.proxy.reconnect);
        var port = U.defVal(opts.port, CONF.proxy.port);
        var check = U.defVal(opts.check, true);

        allure.step("Start http proxy");
        expect(webUrl, "Web URL for http proxy isn't specified").to.exist;

        LOG.info("Starting http proxy...");

        this.httpProxy = this.httpProxy || new this.__HttpProxy({
            url: webUrl,
            useCache: useCache,
            timeout: timeout,
            reconnect: reconnect,
            port: port });
        await this.httpProxy.start();

        if (check) {
            expect(this.httpProxy.isRunning,
                "HTTP proxy was not launched").be.true;
        };
        this._isProxyStarted = true;

        this.webUrl = _.trim(
            this.httpProxy.url + url.parse(CONF.web.url).pathname, "/");

        LOG.info("Http proxy is started");
        allure.pass();

        return this.webUrl;
    },

    getProxyUrl: function (opts) {
        /**
         * Step to get proxy URL.
         *
         * @memberOf ProxySteps
         * @method getProxyUrl
         * @instance
         * @arg {object} [opts] - Step options.
         * @arg {boolean} [opts.check=true] - Flag to check that proxy URL is
         *  defined.
         * @return {string} Proxy URL.
         * @throws {AssertionError} If proxy URL isn't defined.
         * @example
         *
         * var proxiedUrl = $.getProxiedUrl();
         */

        opts = U.defVal(opts, {});
        var check = U.defVal(opts.check, true);

        allure.step("Get http proxy url");
        LOG.info("Getting http proxy url...");

        if (check) {
            expect(this.httpProxy.url,
                "HTTP proxy was not launched").to.exist;
        };

        LOG.info("Http proxy url is got");
        allure.pass();

        return this.httpProxy.url;
    },

    stopHttpProxy: async function (opts) {
        /**
         * Step to stop HTTP proxy. Step call will be skipped if proxy wasn't
         *  launched before.
         *
         * @async
         * @memberOf ProxySteps
         * @method stopHttpProxy
         * @instance
         * @arg {object} [opts] - Step options.
         * @arg {boolean} [opts.check=true] - Flag to check that proxy was
         *  stopped.
         * @return {Promise<boolean>} `true` if step was passed, `false` if was
         *  skipped.
         * @throws {AssertionError} If proxy wasn't stopped.
         * @example
         *
         * await $.stopHttpProxy();
         */

        if (!this._isProxyStarted) {
            LOG.warn("Step to start HTTP proxy wasn't passed yet");
            return false;
        };

        opts = U.defVal(opts, {});
        var check = U.defVal(opts.check, true);

        allure.step("Stop http proxy");
        LOG.info("Stopping http proxy...");

        await this.httpProxy.stop();

        if (check) {
            expect(this.httpProxy.isRunning,
                "HTTP proxy wasn't stopped").be.false;
        };

        this.webUrl = CONF.web.url;
        this._isProxyStarted = false;

        LOG.info("Http proxy is stopped");
        allure.pass();

        return true;
    },

    startGlobalProxy: async function (opts) {
        /**
         * Step to start global proxy. Step recall will be skipped if global
         *  proxy wasn't stopped before.
         *
         * @async
         * @memberOf ProxySteps
         * @method startGlobalProxy
         * @instance
         * @arg {object} [opts] - Step options.
         * @arg {boolean} [opts.useCache] - Flag to use proxy cache for
         *  responses. Default value will be requested from `config.useCache`
         *  if it is specified.
         * @arg {number} [opts.timeout] - Proxy timeout to break connection.
         *  Default value will be requested from `config.timeout.proxy`.
         * @arg {number} [opts.reconnect] - Number of proxy reconnects on failure.
         * @arg {number} [opts.port] - Proxy port. Default value will be
         *  requested from `config.globalProxyPort`.
         * @arg {boolean} [opts.check=true] - Flag to check that proxy was launched.
         * @return {Promise<boolean>} `true` if step was passed, `false` if was
         *  skipped.
         * @throws {AssertionError} If global proxy was not launched.
         * @example
         *
         * await $.startGlobalProxy();
         * await $.startGlobalProxy({ timeout: 30000 });
         */

        if (this._isGlobalProxyStarted) {
            LOG.warn("Step to start global proxy was passed already");
            return false;
        };

        opts = U.defVal(opts, {});
        var useCache = U.defVal(opts.useCache, CONF.cache.use);
        var timeout = U.defVal(opts.timeout, CONF.proxy.timeout);
        var reconnect = U.defVal(opts.reconnect, CONF.proxy.reconnect);
        var port = U.defVal(opts.port, CONF.proxy.globalPort);
        const sslCaDir = U.defVal(opts.sslCaDir, CONF.proxy.sslCaDir);
        var check = U.defVal(opts.check, true);

        allure.step("Start global proxy");
        LOG.info("Starting global proxy...");

        this.globalProxy = this.globalProxy || new this.__GlobalProxy({
            useCache: useCache,
            timeout: timeout,
            reconnect: reconnect,
            port: port,
            sslCaDir: sslCaDir,
            installCertificate: CONF.proxy.installCertificate });

        await this.globalProxy.start();

        if (check) {
            expect(this.globalProxy.isRunning,
                "Global proxy was not launched")
                .to.be.true;
        };

        this._isGlobalProxyStarted = true;

        LOG.info("Global proxy is started");
        allure.pass();

        return true;
    },

    stopGlobalProxy: async function (opts) {
        /**
         * Step to stop global proxy. Step call will be skipped if global proxy
         *  wasn't launched before.
         *
         * @async
         * @memberOf ProxySteps
         * @method stopGlobalProxy
         * @instance
         * @arg {object} [opts] - Step options.
         * @arg {boolean} [opts.check=true] - Flag to check that global
         *  proxy was stopped.
         * @return {Promise<boolean>} `true` if step was passed, `false` if was
         *  skipped.
         * @throws {AssertionError} If global proxy wasn't stopped.
         * @example
         *
         * await $.stopGlobalProxy();
         */

        if (!this._isGlobalProxyStarted) {
            LOG.warn("Step to start global proxy wasn't passed yet");
            return false;
        };

        opts = U.defVal(opts, {});
        var check = U.defVal(opts.check, true);

        allure.step("Stop global proxy");
        LOG.info("Stopping global proxy...");

        await this.globalProxy.stop();

        if (check) {
            expect(this.globalProxy.isRunning,
                "Global proxy wasn't stopped").be.false;
        };
        this._isGlobalProxyStarted = false;

        LOG.info("Global proxy is stopped");
        allure.pass();

        return true;
    },

    limitProxySpeed: function (speed, opts) {
        /**
         * Step to limit proxy speed.
         *
         * @memberOf ProxySteps
         * @method limitProxySpeed
         * @instance
         * @arg {number|object} speed - Proxy limited speed, kb/s.
         * @arg {?number} [speed.req] - Requests speed, kb/s.
         * @arg {?number} [speed.res] - Responses speed, kb/s.
         * @arg {object} [opts] - Step options.
         * @arg {boolean} [opts.check=true] - Flag to check that proxy speed
         *  is limited.
         * @return {boolean} `true` if step was passed.
         * @throws {AssertionError} If proxy speed is not limited.
         * @example
         *
         * $.limitProxySpeed(512);
         * $.limitProxySpeed({ res: 512 });
         */

        this._checkProxy();

        opts = U.defVal(opts, {});
        var check = U.defVal(opts.check, true);

        allure.step(util.format("Limit proxy speed to", speed, "kb/s"));
        LOG.info(util.format("Limiting proxy speed to", speed, "kb/s..."));

        if (this.httpProxy) {
            this.httpProxy.setSpeed(speed);
        };
        if (this.globalProxy) {
            this.globalProxy.setSpeed(speed);
        };

        if (check) {
            if (speed.req === undefined && speed.res === undefined) {
                speed = { req: speed, res: speed };
            };

            if (this.httpProxy) {
                if (speed.req !== undefined) {
                    expect(this.httpProxy.reqSpeed,
                        "HTTP proxy requests speed is not limited")
                        .be.equal(speed.req);
                };
                if (speed.res !== undefined) {
                    expect(this.httpProxy.resSpeed,
                        "HTTP proxy responses speed is not limited")
                        .be.equal(speed.res);
                };
            };
            if (this.globalProxy) {
                if (speed.req !== undefined) {
                    expect(this.globalProxy.reqSpeed,
                        "Global proxy requests speed is not limited")
                        .be.equal(speed.req);
                };
                if (speed.res !== undefined) {
                    expect(this.globalProxy.resSpeed,
                        "Global proxy responses speed is not limited")
                        .be.equal(speed.res);
                };
            };
        };

        LOG.info("Proxy speed is limited");
        allure.pass();

        return true;
    },

    unlimitProxySpeed: function (opts) {
        /**
         * Step to unlimit proxy speed.
         *
         * @memberOf ProxySteps
         * @method unlimitProxySpeed
         * @instance
         * @arg {object} [opts] - Step options.
         * @arg {boolean} [opts.check=true] - Flag to check that proxy speed
         *  is unlimited.
         * @return {boolean} `true` if step is passed.
         * @throws {AssertionError} If proxy speed is still limited.
         * @example
         *
         * $.unlimitProxySpeed();
         */

        this._checkProxy();

        opts = U.defVal(opts, {});
        var check = U.defVal(opts.check, true);

        allure.step("Unlimit proxy speed");
        LOG.info("Unlimiting proxy speed...");

        if (this.httpProxy) {
            this.httpProxy.resetSpeed();
        };
        if (this.globalProxy) {
            this.globalProxy.resetSpeed();
        };

        if (check) {
            if (this.httpProxy) {
                expect(this.httpProxy.reqSpeed,
                    "HTTP proxy requests speed still has limited value")
                    .be.null;
                expect(this.httpProxy.resSpeed,
                    "HTTP proxy responses speed still has limited value")
                    .be.null;
            };
            if (this.globalProxy) {
                expect(this.globalProxy.reqSpeed,
                    "Global proxy requests speed still has limited value")
                    .be.null;
                expect(this.globalProxy.resSpeed,
                    "Global proxy responses speed still has limited value")
                    .be.null;
            };
        };

        LOG.info("Proxy speed is unlimited");
        allure.pass();

        return true;
    },

    measureResponses: function (opts) {
        /**
         * Step to start responses measurement.
         *
         * @memberOf ProxySteps
         * @method measureResponses
         * @instance
         * @arg {object} [opts] - Step options.
         * @arg {boolean} [opts.check=true] - Flag to check that responses
         *  measurement is launched.
         * @return {boolean} `true` if step is passed.
         * @throws {AssertionError} If responses measurement is not launched.
         * @example
         *
         * $.measureResponses();
         */

        this._checkProxy();

        opts = U.defVal(opts, {});
        var check = U.defVal(opts.check, true);

        allure.step("Start to measure proxy responses");
        LOG.info("Starting to measure proxy responses...");

        if (this.httpProxy) {
            this.httpProxy.measureResponses();
        };
        if (this.globalProxy) {
            this.globalProxy.measureResponses();
        };

        if (check) {
            if (this.httpProxy) {
                expect(this.httpProxy.getResponsesData(),
                    "HTTP proxy responses measurement is not launched")
                    .to.exist;
            };
            if (this.globalProxy) {
                expect(this.globalProxy.getResponsesData(),
                    "Global proxy responses measurement is not launched")
                    .to.exist;
            };
        };

        LOG.info("Proxy responses are measuring");
        allure.pass();

        return true;
    },

    getResponsesData: function (opts) {
        /**
         * Step to get measured responses data.
         *
         * @memberOf ProxySteps
         * @method getResponsesData
         * @instance
         * @arg {object} [opts] - Step options.
         * @arg {boolean} [opts.check=true] - Flag to check that responses
         *  data are present.
         * @throws {AssertionError} If responses data are absent.
         * @return {object[]} List of captured response data.
         * @example
         *
         * var responses = $.getResponsesData();
         */

        this._checkProxy();

        opts = U.defVal(opts, {});
        var check = U.defVal(opts.check, true);

        var data = [];

        allure.step("Get responses data");
        LOG.info("Getting responses data...");

        if (this.httpProxy && this.httpProxy.getResponsesData()) {
            data = data.concat(this.httpProxy.getResponsesData());
        };
        if (this.globalProxy && this.globalProxy.getResponsesData()) {
            data = data.concat(this.globalProxy.getResponsesData());
        };

        if (check) {
            expect(_.isEmpty(data),
                "Responses data are absent").be.false;
        };

        LOG.info("Responses data is got");
        allure.pass();

        return data;
    },

    unmeasureResponses: function (opts) {
        /**
         * Step to stop responses measurement.
         *
         * @memberOf ProxySteps
         * @method unmeasureResponses
         * @instance
         * @arg {object} [opts] - Step options.
         * @arg {boolean} [opts.check=true] - Flag to check that responses
         *  measurement is stopped.
         * @return {boolean} `true` if step is passed.
         * @throws {AssertionError} If responses measurement is still running.
         * @example
         *
         * $.unmeasureResponses();
         */

        this._checkProxy();

        opts = U.defVal(opts, {});
        var check = U.defVal(opts.check, true);

        allure.step("Stop to measure proxy responses");
        LOG.info("Stopping to measure proxy responses...");

        if (this.httpProxy) {
            this.httpProxy.unmeasureResponses();
        };
        if (this.globalProxy) {
            this.globalProxy.unmeasureResponses();
        };

        if (check) {
            if (this.httpProxy) {
                expect(this.httpProxy.getResponsesData(),
                    "HTTP proxy responses measurement is still running")
                    .to.be.null;
            };
            if (this.globalProxy) {
                expect(this.globalProxy.getResponsesData(),
                    "Global proxy responses measurement is still running")
                    .to.be.null;
            };
        };

        LOG.info("Proxy responses are not measured");
        allure.pass();

        return true;
    },

    enableCache: function () {
        /**
         * Step to enable cache.
         *
         * @memberOf ProxySteps
         * @method enableCache
         * @instance
         * @return {boolean} `true` if step is passed.
         * @example
         *
         * $.enableCache();
         */

        allure.step("Enable proxy cache");
        this._checkProxy();

        LOG.info("Enabling proxy cache...");

        if (this.httpProxy) this.httpProxy.useCache = true;
        if (this.globalProxy) this.globalProxy.useCache = true;

        LOG.info("Proxy cache is enabled");
        allure.pass();

        return true;
    },

    disableCache: function () {
        /**
         * Step to disable cache.
         *
         * @memberOf ProxySteps
         * @method disableCache
         * @instance
         * @return {boolean} `true` if step is passed.
         * @example
         *
         * $.disableCache();
         */

        allure.step("Disable proxy cache");
        this._checkProxy();

        LOG.info("Disabling proxy cache...");

        if (this.httpProxy) this.httpProxy.useCache = false;
        if (this.globalProxy) this.globalProxy.useCache = false;

        LOG.info("Proxy cache is disabled");
        allure.pass();

        return true;
    },
    /**
     * Helper to check that proxy is launched.
     *
     * @ignore
     * @method
     * @protected
     * @instance
     * @throws {AssertionError} - If no one proxy is launched.
     */
    _checkProxy: function () {
        expect(
            (this.httpProxy && this.httpProxy.isRunning) ||
            (this.globalProxy && this.globalProxy.isRunning),
            "No one proxy is launched").to.be.true;
    },
};
module.exports = ProxySteps;