bbyars/mountebank

View on GitHub
bin/mb

Summary

Maintainability
Test Coverage
#!/usr/bin/env node

'use strict';

const mountebank = require('../src/mountebank'),
    api = require('../src/cli/api'),
    cli = require('../src/cli/cli'),
    fs = require('fs-extra');

function processExists (pid) {
    try {
        // "As a special case, signal 0 can be used to test existence of process"
        // https://nodejs.org/api/process.html#process_process_kill_pid_signal
        process.kill(pid, 0);
        return true;
    }
    catch (e) {
        return false;
    }
}

function serverAt (options) {
    async function start () {
        // Set in case npm dependencies do anything with this
        if (!process.env.NODE_ENV) {
            process.env.NODE_ENV = 'production';
        }

        const server = await mountebank.create(options);

        function shutdown () {
            server.close(() => {
                try {
                    if (fs.existsSync(options.pidfile)) {
                        fs.unlinkSync(options.pidfile);
                    }
                }
                finally {
                    process.exit(); // eslint-disable-line no-process-exit
                }
            });
        }

        process.on('SIGINT', shutdown);
        process.on('SIGTERM', shutdown);

        if (options.configfile) {
            await api.loadConfig(options, server);
        }

        // Useful for build plugins that need to wait for mb to be fully initialized
        // They can wait for the pidfile to be written
        fs.writeFileSync(options.pidfile, process.pid.toString());
    }

    function stop () {
        if (!fs.existsSync(options.pidfile)) {
            return Promise.resolve(true);
        }

        const pid = fs.readFileSync(options.pidfile);
        if (!processExists(pid)) {
            fs.unlinkSync(options.pidfile);
            return Promise.resolve(true);
        }

        return new Promise(resolve => {
            const startTime = new Date(),
                timeout = 1000,
                waitForClose = () => {
                    const elapsedTime = new Date() - startTime;
                    if (!fs.existsSync(options.pidfile)) {
                        resolve();
                    }
                    else if (elapsedTime > timeout) {
                        try {
                            // For Windows, which doesn't register signal handlers
                            fs.unlinkSync(options.pidfile);
                        }
                        catch (err) { /* ignore */ }
                        finally {
                            resolve();
                        }
                    }
                    else {
                        setTimeout(waitForClose, 100);
                    }
                };

            process.kill(pid);
            waitForClose();
        });
    }

    async function restart () {
        await stop();
        await start();
    }

    return {
        start: start,
        stop: stop,
        restart: restart
    };
}

try {
    const server = serverAt(cli.args);

    switch (cli.command) {
        case 'start':
            server.start();
            break;
        case 'stop':
            server.stop();
            break;
        case 'restart':
            server.restart();
            break;
        case 'save':
            api.save(cli.args);
            break;
        case 'replay':
            api.replay(cli.args);
            break;
        case 'help':
            cli.help();
            break;
        default:
            cli.error(`Invalid command '${cli.command}'.`);
            break;
    }
}
catch (err) {
    cli.error(err.message);
}