UNPKG

9.17 kBPlain TextView Raw
1// /<reference path="model/Scenario.ts"/>
2import { readdirSync } from 'fs';
3import { stringify } from 'querystring';
4import { getLogger } from './logging';
5import { Action } from './model/Action';
6import { Scenario } from './model/Scenario';
7import { loadYamlConfiguration, nameFromYamlConfig } from './yamlParsing';
8
9export const loadScenariosById = (
10 path: string,
11 actionCatalog: Action[],
12): Scenario[] => {
13 const resultList: Scenario[] = [];
14 const scenarioFileName = path.split('/').pop() as string; // casting to string is safe because splitting a string results in an array with length >= 1
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
28export 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 // get imports
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 // split into multiple scenario instances
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 // inject loadVariables[_i] into the action definitions
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 // just one instance of the scenario
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
155function 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 // not a load-var-array but custom one
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 // console.log(`${stringPrefix} | ${numberRange} | ${minValue} | ${maxValue} | ${offset}`);
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}