redis mock class

code cleanup

created fabric.spec

redis testcases and additional checks

Signed-off-by: sapthasurendran <saptha.surendran@ibm.com>
This commit is contained in:
sapthasurendran 2021-08-09 11:38:21 +05:30 committed by James Taylor
parent f904adbf6f
commit 6477333743
7 changed files with 231 additions and 107 deletions

View file

@ -1215,9 +1215,9 @@
}
},
"@types/ioredis": {
"version": "4.26.4",
"resolved": "https://registry.npmjs.org/@types/ioredis/-/ioredis-4.26.4.tgz",
"integrity": "sha512-QFbjNq7EnOGw6d1gZZt2h26OFXjx7z+eqEnbCHSrDI1OOLEgOHMKdtIajJbuCr9uO+X9kQQRe7Lz6uxqxl5XKg==",
"version": "4.26.6",
"resolved": "https://registry.npmjs.org/@types/ioredis/-/ioredis-4.26.6.tgz",
"integrity": "sha512-Q9ydXL/5Mot751i7WLCm9OGTj5jlW3XBdkdEW21SkXZ8Y03srbkluFGbM3q8c+vzPW30JOLJ+NsZWHoly0+13A==",
"dev": true,
"requires": {
"@types/node": "*"

View file

@ -0,0 +1,21 @@
import { RedisOptions } from 'ioredis';
class IORedis {
redisOptions: RedisOptions;
constructor(options: RedisOptions) {
this.redisOptions = options;
}
hincrby = jest.fn().mockReturnThis();
multi = jest.fn().mockReturnThis();
del = jest.fn().mockReturnThis();
zrem = jest.fn().mockReturnThis();
exec = jest.fn().mockReturnThis();
hset = jest.fn().mockReturnThis();
zadd = jest.fn().mockReturnThis();
}
export default IORedis;

View file

@ -1,83 +0,0 @@
import { retryTransaction } from '../fabric';
import { getMockedNetwork } from '../__mocks__/fabric-network';
import { Redis } from 'ioredis';
import * as redis from '../redis';
// import { Gateway, Gateway, Gateway } from 'fabric-network';
/**
* retryTransaction
*/
jest.mock('../config');
describe('Testing retryTransaction', () => {
let contract: any = null;
const transaction = {
submit: jest.fn().mockRejectedValue({}),
};
const mockedContact = {
deserializeTransaction: jest.fn().mockReturnValue(transaction),
};
beforeAll(async () => {
const rejectableGetContract = jest
.fn()
.mockImplementation(() => mockedContact);
const network = getMockedNetwork(rejectableGetContract)('');
contract = (await network).getContract('');
});
describe('Check retry increment ', () => {
const transactionId =
'0ae62c01e4c4b112c3f3954a2f11243da76778e46df9ad2783bcbafc79652b95';
const key = `txn:${transactionId}`;
const state = `{"name":"CreateAsset","nonce":"damqinq8nrI4n4qY8lFVsZw7RwG2ufrv","transactionId":${transactionId}`;
const args = '["test111","red",400,"Jean",101]';
const timestamp = 1628078044362;
const savedTransaction = {
timestamp: timestamp.toString(),
state: state,
retries: '',
args: args,
};
let data: Record<string, any> = {};
beforeEach(() => {
data = {};
const clearTransactionDetails = jest.spyOn(
redis,
'clearTransactionDetails'
);
clearTransactionDetails.mockImplementation(
async (redis: Redis, transactionId: string) => {
const key = `txn:${transactionId}`;
delete data[key];
}
);
const incrementRetryCount = jest.spyOn(redis, 'incrementRetryCount');
incrementRetryCount.mockImplementation(
async (redis: Redis, transactionId: string) => {
const key = `txn:${transactionId}`;
data[key].retries = (parseInt(data[key].retries) + 1).toString();
}
);
});
it('retry count should incremnt to 4', async () => {
savedTransaction.retries = '3';
data = { [key]: savedTransaction };
await retryTransaction(
contract,
redis.redis,
transactionId,
savedTransaction
);
expect(data[key]).toMatchObject({
timestamp: timestamp.toString(),
state: state,
retries: '4',
args: args,
});
});
});
});

View file

@ -0,0 +1,94 @@
import { retryTransaction, getGateway } from './fabric';
import { getMockedNetwork } from './__mocks__/fabric-network';
import * as config from './config';
import IORedis from './__mocks__/IORedis';
import { Redis } from 'ioredis';
import { Contract } from 'fabric-network';
jest.mock('./config');
jest.mock('ioredis');
const redisOptions = {
port: config.redisPort,
host: config.redisHost,
username: config.redisUsername,
password: config.redisPassword,
};
const redis = new IORedis(redisOptions) as unknown as Redis;
describe('Testing retryTransaction', () => {
const transactionId =
'0ae62c01e4c4b112c3f3954a2f11243da76778e46df9ad2783bcbafc79652b95';
const state = `{"name":"CreateAsset","nonce":"damqinq8nrI4n4qY8lFVsZw7RwG2ufrv","transactionId":${transactionId}`;
const args = '["test111","red",400,"Jean",101]';
const timestamp = 1628078044362;
const savedTransaction = {
timestamp: timestamp.toString(),
state: state,
retries: '',
args: args,
};
describe('Check retry increment ', () => {
const transactionId =
'0ae62c01e4c4b112c3f3954a2f11243da76778e46df9ad2783bcbafc79652b95';
const state = `{"name":"CreateAsset","nonce":"damqinq8nrI4n4qY8lFVsZw7RwG2ufrv","transactionId":${transactionId}`;
const args = '["test111","red",400,"Jean",101]';
const timestamp = 1628078044362;
const savedTransaction = {
timestamp: timestamp.toString(),
state: state,
retries: '',
args: args,
};
it('Transaction failure, check redis increment func call', async () => {
jest.doMock('fabric-network');
const transaction = {
submit: jest.fn().mockRejectedValue({}),
};
const mockedContact = {
deserializeTransaction: jest.fn().mockReturnValue(transaction),
};
const rejectableGetContract = jest
.fn()
.mockImplementation(() => mockedContact);
const network = getMockedNetwork(rejectableGetContract)('');
const contract: Contract = (await network).getContract('');
savedTransaction.retries = '3';
await retryTransaction(contract, redis, transactionId, savedTransaction);
expect(redis.hincrby).toHaveBeenCalledTimes(1);
});
});
describe('Transaction successful, check redis delete key func call ', () => {
it('call redis increment', async () => {
jest.doMock('fabric-network');
const transaction = {
submit: jest.fn().mockResolvedValue({}),
};
const mockedContact = {
deserializeTransaction: jest.fn().mockReturnValue(transaction),
};
const resolvableGetContract = jest
.fn()
.mockImplementation(() => mockedContact);
const network = getMockedNetwork(resolvableGetContract)('');
const contract: Contract = (await network).getContract('');
savedTransaction.retries = '3';
await retryTransaction(contract, redis, transactionId, savedTransaction);
expect(redis.del).toHaveBeenCalledTimes(1);
});
});
});
describe('Test getGateway', () => {
it('should throw error for invalid org name', async () => {
expect(async () => await getGateway('')).rejects.toThrow(
'Invalid org name for gateway'
);
});
});

View file

@ -67,6 +67,9 @@ const FabricDataMapper: { [key: string]: FabricConfigType } = {
export const getGateway = async (org: string): Promise<Gateway> => {
const fabricConfig = FabricDataMapper[org];
if (fabricConfig == undefined) {
throw new Error('Invalid org name for gateway');
}
logger.debug('Configuring fabric gateway for %s', org);
const wallet = await Wallets.newInMemoryWallet();
@ -78,8 +81,8 @@ export const getGateway = async (org: string): Promise<Gateway> => {
mspId: fabricConfig.mspId,
type: 'X.509',
};
await wallet.put(fabricConfig.identityName, x509Identity);
await wallet.put(fabricConfig.identityName, x509Identity);
const gateway = new Gateway();
const connectOptions: GatewayOptions = {

View file

@ -0,0 +1,75 @@
import IORedis from './__mocks__/IORedis';
import * as config from './config';
import { Redis } from 'ioredis';
import {
clearTransactionDetails,
incrementRetryCount,
storeTransactionDetails,
} from './redis';
jest.mock('ioredis');
jest.mock('./config');
const redisOptions = {
port: config.redisPort,
host: config.redisHost,
username: config.redisUsername,
password: config.redisPassword,
};
const redis = new IORedis(redisOptions) as unknown as Redis;
describe('Testing increment retries ', () => {
const transactionId =
'0ae62c01e4c4b112c3f3954a2f11243da76778e46df9ad2783bcbafc79652b95';
it('Should increment retries for valid transction id', async () => {
await incrementRetryCount(redis, transactionId);
expect(redis.hincrby).toHaveBeenCalledTimes(1);
});
it('Should not increment retries for empty transaction id ', async () => {
await incrementRetryCount(redis, '');
expect(redis.hincrby).toHaveBeenCalledTimes(0);
});
});
describe('Testing storeTransactionDetails ', () => {
const args = '["test111","red",400,"Jean",101]';
const timestamp = 1628078044362;
it('Should store details for valid transction Id', async () => {
const transactionId =
'0ae62c01e4c4b112c3f3954a2f11243da76778e46df9ad2783bcbafc79652b95';
const state = `{"name":"CreateAsset","nonce":"damqinq8nrI4n4qY8lFVsZw7RwG2ufrv","transactionId":${transactionId}`;
await storeTransactionDetails(
redis,
transactionId,
Buffer.from(state),
args,
timestamp
);
expect(redis.hset).toHaveBeenCalledTimes(1);
expect(redis.zadd).toHaveBeenCalledTimes(1);
});
it('Should not store details for empty transction Id', async () => {
const transactionId = '';
const state = `{"name":"CreateAsset","nonce":"damqinq8nrI4n4qY8lFVsZw7RwG2ufrv","transactionId":${transactionId}`;
await storeTransactionDetails(
redis,
transactionId,
Buffer.from(state),
args,
timestamp
);
expect(redis.hset).toHaveBeenCalledTimes(0);
expect(redis.zadd).toHaveBeenCalledTimes(0);
});
});
describe('Testing clearTransactionDetails ', () => {
it('Should clear details ', async () => {
const transactionId =
'0ae62c01e4c4b112c3f3954a2f11243da76778e46df9ad2783bcbafc79652b95';
await clearTransactionDetails(redis, transactionId);
expect(redis.del).toHaveBeenCalledTimes(1);
expect(redis.zrem).toHaveBeenCalledTimes(1);
});
});

View file

@ -23,29 +23,40 @@ export const storeTransactionDetails = async (
transactionArgs: string,
timestamp: number
): Promise<void> => {
const key = `txn:${transactionId}`;
logger.debug(
'Storing transaction details. Key: %s State: %s Args: %s Timestamp: %d',
key,
transactionState,
transactionArgs,
timestamp
);
await redis
.multi()
.hset(
try {
if (transactionId.length === 0) {
throw new Error('Empty transaction Id found');
}
const key = `txn:${transactionId}`;
logger.debug(
'Storing transaction details. Key: %s State: %s Args: %s Timestamp: %d',
key,
'state',
transactionState,
'args',
transactionArgs,
'timestamp',
timestamp,
'retries',
'0'
)
.zadd('index:txn:timestamp', timestamp, transactionId)
.exec();
timestamp
);
await redis
.multi()
.hset(
key,
'state',
transactionState,
'args',
transactionArgs,
'timestamp',
timestamp,
'retries',
'0'
)
.zadd('index:txn:timestamp', timestamp, transactionId)
.exec();
} catch (err) {
logger.error(
err,
'Error storing transaction details. ID %s',
transactionId
);
}
};
export const clearTransactionDetails = async (
@ -78,6 +89,9 @@ export const incrementRetryCount = async (
const key = `txn:${transactionId}`;
logger.debug('Incrementing retries fortransaction Key: %s', key);
try {
if (transactionId.length === 0) {
throw new Error('Empty transaction Id found');
}
await (redis as Redis).hincrby(`txn:${transactionId}`, 'retries', 1);
} catch (err) {
logger.error(