stellar-plus
Version:
beta version of stellar-plus, an all-in-one sdk for the Stellar blockchain
363 lines (362 loc) • 19 kB
JavaScript
"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();
}));
});
});