"use strict";
/**
 * Delta Processor test
 *
 *  Copyright (C) 2010-2019 R-T Specialty, LLC.
 *
 *  This file is part of the Liza Data Collection Framework.
 *
 *  liza is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Affero General Public License as
 *  published by the Free Software Foundation, either version 3 of the
 *  License, or (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU Affero General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
Object.defineProperty(exports, "__esModule", { value: true });
var DeltaProcessor_1 = require("../../src/system/DeltaProcessor");
var delta_1 = require("../../src/bucket/delta");
var events_1 = require("events");
var chai_1 = require("chai");
chai_1.use(require('chai-as-promised'));
describe('system.DeltaProcessor', function () {
    describe('#process', function () {
        [
            {
                label: 'No deltas are processed',
                given: [
                    {
                        id: 123,
                        lastUpdate: 123123123,
                        data: {},
                        ratedata: {},
                        rdelta: {},
                    },
                ],
                expected: [],
            },
            // when quote is initialized: { foo: [ '' ], state: [ 'a' ] }
            {
                label: 'Publishes deltas in order',
                given: [
                    {
                        id: 123,
                        lastUpdate: 123123123,
                        data: {
                            bar: ['initial and unchanged'],
                            foo: ['third'],
                            state: ['a', 'b', 'c', 'd'],
                        },
                        ratedata: {
                            prem: ['rate_second'],
                            state: ['i', 'ii', 'iii'],
                        },
                        rdelta: {
                            data: [
                                {
                                    timestamp: 1,
                                    data: {
                                        foo: [''],
                                        state: [undefined, null],
                                    },
                                },
                                {
                                    timestamp: 3,
                                    data: {
                                        foo: ['first'],
                                        state: [undefined, undefined, null],
                                    },
                                },
                                {
                                    timestamp: 5,
                                    data: {
                                        foo: ['second'],
                                        state: [undefined, undefined, undefined, null],
                                    },
                                },
                            ],
                            ratedata: [
                                {
                                    timestamp: 2,
                                    data: {
                                        prem: [''],
                                        state: [undefined, null],
                                    },
                                },
                                {
                                    timestamp: 4,
                                    data: {
                                        prem: ['rate_first'],
                                        state: [undefined, undefined, null],
                                    },
                                },
                            ],
                        },
                    },
                ],
                expected: [
                    // bucket
                    {
                        doc_id: 123,
                        rdelta: {
                            foo: [''],
                            state: [undefined, null],
                        },
                        bucket: {
                            bar: ['initial and unchanged'],
                            foo: ['first'],
                            state: ['a', 'b'],
                        },
                        ratedata: {
                            prem: [''],
                            state: ['i'],
                        },
                    },
                    // rate
                    {
                        doc_id: 123,
                        rdelta: {
                            prem: [''],
                            state: [undefined, null],
                        },
                        bucket: {
                            bar: ['initial and unchanged'],
                            foo: ['first'],
                            state: ['a', 'b'],
                        },
                        ratedata: {
                            prem: ['rate_first'],
                            state: ['i', 'ii'],
                        },
                    },
                    // bucket
                    {
                        doc_id: 123,
                        rdelta: {
                            foo: ['first'],
                            state: [undefined, undefined, null],
                        },
                        bucket: {
                            bar: ['initial and unchanged'],
                            foo: ['second'],
                            state: ['a', 'b', 'c'],
                        },
                        ratedata: {
                            prem: ['rate_first'],
                            state: ['i', 'ii'],
                        },
                    },
                    // rate
                    {
                        doc_id: 123,
                        rdelta: {
                            prem: ['rate_first'],
                            state: [undefined, undefined, null],
                        },
                        bucket: {
                            bar: ['initial and unchanged'],
                            foo: ['second'],
                            state: ['a', 'b', 'c'],
                        },
                        ratedata: {
                            prem: ['rate_second'],
                            state: ['i', 'ii', 'iii'],
                        },
                    },
                    // bucket
                    {
                        doc_id: 123,
                        rdelta: {
                            foo: ['second'],
                            state: [undefined, undefined, undefined, null],
                        },
                        bucket: {
                            bar: ['initial and unchanged'],
                            foo: ['third'],
                            state: ['a', 'b', 'c', 'd'],
                        },
                        ratedata: {
                            prem: ['rate_second'],
                            state: ['i', 'ii', 'iii'],
                        },
                    },
                ],
            },
            {
                label: 'Publishes deltas in order for multiple documents',
                given: [
                    {
                        id: 123,
                        lastUpdate: 123123123,
                        data: {
                            foo: ['first'],
                            state: ['a', 'b'],
                        },
                        ratedata: {
                            prem: ['rate_first'],
                            state: ['i', 'ii'],
                        },
                        rdelta: {
                            data: [
                                {
                                    timestamp: 1,
                                    data: {
                                        foo: [''],
                                        state: [undefined, null],
                                    },
                                },
                            ],
                            ratedata: [
                                {
                                    timestamp: 4,
                                    data: {
                                        prem: [''],
                                        state: [undefined, null],
                                    },
                                },
                            ],
                        },
                    },
                    // timestamps of this document are sandwiched between
                    // the above to make sure documents are processed
                    // independently (without splicing their deltas together)
                    {
                        id: 234,
                        lastUpdate: 121212123,
                        data: {
                            foo2: ['first'],
                            state: ['a', 'b'],
                        },
                        ratedata: {
                            prem2: ['rate_first'],
                            state: ['i', 'ii'],
                        },
                        rdelta: {
                            data: [
                                {
                                    timestamp: 2,
                                    data: {
                                        foo2: [''],
                                        state: [undefined, null],
                                    },
                                },
                            ],
                            ratedata: [
                                {
                                    timestamp: 3,
                                    data: {
                                        prem2: [''],
                                        state: [undefined, null],
                                    },
                                },
                            ],
                        },
                    },
                ],
                expected: [
                    // bucket
                    {
                        doc_id: 123,
                        rdelta: {
                            foo: [''],
                            state: [undefined, null],
                        },
                        bucket: {
                            foo: ['first'],
                            state: ['a', 'b'],
                        },
                        ratedata: {
                            prem: [''],
                            state: ['i'],
                        },
                    },
                    // rate
                    {
                        doc_id: 123,
                        rdelta: {
                            prem: [''],
                            state: [undefined, null],
                        },
                        bucket: {
                            foo: ['first'],
                            state: ['a', 'b'],
                        },
                        ratedata: {
                            prem: ['rate_first'],
                            state: ['i', 'ii'],
                        },
                    },
                    // bucket
                    {
                        doc_id: 234,
                        rdelta: {
                            foo2: [''],
                            state: [undefined, null],
                        },
                        bucket: {
                            foo2: ['first'],
                            state: ['a', 'b'],
                        },
                        ratedata: {
                            prem2: [''],
                            state: ['i'],
                        },
                    },
                    // rate
                    {
                        doc_id: 234,
                        rdelta: {
                            prem2: [''],
                            state: [undefined, null],
                        },
                        bucket: {
                            foo2: ['first'],
                            state: ['a', 'b'],
                        },
                        ratedata: {
                            prem2: ['rate_first'],
                            state: ['i', 'ii'],
                        },
                    },
                ],
            },
            {
                label: 'trims delta array based on ts',
                given: [
                    {
                        id: 111,
                        lastUpdate: 123123123,
                        deltaPublishedTs: { data: 123 },
                        data: { foo: ['second'] },
                        ratedata: {},
                        rdelta: {
                            data: [
                                {
                                    data: { foo: [''] },
                                    timestamp: 123,
                                },
                                {
                                    data: { foo: ['first'] },
                                    timestamp: 234,
                                },
                            ],
                        },
                        totalPublishDelta: {
                            data: 1,
                        },
                    },
                ],
                expected: [
                    {
                        doc_id: 111,
                        rdelta: { foo: ['first'] },
                        bucket: { foo: ['second'] },
                        ratedata: {},
                    },
                ],
            },
            {
                label: 'Data deltas with step_id higher than topSavedStepId ' +
                    'are not published',
                given: [
                    {
                        id: 111,
                        lastUpdate: 123123123,
                        deltaPublishedTs: { data: 0 },
                        topSavedStepId: 2,
                        data: { foo: ['second'] },
                        ratedata: {},
                        rdelta: {
                            data: [
                                {
                                    data: { foo: [''] },
                                    step_id: 2,
                                    timestamp: 123,
                                },
                                {
                                    data: { foo: ['first'] },
                                    step_id: 3,
                                    timestamp: 234,
                                },
                            ],
                        },
                        totalPublishDelta: {
                            data: 1,
                        },
                    },
                ],
                expected: [
                    {
                        doc_id: 111,
                        rdelta: { foo: [''] },
                        bucket: { foo: ['first'] },
                        ratedata: {},
                    },
                ],
            },
            {
                label: 'Ratedata deltas with step_id higher than topSavedStepId ' +
                    'are published',
                given: [
                    {
                        id: 111,
                        lastUpdate: 123123123,
                        deltaPublishedTs: { data: 0 },
                        topSavedStepId: 2,
                        data: { foo: ['current'] },
                        ratedata: { bar: ['current'] },
                        rdelta: {
                            data: [
                                {
                                    data: { foo: [''] },
                                    step_id: 2,
                                    timestamp: 123,
                                },
                                {
                                    data: { foo: ['data_update_1'] },
                                    step_id: 3,
                                    timestamp: 234,
                                },
                                {
                                    data: { foo: ['data_update_2'] },
                                    step_id: 3,
                                    timestamp: 456,
                                },
                            ],
                            ratedata: [
                                {
                                    data: { bar: ['ratedata_update'] },
                                    step_id: 3,
                                    timestamp: 345,
                                },
                            ],
                        },
                        totalPublishDelta: {
                            data: 1,
                            ratedata: 1,
                        },
                    },
                ],
                expected: [
                    {
                        doc_id: 111,
                        rdelta: { foo: [''] },
                        bucket: { foo: ['data_update_1'] },
                        ratedata: { bar: ['ratedata_update'] },
                    },
                    {
                        doc_id: 111,
                        rdelta: { bar: ['ratedata_update'] },
                        bucket: { foo: ['data_update_2'] },
                        ratedata: { bar: ['current'] },
                    },
                ],
            },
        ].forEach(function (_a) {
            var label = _a.label, given = _a.given, expected = _a.expected;
            return it(label, function () {
                var published = [];
                var dao = createMockDeltaDao();
                var publisher = createMockDeltaPublisher();
                var emitter = new events_1.EventEmitter();
                var mergeDeltasByType = function (deltas) { return deltas; };
                dao.getUnprocessedDocuments = function () {
                    return Promise.resolve(given);
                };
                publisher.publish = function (meta, delta, bucket, ratedata) {
                    published.push({
                        doc_id: meta.id,
                        rdelta: delta.data,
                        bucket: bucket,
                        ratedata: ratedata,
                    });
                    return Promise.resolve();
                };
                var sut = new DeltaProcessor_1.DeltaProcessor(dao, publisher, emitter, delta_1.applyDelta, mergeDeltasByType);
                return chai_1.expect(sut.process())
                    .to.eventually.deep.equal(undefined)
                    .then(function (_) { return chai_1.expect(published).to.deep.equal(expected); });
            });
        });
        [
            {
                label: 'handles empty delta data',
                given: [
                    {
                        id: 111,
                        lastUpdate: 123123123,
                        data: { foo: ['bar'] },
                        ratedata: {},
                        rdelta: {
                            data: [
                                {
                                    data: {},
                                    timestamp: 234,
                                    concluding_save: true,
                                },
                            ],
                        },
                    },
                ],
                expected: [
                    {
                        doc_id: 111,
                        bucket: { foo: ['bar'] },
                        ratedata: {},
                        rdelta: {
                            concluding_save: true,
                            data: {},
                            timestamp: 234,
                            type: 'data',
                        },
                    },
                ],
            },
        ].forEach(function (_a) {
            var label = _a.label, given = _a.given, expected = _a.expected;
            return it(label, function () {
                var published = [];
                var dao = createMockDeltaDao();
                var publisher = createMockDeltaPublisher();
                var emitter = new events_1.EventEmitter();
                var applyDelta = function (bucket, _) { return bucket; };
                var mergeDeltasByType = function (deltas) { return deltas; };
                dao.getUnprocessedDocuments = function () {
                    return Promise.resolve(given);
                };
                publisher.publish = function (meta, delta, bucket, ratedata) {
                    published.push({
                        doc_id: meta.id,
                        rdelta: delta,
                        bucket: bucket,
                        ratedata: ratedata,
                    });
                    return Promise.resolve();
                };
                var sut = new DeltaProcessor_1.DeltaProcessor(dao, publisher, emitter, applyDelta, mergeDeltasByType);
                return chai_1.expect(sut.process())
                    .to.eventually.deep.equal(undefined)
                    .then(function (_) { return chai_1.expect(published).to.deep.equal(expected); });
            });
        });
    });
    describe('Error handling', function () {
        it('Marks document in error state and continues', function () {
            var published = [];
            var error_flag_set = false;
            var dao = createMockDeltaDao();
            var publisher = createMockDeltaPublisher();
            var emitter = new events_1.EventEmitter();
            var applyDelta = function (bucket, _) { return bucket; };
            var mergeDeltasByType = function (deltas) { return deltas; };
            var entity_num = 'Some Agency';
            var entity_id = 4321;
            var lastUpdate = 123123123;
            var createdData = 234234234;
            var exp_date = 123;
            var doc = [
                {
                    id: 123,
                    programId: 'mega',
                    agentName: entity_num,
                    agentEntityId: entity_id,
                    startDate: createdData,
                    lastUpdate: lastUpdate,
                    quoteExpDate: exp_date,
                    quoteSetId: 123,
                    topSavedStepId: 1,
                    deltaPublishedTs: { data: 1 },
                    data: { foo: ['start_bar'] },
                    ratedata: {},
                    rdelta: {
                        data: [
                            {
                                data: { foo: ['first_bar'] },
                                timestamp: 123123,
                                type: 'data',
                                step_id: 1,
                                concluding_save: false,
                            },
                        ],
                        ratedata: [],
                    },
                },
                {
                    id: 234,
                    programId: 'mega',
                    agentName: entity_num,
                    agentEntityId: entity_id,
                    startDate: createdData,
                    lastUpdate: 123123123,
                    quoteExpDate: exp_date,
                    quoteSetId: 234,
                    topSavedStepId: 1,
                    deltaPublishedTs: { data: 1 },
                    data: { foo: ['start_bar'] },
                    ratedata: {},
                    rdelta: {
                        data: [
                            {
                                data: { foo: ['first_bar'] },
                                timestamp: 123123,
                                type: 'data',
                                step_id: 1,
                            },
                        ],
                        ratedata: [],
                    },
                },
            ];
            var expected_published = [
                {
                    meta: {
                        entity_id: 4321,
                        entity_name: 'Some Agency',
                        expDate: 123,
                        id: 123,
                        quoteSetId: 123,
                        program: 'mega',
                        lastUpdate: 123123123,
                        startDate: 234234234,
                        topSavedStepId: 1,
                    },
                    delta: { foo: ['first_bar'] },
                    bucket: { foo: ['start_bar'] },
                    ratedata: {},
                },
                {
                    meta: {
                        entity_id: 4321,
                        entity_name: 'Some Agency',
                        expDate: 123,
                        id: 234,
                        quoteSetId: 234,
                        program: 'mega',
                        lastUpdate: 123123123,
                        startDate: 234234234,
                        topSavedStepId: 1,
                    },
                    delta: { foo: ['first_bar'] },
                    bucket: { foo: ['start_bar'] },
                    ratedata: {},
                },
            ];
            var expected_error = 'Uh oh';
            dao.getUnprocessedDocuments = function () {
                return Promise.resolve(doc);
            };
            dao.markDocumentAsProcessed = function (_doc_id, _ts) {
                return Promise.reject(new Error(expected_error));
            };
            dao.setErrorFlag = function () {
                error_flag_set = true;
                return Promise.resolve();
            };
            publisher.publish = function (meta, delta, bucket, ratedata) {
                published.push({
                    meta: meta,
                    delta: delta.data,
                    bucket: bucket,
                    ratedata: ratedata,
                });
                return Promise.resolve();
            };
            // Prevent node from converting an error event into an error
            emitter.on('error', function () { });
            var sut = new DeltaProcessor_1.DeltaProcessor(dao, publisher, emitter, applyDelta, mergeDeltasByType);
            return chai_1.expect(sut.process())
                .to.eventually.deep.equal(undefined)
                .then(function (_) {
                chai_1.expect(error_flag_set).to.be.true;
                chai_1.expect(published).to.deep.equal(expected_published);
            });
        });
    });
    describe('Error handling', function () {
        it('Failure to set document error state further processing', function () {
            var published = [];
            var caught_error = '';
            var dao = createMockDeltaDao();
            var publisher = createMockDeltaPublisher();
            var emitter = new events_1.EventEmitter();
            var applyDelta = function (bucket, _) { return bucket; };
            var mergeDeltasByType = function (deltas) { return deltas; };
            var doc = [
                {
                    id: 123,
                    programId: 'mega',
                    agentName: 'Some Agency',
                    agentEntityId: 4321,
                    startDate: 234234234,
                    lastUpdate: 123123123,
                    quoteExpDate: 123123123,
                    quoteSetId: 123,
                    topSavedStepId: 1,
                    deltaPublishedTs: { data: 1 },
                    data: { foo: ['start_bar'] },
                    ratedata: {},
                    rdelta: {
                        data: [
                            {
                                data: { foo: ['first_bar'] },
                                timestamp: 123123,
                                type: 'data',
                                step_id: 1,
                                concluding_save: false,
                            },
                        ],
                        ratedata: [],
                    },
                },
                {
                    id: 234,
                    programId: 'mega',
                    agentName: 'Some Agency',
                    agentEntityId: 4321,
                    startDate: 234234234,
                    lastUpdate: 123123123,
                    topSavedStepId: 1,
                    deltaPublishedTs: { data: 1 },
                    data: { foo: ['start_bar'] },
                    ratedata: {},
                    rdelta: {
                        data: [
                            {
                                data: { foo: ['first_bar'] },
                                timestamp: 123123,
                                type: 'data',
                                step_id: 1,
                                concluding_save: false,
                            },
                        ],
                        ratedata: [],
                    },
                },
            ];
            // Only one is published
            var expected_published = [
                {
                    meta: {
                        entity_id: 4321,
                        entity_name: 'Some Agency',
                        expDate: 123123123,
                        id: 123,
                        quoteSetId: 123,
                        program: 'mega',
                        lastUpdate: 123123123,
                        startDate: 234234234,
                        topSavedStepId: 1,
                    },
                    delta: { foo: ['first_bar'] },
                    bucket: { foo: ['start_bar'] },
                    ratedata: {},
                },
            ];
            var expected_error = 'Uh oh';
            dao.getUnprocessedDocuments = function () {
                return Promise.resolve(doc);
            };
            dao.markDocumentAsProcessed = function (_doc_id, _ts) {
                return Promise.reject(new Error("Couldn't mark document"));
            };
            dao.setErrorFlag = function () {
                return Promise.reject(new Error(expected_error));
            };
            publisher.publish = function (meta, delta, bucket, ratedata) {
                published.push({
                    meta: meta,
                    delta: delta.data,
                    bucket: bucket,
                    ratedata: ratedata,
                });
                return Promise.resolve();
            };
            // Prevent node from converting an error event into an error
            emitter.on('error', function () { });
            var sut = new DeltaProcessor_1.DeltaProcessor(dao, publisher, emitter, applyDelta, mergeDeltasByType);
            return chai_1.expect(sut.process().catch(function (e) {
                caught_error = e.message;
            }))
                .to.eventually.deep.equal(undefined)
                .then(function (_) {
                chai_1.expect(caught_error).to.equal(expected_error);
                chai_1.expect(published).to.deep.equal(expected_published);
            });
        });
    });
});
function createMockDeltaDao() {
    return {
        getUnprocessedDocuments: function () {
            return Promise.resolve([]);
        },
        setPublishedTs: function () {
            return Promise.resolve();
        },
        markDocumentAsProcessed: function () {
            return Promise.resolve();
        },
        setErrorFlag: function () {
            return Promise.resolve();
        },
        getErrorCount: function () {
            return Promise.resolve(0);
        },
    };
}
function createMockDeltaPublisher() {
    return {
        publish: function () {
            return Promise.resolve();
        },
    };
}
//# sourceMappingURL=data:application/json;base64, 