"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
Object.defineProperty(exports, "__esModule", { value: true });
const chai_1 = require("chai");
const mongodb_1 = require("mongodb");
const sinon = __importStar(require("sinon"));
const block_1 = require("../../../src/models/block");
const coin_1 = require("../../../src/models/coin");
const transaction_1 = require("../../../src/models/transaction");
const chain_state_1 = require("../../../src/providers/chain-state");
const storage_1 = require("../../../src/services/storage");
const test_block_1 = require("../../data/test-block");
const helpers_1 = require("../../helpers");
const helpers_2 = require("../../helpers");
const unit_1 = require("../../helpers/unit");
describe('Block Model', function () {
    before(unit_1.unitBeforeHelper);
    after(unit_1.unitAfterHelper);
    const addBlockParams = {
        chain: 'BTC',
        network: 'regtest',
        block: test_block_1.TEST_BLOCK,
        height: 1355,
        initialSyncComplete: false,
        processed: true
    };
    describe('addBlock', () => {
        let sandbox;
        beforeEach(() => {
            sandbox = sinon.sandbox.create();
        });
        afterEach(() => {
            sandbox.restore();
        });
        it('should be able to add a block', async () => {
            const newBlock = Object.assign({ save: () => Promise.resolve() }, block_1.BitcoinBlockStorage, addBlockParams);
            (0, helpers_1.mockStorage)(newBlock);
            sandbox.stub(block_1.BitcoinBlockStorage, 'handleReorg').resolves();
            sandbox.stub(transaction_1.TransactionStorage, 'batchImport').resolves();
            const result = await block_1.BitcoinBlockStorage.addBlock(addBlockParams);
            (0, chai_1.expect)(result);
        });
    });
    describe('BlockModel find options', () => {
        it('should be able to create query options', () => {
            const id = new mongodb_1.ObjectID();
            const { query, options } = storage_1.Storage.getFindOptions(block_1.BitcoinBlockStorage, {
                since: id,
                paging: '_id',
                limit: 100,
                direction: -1
            });
            (0, chai_1.expect)(options.limit).to.be.eq(100);
            (0, chai_1.expect)(query._id).to.be.deep.eq({ $lt: id });
            (0, chai_1.expect)(options.sort).to.be.deep.eq({ _id: -1 });
        });
        it('should default to descending', () => {
            const id = new mongodb_1.ObjectID();
            const { query, options } = storage_1.Storage.getFindOptions(block_1.BitcoinBlockStorage, {
                since: id,
                paging: '_id',
                limit: 100
            });
            (0, chai_1.expect)(options.sort).to.be.deep.eq({ _id: -1 });
            (0, chai_1.expect)(options.limit).to.be.eq(100);
            (0, chai_1.expect)(query._id).to.be.deep.eq({ $lt: id });
        });
        it('should allow ascending', () => {
            const id = new mongodb_1.ObjectID();
            const { query, options } = storage_1.Storage.getFindOptions(block_1.BitcoinBlockStorage, {
                since: id,
                paging: '_id',
                limit: 100,
                direction: 1
            });
            (0, chai_1.expect)(options.sort).to.be.deep.eq({ _id: 1 });
            (0, chai_1.expect)(options.limit).to.be.eq(100);
            (0, chai_1.expect)(query._id).to.be.deep.eq({ $gt: id });
        });
    });
    describe('getLocalTip', () => {
        let sandbox;
        beforeEach(() => {
            sandbox = sinon.sandbox.create();
        });
        afterEach(() => {
            sandbox.restore();
        });
        it('should return the new tip', async () => {
            const newBlock = Object.assign({ save: () => Promise.resolve() }, block_1.BitcoinBlockStorage, addBlockParams);
            (0, helpers_1.mockStorage)(newBlock);
            const params = { chain: 'BTC', network: 'regtest' };
            const result = await chain_state_1.ChainStateProvider.getLocalTip(params);
            (0, chai_1.expect)(result.height).to.deep.equal(addBlockParams.height);
            (0, chai_1.expect)(result.chain).to.deep.equal(addBlockParams.chain);
            (0, chai_1.expect)(result.network).to.deep.equal(addBlockParams.network);
        });
    });
    describe('getPoolInfo', () => {
        xit('should return pool info given a coinbase string');
    });
    describe('getLocatorHashes', () => {
        let sandbox;
        beforeEach(() => {
            sandbox = sinon.sandbox.create();
        });
        afterEach(() => {
            sandbox.restore();
        });
        it('should return 65 zeros if there are no processed blocks for the chain and network', async () => {
            const params = { chain: 'BTC', network: 'regtest' };
            const result = await chain_state_1.ChainStateProvider.getLocatorHashes(params);
            (0, chai_1.expect)(result).to.deep.equal([Array(65).join('0')]);
        });
    });
    describe('handleReorg', () => {
        let sandbox;
        beforeEach(() => {
            sandbox = sinon.sandbox.create();
        });
        afterEach(() => {
            sandbox.restore();
        });
        it('should return if localTip hash equals the previous hash', async () => {
            Object.assign(block_1.BitcoinBlockStorage.collection, (0, helpers_2.mockCollection)(null));
            Object.assign(transaction_1.TransactionStorage.collection, (0, helpers_2.mockCollection)(null));
            Object.assign(coin_1.CoinStorage.collection, (0, helpers_2.mockCollection)(null));
            const blockModelRemoveSpy = block_1.BitcoinBlockStorage.collection.deleteMany;
            const transactionModelRemoveSpy = transaction_1.TransactionStorage.collection.deleteMany;
            const coinModelRemoveSpy = coin_1.CoinStorage.collection.deleteMany;
            const coinModelUpdateSpy = coin_1.CoinStorage.collection.updateMany;
            const params = {
                header: {
                    prevHash: '3420349f63d96f257d56dd970f6b9079af9cf2784c267a13b1ac339d47031fe9',
                    hash: '64bfb3eda276ae4ae5b64d9e36c9c0b629bc767fb7ae66f9d55d2c5c8103a929',
                    time: 1526756523,
                    version: 536870912,
                    merkleRoot: '08e23107e8449f02568d37d37aa76e840e55bbb5f100ed8ad257af303db88c08',
                    bits: parseInt('207fffff', 16),
                    nonce: 2
                },
                chain: 'BTC',
                network: 'regtest'
            };
            await block_1.BitcoinBlockStorage.handleReorg(params);
            (0, chai_1.expect)(blockModelRemoveSpy.notCalled).to.be.true;
            (0, chai_1.expect)(transactionModelRemoveSpy.notCalled).to.be.true;
            (0, chai_1.expect)(coinModelRemoveSpy.notCalled).to.be.true;
            (0, chai_1.expect)(coinModelUpdateSpy.notCalled).to.be.true;
        });
        it('should return if localTip height is zero', async () => {
            const blockModelRemoveSpy = block_1.BitcoinBlockStorage.collection.deleteMany;
            const transactionModelRemoveSpy = transaction_1.TransactionStorage.collection.deleteMany;
            const coinModelRemoveSpy = coin_1.CoinStorage.collection.deleteMany;
            const coinModelUpdateSpy = coin_1.CoinStorage.collection.updateMany;
            const blockMethodParams = {
                chain: 'BTC',
                network: 'regtest',
                block: test_block_1.TEST_BLOCK,
                height: 1355
            };
            const params = Object.assign(block_1.BitcoinBlockStorage, blockMethodParams);
            await block_1.BitcoinBlockStorage.handleReorg(params);
            (0, chai_1.expect)(blockModelRemoveSpy.notCalled).to.be.true;
            (0, chai_1.expect)(transactionModelRemoveSpy.notCalled).to.be.true;
            (0, chai_1.expect)(coinModelRemoveSpy.notCalled).to.be.true;
            (0, chai_1.expect)(coinModelUpdateSpy.notCalled).to.be.true;
        });
        it('should call blockModel deleteMany', async () => {
            (0, helpers_1.mockStorage)({
                height: 1,
                previousBlockHash: '3420349f63d96f257d56dd970f6b9079af9cf2784c267a13b1ac339d47031fe9'
            });
            const blockMethodParams = {
                chain: 'BTC',
                network: 'regtest',
                block: test_block_1.TEST_BLOCK,
                height: 1355
            };
            const params = Object.assign(block_1.BitcoinBlockStorage, blockMethodParams);
            const removeSpy = block_1.BitcoinBlockStorage.collection.deleteMany;
            await block_1.BitcoinBlockStorage.handleReorg(params);
            (0, chai_1.expect)(removeSpy.called).to.be.true;
        });
        it('should call transactionModel deleteMany', async () => {
            (0, helpers_1.mockStorage)({
                height: 1,
                previousBlockHash: '3420349f63d96f257d56dd970f6b9079af9cf2784c267a13b1ac339d47031fe9'
            });
            const blockMethodParams = {
                chain: 'BTC',
                network: 'regtest',
                block: test_block_1.TEST_BLOCK,
                height: 1355
            };
            const params = Object.assign(block_1.BitcoinBlockStorage, blockMethodParams);
            const removeSpy = transaction_1.TransactionStorage.collection.deleteMany;
            await block_1.BitcoinBlockStorage.handleReorg(params);
            (0, chai_1.expect)(removeSpy.called).to.be.true;
        });
        it('should call coinModel deleteMany', async () => {
            (0, helpers_1.mockStorage)({
                height: 1,
                previousBlockHash: '3420349f63d96f257d56dd970f6b9079af9cf2784c267a13b1ac339d47031fe9'
            });
            const blockMethodParams = {
                chain: 'BTC',
                network: 'regtest',
                block: test_block_1.TEST_BLOCK,
                height: 1355
            };
            const params = Object.assign(block_1.BitcoinBlockStorage, blockMethodParams);
            const collectionSpy = storage_1.Storage.db.collection;
            const removeSpy = coin_1.CoinStorage.collection.deleteMany;
            await block_1.BitcoinBlockStorage.handleReorg(params);
            (0, chai_1.expect)(collectionSpy.calledOnceWith('coins'));
            (0, chai_1.expect)(removeSpy.callCount).to.eq(3);
        });
        it('should call coinModel update', async () => {
            (0, helpers_1.mockStorage)({
                height: 1,
                previousBlockHash: '3420349f63d96f257d56dd970f6b9079af9cf2784c267a13b1ac339d47031fe9'
            });
            const blockMethodParams = {
                chain: 'BTC',
                network: 'regtest',
                block: test_block_1.TEST_BLOCK,
                height: 1355
            };
            const params = Object.assign(block_1.BitcoinBlockStorage, blockMethodParams);
            const collectionSpy = storage_1.Storage.db.collection;
            const updateSpy = coin_1.CoinStorage.collection.updateMany;
            await block_1.BitcoinBlockStorage.handleReorg(params);
            (0, chai_1.expect)(collectionSpy.calledOnceWith('coins'));
            (0, chai_1.expect)(updateSpy.called).to.be.true;
        });
    });
    describe('_apiTransform', () => {
        it('should return the transform object with block values', () => {
            const block = {
                chain: 'BTC',
                network: 'mainnet',
                height: 1,
                hash: 'abcd',
                version: 1,
                merkleRoot: 'deff',
                time: new Date(),
                timeNormalized: new Date(),
                nonce: 1,
                previousBlockHash: 'aabb',
                nextBlockHash: 'bbcc',
                transactionCount: 1,
                size: 255,
                bits: 256,
                reward: 5000000000,
                processed: true
            };
            const result = block_1.BitcoinBlockStorage._apiTransform(block, { object: true });
            (0, chai_1.expect)(result.hash).to.be.equal(block.hash);
            (0, chai_1.expect)(result.height).to.be.equal(block.height);
            (0, chai_1.expect)(result.version).to.be.equal(block.version);
            (0, chai_1.expect)(result.size).to.be.equal(block.size);
            (0, chai_1.expect)(result.merkleRoot).to.be.equal(block.merkleRoot);
            (0, chai_1.expect)(result.time).to.equal(block.time);
            (0, chai_1.expect)(result.timeNormalized).to.equal(block.timeNormalized);
            (0, chai_1.expect)(result.nonce).to.be.equal(block.nonce);
            (0, chai_1.expect)(result.bits).to.be.equal(block.bits);
            (0, chai_1.expect)(result.previousBlockHash).to.be.equal(block.previousBlockHash);
            (0, chai_1.expect)(result.nextBlockHash).to.be.equal(block.nextBlockHash);
            (0, chai_1.expect)(result.transactionCount).to.be.equal(block.transactionCount);
            (0, chai_1.expect)(result).to.not.have.property('processed');
        });
    });
});
//# sourceMappingURL=block.test.js.map