Skip to content
This repository has been archived by the owner on Jun 11, 2024. It is now read-only.

Commit

Permalink
Merge pull request #1779 from LiskHQ/1738-improve-gateway-test-suite
Browse files Browse the repository at this point in the history
Improve gateway test suite
  • Loading branch information
sameersubudhi authored Aug 9, 2023
2 parents 7c01a24 + 12550c9 commit 1fc240e
Show file tree
Hide file tree
Showing 107 changed files with 4,376 additions and 1,037 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ const config = require('../../../config');

const { getPosConstants } = require('./pos/constants');
const { getFeeEstimates } = require('./feeEstimates');
const regex = require('../../regex');

const SIZE_BYTE_SIGNATURE = 64;
const SIZE_BYTE_ID = 32;
Expand Down Expand Up @@ -214,6 +215,16 @@ const estimateTransactionFees = async params => {
meta: {},
};

// Test all regex
const { tokenID, recipientAddress } = params.transaction.params;
if (tokenID && !regex.TOKEN_ID.test(tokenID)) {
throw new ValidationException('Incorrect \'tokenID\' specified in transaction params.');
}

if (recipientAddress && !regex.ADDRESS_LISK32.test(recipientAddress)) {
throw new ValidationException('Incorrect \'recipientAddress\' specified in transaction params.');
}

const senderAddress = getLisk32AddressFromPublicKey(params.transaction.senderPublicKey);
const { data: authAccountInfo } = await getAuthAccountInfo({ address: senderAddress });

Expand Down
16 changes: 14 additions & 2 deletions services/blockchain-indexer/shared/dataService/transactions.js
Original file line number Diff line number Diff line change
Expand Up @@ -129,8 +129,20 @@ const initPendingTransactionsList = () => business.loadAllPendingTransactions();
const reload = () => business.loadAllPendingTransactions();

const dryRunTransactions = async params => {
const response = await business.dryRunTransactions(params);
return response;
try {
const response = await business.dryRunTransactions(params);
return response;
} catch (err) {
if (err.message.includes('ECONNREFUSED')) return {
data: { error: 'Unable to reach a network node.' },
status: 'INTERNAL_SERVER_ERROR',
};

return {
data: { error: `Failed to dry run transaction: ${err.message}.` },
status: 'BAD_REQUEST',
};
}
};

const estimateTransactionFees = async params => {
Expand Down
2 changes: 1 addition & 1 deletion services/blockchain-indexer/shared/utils/array.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ const sortComparator = (sortParam) => {
return comparator;
};

const isSubstringInArray = (collection, pattern) => collection.some((item) => item
const isSubstringInArray = (collection, pattern) => collection.some((item) => item && pattern
&& item.toLowerCase().includes(pattern.toLowerCase()));

module.exports = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -507,6 +507,46 @@ describe('Test transaction fees estimates', () => {
await expect(estimateTransactionFees(mockTxRequest)).rejects.toBeTruthy();
});

it('should throw Validation Exception when TOKEN_ID specified are incorrect', async () => {
// Mock the return values of the functions
getLisk32AddressFromPublicKey.mockReturnValue(mockTxsenderAddress);
getAuthAccountInfo.mockResolvedValue(mockTxAuthAccountInfo);
requestConnector
.mockReturnValueOnce(mockTxrequestConnector)
.mockReturnValue({ userAccount: '1', escrowAccount: '0', minFee: '130000', size: 160 });
getFeeEstimates.mockReturnValue(mockTxFeeEstimate);
calcAdditionalFees.mockResolvedValue({});
calcMessageFee.mockResolvedValue({});
getPosConstants.mockResolvedValue(posConstants);

const { estimateTransactionFees } = require(mockedTransactionFeeEstimatesFilePath);

mockTxRequest.transaction.params.tokenID = 'invalidTokenID';

// Call the function
await expect(estimateTransactionFees(mockTxRequest)).rejects.toBeTruthy();
});

it('should throw Validation Exception when address specified are incorrect', async () => {
// Mock the return values of the functions
getLisk32AddressFromPublicKey.mockReturnValue(mockTxsenderAddress);
getAuthAccountInfo.mockResolvedValue(mockTxAuthAccountInfo);
requestConnector
.mockReturnValueOnce(mockTxrequestConnector)
.mockReturnValue({ userAccount: '1', escrowAccount: '0', minFee: '130000', size: 160 });
getFeeEstimates.mockReturnValue(mockTxFeeEstimate);
calcAdditionalFees.mockResolvedValue({});
calcMessageFee.mockResolvedValue({});
getPosConstants.mockResolvedValue(posConstants);

const { estimateTransactionFees } = require(mockedTransactionFeeEstimatesFilePath);

mockTxRequest.transaction.params.recipientAddress = 'invalidAddress';

// Call the function
await expect(estimateTransactionFees(mockTxRequest)).rejects.toBeTruthy();
});

describe('Test estimateTransactionFees method for interoperability transactions', () => {
const transactionsMap = {
'interoperability:submitMainchainCrossChainUpdate': {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,12 @@
* Removal or modification of this copyright notice is prohibited.
*
*/
const { isIncludePendingTransactions } = require('../../../../shared/dataService/transactions');
/* eslint-disable import/no-dynamic-require */
const { resolve } = require('path');

// All file paths
const transactionsFilePath = resolve(`${__dirname}/../../../../shared/dataService/transactions`);
const mockBusinessFilePath = resolve(`${__dirname}/../../../../shared/dataService/business`);

// Mock KeyValueStore table
jest.mock('lisk-service-framework', () => {
Expand All @@ -35,28 +40,94 @@ jest.mock('lisk-service-framework', () => {
};
});

describe('dryRunTransactions', () => {
afterEach(() => {
jest.clearAllMocks();
jest.resetModules();
});

it('should return response on successful operation', async () => {
const mockResponse = { data: 'Success' };

const business = require(mockBusinessFilePath);
jest.mock(mockBusinessFilePath, () => ({
dryRunTransactions: jest.fn().mockResolvedValueOnce(mockResponse),
}));

const { dryRunTransactions } = require(transactionsFilePath);
const response = await dryRunTransactions({});

expect(response).toEqual(mockResponse);
expect(business.dryRunTransactions).toHaveBeenCalledWith({});
});

it('should return internal server error response on network error', async () => {
const mockError = new Error('ECONNREFUSED');
const expectedErrorResponse = {
data: { error: 'Unable to reach a network node.' },
status: 'INTERNAL_SERVER_ERROR',
};

const business = require(mockBusinessFilePath);
jest.mock(mockBusinessFilePath, () => ({
dryRunTransactions: jest.fn(() => {
throw new Error(mockError);
}),
}));

const { dryRunTransactions } = require(transactionsFilePath); // Update with the correct path
const response = await dryRunTransactions({});
expect(response).toEqual(expectedErrorResponse);
expect(business.dryRunTransactions).toHaveBeenCalledWith({});
});

it('should return bad request error response on other error', async () => {
const mockError = new Error('Some other error');
const expectedErrorResponse = {
data: { error: `Failed to dry run transaction: Error: ${mockError.message}.` },
status: 'BAD_REQUEST',
};

const business = require(mockBusinessFilePath);
jest.mock(mockBusinessFilePath, () => ({
dryRunTransactions: jest.fn(() => {
throw new Error(mockError);
}),
}));

const { dryRunTransactions } = require(transactionsFilePath); // Update with the correct path
const response = await dryRunTransactions({});
expect(response).toEqual(expectedErrorResponse);
expect(business.dryRunTransactions).toHaveBeenCalledWith({});
});
});

describe('Test isIncludePendingTransactions method', () => {
it('should return true when called with pending execution status', async () => {
const executionStatus = 'pending,success';
const { isIncludePendingTransactions } = require(transactionsFilePath);
const result = isIncludePendingTransactions(executionStatus);
expect(typeof result).toBe('boolean');
expect(result).toBe(true);
});

it('should return false when called without pending execution status', async () => {
const executionStatus = 'success,fail';
const { isIncludePendingTransactions } = require(transactionsFilePath);
const result = isIncludePendingTransactions(executionStatus);
expect(typeof result).toBe('boolean');
expect(result).toBe(false);
});

it('should return false when called with undefined', async () => {
const { isIncludePendingTransactions } = require(transactionsFilePath);
const result = isIncludePendingTransactions(undefined);
expect(typeof result).toBe('boolean');
expect(result).toBe(false);
});

it('should return false when called with null', async () => {
const { isIncludePendingTransactions } = require(transactionsFilePath);
const result = isIncludePendingTransactions(null);
expect(typeof result).toBe('boolean');
expect(result).toBe(false);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,7 @@ describe('Unit tests for array utilities', () => {
const collection = ['apple', 'banana', 'cherry'];
const pattern = '';

expect(isSubstringInArray(collection, pattern)).toBe(true);
expect(isSubstringInArray(collection, pattern)).toBe(false);
});
});
});
2 changes: 1 addition & 1 deletion services/gateway/apis/http-version3/methods/blocks.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ module.exports = {
blockID: { optional: true, type: 'string', min: 1, max: 64, pattern: regex.BLOCK_ID },
height: { optional: true, type: 'string', min: 0, pattern: regex.HEIGHT_RANGE },
timestamp: { optional: true, type: 'string', min: 1, pattern: regex.TIMESTAMP_RANGE },
generatorAddress: { optional: true, type: 'string', min: 38, max: 41, pattern: regex.ADDRESS_LISK32 },
generatorAddress: { optional: true, type: 'string', min: 41, max: 41, pattern: regex.ADDRESS_LISK32 },
limit: { optional: true, type: 'number', min: 1, max: 100, default: 10, pattern: regex.LIMIT },
offset: { optional: true, type: 'number', min: 0, default: 0, pattern: regex.OFFSET },
sort: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
*/
const dryRunTransactionsSource = require('../../../sources/version3/transactionsDryRun');
const envelope = require('../../../sources/version3/mappings/stdEnvelope');
const regex = require('../../../shared/regex');
const { getSwaggerDescription } = require('../../../shared/utils');

module.exports = {
Expand All @@ -30,14 +31,14 @@ module.exports = {
optional: false,
type: 'object',
props: {
id: { type: 'string' },
module: { type: 'string' },
command: { type: 'string' },
fee: { type: 'string' },
nonce: { type: 'string' },
senderPublicKey: { type: 'string' },
signatures: { type: 'array', items: 'string' },
params: { type: 'object' },
id: { type: 'string', pattern: regex.HASH_SHA256 },
module: { type: 'string', pattern: regex.MODULE },
command: { type: 'string', pattern: regex.COMMAND },
fee: { type: 'string', pattern: regex.FEE },
nonce: { type: 'string', min: 1, pattern: regex.NONCE },
senderPublicKey: { type: 'string', pattern: regex.PUBLIC_KEY },
signatures: { type: 'array', optional: true, min: 0, items: { type: 'string', pattern: regex.HASH_SHA512 } },
params: { type: 'object', optional: false, minProps: 1 },
},
},
],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,9 @@ module.exports = {
props: {
module: { optional: false, type: 'string', pattern: regex.MODULE },
command: { optional: false, type: 'string', pattern: regex.COMMAND },
fee: { optional: true, type: 'string', pattern: regex.FEE },
nonce: { optional: false, type: 'string', pattern: regex.NONCE },
senderPublicKey: { optional: false, type: 'string', pattern: regex.PUBLIC_KEY },
signatures: { optional: true, type: 'array', min: 0, items: { type: 'string', pattern: regex.HASH_SHA512 } },
signatures: { optional: true, type: 'array', min: 1, items: { type: 'string', pattern: regex.HASH_SHA512 } },
params: { optional: false, type: 'object' },
},
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -595,6 +595,17 @@
"example": "a3f96c50d0446220ef2f98240898515cbba8155730679ca35326d98dcfb680f0",
"description": "The public key is derived from the private key of the owner of the account.\nIt can be used to validate that the private key belongs to the owner, but not provide access to the owner's private key."
},
"signatures": {
"type": "array",
"description": "An array representing signature(s) of the transaction sender.",
"minItems": 1,
"items": {
"type": "string"
},
"example": [
"48425002226745847e155cf5480478c2336a43bb178439e9058cc2b50e26335cf7c8360b6c6a49793d7ae8d087bc746cab9618655e6a0adba4694cce2015b50f"
]
},
"params": {
"type": "object",
"properties": {
Expand Down
5 changes: 3 additions & 2 deletions services/gateway/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,12 @@
*
*/
const path = require('path');
const { MoleculerError, MoleculerServerError } = require('moleculer').Errors;
const {
Microservice,
Logger,
LoggerConfig,
Libs,
Exceptions: { ValidationException },
} = require('lisk-service-framework');

const config = require('./config');
Expand Down Expand Up @@ -142,7 +142,8 @@ tempApp.run().then(async () => {
},

onError(req, res, err) {
if (err instanceof ValidationException === false) {
if (err instanceof MoleculerError === false
&& err instanceof MoleculerServerError === false) {
res.setHeader('Content-Type', 'application/json');
res.writeHead(err.code || 500);
res.end(JSON.stringify({
Expand Down
1 change: 1 addition & 0 deletions services/gateway/shared/registerRpcApi.js
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@ const registerApi = (apiNames, config, registeredModuleNames) => {
if (data.data && data.status) {
if (data.status === 'INVALID_PARAMS') throw new MoleculerClientError({ code: INVALID_PARAMS[0], message: data.data.error });
if (data.status === 'SERVICE_UNAVAILABLE') throw new MoleculerClientError({ code: SERVICE_UNAVAILABLE[0], message: data.data.error });
if (data.status !== 'ACCEPTED') throw new MoleculerClientError({ code: INVALID_REQUEST[0], message: data.data.error });
}

return transformResponse(methodPaths[req.method], data);
Expand Down
Loading

0 comments on commit 1fc240e

Please sign in to comment.