UNPKG

stellar-plus

Version:

beta version of stellar-plus, an all-in-one sdk for the Stellar blockchain

363 lines (362 loc) 19 kB
"use strict"; /* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable @typescript-eslint/no-unsafe-return */ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ Object.defineProperty(exports, "__esModule", { value: true }); const tslib_1 = require("tslib"); const classic_1 = require("../../../stellar-plus/asset/classic"); const errors_1 = require("../../../stellar-plus/asset/classic/errors"); const types_1 = require("../../../stellar-plus/core/pipelines/build-transaction/types"); const types_2 = require("../../../stellar-plus/core/pipelines/classic-transaction/types"); const horizon_1 = require("../../../stellar-plus/horizon"); const network_1 = require("../../../stellar-plus/network"); const transaction_mock_1 = require("../../../stellar-plus/test/mocks/transaction-mock"); jest.mock('@stellar/stellar-sdk', () => { // The mock doesnt spread the whole originalModule because some internal exported objects cause failures // so we just unmock the necessary items. // uncomment and use the following line if you need to check the contents of the module: // const originalModule: typeof import('@stellar/stellar-sdk') = jest.requireActual('@stellar/stellar-sdk') const originalModule = jest.requireActual('@stellar/stellar-sdk'); return { xdr: originalModule.xdr, Asset: originalModule.Asset, TransactionBuilder: originalModule.TransactionBuilder, Operation: { payment: jest.fn().mockReturnValue('paymentOp'), changeTrust: jest.fn().mockReturnValue('changeTrustOp'), }, }; }); jest.mock('stellar-plus/horizon', () => ({ HorizonHandlerClient: jest.fn().mockImplementation(() => ({ account: jest.fn(), loadAccount: jest.fn(), })), })); const MOCKED_HORIZON_HANDLER = horizon_1.HorizonHandlerClient; const TESTNET_CONFIG = (0, network_1.TestNet)(); const MOCKED_PK = 'GACF23GKVFTU77K6W6PWSVN7YBM63UHDULILIEXJO6FR4YKMJ7FW3DTI'; const MOCKED_PK_B = 'GBCBCTQ6YH3XFYDDGARNGYSS2LGTX5CA6P3P2K6ODSRNBKKK7BWMEEVM'; describe('Classic Asset Handler', () => { describe('Initialization', () => { it('should be able to create a new instance with just the asset parameters', () => { const asset = new classic_1.ClassicAssetHandler({ code: 'CAKE', issuerAccount: MOCKED_PK, networkConfig: TESTNET_CONFIG, }); expect(asset).toBeDefined(); expect(asset).toBeInstanceOf(classic_1.ClassicAssetHandler); expect(asset.code).toBe('CAKE'); expect(asset.issuerPublicKey).toBe(MOCKED_PK); }); it('should be able to create a new instance with the issuer account handler', () => { const mockedIssuerAccountHandler = (0, transaction_mock_1.mockAccountHandler)({ accountKey: MOCKED_PK }); const asset = new classic_1.ClassicAssetHandler({ code: 'CAKE', issuerAccount: mockedIssuerAccountHandler, networkConfig: TESTNET_CONFIG, }); expect(asset).toBeDefined(); expect(asset).toBeInstanceOf(classic_1.ClassicAssetHandler); expect(asset.code).toBe('CAKE'); expect(asset.issuerPublicKey).toBe(MOCKED_PK); }); it('should initialize the type as credit_alphanum4 based on the code length', () => { const asset = new classic_1.ClassicAssetHandler({ code: 'CAKE', issuerAccount: MOCKED_PK, networkConfig: TESTNET_CONFIG, }); expect(asset.type).toBe('credit_alphanum4'); }); it('should initialize the type as credit_alphanum12 based on the code length', () => { const asset = new classic_1.ClassicAssetHandler({ code: 'CAKECAKECAKE', issuerAccount: MOCKED_PK, networkConfig: TESTNET_CONFIG, }); expect(asset.type).toBe('credit_alphanum12'); }); it('should initialize the type as native if the code is XLM as has no issuer', () => { const asset = new classic_1.ClassicAssetHandler({ code: 'XLM', networkConfig: TESTNET_CONFIG, }); expect(asset.type).toBe('native'); }); it('should initialize the type as credit_alphanum4 if the code is XLM as has an issuer', () => { const asset = new classic_1.ClassicAssetHandler({ code: 'XLM', issuerAccount: MOCKED_PK, networkConfig: TESTNET_CONFIG, }); expect(asset.type).toBe('credit_alphanum4'); }); it('should accept options for the Classic Transaction pipeline', () => { const mockedPlugin = jest.mocked({ preProcess: jest.fn(), postProcess: jest.fn(), type: types_2.ClassicTransactionPipelineType.id, }); const mockedInnerPlugin = jest.mocked({ preProcess: jest.fn(), postProcess: jest.fn(), type: types_1.BuildTransactionPipelineType.id, }); const asset = new classic_1.ClassicAssetHandler({ code: 'CAKE', issuerAccount: MOCKED_PK, networkConfig: TESTNET_CONFIG, options: { classicTransactionPipeline: { plugins: [mockedPlugin, mockedInnerPlugin], }, }, }); const spyPipeline = jest.mocked(asset.classicTransactionPipeline); expect(asset).toBeDefined(); expect(asset).toBeInstanceOf(classic_1.ClassicAssetHandler); expect(spyPipeline.plugins).toContain(mockedPlugin); expect(spyPipeline.plugins).not.toContain(mockedInnerPlugin); expect(spyPipeline.innerPlugins).toContain(mockedInnerPlugin); expect(spyPipeline.innerPlugins).not.toContain(mockedPlugin); }); }); describe('Core Functionalities', () => { let asset; beforeEach(() => { jest.clearAllMocks(); const mockedIssuerAccountHandler = (0, transaction_mock_1.mockAccountHandler)({ accountKey: MOCKED_PK }); asset = new classic_1.ClassicAssetHandler({ code: 'CAKE', issuerAccount: mockedIssuerAccountHandler, networkConfig: TESTNET_CONFIG, }); }); it('should be able to get the asset symbol', () => tslib_1.__awaiter(void 0, void 0, void 0, function* () { expect(asset.symbol()).resolves.toBe('CAKE'); })); it('should be able to get the decimals', () => tslib_1.__awaiter(void 0, void 0, void 0, function* () { expect(asset.decimals()).resolves.toBe(7); // Currently fixed for classic assets })); it('should be able to get the asset name', () => tslib_1.__awaiter(void 0, void 0, void 0, function* () { expect(asset.name()).resolves.toBe('CAKE'); // Currently defaults to code for classic assets })); it('should be able to get the balance of an account for this asset', () => tslib_1.__awaiter(void 0, void 0, void 0, function* () { const mockedBalance = '100.0000000'; MOCKED_HORIZON_HANDLER.mockImplementationOnce(() => ({ loadAccount: jest.fn().mockImplementation(() => { return { balances: [ { asset_code: 'CAKE', asset_issuer: MOCKED_PK, balance: mockedBalance, asset_type: 'credit_alphanum4', }, { asset_code: 'CAKECAKECAKE', asset_issuer: MOCKED_PK, balance: '250', asset_type: 'credit_alphanum12', }, ], }; }), })); const asset = new classic_1.ClassicAssetHandler({ code: 'CAKE', issuerAccount: MOCKED_PK, networkConfig: TESTNET_CONFIG, }); expect(asset.balance(MOCKED_PK)).resolves.toBe(Number(mockedBalance)); })); it('should be able to get the balance of an account for this asset if it is native', () => tslib_1.__awaiter(void 0, void 0, void 0, function* () { const mockedBalance = '100.0000000'; MOCKED_HORIZON_HANDLER.mockImplementationOnce(() => ({ loadAccount: jest.fn().mockImplementation(() => { return { balances: [ { asset_code: 'XLM', balance: mockedBalance, asset_type: 'native', }, { asset_code: 'CAKECAKECAKE', asset_issuer: MOCKED_PK, balance: '250', asset_type: 'credit_alphanum12', }, ], }; }), })); const asset = new classic_1.ClassicAssetHandler({ code: 'XLM', networkConfig: TESTNET_CONFIG, }); expect(asset.balance(MOCKED_PK)).resolves.toBe(Number(mockedBalance)); })); it('should return 0 when there is no balance for an account for this asset', () => tslib_1.__awaiter(void 0, void 0, void 0, function* () { MOCKED_HORIZON_HANDLER.mockImplementationOnce(() => ({ loadAccount: jest.fn().mockImplementation(() => { return { balances: [ { asset_code: 'CAKECAKECAKE', asset_issuer: MOCKED_PK, balance: '250', asset_type: 'credit_alphanum12', }, ], }; }), })); const asset = new classic_1.ClassicAssetHandler({ code: 'CAKE', issuerAccount: MOCKED_PK, networkConfig: TESTNET_CONFIG, }); expect(asset.balance(MOCKED_PK)).resolves.toBe(0); })); it('should be able to perform a transfer operation for a given account', () => tslib_1.__awaiter(void 0, void 0, void 0, function* () { const mockedAccountHandler = (0, transaction_mock_1.mockAccountHandler)({ accountKey: MOCKED_PK }); const sender = MOCKED_PK; const receiver = MOCKED_PK_B; const asset = new classic_1.ClassicAssetHandler({ code: 'CAKE', issuerAccount: MOCKED_PK, networkConfig: TESTNET_CONFIG, }); const mockedTxInvocation = { header: { source: MOCKED_PK, fee: '100', timeout: 45 }, signers: [mockedAccountHandler], }; const spyExecute = jest.spyOn(asset.classicTransactionPipeline, 'execute').mockResolvedValue({}); const args = Object.assign({ from: sender, to: receiver, amount: 120 }, mockedTxInvocation); yield asset.transfer(args); expect(spyExecute).toHaveBeenCalledExactlyOnceWith({ txInvocation: args, operations: ['paymentOp'], options: {}, }); })); it('should be able to perform a burn operation for a given account', () => tslib_1.__awaiter(void 0, void 0, void 0, function* () { const mockedAccountHandler = (0, transaction_mock_1.mockAccountHandler)({ accountKey: MOCKED_PK }); const sender = MOCKED_PK; const issuer = MOCKED_PK_B; const asset = new classic_1.ClassicAssetHandler({ code: 'CAKE', issuerAccount: issuer, networkConfig: TESTNET_CONFIG, }); const mockedTxInvocation = { header: { source: MOCKED_PK, fee: '100', timeout: 45 }, signers: [mockedAccountHandler], }; const spyTransfer = jest.spyOn(asset, 'transfer').mockResolvedValue({}); const args = Object.assign({ from: sender, amount: 120 }, mockedTxInvocation); yield asset.burn(args); expect(spyTransfer).toHaveBeenCalledExactlyOnceWith(Object.assign(Object.assign({}, args), { to: issuer })); })); it('should be able to perform a mint operation to a given account, adding the issuer as signer', () => tslib_1.__awaiter(void 0, void 0, void 0, function* () { const mockedIssuerAccountHandler = (0, transaction_mock_1.mockAccountHandler)({ accountKey: MOCKED_PK }); const mockedUserAccountHandler = (0, transaction_mock_1.mockAccountHandler)({ accountKey: MOCKED_PK_B }); const asset = new classic_1.ClassicAssetHandler({ code: 'CAKE', issuerAccount: mockedIssuerAccountHandler, networkConfig: TESTNET_CONFIG, }); const mockedTxInvocation = { header: { source: mockedUserAccountHandler.getPublicKey(), fee: '100', timeout: 45 }, signers: [mockedUserAccountHandler], }; const spyExecute = jest.spyOn(asset.classicTransactionPipeline, 'execute').mockResolvedValue({}); const args = Object.assign({ to: mockedUserAccountHandler.getPublicKey(), amount: 120 }, mockedTxInvocation); yield asset.mint(args); expect(spyExecute).toHaveBeenCalledExactlyOnceWith({ txInvocation: Object.assign(Object.assign({}, args), { signers: [mockedUserAccountHandler, mockedIssuerAccountHandler] }), operations: ['paymentOp'], options: {}, }); })); it('should be able to perform a transaction to add trustline for a given account', () => tslib_1.__awaiter(void 0, void 0, void 0, function* () { const mockedAccountHandler = (0, transaction_mock_1.mockAccountHandler)({ accountKey: MOCKED_PK }); const asset = new classic_1.ClassicAssetHandler({ code: 'CAKE', issuerAccount: MOCKED_PK, networkConfig: TESTNET_CONFIG, }); const mockedTxInvocation = { header: { source: MOCKED_PK, fee: '100', timeout: 45 }, signers: [mockedAccountHandler], }; const spyExecute = jest.spyOn(asset.classicTransactionPipeline, 'execute').mockResolvedValue({}); const args = Object.assign({ to: mockedAccountHandler.getPublicKey() }, mockedTxInvocation); yield asset.addTrustline(args); expect(spyExecute).toHaveBeenCalledExactlyOnceWith({ txInvocation: args, operations: ['changeTrustOp'], options: {}, }); })); it('should be able to perform a transaction to add trustline and mint operation to a given account, adding the issuer as signer', () => tslib_1.__awaiter(void 0, void 0, void 0, function* () { const mockedIssuerAccountHandler = (0, transaction_mock_1.mockAccountHandler)({ accountKey: MOCKED_PK }); const mockedUserAccountHandler = (0, transaction_mock_1.mockAccountHandler)({ accountKey: MOCKED_PK_B }); const asset = new classic_1.ClassicAssetHandler({ code: 'CAKE', issuerAccount: mockedIssuerAccountHandler, networkConfig: TESTNET_CONFIG, }); const mockedTxInvocation = { header: { source: mockedUserAccountHandler.getPublicKey(), fee: '100', timeout: 45 }, signers: [mockedUserAccountHandler], }; const spyExecute = jest.spyOn(asset.classicTransactionPipeline, 'execute').mockResolvedValue({}); const args = Object.assign({ to: mockedUserAccountHandler.getPublicKey(), amount: 120 }, mockedTxInvocation); yield asset.addTrustlineAndMint(args); expect(spyExecute).toHaveBeenCalledExactlyOnceWith({ txInvocation: Object.assign(Object.assign({}, args), { signers: [mockedUserAccountHandler, mockedIssuerAccountHandler] }), operations: ['changeTrustOp', 'paymentOp'], options: {}, }); })); }); describe('Error Handling', () => { let asset; beforeEach(() => { jest.clearAllMocks(); const mockedIssuerAccountHandler = (0, transaction_mock_1.mockAccountHandler)({ accountKey: MOCKED_PK }); asset = new classic_1.ClassicAssetHandler({ code: 'CAKE', issuerAccount: mockedIssuerAccountHandler, networkConfig: TESTNET_CONFIG, }); }); it('should throw an error if trying to approve an account', () => tslib_1.__awaiter(void 0, void 0, void 0, function* () { //Not implemented yet expect(asset.approve()).rejects.toThrow('Method not implemented.'); })); it('should throw an error if trying to clawback from an account', () => tslib_1.__awaiter(void 0, void 0, void 0, function* () { //Not implemented yet expect(asset.clawback()).rejects.toThrow('Method not implemented.'); })); it('should throw an error if the function invoked require an issuer handler and it is missing', () => tslib_1.__awaiter(void 0, void 0, void 0, function* () { const asset = new classic_1.ClassicAssetHandler({ code: 'CAKE', issuerAccount: MOCKED_PK, networkConfig: TESTNET_CONFIG, }); const mockedTxInvocation = { header: { source: MOCKED_PK_B, fee: '100', timeout: 45 }, signers: [(0, transaction_mock_1.mockAccountHandler)({ accountKey: MOCKED_PK_B })], }; const spyExecute = jest.spyOn(asset.classicTransactionPipeline, 'execute').mockResolvedValue({}); const args = Object.assign({ to: MOCKED_PK_B, amount: 120 }, mockedTxInvocation); yield expect(asset.addTrustlineAndMint(args)).rejects.toThrow(errors_1.CAHError.issuerAccountNotDefined()); expect(spyExecute).not.toHaveBeenCalled(); })); }); });