Skip to content

Commit

Permalink
feat(tests): Include report section for when tests fail due to missin…
Browse files Browse the repository at this point in the history
…g opportunity data (random mode) (#647)
  • Loading branch information
lukehesluke authored Mar 11, 2024
1 parent fb188b7 commit e2f4329
Show file tree
Hide file tree
Showing 6 changed files with 264 additions and 13 deletions.
1 change: 1 addition & 0 deletions packages/openactive-broker-microservice/src/core.js
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,7 @@ function getRandomOpportunityRoute(req, res) {
logError(`Random Bookable Opportunity from seller ${sellerId} for ${criteriaName} within ${bookingFlow} (${opportunityType}) call failed: No matching opportunities found`);
res.status(404).json({
error: `Opportunity of type '${opportunityType}' that has an \`organizer\`/\`provider\` with \`@id\` '${sellerId}', that also matched '${criteriaName}' within '${bookingFlow}', was not found within the feeds.`,
type: 'OpportunityNotFound',
...result,
});
}
Expand Down
93 changes: 89 additions & 4 deletions packages/openactive-integration-tests/test/helpers/logger.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,27 @@
// TODO fix these issues!
/* eslint-disable arrow-parens */
/* eslint-disable prefer-destructuring */
/* eslint-disable no-var */
/* eslint-disable semi */
/* eslint-disable vars-on-top */
/* eslint-disable no-shadow */
/* eslint-disable no-unused-vars */
/* eslint-disable no-param-reassign */
/* eslint-disable arrow-body-style */
/* eslint-disable no-unused-expressions */
/* eslint-disable no-else-return */
/* eslint-disable no-return-assign */
/* eslint-disable comma-dangle */
/* eslint-disable no-trailing-spaces */
/* eslint-disable object-shorthand */
/* eslint-disable getter-return */
/* eslint-disable consistent-return */
/* eslint-disable prefer-const */
/* eslint-disable class-methods-use-this */
/* eslint-disable space-before-function-paren */
/* eslint-disable object-curly-spacing */
/* eslint-disable import/no-useless-path-segments */
/* eslint-disable quotes */
const _ = require("lodash");
const {promises: fs} = require("fs");
const mapping = require('../helpers/mapping');
Expand All @@ -7,12 +31,40 @@ const { getConfigVarOrThrow } = require('./config-utils');
* @typedef {import('chakram').ChakramResponse} ChakramResponse
*/

/**
* @typedef {{
* type: 'OpportunityNotFound';
* opportunityType: string;
* testOpportunityCriteria: string;
* bookingFlow: string;
* sellerId: string;
* }} OpportunityNotFoundEvent An Opportunity was requested from the
* Booking System (possibly via Broker Microservice) but was not found.
* At report generation, this is used to show the user if their Booking
* System has run out of data in random mode.
*
* @typedef {OpportunityNotFoundEvent} FlowStageLogEvent A noteworthy event that
* occurred during a flow stage.
* These events can then be used by the report generator to add detail to the
* flow stage or summarized for the whole test run.
*
* @typedef {{
* request?: Record<string, unknown>;
* response?: {
* validations?: unknown;
* specs?: unknown[];
* };
* events: FlowStageLogEvent[]
* }} FlowStageLog
*/

const OUTPUT_PATH = getConfigVarOrThrow('integrationTests', 'outputPath');
const USE_RANDOM_OPPORTUNITIES = getConfigVarOrThrow('integrationTests', 'useRandomOpportunities');

// abstract class, implement shared methods
class BaseLogger {
constructor () {
/** @type {{[stage: string]: FlowStageLog}} */
this.flow = {};
this.logs = [];
this.timestamp = (new Date()).toString();
Expand Down Expand Up @@ -68,15 +120,29 @@ class BaseLogger {
return log;
}

/**
* Ensure that there is a FlowStageLog stored for the given stage.
* Create one if it doesn't exist.
*
* @param {string} stage
*/
_ensureFlowStage(stage) {
if (!this.flow[stage]) {
this.flow[stage] = {
events: [],
};
}
}

recordRequest (stage, request) {
if (!this.flow[stage]) this.flow[stage] = {};
this._ensureFlowStage(stage);
if (!this.flow[stage].request) this.flow[stage].request = {};

this.flow[stage].request = request;
}

recordResponse (stage, response) {
if (!this.flow[stage]) this.flow[stage] = {};
this._ensureFlowStage(stage);
if (!this.flow[stage].response) this.flow[stage].response = {};

let fields = {
Expand All @@ -103,6 +169,15 @@ class BaseLogger {
Object.assign(this.flow[stage].response, fields);
}

/**
* @param {string} stage
* @param {FlowStageLogEvent} event
*/
recordFlowStageEvent(stage, event) {
this._ensureFlowStage(stage);
this.flow[stage].events.push(event);
}

/**
* @param {string} stage
* @param {{[k: string]: unknown}} request
Expand Down Expand Up @@ -159,7 +234,7 @@ class BaseLogger {
}

recordResponseValidations (stage, data) {
if (!this.flow[stage]) this.flow[stage] = {};
this._ensureFlowStage(stage);
if (!this.flow[stage].response) this.flow[stage].response = {};

this.flow[stage].response.validations = data;
Expand Down Expand Up @@ -341,6 +416,16 @@ class BaseLogger {
get numPassed () {
return this.specStatusCounts.passed;
}

get opportunityTypeName() {
if ('opportunityType' in this && 'bookingFlow' in this) {
return `${this.bookingFlow} >> ${this.opportunityType}`;
}
if ('opportunityType' in this) {
return this.opportunityType;
}
return 'Generic';
}
}

class Logger extends BaseLogger {
Expand Down Expand Up @@ -441,7 +526,7 @@ class ReporterLogger extends BaseLogger {
}

recordTestResult (stage, data) {
if (!this.flow[stage]) this.flow[stage] = {};
this._ensureFlowStage(stage);
if (!this.flow[stage].response) this.flow[stage].response = {};
if (!this.flow[stage].response.specs) this.flow[stage].response.specs = [];

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -448,8 +448,9 @@ class RequestHelper {
sellerId,
sellerType,
}) {
const stage = `Local Microservice Test Interface for OrderItem ${orderItemPosition}`;
const respObj = await this.post(
`Local Microservice Test Interface for OrderItem ${orderItemPosition}`,
stage,
`${MICROSERVICE_BASE}/test-interface/datasets/${TEST_DATASET_IDENTIFIER}/opportunities`,
createTestInterfaceOpportunity({
opportunityType,
Expand All @@ -461,6 +462,19 @@ class RequestHelper {
timeout: OPEN_BOOKING_API_REQUEST_TIMEOUT,
},
);
const opportunityNotFound = (
respObj.response.statusCode === 404
&& respObj.body?.type === 'OpportunityNotFound'
);
if (opportunityNotFound) {
this.logger.recordFlowStageEvent(stage, {
type: 'OpportunityNotFound',
opportunityType,
testOpportunityCriteria,
bookingFlow,
sellerId,
});
}

return respObj;
}
Expand Down
Loading

0 comments on commit e2f4329

Please sign in to comment.