<commit_before>/*
 * Copyright 2017 Telefonica Investigación y Desarrollo, S.A.U
 *
 * This file is part of perseo-fe
 *
 * perseo-fe 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.
 *
 * perseo-fe 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 perseo-fe.
 * If not, see http://www.gnu.org/licenses/.
 *
 * For those usages not covered by the GNU Affero General Public License
 * please contact with iot_support at tid dot es
 */

'use strict';

var async = require('async'),
    should = require('should'),
    util = require('util'),
    clients = require('../../utils/clients'),
    utilsT = require('../../utils/utilsT'),
    testEnv = require('../../utils/testEnvironment'),
    metrics = require('../../../lib/models/metrics'),
    URL = require('url').URL;

describe('Metrics', function() {
    beforeEach(testEnv.commonBeforeEach);
    afterEach(testEnv.commonAfterEach);

    describe('#PostAction()', function() {
        it('should count fired valid actions', function(done) {
            var cases = utilsT.loadDirExamples('./test/data/good_actions');
            metrics.GetDecorated(true); // reset metrics
            async.eachSeries(
                cases,
                function(c, callback) {
                    clients.PostAction(c.object, function(error, data) {
                        should.not.exist(error);
                        data.should.have.property('statusCode', 200);
                        return callback(null);
                    });
                },
                function(error) {
                    var m = metrics.GetDecorated(true),
                        msub;

                    should.not.exist(error);
                    should.exists(m);
                    should.exists(m.services);
                    should.exists(m.services.unknownt);
                    should.exists(m.services.unknownt.subservices);
                    should.exists(m.services.unknownt.subservices['/']);
                    msub = m.services.unknownt.subservices['/'];

                    should.equal(m.services.unknownt.sum.firedRules, cases.length);

                    return done();
                }
            );
        });

        it('should increment successful sms', function(done) {
            var rule = utilsT.loadExample('./test/data/good_rules/blood_rule_sms.json'),
                action = utilsT.loadExample('./test/data/good_actions/action_sms.json');
            utilsT.getConfig().sms.URL = util.format('http://localhost:%s', utilsT.fakeHttpServerPort);
            metrics.GetDecorated(true); // reset metrics
            async.series(
                [
                    function(callback) {
                        clients.PostRule(rule, function(error, data) {
                            should.not.exist(error);
                            data.should.have.property('statusCode', 200);
                            return callback(null);
                        });
                    },
                    function(callback) {
                        clients.PostAction(action, function(error, data) {
                            should.not.exist(error);
                            data.should.have.property('statusCode', 200);

                            setTimeout(function() {
                                var m = metrics.GetDecorated(true),
                                    msub;

                                should.exists(m);
                                should.exists(m.services);
                                should.exists(m.services.unknownt);
                                should.exists(m.services.unknownt.subservices);
                                should.exists(m.services.unknownt.subservices['/']);
                                msub = m.services.unknownt.subservices['/'];

                                should.equal(m.services.unknownt.sum.actionSMS, 1);
                                should.equal(m.services.unknownt.sum.okActionSMS, 1);
                                should.equal(m.services.unknownt.sum.failedActionSMS, 0);
                                should.equal(m.services.unknownt.sum.outgoingTransactions, 1);
                                should.equal(m.services.unknownt.sum.outgoingTransactionsErrors, 0);
                                return callback();
                            }, 50);
                        });
                    }
                ],
                done
            );
        });

        it('should increment error for failed sms', function(done) {
            var rule = utilsT.loadExample('./test/data/good_rules/blood_rule_sms.json'),
                action = utilsT.loadExample('./test/data/good_actions/action_sms.json');
            utilsT.getConfig().sms.URL = '';
            metrics.GetDecorated(true); // reset metrics
            async.series(
                [
                    function(callback) {
                        clients.PostRule(rule, function(error, data) {
                            should.not.exist(error);
                            data.should.have.property('statusCode', 200);
                            return callback(null);
                        });
                    },
                    function(callback) {
                        clients.PostAction(action, function(error, data) {
                            should.not.exist(error);
                            data.should.have.property('statusCode', 200);

                            setTimeout(function() {
                                var m = metrics.GetDecorated(true),
                                    msub;
                                should.exists(m);
                                should.exists(m.services);
                                should.exists(m.services.unknownt);
                                should.exists(m.services.unknownt.subservices);
                                should.exists(m.services.unknownt.subservices['/']);
                                msub = m.services.unknownt.subservices['/'];

                                should.equal(m.services.unknownt.sum.actionSMS, 1);
                                should.equal(m.services.unknownt.sum.okActionSMS, 0);
                                should.equal(m.services.unknownt.sum.failedActionSMS, 1);
                                should.equal(m.services.unknownt.sum.outgoingTransactions, 1);
                                should.equal(m.services.unknownt.sum.outgoingTransactionsErrors, 1);
                                return callback();
                            }, 50);
                        });
                    }
                ],
                done
            );
        });

        it('should increment a successful action for update', function(done) {
            var rule = utilsT.loadExample('./test/data/good_rules/blood_rule_update.json'),
                action = utilsT.loadExample('./test/data/good_actions/action_update.json');
            utilsT.getConfig().orion.URL = new URL(util.format('http://localhost:%s', utilsT.fakeHttpServerPort));
            metrics.GetDecorated(true); // reset metrics
            async.series(
                [
                    function(callback) {
                        clients.PostRule(rule, function(error, data) {
                            should.not.exist(error);
                            data.should.have.property('statusCode', 200);
                            return callback(null);
                        });
                    },
                    function(callback) {
                        clients.PostAction(action, function(error, data) {
                            should.not.exist(error);
                            data.should.have.property('statusCode', 200);
                            setTimeout(function() {
                                var m = metrics.GetDecorated(true),
                                    msub;
                                should.exists(m);
                                should.exists(m.services);
                                should.exists(m.services.unknownt);
                                should.exists(m.services.unknownt.subservices);
                                should.exists(m.services.unknownt.subservices['/']);
                                msub = m.services.unknownt.subservices['/'];

                                should.equal(m.services.unknownt.sum.actionEntityUpdate, 1);
                                should.equal(m.services.unknownt.sum.okActionEntityUpdate, 1);
                                should.equal(m.services.unknownt.sum.failedActionEntityUpdate, 0);
                                should.equal(m.services.unknownt.sum.outgoingTransactions, 1);
                                should.equal(m.services.unknownt.sum.outgoingTransactionsErrors, 0);
                                return callback();
                            }, 50);
                        });
                    }
                ],
                done
            );
        });
        it('should increment a failed for update', function(done) {
            var rule = utilsT.loadExample('./test/data/good_rules/blood_rule_update.json'),
                action = utilsT.loadExample('./test/data/good_actions/action_update.json');
            utilsT.getConfig().orion.URL = new URL('http://inventedurl.notexists.com');
            metrics.GetDecorated(true); // reset metrics
            async.series(
                [
                    function(callback) {
                        clients.PostRule(rule, function(error, data) {
                            should.not.exist(error);
                            data.should.have.property('statusCode', 200);
                            return callback(null);
                        });
                    },
                    function(callback) {
                        clients.PostAction(action, function(error, data) {
                            should.not.exist(error);
                            data.should.have.property('statusCode', 200);
                            setTimeout(function() {
                                var m = metrics.GetDecorated(true),
                                    msub;
                                should.exists(m);
                                should.exists(m.services);
                                should.exists(m.services.unknownt);
                                should.exists(m.services.unknownt.subservices);
                                should.exists(m.services.unknownt.subservices['/']);
                                msub = m.services.unknownt.subservices['/'];

                                should.equal(m.services.unknownt.sum.actionEntityUpdate, 1);
                                should.equal(m.services.unknownt.sum.okActionEntityUpdate, 0);
                                should.equal(m.services.unknownt.sum.failedActionEntityUpdate, 0);
                                should.equal(m.services.unknownt.sum.outgoingTransactions, 1);
                                should.equal(m.services.unknownt.sum.outgoingTransactionsErrors, 0);
                                return callback();
                            }, 1500);
                        });
                    }
                ],
                done
            );
        });

        it('should increment successful post', function(done) {
            var rule = utilsT.loadExample('./test/data/good_rules/blood_rule_post.json'),
                action = utilsT.loadExample('./test/data/good_actions/action_post.json');
            metrics.GetDecorated(true); // reset metrics
            rule.action.parameters.url = util.format('http://localhost:%s', utilsT.fakeHttpServerPort);
            async.series(
                [
                    function(callback) {
                        clients.PostRule(rule, function(error, data) {
                            should.not.exist(error);
                            data.should.have.property('statusCode', 200);
                            return callback(null);
                        });
                    },
                    function(callback) {
                        clients.PostAction(action, function(error, data) {
                            should.not.exist(error);
                            data.should.have.property('statusCode', 200);
                            setTimeout(function() {
                                var m = metrics.GetDecorated(true),
                                    msub;
                                should.exists(m);
                                should.exists(m.services);
                                should.exists(m.services.unknownt);
                                should.exists(m.services.unknownt.subservices);
                                should.exists(m.services.unknownt.subservices['/']);
                                msub = m.services.unknownt.subservices['/'];

                                should.equal(m.services.unknownt.sum.actionHttpPost, 1);
                                should.equal(m.services.unknownt.sum.okActionHttpPost, 1);
                                should.equal(m.services.unknownt.sum.failedActionHttpPost, 0);
                                should.equal(m.services.unknownt.sum.outgoingTransactions, 1);
                                should.equal(m.services.unknownt.sum.outgoingTransactionsErrors, 0);
                                return callback();
                            }, 50);
                        });
                    }
                ],
                done
            );
        });

        it('should increment error for failed post', function(done) {
            var rule = utilsT.loadExample('./test/data/good_rules/blood_rule_post.json'),
                action = utilsT.loadExample('./test/data/good_actions/action_post.json');
            metrics.GetDecorated(true); // reset metrics
            rule.action.parameters.url = '';
            async.series(
                [
                    function(callback) {
                        clients.PostRule(rule, function(error, data) {
                            should.not.exist(error);
                            data.should.have.property('statusCode', 200);
                            return callback(null);
                        });
                    },
                    function(callback) {
                        clients.PostAction(action, function(error, data) {
                            should.not.exist(error);
                            data.should.have.property('statusCode', 200);
                            setTimeout(function() {
                                var m = metrics.GetDecorated(true),
                                    msub;
                                should.exists(m);
                                should.exists(m.services);
                                should.exists(m.services.unknownt);
                                should.exists(m.services.unknownt.subservices);
                                should.exists(m.services.unknownt.subservices['/']);
                                msub = m.services.unknownt.subservices['/'];

                                should.equal(m.services.unknownt.sum.actionHttpPost, 1);
                                should.equal(m.services.unknownt.sum.okActionHttpPost, 0);
                                should.equal(m.services.unknownt.sum.failedActionHttpPost, 1);
                                should.equal(m.services.unknownt.sum.outgoingTransactions, 1);
                                should.equal(m.services.unknownt.sum.outgoingTransactionsErrors, 1);
                                return callback();
                            }, 50);
                        });
                    }
                ],
                done
            );
        });

        //
        // Missing case for successful email ... needed a SMTP fake server
        //
        // it('should increment successful email', function(done) {...}

        it('should increment error for failed email', function(done) {
            var rule = utilsT.loadExample('./test/data/good_rules/blood_rule_email.json'),
                action = utilsT.loadExample('./test/data/good_actions/action_email.json');
            utilsT.getConfig().smtp.host = 'averyfarwayhosthatnotexist';
            metrics.GetDecorated(true); // reset metrics
            async.series(
                [
                    function(callback) {
                        clients.PostRule(rule, function(error, data) {
                            should.not.exist(error);
                            data.should.have.property('statusCode', 200);
                            return callback(null);
                        });
                    },
                    function(callback) {
                        clients.PostAction(action, function(error, data) {
                            should.not.exist(error);
                            data.should.have.property('statusCode', 200);

                            setTimeout(function() {
                                var m = metrics.GetDecorated(true),
                                    msub;
                                should.exists(m);
                                should.exists(m.services);
                                should.exists(m.services.unknownt);
                                should.exists(m.services.unknownt.subservices);
                                should.exists(m.services.unknownt.subservices['/']);
                                msub = m.services.unknownt.subservices['/'];

                                should.equal(m.services.unknownt.sum.actionEmail, 1);
                                should.equal(m.services.unknownt.sum.okActionEmail, 0);
                                should.equal(m.services.unknownt.sum.failedActionEmail, 1);
                                should.equal(m.services.unknownt.sum.outgoingTransactions, 1);
                                should.equal(m.services.unknownt.sum.outgoingTransactionsErrors, 1);
                                return callback();
                            }, 50);
                        });
                    }
                ],
                done
            );
        });
    });
});
<commit_msg>FIX unit test<commit_after>/*
 * Copyright 2017 Telefonica Investigación y Desarrollo, S.A.U
 *
 * This file is part of perseo-fe
 *
 * perseo-fe 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.
 *
 * perseo-fe 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 perseo-fe.
 * If not, see http://www.gnu.org/licenses/.
 *
 * For those usages not covered by the GNU Affero General Public License
 * please contact with iot_support at tid dot es
 */

'use strict';

var async = require('async'),
    should = require('should'),
    util = require('util'),
    clients = require('../../utils/clients'),
    utilsT = require('../../utils/utilsT'),
    testEnv = require('../../utils/testEnvironment'),
    metrics = require('../../../lib/models/metrics'),
    URL = require('url').URL;

describe('Metrics', function() {
    beforeEach(testEnv.commonBeforeEach);
    afterEach(testEnv.commonAfterEach);

    describe('#PostAction()', function() {
        it('should count fired valid actions', function(done) {
            var cases = utilsT.loadDirExamples('./test/data/good_actions');
            metrics.GetDecorated(true); // reset metrics
            async.eachSeries(
                cases,
                function(c, callback) {
                    clients.PostAction(c.object, function(error, data) {
                        should.not.exist(error);
                        data.should.have.property('statusCode', 200);
                        return callback(null);
                    });
                },
                function(error) {
                    var m = metrics.GetDecorated(true),
                        msub;

                    should.not.exist(error);
                    should.exists(m);
                    should.exists(m.services);
                    should.exists(m.services.unknownt);
                    should.exists(m.services.unknownt.subservices);
                    should.exists(m.services.unknownt.subservices['/']);
                    msub = m.services.unknownt.subservices['/'];

                    should.equal(m.services.unknownt.sum.firedRules, cases.length);

                    return done();
                }
            );
        });

        it('should increment successful sms', function(done) {
            var rule = utilsT.loadExample('./test/data/good_rules/blood_rule_sms.json'),
                action = utilsT.loadExample('./test/data/good_actions/action_sms.json');
            utilsT.getConfig().sms.URL = util.format('http://localhost:%s', utilsT.fakeHttpServerPort);
            metrics.GetDecorated(true); // reset metrics
            async.series(
                [
                    function(callback) {
                        clients.PostRule(rule, function(error, data) {
                            should.not.exist(error);
                            data.should.have.property('statusCode', 200);
                            return callback(null);
                        });
                    },
                    function(callback) {
                        clients.PostAction(action, function(error, data) {
                            should.not.exist(error);
                            data.should.have.property('statusCode', 200);

                            setTimeout(function() {
                                var m = metrics.GetDecorated(true),
                                    msub;

                                should.exists(m);
                                should.exists(m.services);
                                should.exists(m.services.unknownt);
                                should.exists(m.services.unknownt.subservices);
                                should.exists(m.services.unknownt.subservices['/']);
                                msub = m.services.unknownt.subservices['/'];

                                should.equal(m.services.unknownt.sum.actionSMS, 1);
                                should.equal(m.services.unknownt.sum.okActionSMS, 1);
                                should.equal(m.services.unknownt.sum.failedActionSMS, 0);
                                should.equal(m.services.unknownt.sum.outgoingTransactions, 1);
                                should.equal(m.services.unknownt.sum.outgoingTransactionsErrors, 0);
                                return callback();
                            }, 50);
                        });
                    }
                ],
                done
            );
        });

        it('should increment error for failed sms', function(done) {
            var rule = utilsT.loadExample('./test/data/good_rules/blood_rule_sms.json'),
                action = utilsT.loadExample('./test/data/good_actions/action_sms.json');
            utilsT.getConfig().sms.URL = '';
            metrics.GetDecorated(true); // reset metrics
            async.series(
                [
                    function(callback) {
                        clients.PostRule(rule, function(error, data) {
                            should.not.exist(error);
                            data.should.have.property('statusCode', 200);
                            return callback(null);
                        });
                    },
                    function(callback) {
                        clients.PostAction(action, function(error, data) {
                            should.not.exist(error);
                            data.should.have.property('statusCode', 200);

                            setTimeout(function() {
                                var m = metrics.GetDecorated(true),
                                    msub;
                                should.exists(m);
                                should.exists(m.services);
                                should.exists(m.services.unknownt);
                                should.exists(m.services.unknownt.subservices);
                                should.exists(m.services.unknownt.subservices['/']);
                                msub = m.services.unknownt.subservices['/'];

                                should.equal(m.services.unknownt.sum.actionSMS, 1);
                                should.equal(m.services.unknownt.sum.okActionSMS, 0);
                                should.equal(m.services.unknownt.sum.failedActionSMS, 1);
                                should.equal(m.services.unknownt.sum.outgoingTransactions, 1);
                                should.equal(m.services.unknownt.sum.outgoingTransactionsErrors, 1);
                                return callback();
                            }, 50);
                        });
                    }
                ],
                done
            );
        });

        it('should increment a successful action for update', function(done) {
            var rule = utilsT.loadExample('./test/data/good_rules/blood_rule_update.json'),
                action = utilsT.loadExample('./test/data/good_actions/action_update.json');
            utilsT.getConfig().orion.URL = new URL(util.format('http://localhost:%s', utilsT.fakeHttpServerPort));
            metrics.GetDecorated(true); // reset metrics
            async.series(
                [
                    function(callback) {
                        clients.PostRule(rule, function(error, data) {
                            should.not.exist(error);
                            data.should.have.property('statusCode', 200);
                            return callback(null);
                        });
                    },
                    function(callback) {
                        clients.PostAction(action, function(error, data) {
                            should.not.exist(error);
                            data.should.have.property('statusCode', 200);
                            setTimeout(function() {
                                var m = metrics.GetDecorated(true),
                                    msub;
                                should.exists(m);
                                should.exists(m.services);
                                should.exists(m.services.unknownt);
                                should.exists(m.services.unknownt.subservices);
                                should.exists(m.services.unknownt.subservices['/']);
                                msub = m.services.unknownt.subservices['/'];

                                should.equal(m.services.unknownt.sum.actionEntityUpdate, 1);
                                should.equal(m.services.unknownt.sum.okActionEntityUpdate, 1);
                                should.equal(m.services.unknownt.sum.failedActionEntityUpdate, 0);
                                should.equal(m.services.unknownt.sum.outgoingTransactions, 1);
                                should.equal(m.services.unknownt.sum.outgoingTransactionsErrors, 0);
                                return callback();
                            }, 50);
                        });
                    }
                ],
                done
            );
        });
        it('should increment a failed for update', function(done) {
            var rule = utilsT.loadExample('./test/data/good_rules/blood_rule_update.json'),
                action = utilsT.loadExample('./test/data/good_actions/action_update.json');
            utilsT.getConfig().orion.URL = new URL('http://inventedurl.notexists.com');
            metrics.GetDecorated(true); // reset metrics
            async.series(
                [
                    function(callback) {
                        clients.PostRule(rule, function(error, data) {
                            should.not.exist(error);
                            data.should.have.property('statusCode', 200);
                            return callback(null);
                        });
                    },
                    function(callback) {
                        clients.PostAction(action, function(error, data) {
                            should.not.exist(error);
                            data.should.have.property('statusCode', 200);
                            setTimeout(function() {
                                var m = metrics.GetDecorated(true),
                                    msub;
                                should.exists(m);
                                should.exists(m.services);
                                should.exists(m.services.unknownt);
                                should.exists(m.services.unknownt.subservices);
                                should.exists(m.services.unknownt.subservices['/']);
                                msub = m.services.unknownt.subservices['/'];

                                should.equal(m.services.unknownt.sum.actionEntityUpdate, 1);
                                should.equal(m.services.unknownt.sum.okActionEntityUpdate, 0);
                                should.equal(m.services.unknownt.sum.failedActionEntityUpdate, 1);
                                should.equal(m.services.unknownt.sum.outgoingTransactions, 1);
                                should.equal(m.services.unknownt.sum.outgoingTransactionsErrors, 1);
                                return callback();
                            }, 1500);
                        });
                    }
                ],
                done
            );
        });

        it('should increment successful post', function(done) {
            var rule = utilsT.loadExample('./test/data/good_rules/blood_rule_post.json'),
                action = utilsT.loadExample('./test/data/good_actions/action_post.json');
            metrics.GetDecorated(true); // reset metrics
            rule.action.parameters.url = util.format('http://localhost:%s', utilsT.fakeHttpServerPort);
            async.series(
                [
                    function(callback) {
                        clients.PostRule(rule, function(error, data) {
                            should.not.exist(error);
                            data.should.have.property('statusCode', 200);
                            return callback(null);
                        });
                    },
                    function(callback) {
                        clients.PostAction(action, function(error, data) {
                            should.not.exist(error);
                            data.should.have.property('statusCode', 200);
                            setTimeout(function() {
                                var m = metrics.GetDecorated(true),
                                    msub;
                                should.exists(m);
                                should.exists(m.services);
                                should.exists(m.services.unknownt);
                                should.exists(m.services.unknownt.subservices);
                                should.exists(m.services.unknownt.subservices['/']);
                                msub = m.services.unknownt.subservices['/'];

                                should.equal(m.services.unknownt.sum.actionHttpPost, 1);
                                should.equal(m.services.unknownt.sum.okActionHttpPost, 1);
                                should.equal(m.services.unknownt.sum.failedActionHttpPost, 0);
                                should.equal(m.services.unknownt.sum.outgoingTransactions, 1);
                                should.equal(m.services.unknownt.sum.outgoingTransactionsErrors, 0);
                                return callback();
                            }, 50);
                        });
                    }
                ],
                done
            );
        });

        it('should increment error for failed post', function(done) {
            var rule = utilsT.loadExample('./test/data/good_rules/blood_rule_post.json'),
                action = utilsT.loadExample('./test/data/good_actions/action_post.json');
            metrics.GetDecorated(true); // reset metrics
            rule.action.parameters.url = '';
            async.series(
                [
                    function(callback) {
                        clients.PostRule(rule, function(error, data) {
                            should.not.exist(error);
                            data.should.have.property('statusCode', 200);
                            return callback(null);
                        });
                    },
                    function(callback) {
                        clients.PostAction(action, function(error, data) {
                            should.not.exist(error);
                            data.should.have.property('statusCode', 200);
                            setTimeout(function() {
                                var m = metrics.GetDecorated(true),
                                    msub;
                                should.exists(m);
                                should.exists(m.services);
                                should.exists(m.services.unknownt);
                                should.exists(m.services.unknownt.subservices);
                                should.exists(m.services.unknownt.subservices['/']);
                                msub = m.services.unknownt.subservices['/'];

                                should.equal(m.services.unknownt.sum.actionHttpPost, 1);
                                should.equal(m.services.unknownt.sum.okActionHttpPost, 0);
                                should.equal(m.services.unknownt.sum.failedActionHttpPost, 1);
                                should.equal(m.services.unknownt.sum.outgoingTransactions, 1);
                                should.equal(m.services.unknownt.sum.outgoingTransactionsErrors, 1);
                                return callback();
                            }, 50);
                        });
                    }
                ],
                done
            );
        });

        //
        // Missing case for successful email ... needed a SMTP fake server
        //
        // it('should increment successful email', function(done) {...}

        it('should increment error for failed email', function(done) {
            var rule = utilsT.loadExample('./test/data/good_rules/blood_rule_email.json'),
                action = utilsT.loadExample('./test/data/good_actions/action_email.json');
            utilsT.getConfig().smtp.host = 'averyfarwayhosthatnotexist';
            metrics.GetDecorated(true); // reset metrics
            async.series(
                [
                    function(callback) {
                        clients.PostRule(rule, function(error, data) {
                            should.not.exist(error);
                            data.should.have.property('statusCode', 200);
                            return callback(null);
                        });
                    },
                    function(callback) {
                        clients.PostAction(action, function(error, data) {
                            should.not.exist(error);
                            data.should.have.property('statusCode', 200);

                            setTimeout(function() {
                                var m = metrics.GetDecorated(true),
                                    msub;
                                should.exists(m);
                                should.exists(m.services);
                                should.exists(m.services.unknownt);
                                should.exists(m.services.unknownt.subservices);
                                should.exists(m.services.unknownt.subservices['/']);
                                msub = m.services.unknownt.subservices['/'];

                                should.equal(m.services.unknownt.sum.actionEmail, 1);
                                should.equal(m.services.unknownt.sum.okActionEmail, 0);
                                should.equal(m.services.unknownt.sum.failedActionEmail, 1);
                                should.equal(m.services.unknownt.sum.outgoingTransactions, 1);
                                should.equal(m.services.unknownt.sum.outgoingTransactionsErrors, 1);
                                return callback();
                            }, 50);
                        });
                    }
                ],
                done
            );
        });
    });
});
<|endoftext|>


<commit_before>/*global DOMPurify, Marionette, moment */

hqDefine("cloudcare/js/formplayer/menus/views/query", function () {
    // 'hqwebapp/js/hq.helpers' is a dependency. It needs to be added
    // explicitly when webapps is migrated to requirejs
    var FormplayerFrontend = hqImport("cloudcare/js/formplayer/app");
    var separator = " to ";
        dateFormat = "YYYY-MM-DD";
    var selectDelimiter = "#,#"; // Formplayer also uses this
    var Const = hqImport("cloudcare/js/form_entry/const"),
        Utils = hqImport("cloudcare/js/form_entry/utils"),
        initialPageData = hqImport("hqwebapp/js/initial_page_data");

    // special format handled by CaseSearch API
    var encodeValue = function (model, value) {
            if (!value) {
                return value;
            }
            if (model.get("input") === "daterange") {
                return "__range__" + value.replace(separator, "__");
            }
            if (model.get('input') === 'address') {
                // skip geocoder address
                return true;
            }
            if (model.get('input') === 'select') {
                return value.join(selectDelimiter);
            }
            return value;
        },
        decodeValue = function (model, value) {
            if (!value) {
                return value;
            }
            if (model.get("input") === "daterange") {
                return value.replace("__range__", "").replace("__", separator);
            }
            if (model.get('input') === 'select') {
                return value.split(selectDelimiter);
            }
            return value;
        };

    var QueryView = Marionette.View.extend({
        tagName: "tr",
        className: "formplayer-request",
        template: _.template($("#query-view-item-template").html() || ""),

        templateContext: function () {
            var imageUri = this.options.model.get('imageUri'),
                audioUri = this.options.model.get('audioUri'),
                appId = this.model.collection.appId,
                value = this.options.model.get('value');

            return {
                imageUrl: imageUri ? FormplayerFrontend.getChannel().request('resourceMap', imageUri, appId) : "",
                audioUrl: audioUri ? FormplayerFrontend.getChannel().request('resourceMap', audioUri, appId) : "",
                value: value,
            };
        },

        initialize: function () {
            // If input doesn't have a default value, check to see if there's a sticky value from user's last search
            if (!this.options.model.get('value')) {
                var stickyValue = hqImport("cloudcare/js/formplayer/utils/util").getStickyQueryInputs()[this.options.model.get('id')];
                this.options.model.set('value', decodeValue(this.options.model, stickyValue));
            }
        },

        ui: {
            valueDropdown: 'select.query-field',
            hqHelp: '.hq-help',
            dateRange: 'input.daterange',
            queryField: '.query-field',
        },

        modelEvents: {
            'change': 'render',
        },

        geocoderItemCallback: function (addressTopic) {
            return function (item) {
                var broadcastObj = Utils.getBroadcastObject(item);
                $.publish(addressTopic, broadcastObj);
                return item.place_name;
            };
        },

        geocoderOnClearCallback: function (addressTopic) {
            return function () {
                $.publish(addressTopic, Const.NO_ANSWER);
            };
        },

        updateReceiver: function (element) {
            return function (_event, broadcastObj) {
                // e.g. format is home-state, home-zipcode, home-us_state||country
                var receiveExpression = element.data().receive;
                var receiveField = receiveExpression.split("-")[1];
                var value = null;
                if (broadcastObj === undefined || broadcastObj === Const.NO_ANSWER) {
                    value = Const.NO_ANSWER;
                } else if (broadcastObj[receiveField]) {
                    value = broadcastObj[receiveField];
                } else {
                    // match home-us_state||country style
                    var fields = receiveField.split('||');
                    $.each(fields, function (i, field) {
                        if (broadcastObj[field] !== undefined) {
                            value = broadcastObj[field];
                            return false;
                        }
                    });
                }
                // lookup DOM element again, in case the element got rerendered
                var domElement = $('*[data-receive="' + receiveExpression + '"]');
                if (domElement.is('input')) {
                    domElement.val(value);
                    domElement.change();
                }
                else {
                    // Set lookup table option by label
                    var matchingOption = function (el) {
                        return el.find("option").filter(function (_) {
                            return $(this).text().trim() === value;
                        });
                    }
                    var option = matchingOption(domElement);
                    if (domElement[0].multiple === true) {
                        var val = option.val();
                        if (option.length === 1 && domElement.val().indexOf(val) === -1) {
                            domElement.val(
                                domElement.val().concat(val)
                            ).trigger("change");
                        }
                    }
                    else {
                        if (option.length === 1) {
                            domElement.val(String(option.index() - 1)).trigger("change");
                        } else {
                            domElement.val("-1").trigger('change');
                        }
                    }
                }
            };
        },

        onAttach: function () {
            var self = this;
            this.ui.queryField.each(function () {
                // Set geocoder receivers to subscribe
                var receiveExpression = $(this).data().receive;
                if (receiveExpression !== undefined && receiveExpression !== "") {
                    var topic = receiveExpression.split("-")[0];
                    $.subscribe(topic, self.updateReceiver($(this)));
                }
                // Set geocoder address publish
                var addressTopic = $(this).data().address;
                if (addressTopic !== undefined && addressTopic !== "") {
                    // set this up as mapbox input
                    var inputId = addressTopic + "_mapbox";
                    if (!initialPageData.get("has_geocoder_privs")) {
                        $("#" + inputId).addClass('unsupported alert alert-warning');
                        $("#" + inputId).text(gettext(
                            "Sorry, this input is not supported because your project doesn't have a Geocoder privilege")
                        );
                        return true;
                    }
                    Utils.renderMapboxInput(
                        inputId,
                        self.geocoderItemCallback(addressTopic),
                        self.geocoderOnClearCallback(addressTopic),
                        initialPageData
                    );
                    var divEl = $('.mapboxgl-ctrl-geocoder');
                    divEl.css("max-width", "none");
                    divEl.css("width", "100%");
                }
            });
        },

        onRender: function () {
            this.ui.valueDropdown.select2({
                allowClear: true,
                placeholder: " ",   // required for allowClear to work
                escapeMarkup: function (m) { return DOMPurify.sanitize(m); },
            });
            this.ui.hqHelp.hqHelp();
            this.ui.dateRange.daterangepicker({
                locale: {
                    format: dateFormat,
                    separator: separator,
                    cancelLabel: gettext('Clear'),
                },
                autoUpdateInput: false,
            });
            this.ui.dateRange.on('cancel.daterangepicker', function () {
                $(this).val('').trigger('change');
            });
            this.ui.dateRange.on('apply.daterangepicker', function(ev, picker) {
                $(this).val(picker.startDate.format(dateFormat) + separator + picker.endDate.format(dateFormat)).trigger('change');
            });
            this.ui.dateRange.on('change', function () {
                // Validate free-text input. Accept anything moment can recognize as a date, reformatting for ES.
                var $input = $(this),
                    oldValue = $input.val(),
                    parts = _.map(oldValue.split(separator), function (v) { return moment(v); }),
                    newValue = '';
                if (parts.length === 2 && _.every(parts, function (part) { return part.isValid(); })) {
                    newValue = parts[0].format(dateFormat) + separator + parts[1].format(dateFormat);
                }
                if (oldValue !== newValue) {
                    $input.val(newValue).trigger('change');
                }
            });
            if (this.options.model.get('hidden') === 'true') {
                this.$el.hide();
            }
        },
    });

    var QueryListView = Marionette.CollectionView.extend({
        tagName: "div",
        template: _.template($("#query-view-list-template").html() || ""),
        childView: QueryView,
        childViewContainer: "tbody",

        initialize: function (options) {
            this.parentModel = options.collection.models;
        },

        templateContext: function () {
            return {
                title: this.options.title,
            };
        },

        ui: {
            clearButton: '#query-clear-button',
            submitButton: '#query-submit-button',
            valueDropdown: 'select.query-field',
            valueInput: 'input.query-field',
        },

        events: {
            'change @ui.valueDropdown': 'changeDropdown',
            'change @ui.valueInput': 'setStickyQueryInputs',
            'click @ui.clearButton': 'clearAction',
            'click @ui.submitButton': 'submitAction',
        },

        getAnswers: function () {
            var $inputGroups = $(".query-input-group"),
                answers = {},
                model = this.parentModel;
            $inputGroups.each(function (index) {
                var queryValue = $(this).find('.query-field').val(),
                    searchForBlank = $(this).find('.search-for-blank').prop('checked');
                if (queryValue !== '' || searchForBlank) {
                    answers[model[index].get('id')] = encodeValue(model[index], queryValue);
                }
            });
            return answers;
        },

        changeDropdown: function (e) {
            e.preventDefault();
            var self = this;
            var $fields = $(".query-field");

            // If there aren't at least two dropdowns, there are no dependencies
            if ($fields.filter("select").length < 2) {
                return;
            }

            var Util = hqImport("cloudcare/js/formplayer/utils/util");
            var urlObject = Util.currentUrlToObject();
            urlObject.setQueryData(this.getAnswers(), false);
            var fetchingPrompts = FormplayerFrontend.getChannel().request("app:select:menus", urlObject);
            $.when(fetchingPrompts).done(function (response) {
                for (var i = 0; i < response.models.length; i++) {
                    var choices = response.models[i].get('itemsetChoices');
                    if (choices) {
                        var $field = $($fields.get(i)),
                            value = $field.val();

                        $field.select2('close');    // force close dropdown, the set below can interfere with this when clearing selection
                        if (!$field.attr('multiple')) {
                            value = String(value);
                        }
                        self.collection.models[i].set({
                            itemsetChoices: choices,
                            value: value,
                        });
                        $field.trigger('change.select2');
                    }
                }
                self.setStickyQueryInputs();
            });
        },

        clearAction: function () {
            var self = this,
                fields = $(".query-field");
            fields.each(function () {
                this.value = '';
                $(this).trigger('change.select2');
            });
            self.setStickyQueryInputs();
            // clear geocoder input if any
            $('.mapboxgl-ctrl-geocoder--button').click();
        },

        submitAction: function (e) {
            e.preventDefault();
            FormplayerFrontend.trigger("menu:query", this.getAnswers());
        },

        setStickyQueryInputs: function () {
            var Util = hqImport("cloudcare/js/formplayer/utils/util");
            Util.setStickyQueryInputs(this.getAnswers());
        },
    });

    return function (data) {
        return new QueryListView(data);
    };
});
<commit_msg>Disable input field when searching for blank<commit_after>/*global DOMPurify, Marionette, moment */

hqDefine("cloudcare/js/formplayer/menus/views/query", function () {
    // 'hqwebapp/js/hq.helpers' is a dependency. It needs to be added
    // explicitly when webapps is migrated to requirejs
    var FormplayerFrontend = hqImport("cloudcare/js/formplayer/app");
    var separator = " to ";
        dateFormat = "YYYY-MM-DD";
    var selectDelimiter = "#,#"; // Formplayer also uses this
    var Const = hqImport("cloudcare/js/form_entry/const"),
        Utils = hqImport("cloudcare/js/form_entry/utils"),
        initialPageData = hqImport("hqwebapp/js/initial_page_data");

    // special format handled by CaseSearch API
    var encodeValue = function (model, value) {
            if (!value) {
                return value;
            }
            if (model.get("input") === "daterange") {
                return "__range__" + value.replace(separator, "__");
            }
            if (model.get('input') === 'address') {
                // skip geocoder address
                return true;
            }
            if (model.get('input') === 'select') {
                return value.join(selectDelimiter);
            }
            return value;
        },
        decodeValue = function (model, value) {
            if (!value) {
                return value;
            }
            if (model.get("input") === "daterange") {
                return value.replace("__range__", "").replace("__", separator);
            }
            if (model.get('input') === 'select') {
                return value.split(selectDelimiter);
            }
            return value;
        };

    var QueryView = Marionette.View.extend({
        tagName: "tr",
        className: "formplayer-request",
        template: _.template($("#query-view-item-template").html() || ""),

        templateContext: function () {
            var imageUri = this.options.model.get('imageUri'),
                audioUri = this.options.model.get('audioUri'),
                appId = this.model.collection.appId,
                value = this.options.model.get('value');

            return {
                imageUrl: imageUri ? FormplayerFrontend.getChannel().request('resourceMap', imageUri, appId) : "",
                audioUrl: audioUri ? FormplayerFrontend.getChannel().request('resourceMap', audioUri, appId) : "",
                value: value,
            };
        },

        initialize: function () {
            // If input doesn't have a default value, check to see if there's a sticky value from user's last search
            if (!this.options.model.get('value')) {
                var stickyValue = hqImport("cloudcare/js/formplayer/utils/util").getStickyQueryInputs()[this.options.model.get('id')];
                this.options.model.set('value', decodeValue(this.options.model, stickyValue));
            }
        },

        ui: {
            valueDropdown: 'select.query-field',
            hqHelp: '.hq-help',
            dateRange: 'input.daterange',
            queryField: '.query-field',
            blankSearchCheckbox: 'input.search-for-blank',
        },

        events: {
            'change @ui.blankSearchCheckbox': 'toggleInputField',
        },

        modelEvents: {
            'change': 'render',
        },

        geocoderItemCallback: function (addressTopic) {
            return function (item) {
                var broadcastObj = Utils.getBroadcastObject(item);
                $.publish(addressTopic, broadcastObj);
                return item.place_name;
            };
        },

        geocoderOnClearCallback: function (addressTopic) {
            return function () {
                $.publish(addressTopic, Const.NO_ANSWER);
            };
        },

        updateReceiver: function (element) {
            return function (_event, broadcastObj) {
                // e.g. format is home-state, home-zipcode, home-us_state||country
                var receiveExpression = element.data().receive;
                var receiveField = receiveExpression.split("-")[1];
                var value = null;
                if (broadcastObj === undefined || broadcastObj === Const.NO_ANSWER) {
                    value = Const.NO_ANSWER;
                } else if (broadcastObj[receiveField]) {
                    value = broadcastObj[receiveField];
                } else {
                    // match home-us_state||country style
                    var fields = receiveField.split('||');
                    $.each(fields, function (i, field) {
                        if (broadcastObj[field] !== undefined) {
                            value = broadcastObj[field];
                            return false;
                        }
                    });
                }
                // lookup DOM element again, in case the element got rerendered
                var domElement = $('*[data-receive="' + receiveExpression + '"]');
                if (domElement.is('input')) {
                    domElement.val(value);
                    domElement.change();
                }
                else {
                    // Set lookup table option by label
                    var matchingOption = function (el) {
                        return el.find("option").filter(function (_) {
                            return $(this).text().trim() === value;
                        });
                    }
                    var option = matchingOption(domElement);
                    if (domElement[0].multiple === true) {
                        var val = option.val();
                        if (option.length === 1 && domElement.val().indexOf(val) === -1) {
                            domElement.val(
                                domElement.val().concat(val)
                            ).trigger("change");
                        }
                    }
                    else {
                        if (option.length === 1) {
                            domElement.val(String(option.index() - 1)).trigger("change");
                        } else {
                            domElement.val("-1").trigger('change');
                        }
                    }
                }
            };
        },

        onAttach: function () {
            var self = this;
            this.ui.queryField.each(function () {
                // Set geocoder receivers to subscribe
                var receiveExpression = $(this).data().receive;
                if (receiveExpression !== undefined && receiveExpression !== "") {
                    var topic = receiveExpression.split("-")[0];
                    $.subscribe(topic, self.updateReceiver($(this)));
                }
                // Set geocoder address publish
                var addressTopic = $(this).data().address;
                if (addressTopic !== undefined && addressTopic !== "") {
                    // set this up as mapbox input
                    var inputId = addressTopic + "_mapbox";
                    if (!initialPageData.get("has_geocoder_privs")) {
                        $("#" + inputId).addClass('unsupported alert alert-warning');
                        $("#" + inputId).text(gettext(
                            "Sorry, this input is not supported because your project doesn't have a Geocoder privilege")
                        );
                        return true;
                    }
                    Utils.renderMapboxInput(
                        inputId,
                        self.geocoderItemCallback(addressTopic),
                        self.geocoderOnClearCallback(addressTopic),
                        initialPageData
                    );
                    var divEl = $('.mapboxgl-ctrl-geocoder');
                    divEl.css("max-width", "none");
                    divEl.css("width", "100%");
                }
            });
        },

        onRender: function () {
            this.ui.valueDropdown.select2({
                allowClear: true,
                placeholder: " ",   // required for allowClear to work
                escapeMarkup: function (m) { return DOMPurify.sanitize(m); },
            });
            this.ui.hqHelp.hqHelp();
            this.ui.dateRange.daterangepicker({
                locale: {
                    format: dateFormat,
                    separator: separator,
                    cancelLabel: gettext('Clear'),
                },
                autoUpdateInput: false,
            });
            this.ui.dateRange.on('cancel.daterangepicker', function () {
                $(this).val('').trigger('change');
            });
            this.ui.dateRange.on('apply.daterangepicker', function(ev, picker) {
                $(this).val(picker.startDate.format(dateFormat) + separator + picker.endDate.format(dateFormat)).trigger('change');
            });
            this.ui.dateRange.on('change', function () {
                // Validate free-text input. Accept anything moment can recognize as a date, reformatting for ES.
                var $input = $(this),
                    oldValue = $input.val(),
                    parts = _.map(oldValue.split(separator), function (v) { return moment(v); }),
                    newValue = '';
                if (parts.length === 2 && _.every(parts, function (part) { return part.isValid(); })) {
                    newValue = parts[0].format(dateFormat) + separator + parts[1].format(dateFormat);
                }
                if (oldValue !== newValue) {
                    $input.val(newValue).trigger('change');
                }
            });
            if (this.options.model.get('hidden') === 'true') {
                this.$el.hide();
            }
        },

        toggleInputField: function () {
            this.ui.queryField.prop('disabled', this.ui.blankSearchCheckbox.prop('checked'));
        },

    });

    var QueryListView = Marionette.CollectionView.extend({
        tagName: "div",
        template: _.template($("#query-view-list-template").html() || ""),
        childView: QueryView,
        childViewContainer: "tbody",

        initialize: function (options) {
            this.parentModel = options.collection.models;
        },

        templateContext: function () {
            return {
                title: this.options.title,
            };
        },

        ui: {
            clearButton: '#query-clear-button',
            submitButton: '#query-submit-button',
            valueDropdown: 'select.query-field',
            valueInput: 'input.query-field',
        },

        events: {
            'change @ui.valueDropdown': 'changeDropdown',
            'change @ui.valueInput': 'setStickyQueryInputs',
            'click @ui.clearButton': 'clearAction',
            'click @ui.submitButton': 'submitAction',
        },

        getAnswers: function () {
            var $inputGroups = $(".query-input-group"),
                answers = {},
                model = this.parentModel;
            $inputGroups.each(function (index) {
                var queryValue = $(this).find('.query-field').val(),
                    searchForBlank = $(this).find('.search-for-blank').prop('checked');

                if (searchForBlank) {
                    answers[model[index].get('id')] = '';
                } else if (queryValue !== '') {
                    answers[model[index].get('id')] = encodeValue(model[index], queryValue);
                }
            });
            return answers;
        },

        changeDropdown: function (e) {
            e.preventDefault();
            var self = this;
            var $fields = $(".query-field");

            // If there aren't at least two dropdowns, there are no dependencies
            if ($fields.filter("select").length < 2) {
                return;
            }

            var Util = hqImport("cloudcare/js/formplayer/utils/util");
            var urlObject = Util.currentUrlToObject();
            urlObject.setQueryData(this.getAnswers(), false);
            var fetchingPrompts = FormplayerFrontend.getChannel().request("app:select:menus", urlObject);
            $.when(fetchingPrompts).done(function (response) {
                for (var i = 0; i < response.models.length; i++) {
                    var choices = response.models[i].get('itemsetChoices');
                    if (choices) {
                        var $field = $($fields.get(i)),
                            value = $field.val();

                        $field.select2('close');    // force close dropdown, the set below can interfere with this when clearing selection
                        if (!$field.attr('multiple')) {
                            value = String(value);
                        }
                        self.collection.models[i].set({
                            itemsetChoices: choices,
                            value: value,
                        });
                        $field.trigger('change.select2');
                    }
                }
                self.setStickyQueryInputs();
            });
        },

        clearAction: function () {
            var self = this,
                fields = $(".query-field");
            fields.each(function () {
                this.value = '';
                $(this).trigger('change.select2');
            });
            self.setStickyQueryInputs();
            // clear geocoder input if any
            $('.mapboxgl-ctrl-geocoder--button').click();
        },

        submitAction: function (e) {
            e.preventDefault();
            FormplayerFrontend.trigger("menu:query", this.getAnswers());
        },

        setStickyQueryInputs: function () {
            var Util = hqImport("cloudcare/js/formplayer/utils/util");
            Util.setStickyQueryInputs(this.getAnswers());
        },
    });

    return function (data) {
        return new QueryListView(data);
    };
});
<|endoftext|>