Skip to content

Commit

Permalink
FABN-1519: More robust private data scenario test (#193)
Browse files Browse the repository at this point in the history
The ordering of the Then assertions required waits to be introduced
to minimise timing issues causing spurious failures. Re-ordered to
ensure tests happen only when there is data available to test.

Signed-off-by: Mark S. Lewis <[email protected]>
  • Loading branch information
bestbeforetoday authored Mar 20, 2020
1 parent 192678e commit abe6e78
Show file tree
Hide file tree
Showing 3 changed files with 49 additions and 94 deletions.
2 changes: 1 addition & 1 deletion test/ts-scenario/features/events.feature
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,8 @@ Feature: Node SDK Events
Scenario: Using a gateway I can listen to private block events emitted by networks
When I use the gateway named event_gateway to listen for private block events with a listener named privateBlockListener on channel eventschannel
And I use the gateway named event_gateway to submit a transaction with args [privateValuePut] for contract events instantiated on channel eventschannel
Then I check event private data has myprivatedata with a listener named privateBlockListener
Then I receive a minimum 1 events from the listener named privateBlockListener
And the listener named privateBlockListener should have private data containing "myprivatedata"

Scenario: Using a gateway I can stop listening to private block events emitted by networks
Given I am listening for private block events with a listener named privateBlockListener
Expand Down
15 changes: 4 additions & 11 deletions test/ts-scenario/steps/event-listeners.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,6 @@ Given(/^I am listening for transaction events with a listener named (.+?)$/, {ti
Listeners.checkTransactionListenerDetails(listenerName, Constants.TRANSACTION, isActive);
});

// private data check
Given(/^I check event private data has (.+?) with a listener named (.+?)$/, {timeout: Constants.STEP_SHORT as number }, async (privateData: string, listenerName: string) => {
// wait for events to catch up
await BaseUtils.sleep(Constants.INC_SHORT);

Listeners.checkBlockListenerPrivatePayloads(listenerName, privateData);
});

// Contract events
When(/^I use the gateway named (.+?) to listen for (filtered|full) contract events named (.+?) with a listener named (.+?) for the smart contract named (.+?) on channel (.+?)$/, {timeout: Constants.STEP_SHORT as number}, async (gatewayName: string, eventType: EventType, eventName: string, listenerName: string, ccName: string, channelName: string) => {
return await Listeners.createContractListener(gatewayName, channelName, ccName, eventName, listenerName, eventType);
Expand All @@ -59,15 +51,16 @@ When(/^I unregister the listener named (.+?)$/, {timeout: Constants.STEP_SHORT a

Then(/^I receive ([0-9]+) events from the listener named (.+?)$/, {timeout: Constants.STEP_SHORT as number }, async (calls: number, listenerName: string) => {
await Listeners.checkListenerCallNumber(listenerName, calls, Constants.EXACT);
Listeners.resetListenerCalls(listenerName);
});

Then(/^I receive a minimum ([0-9]+) events from the listener named (.+?)$/, {timeout: Constants.STEP_SHORT as number }, async (calls: number, listenerName: string) => {
await Listeners.checkListenerCallNumber(listenerName, calls, Constants.GREATER_THAN);
Listeners.resetListenerCalls(listenerName);
});

Then(/^I receive a maximum ([0-9]+) events from the listener named (.+?)$/, {timeout: Constants.STEP_SHORT as number }, async (calls: number, listenerName: string) => {
await Listeners.checkListenerCallNumber(listenerName, calls, Constants.LESS_THAN);
Listeners.resetListenerCalls(listenerName);
});

Then('the listener named {word} should have private data containing {string}', {timeout: Constants.STEP_SHORT as number }, async (listenerName: string, privateData: string) => {
Listeners.checkBlockListenerPrivatePayloads(listenerName, privateData);
});
126 changes: 44 additions & 82 deletions test/ts-scenario/steps/lib/listeners.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,49 +17,21 @@ export async function createContractListener(gatewayName: string, channelName: s
const gateway: Gateway = gateways.get(gatewayName).gateway;
const contract: Contract = await GatewayHelper.retrieveContractFromGateway(gateway, channelName, ccName);

let listeners: Map<string, any> = stateStore.get(Constants.LISTENERS);
const listenerObject: any = {
active: true,
calls: 0,
eventName,
eventType: type,
listener: {},
payloads: [],
type: Constants.CONTRACT,
};

// If no listeners, then create the new map item
if (!listeners) {
listeners = new Map();
stateStore.set(Constants.LISTENERS, listeners);
}

const contractListener: ContractListener = async (event: ContractEvent) => {
BaseUtils.logMsg(`-> Received a contract event for listener [${listenerName}] of eventName ${eventName}`);

if (event.eventName !== eventName) {
return;
}

// TODO: support for full blocks
// if (!filtered) {
// const [event]: any = args as any;
// if (event && Object.prototype.hasOwnProperty.call(event, 'payload')) {
// BaseUtils.checkString(event.payload.toString('utf8'), 'content', true);
// }
// }

const tlisteners: any = stateStore.get(Constants.LISTENERS);
if (tlisteners) {
const listenerUpdate: any = tlisteners.get(listenerName);
if (listenerUpdate) {
listenerUpdate.payloads.push(event);
listenerUpdate.calls = listenerUpdate.payloads.length;
}
if (event.eventName === eventName) {
listenerObject.payloads.push(event);
}
};

// Create the listener
const listenerOptions: ListenerOptions = {
startBlock,
type
Expand All @@ -69,30 +41,22 @@ export async function createContractListener(gatewayName: string, channelName: s
// Roll into a listener object to store
listenerObject.listener = contractListener;
listenerObject.remove = () => contract.removeContractListener(contractListener);
listeners.set(listenerName, listenerObject);
stateStore.set(Constants.LISTENERS, listeners);
putListenerObject(listenerName, listenerObject);
}

export async function createBlockListener(gatewayName: string, channelName: string, listenerName: string, type: EventType, startBlock?: number, endBlock?: number): Promise<void> {
const gateways: Map<string, any> = stateStore.get(Constants.GATEWAYS);
const gateway: Gateway = gateways.get(gatewayName).gateway;
const network: Network = await gateway.getNetwork(channelName);

let listeners: Map<string, any> = stateStore.get(Constants.LISTENERS);
const listenerObject: any = {
active: true,
calls: 0,
eventType: type,
listener: {},
payloads: [],
type: Constants.BLOCK
};

// If no listeners, then create the new map item
if (!listeners) {
listeners = new Map();
}

// Create the listener
const listener: BlockListener = async (blockEvent: BlockEvent) => {
BaseUtils.logMsg('->Received a block event', listenerName);
Expand All @@ -103,21 +67,12 @@ export async function createBlockListener(gatewayName: string, channelName: stri
BaseUtils.checkSizeEquality(blockEvent.blockNumber.toNumber(), endBlock + 1, false, true);
}

const tlisteners: any = stateStore.get(Constants.LISTENERS);
if (tlisteners) {
const listenerUpdate: any = tlisteners.get(listenerName);
if (listenerUpdate) {
listenerUpdate.payloads.push(blockEvent);
listenerUpdate.calls = listenerUpdate.payloads.length;
BaseUtils.logMsg('->Received a block event - added blockevent to payloads', listenerName);
const transactionEvents = blockEvent.getTransactionEvents();
for (const transactionEvent of transactionEvents) {
if (transactionEvent.privateData) {
BaseUtils.logMsg('->Received a block event - blockevent has privateData', JSON.stringify(transactionEvent.privateData));
}
}
} else {
BaseUtils.logMsg('->Received a block event - did not find the listener', listenerName);
listenerObject.payloads.push(blockEvent);
BaseUtils.logMsg('->Received a block event - added blockevent to payloads', listenerName);
const transactionEvents = blockEvent.getTransactionEvents();
for (const transactionEvent of transactionEvents) {
if (transactionEvent.privateData) {
BaseUtils.logMsg('->Received a block event - blockevent has privateData', JSON.stringify(transactionEvent.privateData));
}
}

Expand All @@ -134,26 +89,32 @@ export async function createBlockListener(gatewayName: string, channelName: stri
// Roll into a listener object to store
listenerObject.listener = listener;
listenerObject.remove = () => network.removeBlockListener(listener);
listeners.set(listenerName, listenerObject);
putListenerObject(listenerName, listenerObject);
BaseUtils.logMsg('->Stored a block event listener:', listenerName);

stateStore.set(Constants.LISTENERS, listeners);
}

export function getListenerObject(listenerName: string): any {
const listeners: Map<string, any> = stateStore.get(Constants.LISTENERS);
if (!listeners || !listeners.has(listenerName)) {
function getListenerObject(listenerName: string): any {
const listener = getListeners().get(listenerName);
if (!listener) {
const msg: string = `Unable to find listener with name ${listenerName}`;
BaseUtils.logAndThrow(msg);
} else {
return listeners.get(listenerName);
return listener;
}
}

export function resetListenerCalls(listenerName: string): void {
const listener: any = getListenerObject(listenerName);
listener.payloads = [];
listener.calls = 0;
function putListenerObject(name: string, listener: any): void {
getListeners().set(name, listener);
}

function getListeners(): Map<string, any> {
let listeners: Map<string, any> = stateStore.get(Constants.LISTENERS);
if (!listeners) {
listeners = new Map();
stateStore.set(Constants.LISTENERS, listeners);
}

return listeners;
}

export async function checkListenerCallNumber(listenerName: string, compareNumber: number, type: string): Promise<void> {
Expand All @@ -163,13 +124,13 @@ export async function checkListenerCallNumber(listenerName: string, compareNumbe
let condition: boolean;
switch (type) {
case Constants.EXACT:
condition = Number(getListenerObject(listenerName).calls) === Number(compareNumber);
condition = Number(getListenerObject(listenerName).payloads.length) === Number(compareNumber);
break;
case Constants.GREATER_THAN:
condition = Number(getListenerObject(listenerName).calls) >= Number(compareNumber);
condition = Number(getListenerObject(listenerName).payloads.length) >= Number(compareNumber);
break;
case Constants.LESS_THAN:
condition = Number(getListenerObject(listenerName).calls) <= Number(compareNumber);
condition = Number(getListenerObject(listenerName).payloads.length) <= Number(compareNumber);
break;
default:
throw new Error(`Unknown condition type ${type} passed to checkListenerCallNumber()`);
Expand All @@ -189,7 +150,7 @@ export async function checkListenerCallNumber(listenerName: string, compareNumbe
}, Constants.STEP_SHORT);
});

const gatewayListenerCalls: number = getListenerObject(listenerName).calls;
const gatewayListenerCalls: number = getListenerObject(listenerName).payloads.length;
switch (type) {
case Constants.EXACT:
if (Number(gatewayListenerCalls) !== Number(compareNumber)) {
Expand Down Expand Up @@ -242,19 +203,19 @@ export function checkBlockListenerDetails(listenerName: string, listenerType: st
}

export function checkBlockListenerPrivatePayloads(listenerName: string, checkData: string): void {
const listenerObject: any = getListenerObject(listenerName);
let found = false;
for (const payload of listenerObject.payloads) {
const transactionEvents = payload.getTransactionEvents();
for (const transactionEvent of transactionEvents) {
if (transactionEvent.privateData) {
BaseUtils.logMsg('->Transaction Payload has privateData', JSON.stringify(transactionEvent.privateData));
if (JSON.stringify(transactionEvent.privateData).includes(checkData)) {
found = true;
}
}
}
}
const listenerObject = getListenerObject(listenerName);
const blockEvents: BlockEvent[] = listenerObject.payloads;

const found = blockEvents.some((blockEvent) => {
return blockEvent.getTransactionEvents()
.filter((transactionEvent) => transactionEvent.privateData)
.map((transactionEvent) => JSON.stringify(transactionEvent.privateData))
.some((privateDataJson) => {
BaseUtils.logMsg('->Transaction Payload has privateData', privateDataJson);
return privateDataJson.includes(checkData);
});
});

if (found) {
BaseUtils.logMsg('->Transaction Payload privateData checks out', listenerName);
} else {
Expand All @@ -277,4 +238,5 @@ export function unregisterListener(listenerName: string) {
const listenerObject = getListenerObject(listenerName);
listenerObject.remove();
listenerObject.active = false;
listenerObject.payloads = [];
}

0 comments on commit abe6e78

Please sign in to comment.