-
Notifications
You must be signed in to change notification settings - Fork 9
/
start.js
208 lines (179 loc) · 7.93 KB
/
start.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
/*
* This script runs the broker microservice in the background, and then executes the tests.
* It kills the broker microservice before exiting, and returns the exit code of either in case of failure.
*/
const { fork } = require('child_process');
const nodeCleanup = require('node-cleanup');
const inquirer = require('inquirer');
const config = require('config');
const readline = require('readline');
const IS_RUNNING_IN_CI = config.has('ci') ? config.get('ci') : false;
if (IS_RUNNING_IN_CI) {
console.log('OpenActive Test Suite running in non-interactive mode, as `ci` is set to `true`\n');
}
// Force TTY based on environment variable to ensure correct TTY output from piped child process
if (process.stdout.isTTY) {
process.env.FORCE_TTY = 'true';
process.env.FORCE_TTY_COLUMNS = process.stdout.columns;
// Force Chalk to display colours
process.env.FORCE_COLOR = 'true'
}
const BOOKABLE_OPPORTUNITY_TYPES_IN_SCOPE = config.get('integrationTests.bookableOpportunityTypesInScope');
const BOOKING_FLOWS_IN_SCOPE = config.get('integrationTests.bookingFlowsInScope');
let bookableOpportunityTypes = Object.entries(BOOKABLE_OPPORTUNITY_TYPES_IN_SCOPE).filter(([, v]) => v).map(([k]) => k);
let bookingFlows = Object.entries(BOOKING_FLOWS_IN_SCOPE).filter(([, v]) => v).map(([k]) => k);
let flowPathOptions = [
{name: `All`, value: ''},
...bookingFlows.flatMap(bookingFlow =>
[...bookableOpportunityTypes, 'Multiple'].map(bookableOpportunityType =>
({name: `${bookingFlow} >> ${bookableOpportunityType}`, value: `${bookingFlow}/${bookableOpportunityType}`})
)
).sort((a, b) => (a.name > b.name) ? 1 : -1),
];
let currentFlowPathSelection = '';
// Override waitForHarvestCompletion in the environment
const nodeConfig = process.env.NODE_CONFIG ? JSON.parse(process.env.NODE_CONFIG) : {};
if (!nodeConfig.broker) nodeConfig.broker = {};
nodeConfig.broker.waitForHarvestCompletion = true;
process.env.NODE_CONFIG = JSON.stringify(nodeConfig);
let microservice = null;
let integrationTests = null;
let prompt;
// Note this is triggered in CI mode when Ctrl+C is pressed, as well as when the process receives a SIGINT signal
nodeCleanup(function (exitCode, signal) {
// The first time Ctrl+C is pressed in CI mode, kill the microservice and integrationTests,
// and uninstall the cleanup handler, then wait for the parent process to exit naturally
// once both child processes have exited.
if (microservice !== null) microservice.kill();
microservice = null;
if (integrationTests !== null) integrationTests.kill();
nodeCleanup.uninstall(); // don't call this cleanup handler again
return false; // don't exit yet, wait for child processes to exit
});
function setupEscapeKey()
{
// inquirer resets raw mode after the prompt completes, so resume it to allow esc to be detected
if (process.stdin.isTTY) {
process.stdin.setRawMode(true);
}
// inquirer pauses the stream after the prompt completes, so resume it to allow esc to be detected
process.stdin.resume();
}
if (!IS_RUNNING_IN_CI) {
if (!process.stdin.isTTY) {
console.log(`
*******************************************************************************
* WARNING: TEST SUITE IS RUNNING IN INTERACTIVE MODE OUTSIDE OF A TTY SESSION *
* *
* For best results when running interactively, test suite should be run from *
* within the terminal of MacOS or Linux, or from Windows Command Prompt. *
* *
* For best results when running in a continuous integration environment, *
* disable interactive mode by setting "ci": true in the config. *
* *
*******************************************************************************
`);
}
// Setup escape key to cancel running tests, and handle Ctrl+C in interactive mode
readline.emitKeypressEvents(process.stdin);
process.stdin.on('keypress', (ch, key) => {
if (!key) {
return;
}
if (key.name === 'escape') {
if (integrationTests !== null) integrationTests.kill();
} else if (key.ctrl && key.name === 'c') {
// In interactive mode, a clean exit is achieved by killing the microservice first, which will then kill the integration tests:
// microservice.kill() -> integrationTests.kill() + process.exit()
// This is important as it allows the microservice to reset the terminal before it loses access to stdout at process.exit()
if (microservice !== null) microservice.kill();
}
});
setupEscapeKey();
}
microservice = fork('app.js', [], { cwd: './packages/openactive-broker-microservice/', silent: true } );
// Pipe output from microservice to stdout
microservice.stdout.pipe(process.stdout);
microservice.stderr.pipe(process.stderr);
// If microservice exits, kill the integration tests (as something has gone wrong somewhere)
microservice.on('exit', (code) => {
microservice = null;
// Close prompt if currently open
prompt?.ui?.close();
// If exit code is not successful, use this for the result of the whole process (to ensure CI fails)
if (integrationTests !== null) integrationTests.kill();
if (code !== 0 && code !== null) process.exitCode = code;
if (!IS_RUNNING_IN_CI) {
process.exit();
}
});
// Wait for microservice to start listening before starting integration tests
microservice.once('message', (message) => {
if (message === 'listening') {
launchIntegrationTests(process.argv.slice(2), '');
}
});
function launchIntegrationTests(args, singleFlowPathMode) {
process.env.SINGLE_FLOW_PATH_MODE = singleFlowPathMode;
integrationTests = fork('./node_modules/jest/bin/jest.js', args, { cwd: './packages/openactive-integration-tests/'} );
if (IS_RUNNING_IN_CI) {
// When integration tests exit, kill the microservice
integrationTests.on('exit', (code) => {
// If exit code is not successful, use this for the result of the whole process (to ensure CI fails)
if (code !== 0 && code !== null) process.exitCode = code;
if (microservice !== null) microservice.kill();
});
} else {
integrationTests.on('exit', async (code) => {
if (!microservice) return;
// Ensure that harvesting is paused even in the event of a fatal error within the test suite
microservice.send('pause');
// Hide microservice output
microservice.stdout.pause();
microservice.stderr.pause();
const testArgs = args.join(' ');
console.log(`
When data feeds are stable or when using 'controlled' mode, tests can be rerun
quickly without reharvesting. However, for 'random' mode, if data feeds have
been updated without the RPDE 'modified' property being updated (e.g. when
implementing the RPDE feed itself), please exit the test suite and rerun it,
in order to harvest the latest data.
`);
prompt = inquirer.prompt([
{
type: 'input',
name: 'testArgs',
message: 'Rerun tests (ctrl+c to exit)?',
default: testArgs
},
{
type: 'rawlist',
name: 'flowPath',
message: 'Which flow path?',
choices: flowPathOptions,
loop: false,
pageSize: 20,
default: currentFlowPathSelection,
}
]);
var response = await prompt;
console.log('');
if (typeof response.flowPath === 'string') {
currentFlowPathSelection = response.flowPath;
launchIntegrationTests(response.testArgs.split(' '), response.flowPath);
} else {
// If ctrl+c or ctrl+d was pressed
if (microservice !== null) microservice.kill();
}
// Resume microservice
microservice.send('resume');
// Show microservice output
microservice.stdout.resume();
microservice.stderr.resume();
// Ensure escape key can be captured
if (!IS_RUNNING_IN_CI) {
setupEscapeKey();
}
});
}
}