1 |
|
2 | import { readdirSync } from 'fs';
|
3 | import { stringify } from 'querystring';
|
4 | import { getLogger } from './logging';
|
5 | import { Action } from './model/Action';
|
6 | import { Scenario } from './model/Scenario';
|
7 | import { loadYamlConfiguration, nameFromYamlConfig } from './yamlParsing';
|
8 |
|
9 | export const loadScenariosById = (
|
10 | path: string,
|
11 | actionCatalog: Action[],
|
12 | ): Scenario[] => {
|
13 | const resultList: Scenario[] = [];
|
14 | const scenarioFileName = path.split('/').pop() as string;
|
15 | const scenarioName = scenarioFileName.replace('.yaml', '');
|
16 |
|
17 | loadAllScenarios(path.substring(0, path.lastIndexOf('/')), actionCatalog)
|
18 | .filter(s => s.name.startsWith(scenarioName))
|
19 | .forEach(s => resultList.push(s));
|
20 |
|
21 | if (resultList.length > 0) {
|
22 | return resultList;
|
23 | }
|
24 | getLogger().error(`Scenario '${scenarioName}' not found in the directory!`);
|
25 | return process.exit(1);
|
26 | };
|
27 |
|
28 | export const loadAllScenarios = (
|
29 | path: string,
|
30 | actionCatalog: Action[],
|
31 | ): Scenario[] => {
|
32 | const loadedScenarios: Scenario[] = [];
|
33 |
|
34 | readdirSync(`${path}`).forEach(file => {
|
35 | const scenarioDef = loadYamlConfiguration(`${path}/${file}`);
|
36 | if (scenarioDef) {
|
37 |
|
38 | const scenarioImports: Scenario[] = [];
|
39 | if (scenarioDef.import) {
|
40 | const scenarioNamesToBeImported: string[] = scenarioDef.import;
|
41 | if (
|
42 | scenarioNamesToBeImported.every(
|
43 | i => loadedScenarios.findIndex(s => s.name === i) >= 0,
|
44 | )
|
45 | ) {
|
46 | loadedScenarios
|
47 | .filter(s => scenarioNamesToBeImported.includes(s.name))
|
48 | .forEach(s => scenarioImports.push(s));
|
49 | } else {
|
50 | getLogger(nameFromYamlConfig(file)).error(
|
51 | `One of the imports (${scenarioNamesToBeImported}) are missing or were not loaded prior to this one!`,
|
52 | );
|
53 | }
|
54 | }
|
55 |
|
56 | if (scenarioDef.loadFactor) {
|
57 | for (let i = 0; i < scenarioDef.loadFactor; i++) {
|
58 | const scenarioNameWithIdx = `${nameFromYamlConfig(
|
59 | file,
|
60 | )}-${i}`;
|
61 | const ctx = { scenario: scenarioNameWithIdx };
|
62 |
|
63 | const actionCatalogWithReplacedLoadVariables: Action[] = JSON.parse(
|
64 | JSON.stringify(actionCatalog),
|
65 | );
|
66 |
|
67 |
|
68 | if (scenarioDef.loadVariables) {
|
69 | const currentLoadVariables = getLoadVariableTreeForLoadIdx(
|
70 | scenarioDef.loadVariables,
|
71 | i,
|
72 | );
|
73 | for (const current of Object.entries(
|
74 | currentLoadVariables,
|
75 | )) {
|
76 | const currentLoad = current[1];
|
77 |
|
78 | const actionToBeReplaced: any = actionCatalogWithReplacedLoadVariables.find(
|
79 | a => a.name === (currentLoad as any).name,
|
80 | );
|
81 | if (
|
82 | actionToBeReplaced &&
|
83 | typeof currentLoad === 'object' &&
|
84 | currentLoad
|
85 | ) {
|
86 | for (const key of Object.keys(currentLoad)) {
|
87 | if (key !== 'name') {
|
88 | if (
|
89 | actionToBeReplaced[key] &&
|
90 | typeof actionToBeReplaced[key] ===
|
91 | 'object'
|
92 | ) {
|
93 | getLogger(
|
94 | scenarioNameWithIdx,
|
95 | ).debug(
|
96 | `Replacing "${stringify(
|
97 | actionToBeReplaced[key],
|
98 | )}" with "${stringify(
|
99 | currentLoad[key],
|
100 | )}" for key "${key}"`,
|
101 | Object.assign(ctx, {
|
102 | action:
|
103 | actionToBeReplaced.name,
|
104 | }),
|
105 | );
|
106 | Object.assign(
|
107 | actionToBeReplaced[key],
|
108 | currentLoad[key],
|
109 | );
|
110 | } else if (actionToBeReplaced[key]) {
|
111 | getLogger(
|
112 | scenarioNameWithIdx,
|
113 | ).debug(
|
114 | `Replacing "${actionToBeReplaced[key]}" with "${currentLoad[key]}" for key "${key}"`,
|
115 | Object.assign(ctx, {
|
116 | action:
|
117 | actionToBeReplaced.name,
|
118 | }),
|
119 | );
|
120 | actionToBeReplaced[key] =
|
121 | currentLoad[key];
|
122 | }
|
123 | }
|
124 | }
|
125 | }
|
126 | }
|
127 | }
|
128 |
|
129 | loadedScenarios.push(
|
130 | new Scenario(
|
131 | scenarioNameWithIdx,
|
132 | scenarioDef,
|
133 | actionCatalogWithReplacedLoadVariables,
|
134 | scenarioImports,
|
135 | ),
|
136 | );
|
137 | }
|
138 | } else {
|
139 |
|
140 | loadedScenarios.push(
|
141 | new Scenario(
|
142 | nameFromYamlConfig(file),
|
143 | scenarioDef,
|
144 | actionCatalog,
|
145 | scenarioImports,
|
146 | ),
|
147 | );
|
148 | }
|
149 | }
|
150 | });
|
151 |
|
152 | return loadedScenarios;
|
153 | };
|
154 |
|
155 | function getLoadVariableTreeForLoadIdx(rootObject: any, idx: number): any {
|
156 | const res = {};
|
157 | Object.assign(res, rootObject);
|
158 |
|
159 | for (const propEntry of Object.entries(rootObject)) {
|
160 | if (propEntry[0] === 'name') continue;
|
161 |
|
162 | if (Array.isArray(propEntry[1])) {
|
163 | if (typeof propEntry[1][0] === 'object') {
|
164 |
|
165 | const arr: unknown[] = [];
|
166 | arr.push(getLoadVariableTreeForLoadIdx(propEntry[1][0], idx));
|
167 |
|
168 | Object.defineProperty(res, propEntry[0], { value: arr });
|
169 | } else {
|
170 | const loadVars = propEntry[1] as string[];
|
171 | if (loadVars.length === 1 && loadVars[0].indexOf('...') >= 0) {
|
172 | const stringPrefix = loadVars[0].substr(
|
173 | 0,
|
174 | loadVars[0].indexOf('$$'),
|
175 | );
|
176 | const numberRange = loadVars[0]
|
177 | .replace('$$', '')
|
178 | .substring(stringPrefix.length);
|
179 | const limits = numberRange.split('...');
|
180 | const minValue = Number.parseInt(limits[0], 10);
|
181 | const maxValue = Number.parseInt(limits[1], 10);
|
182 | const offset =
|
183 | minValue + idx <= maxValue
|
184 | ? idx
|
185 | : minValue + idx - maxValue;
|
186 |
|
187 | Object.defineProperty(res, propEntry[0], {
|
188 | value: `${stringPrefix}${minValue + offset}`,
|
189 | });
|
190 | } else {
|
191 | Object.defineProperty(res, propEntry[0], {
|
192 | value: loadVars[idx % loadVars.length],
|
193 | });
|
194 | }
|
195 | }
|
196 | } else {
|
197 | Object.defineProperty(res, propEntry[0], {
|
198 | value: getLoadVariableTreeForLoadIdx(
|
199 | rootObject[propEntry[0]],
|
200 | idx,
|
201 | ),
|
202 | });
|
203 | }
|
204 | }
|
205 | return res;
|
206 | }
|