mirror of
https://github.com/hyperledger/fabric-samples.git
synced 2026-06-17 15:35:09 +00:00
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:
parent
f904adbf6f
commit
6477333743
7 changed files with 231 additions and 107 deletions
|
|
@ -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": "*"
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
@ -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,
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
94
asset-transfer-basic/rest-api-typescript/src/fabric.spec.ts
Normal file
94
asset-transfer-basic/rest-api-typescript/src/fabric.spec.ts
Normal 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'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
|
@ -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 = {
|
||||
|
|
|
|||
75
asset-transfer-basic/rest-api-typescript/src/redis.spec.ts
Normal file
75
asset-transfer-basic/rest-api-typescript/src/redis.spec.ts
Normal 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);
|
||||
});
|
||||
});
|
||||
|
|
@ -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(
|
||||
|
|
|
|||
Loading…
Reference in a new issue