"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;
    };
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const bson_1 = require("bson");
const chai_1 = require("chai");
const sinon = __importStar(require("sinon"));
const request_1 = __importDefault(require("request"));
const walletAddress_1 = require("../../../src/models/walletAddress");
const csp_1 = require("../../../src/modules/ripple/api/csp");
const block_1 = require("../../../src/modules/ripple/models/block");
const transaction_1 = require("../../../src/modules/ripple/models/transaction");
const rippletxs_fixture_1 = require("../../fixtures/rippletxs.fixture");
const helpers_1 = require("../../helpers");
const integration_1 = require("../../helpers/integration");
describe('Ripple Api', function () {
    // eslint-disable-next-line @typescript-eslint/no-this-alias
    const suite = this;
    const network = 'testnet';
    this.timeout(30000);
    before(integration_1.intBeforeHelper);
    after(async () => {
        await (0, integration_1.intAfterHelper)(suite);
        const client = await csp_1.XRP.getClient(network);
        client.rpc.disconnect();
    });
    beforeEach(async () => {
        await (0, helpers_1.resetDatabase)();
    });
    it('should be able to get the ledger', async () => {
        const client = await csp_1.XRP.getClient(network);
        const { ledger } = await client.getBlock();
        (0, chai_1.expect)(ledger).to.exist;
        (0, chai_1.expect)(ledger.ledger_hash).to.exist;
    });
    it('should be able to get local tip', async () => {
        const chain = 'XRP';
        await block_1.XrpBlockStorage.collection.insertOne({
            chain,
            network,
            height: 5,
            hash: '528f01c17829622ed6a4af51b3b3f6c062f304fa60e66499c9cbb8622c8407f7',
            time: new Date(1526326784),
            timeNormalized: new Date(1526326784),
            transactionCount: 1,
            reward: 50,
            previousBlockHash: '64bfb3eda276ae4ae5b64d9e36c9c0b629bc767fb7ae66f9d55d2c5c8103a929',
            nextBlockHash: '',
            size: 264,
            processed: true
        });
        const tip = await csp_1.XRP.getLocalTip({ chain, network });
        (0, chai_1.expect)(tip).to.exist;
        (0, chai_1.expect)(tip.hash).to.exist;
        (0, chai_1.expect)(tip.hash).to.eq('528f01c17829622ed6a4af51b3b3f6c062f304fa60e66499c9cbb8622c8407f7');
    });
    for (const tx of rippletxs_fixture_1.RippleTxs) {
        it('should transform a ripple rpc response into a bitcore transaction: ' + tx.hash, async () => {
            const bitcoreTx = await csp_1.XRP.transform(tx, 'testnet');
            (0, chai_1.expect)(bitcoreTx).to.have.property('chain');
            (0, chai_1.expect)(tx.Account).to.eq(bitcoreTx.from);
            (0, chai_1.expect)(tx.ledger_index).to.eq(bitcoreTx.blockHeight);
            (0, chai_1.expect)(tx.Fee).to.eq((bitcoreTx.fee).toString());
            const nodes = tx.meta.AffectedNodes.filter(node => 'ModifiedNode' in node && node.ModifiedNode.FinalFields?.Account == tx.Account);
            const sentVal = nodes.reduce((acc, node) => acc += 'ModifiedNode' in node ? Number(node.ModifiedNode.FinalFields?.Balance) - Number(node.ModifiedNode.PreviousFields?.Balance) : 0, 0);
            (0, chai_1.expect)(sentVal).to.be.lt(0);
            if (tx.meta.delivered_amount) {
                const modNodes = tx.meta.AffectedNodes.filter(n => 'ModifiedNode' in n && n.ModifiedNode.FinalFields?.Account === bitcoreTx.to);
                const createNodes = tx.meta.AffectedNodes.filter(n => 'CreatedNode' in n && n.CreatedNode.NewFields.Account === bitcoreTx.to);
                (0, chai_1.expect)(modNodes.length + createNodes.length > 0).to.equal(true);
                (0, chai_1.expect)(tx.meta.delivered_amount).to.eq(bitcoreTx.value.toString());
                let receivedVal = modNodes.reduce((acc, node) => acc += 'ModifiedNode' in node ? Number(node.ModifiedNode.FinalFields?.Balance) - Number(node.ModifiedNode.PreviousFields?.Balance) : 0, 0);
                receivedVal += createNodes.reduce((acc, node) => acc += 'CreatedNode' in node ? Number(node.CreatedNode.NewFields.Balance) : 0, 0);
                (0, chai_1.expect)(receivedVal).to.be.gt(0);
            }
        });
    }
    it('should tag txs from a wallet', async () => {
        const chain = 'XRP';
        const network = 'testnet';
        const wallet = new bson_1.ObjectId();
        const address = 'rN33DVnneYUUgTmcxXnXvgAL1BECuLZ8pm';
        await walletAddress_1.WalletAddressStorage.collection.insertOne({
            chain,
            network,
            wallet,
            address,
            processed: true
        });
        for (const tx of rippletxs_fixture_1.RippleTxs) {
            const bitcoreTx = (await csp_1.XRP.transform(tx, network));
            const bitcoreCoins = csp_1.XRP.transformToCoins(tx, network);
            const { transaction, coins } = await csp_1.XRP.tag(chain, network, bitcoreTx, bitcoreCoins);
            (0, chai_1.expect)(transaction.wallets.length).eq(1);
            (0, chai_1.expect)(transaction.wallets[0].equals(wallet));
            let hasACoin = false;
            for (const coin of coins) {
                if (coin.address == address) {
                    hasACoin = true;
                    (0, chai_1.expect)(coin.wallets.length).eq(1);
                    (0, chai_1.expect)(coin.wallets[0].equals(wallet));
                }
            }
            (0, chai_1.expect)(hasACoin).eq(true);
        }
    });
    it('should get sequence', async () => {
        const sequence = await csp_1.XRP.getAccountNonce(network, 'rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh');
        (0, chai_1.expect)(sequence).to.exist;
        (0, chai_1.expect)(sequence).to.be.a('number');
    });
    it('should get flags', async () => {
        const flags = await csp_1.XRP.getAccountFlags(network, 'rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh');
        (0, chai_1.expect)(flags).to.exist;
        (0, chai_1.expect)(flags).to.haveOwnProperty('requireDestinationTag');
    });
    it('should save tagged transactions to the database', async () => {
        const chain = 'XRP';
        const network = 'testnet';
        const wallet = new bson_1.ObjectId();
        const address = 'rN33DVnneYUUgTmcxXnXvgAL1BECuLZ8pm';
        await walletAddress_1.WalletAddressStorage.collection.insertOne({
            chain,
            network,
            wallet,
            address,
            processed: true
        });
        const blockTxs = new Array();
        const blockCoins = new Array();
        for (const tx of rippletxs_fixture_1.RippleTxs) {
            const bitcoreTx = csp_1.XRP.transform(tx, network);
            const bitcoreCoins = csp_1.XRP.transformToCoins(tx, network);
            const { transaction, coins } = await csp_1.XRP.tag(chain, network, bitcoreTx, bitcoreCoins);
            blockTxs.push(transaction);
            blockCoins.push(...coins);
        }
        await transaction_1.XrpTransactionStorage.batchImport({
            txs: blockTxs,
            coins: blockCoins,
            chain,
            network,
            initialSyncComplete: false
        });
        const walletTxs = await transaction_1.XrpTransactionStorage.collection.find({ chain, network, wallets: wallet }).toArray();
        (0, chai_1.expect)(walletTxs.length).eq(rippletxs_fixture_1.RippleTxs.length);
    });
    describe('getBlockBeforeTime', () => {
        // For these tests, we simulate ledgers with a 1 second interval, so the close_time is xrpEpoch + (ledger_index * 1000)
        const xrpEpoch = new Date('2000-01-01T00:00:00.000Z');
        const getCloseTime = (ledgerIndex) => new Date(xrpEpoch.getTime() + (ledgerIndex * 1000));
        const sandbox = sinon.createSandbox();
        let requestStub;
        const validBody = {
            result: {
                status: 'success',
                ledger: {
                    ledger_hash: 'abc123',
                    ledger_index: 12,
                    parent_hash: 'abc122',
                    close_time: getCloseTime(12).getTime() / 1000,
                    close_time_human: getCloseTime(12).toUTCString(),
                    closed: true
                }
            }
        };
        const invalidBody = { result: { status: 'error' } };
        let time;
        const _configBak = csp_1.XRP.config;
        before(async () => {
            await block_1.XrpBlockStorage.collection.deleteMany({});
        });
        beforeEach(async () => {
            requestStub = sandbox.stub(request_1.default, 'post');
            await block_1.XrpBlockStorage.collection.insertMany([{
                    chain: 'XRP',
                    network: 'testnet',
                    height: 12,
                    timeNormalized: getCloseTime(12),
                    hash: 'abc123',
                    time: getCloseTime(12),
                    transactionCount: 1,
                    reward: 50,
                    previousBlockHash: 'abc122',
                    nextBlockHash: 'abc124',
                    size: 264,
                    processed: true
                }, {
                    chain: 'XRP',
                    network: 'testnet',
                    height: 13,
                    timeNormalized: getCloseTime(13),
                    hash: 'abc124',
                    time: getCloseTime(13),
                    transactionCount: 1,
                    reward: 50,
                    previousBlockHash: 'abc123',
                    nextBlockHash: '',
                    size: 264,
                    processed: true
                }]);
            time = getCloseTime(12).toISOString();
        });
        afterEach(async () => {
            sandbox.restore();
            await block_1.XrpBlockStorage.collection.deleteMany({});
            csp_1.XRP.config = _configBak;
        });
        it('should return block', async () => {
            requestStub.callsFake(function (req, cb) {
                validBody.result.ledger.ledger_index = req.body.params[0].ledger_index;
                validBody.result.ledger.close_time = getCloseTime(req.body.params[0].ledger_index).getTime() / 1000;
                validBody.result.ledger.close_time_human = new Date(validBody.result.ledger.close_time * 1000).toUTCString();
                return cb(null, { body: validBody });
            });
            const res = await csp_1.XRP.getBlockBeforeTime({ chain: 'XRP', network: 'testnet', time });
            (0, chai_1.expect)(res).to.deep.equal({
                chain: 'XRP',
                network: 'testnet',
                hash: 'abc123',
                height: 12,
                previousBlockHash: 'abc122',
                processed: true,
                time: getCloseTime(12),
                timeNormalized: getCloseTime(12),
                reward: 0,
                size: 0,
                transactionCount: 0,
                nextBlockHash: ''
            });
        });
        it('should respond null if date is too early', async () => {
            const res = await csp_1.XRP.getBlockBeforeTime({ chain: 'XRP', network: 'testnet', time: getCloseTime(-1).toISOString() });
            (0, chai_1.expect)(res).to.be.null;
        });
        it('should resolve on empty response', async () => {
            requestStub.callsArgWith(1, null, null);
            const res = await csp_1.XRP.getBlockBeforeTime({ chain: 'XRP', network: 'testnet', time });
            (0, chai_1.expect)(res).to.be.null;
        });
        it('should throw on invalid time', async () => {
            try {
                await csp_1.XRP.getBlockBeforeTime({ chain: 'XRP', network: 'testnet', time: 'not-a-time' });
                throw new Error('should have thrown');
            }
            catch (err) {
                (0, chai_1.expect)(err.message).to.equal('Invalid time value');
            }
        });
        it('should throw on response error', async () => {
            requestStub.callsArgWith(1, 'Unresponsive server', validBody);
            try {
                await csp_1.XRP.getBlockBeforeTime({ chain: 'XRP', network: 'testnet', time });
                throw new Error('should have thrown');
            }
            catch (err) {
                (0, chai_1.expect)(err).to.equal('Unresponsive server');
            }
        });
        it('should return null on error response', async () => {
            requestStub.callsArgWith(1, null, invalidBody);
            const res = await csp_1.XRP.getBlockBeforeTime({ chain: 'XRP', network: 'testnet', time });
            (0, chai_1.expect)(res).to.be.null;
        });
        it('should throw on mis-configuration', async () => {
            requestStub.callsArgWith(1, null, validBody);
            csp_1.XRP.config = {};
            try {
                await csp_1.XRP.getBlockBeforeTime({ chain: 'XRP', network: 'testnet', time });
                throw new Error('should have thrown');
            }
            catch (err) {
                (0, chai_1.expect)(err.message).to.equal('Cannot read properties of undefined (reading \'provider\')');
            }
        });
    });
});
//# sourceMappingURL=csp.test.js.map