@atom8n/n8n-benchmark 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +4 -0
- package/Dockerfile +63 -0
- package/README.md +122 -0
- package/bin/n8n-benchmark +13 -0
- package/biome.jsonc +7 -0
- package/dist/build.tsbuildinfo +1 -0
- package/dist/commands/list.d.ts +8 -0
- package/dist/commands/list.js +23 -0
- package/dist/commands/list.js.map +1 -0
- package/dist/commands/run.d.ts +24 -0
- package/dist/commands/run.js +128 -0
- package/dist/commands/run.js.map +1 -0
- package/dist/config/common-flags.d.ts +1 -0
- package/dist/config/common-flags.js +9 -0
- package/dist/config/common-flags.js.map +1 -0
- package/dist/n8n-api-client/authenticated-n8n-api-client.d.ts +15 -0
- package/dist/n8n-api-client/authenticated-n8n-api-client.js +67 -0
- package/dist/n8n-api-client/authenticated-n8n-api-client.js.map +1 -0
- package/dist/n8n-api-client/credentials-api-client.d.ts +9 -0
- package/dist/n8n-api-client/credentials-api-client.js +24 -0
- package/dist/n8n-api-client/credentials-api-client.js.map +1 -0
- package/dist/n8n-api-client/data-table-api-client.d.ts +9 -0
- package/dist/n8n-api-client/data-table-api-client.js +23 -0
- package/dist/n8n-api-client/data-table-api-client.js.map +1 -0
- package/dist/n8n-api-client/n8n-api-client.d.ts +13 -0
- package/dist/n8n-api-client/n8n-api-client.js +82 -0
- package/dist/n8n-api-client/n8n-api-client.js.map +1 -0
- package/dist/n8n-api-client/n8n-api-client.types.d.ts +21 -0
- package/dist/n8n-api-client/n8n-api-client.types.js +3 -0
- package/dist/n8n-api-client/n8n-api-client.types.js.map +1 -0
- package/dist/n8n-api-client/project-api-client.d.ts +6 -0
- package/dist/n8n-api-client/project-api-client.js +14 -0
- package/dist/n8n-api-client/project-api-client.js.map +1 -0
- package/dist/n8n-api-client/workflows-api-client.d.ts +11 -0
- package/dist/n8n-api-client/workflows-api-client.js +30 -0
- package/dist/n8n-api-client/workflows-api-client.js.map +1 -0
- package/dist/scenario/scenario-data-loader.d.ts +13 -0
- package/dist/scenario/scenario-data-loader.js +84 -0
- package/dist/scenario/scenario-data-loader.js.map +1 -0
- package/dist/scenario/scenario-loader.d.ts +7 -0
- package/dist/scenario/scenario-loader.js +101 -0
- package/dist/scenario/scenario-loader.js.map +1 -0
- package/dist/test-execution/app-metrics-poller.d.ts +13 -0
- package/dist/test-execution/app-metrics-poller.js +54 -0
- package/dist/test-execution/app-metrics-poller.js.map +1 -0
- package/dist/test-execution/k6-executor.d.ts +33 -0
- package/dist/test-execution/k6-executor.js +120 -0
- package/dist/test-execution/k6-executor.js.map +1 -0
- package/dist/test-execution/k6-summary.d.ts +82 -0
- package/dist/test-execution/k6-summary.js +2 -0
- package/dist/test-execution/k6-summary.js.map +1 -0
- package/dist/test-execution/prometheus-metrics-parser.d.ts +9 -0
- package/dist/test-execution/prometheus-metrics-parser.js +44 -0
- package/dist/test-execution/prometheus-metrics-parser.js.map +1 -0
- package/dist/test-execution/scenario-data-importer.d.ts +21 -0
- package/dist/test-execution/scenario-data-importer.js +108 -0
- package/dist/test-execution/scenario-data-importer.js.map +1 -0
- package/dist/test-execution/scenario-runner.d.ts +18 -0
- package/dist/test-execution/scenario-runner.js +46 -0
- package/dist/test-execution/scenario-runner.js.map +1 -0
- package/dist/test-execution/test-report.d.ts +56 -0
- package/dist/test-execution/test-report.js +65 -0
- package/dist/test-execution/test-report.js.map +1 -0
- package/dist/types/scenario.d.ts +16 -0
- package/dist/types/scenario.js +3 -0
- package/dist/types/scenario.js.map +1 -0
- package/eslint.config.mjs +22 -0
- package/infra/.terraform.lock.hcl +60 -0
- package/infra/benchmark-env.tf +54 -0
- package/infra/modules/benchmark-vm/output.tf +11 -0
- package/infra/modules/benchmark-vm/vars.tf +29 -0
- package/infra/modules/benchmark-vm/vm.tf +126 -0
- package/infra/output.tf +16 -0
- package/infra/providers.tf +23 -0
- package/infra/vars.tf +34 -0
- package/package.json +55 -0
- package/scenarios/binary-data/binary-data.json +67 -0
- package/scenarios/binary-data/binary-data.manifest.json +7 -0
- package/scenarios/binary-data/binary-data.script.js +29 -0
- package/scenarios/credential-http-node/credential-bearer.json +8 -0
- package/scenarios/credential-http-node/credential-http-node.json +241 -0
- package/scenarios/credential-http-node/credential-http-node.manifest.json +10 -0
- package/scenarios/credential-http-node/credential-http-node.script.js +30 -0
- package/scenarios/data-table-node/data-table-node.json +168 -0
- package/scenarios/data-table-node/data-table-node.manifest.json +10 -0
- package/scenarios/data-table-node/data-table-node.script.js +38 -0
- package/scenarios/data-table-node/data-table.json +25 -0
- package/scenarios/http-node/http-node.json +213 -0
- package/scenarios/http-node/http-node.manifest.json +7 -0
- package/scenarios/http-node/http-node.script.js +30 -0
- package/scenarios/js-code-node/js-code-node.json +96 -0
- package/scenarios/js-code-node/js-code-node.manifest.json +7 -0
- package/scenarios/js-code-node/js-code-node.script.js +29 -0
- package/scenarios/multiple-webhooks/multiple-webhooks.manifest.json +20 -0
- package/scenarios/multiple-webhooks/multiple-webhooks.script.js +19 -0
- package/scenarios/multiple-webhooks/multiple-webhooks1.json +25 -0
- package/scenarios/multiple-webhooks/multiple-webhooks10.json +25 -0
- package/scenarios/multiple-webhooks/multiple-webhooks2.json +25 -0
- package/scenarios/multiple-webhooks/multiple-webhooks3.json +25 -0
- package/scenarios/multiple-webhooks/multiple-webhooks4.json +25 -0
- package/scenarios/multiple-webhooks/multiple-webhooks5.json +25 -0
- package/scenarios/multiple-webhooks/multiple-webhooks6.json +25 -0
- package/scenarios/multiple-webhooks/multiple-webhooks7.json +25 -0
- package/scenarios/multiple-webhooks/multiple-webhooks8.json +25 -0
- package/scenarios/multiple-webhooks/multiple-webhooks9.json +25 -0
- package/scenarios/py-code-node/py-code-node.json +98 -0
- package/scenarios/py-code-node/py-code-node.manifest.json +7 -0
- package/scenarios/py-code-node/py-code-node.script.js +29 -0
- package/scenarios/scenario.schema.json +51 -0
- package/scenarios/set-node-expressions/set-node-expressions.json +91 -0
- package/scenarios/set-node-expressions/set-node-expressions.manifest.json +7 -0
- package/scenarios/set-node-expressions/set-node-expressions.script.js +18 -0
- package/scenarios/single-webhook/single-webhook.json +25 -0
- package/scenarios/single-webhook/single-webhook.manifest.json +7 -0
- package/scenarios/single-webhook/single-webhook.script.js +18 -0
- package/scripts/bootstrap.sh +63 -0
- package/scripts/clients/docker-compose-client.mjs +45 -0
- package/scripts/clients/ssh-client.mjs +37 -0
- package/scripts/clients/terraform-client.mjs +71 -0
- package/scripts/destroy-cloud-env.mjs +86 -0
- package/scripts/mock-api/mappings/mockApiData.json +92110 -0
- package/scripts/n8n-setups/postgres/docker-compose.yml +76 -0
- package/scripts/n8n-setups/postgres/setup.mjs +15 -0
- package/scripts/n8n-setups/scaling-multi-main/docker-compose.yml +230 -0
- package/scripts/n8n-setups/scaling-multi-main/nginx.conf +24 -0
- package/scripts/n8n-setups/scaling-multi-main/setup.mjs +15 -0
- package/scripts/n8n-setups/scaling-single-main/docker-compose.yml +174 -0
- package/scripts/n8n-setups/scaling-single-main/setup.mjs +15 -0
- package/scripts/n8n-setups/sqlite/docker-compose.yml +55 -0
- package/scripts/n8n-setups/sqlite/setup.mjs +15 -0
- package/scripts/provision-cloud-env.mjs +36 -0
- package/scripts/run-for-n8n-setup.mjs +175 -0
- package/scripts/run-in-cloud.mjs +167 -0
- package/scripts/run-locally.mjs +73 -0
- package/scripts/run.mjs +192 -0
- package/scripts/utils/flags.mjs +20 -0
- package/src/commands/list.ts +26 -0
- package/src/commands/run.ts +140 -0
- package/src/config/common-flags.ts +6 -0
- package/src/n8n-api-client/authenticated-n8n-api-client.ts +88 -0
- package/src/n8n-api-client/credentials-api-client.ts +28 -0
- package/src/n8n-api-client/data-table-api-client.ts +30 -0
- package/src/n8n-api-client/n8n-api-client.ts +85 -0
- package/src/n8n-api-client/n8n-api-client.types.ts +27 -0
- package/src/n8n-api-client/project-api-client.ts +11 -0
- package/src/n8n-api-client/workflows-api-client.ts +38 -0
- package/src/scenario/scenario-data-loader.ts +75 -0
- package/src/scenario/scenario-loader.ts +90 -0
- package/src/test-execution/app-metrics-poller.ts +81 -0
- package/src/test-execution/k6-executor.ts +192 -0
- package/src/test-execution/k6-summary.ts +255 -0
- package/src/test-execution/prometheus-metrics-parser.ts +63 -0
- package/src/test-execution/scenario-data-importer.ts +165 -0
- package/src/test-execution/scenario-runner.ts +76 -0
- package/src/test-execution/test-report.ts +152 -0
- package/src/types/scenario.ts +33 -0
- package/tsconfig.build.json +9 -0
- package/tsconfig.json +14 -0
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.AppMetricsPoller = void 0;
|
|
4
|
+
class AppMetricsPoller {
|
|
5
|
+
constructor(metricsUrl, pollIntervalMs = 5000) {
|
|
6
|
+
this.metricsUrl = metricsUrl;
|
|
7
|
+
this.pollIntervalMs = pollIntervalMs;
|
|
8
|
+
this.intervalId = undefined;
|
|
9
|
+
this.metricsData = [];
|
|
10
|
+
this.isRunning = false;
|
|
11
|
+
this.isStopped = false;
|
|
12
|
+
}
|
|
13
|
+
start() {
|
|
14
|
+
if (this.isRunning) {
|
|
15
|
+
throw new Error('Metrics poller is already running');
|
|
16
|
+
}
|
|
17
|
+
if (this.isStopped) {
|
|
18
|
+
throw new Error('Metrics poller has been stopped and cannot be restarted');
|
|
19
|
+
}
|
|
20
|
+
this.isRunning = true;
|
|
21
|
+
this.metricsData = [];
|
|
22
|
+
void this.pollMetrics();
|
|
23
|
+
this.intervalId = setInterval(() => {
|
|
24
|
+
void this.pollMetrics();
|
|
25
|
+
}, this.pollIntervalMs);
|
|
26
|
+
}
|
|
27
|
+
stop() {
|
|
28
|
+
if (this.intervalId) {
|
|
29
|
+
clearInterval(this.intervalId);
|
|
30
|
+
this.intervalId = undefined;
|
|
31
|
+
}
|
|
32
|
+
this.isRunning = false;
|
|
33
|
+
this.isStopped = true;
|
|
34
|
+
}
|
|
35
|
+
getMetricsData() {
|
|
36
|
+
return this.metricsData;
|
|
37
|
+
}
|
|
38
|
+
async pollMetrics() {
|
|
39
|
+
try {
|
|
40
|
+
const response = await fetch(this.metricsUrl);
|
|
41
|
+
if (!response.ok) {
|
|
42
|
+
console.warn(`Failed to poll metrics: ${response.status} ${response.statusText}`);
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
const metricsText = await response.text();
|
|
46
|
+
this.metricsData.push(metricsText);
|
|
47
|
+
}
|
|
48
|
+
catch (error) {
|
|
49
|
+
console.warn(`Error polling metrics: ${error instanceof Error ? error.message : String(error)}`);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
exports.AppMetricsPoller = AppMetricsPoller;
|
|
54
|
+
//# sourceMappingURL=app-metrics-poller.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"app-metrics-poller.js","sourceRoot":"","sources":["../../src/test-execution/app-metrics-poller.ts"],"names":[],"mappings":";;;AAOA,MAAa,gBAAgB;IAM5B,YACkB,UAAkB,EAClB,iBAAyB,IAAI;QAD7B,eAAU,GAAV,UAAU,CAAQ;QAClB,mBAAc,GAAd,cAAc,CAAe;QAPvC,eAAU,GAA+B,SAAS,CAAC;QACnD,gBAAW,GAAa,EAAE,CAAC;QAC3B,cAAS,GAAG,KAAK,CAAC;QAClB,cAAS,GAAG,KAAK,CAAC;IAKvB,CAAC;IAKJ,KAAK;QACJ,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;YACpB,MAAM,IAAI,KAAK,CAAC,mCAAmC,CAAC,CAAC;QACtD,CAAC;QACD,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;YACpB,MAAM,IAAI,KAAK,CAAC,yDAAyD,CAAC,CAAC;QAC5E,CAAC;QAED,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC;QACtB,IAAI,CAAC,WAAW,GAAG,EAAE,CAAC;QAGtB,KAAK,IAAI,CAAC,WAAW,EAAE,CAAC;QAGxB,IAAI,CAAC,UAAU,GAAG,WAAW,CAAC,GAAG,EAAE;YAClC,KAAK,IAAI,CAAC,WAAW,EAAE,CAAC;QACzB,CAAC,EAAE,IAAI,CAAC,cAAc,CAAC,CAAC;IACzB,CAAC;IAKD,IAAI;QACH,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;YACrB,aAAa,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;YAC/B,IAAI,CAAC,UAAU,GAAG,SAAS,CAAC;QAC7B,CAAC;QACD,IAAI,CAAC,SAAS,GAAG,KAAK,CAAC;QACvB,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC;IACvB,CAAC;IAKD,cAAc;QACb,OAAO,IAAI,CAAC,WAAW,CAAC;IACzB,CAAC;IAKO,KAAK,CAAC,WAAW;QACxB,IAAI,CAAC;YACJ,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;YAE9C,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;gBAClB,OAAO,CAAC,IAAI,CAAC,2BAA2B,QAAQ,CAAC,MAAM,IAAI,QAAQ,CAAC,UAAU,EAAE,CAAC,CAAC;gBAClF,OAAO;YACR,CAAC;YAED,MAAM,WAAW,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;YAC1C,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QACpC,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YAChB,OAAO,CAAC,IAAI,CACX,0BAA0B,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAClF,CAAC;QACH,CAAC;IACF,CAAC;CACD;AAzED,4CAyEC"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { type K6Tag } from '../test-execution/test-report';
|
|
2
|
+
import type { Scenario } from '../types/scenario';
|
|
3
|
+
export type { K6Tag };
|
|
4
|
+
export type K6ExecutorOpts = {
|
|
5
|
+
k6ExecutablePath: string;
|
|
6
|
+
vus: number;
|
|
7
|
+
duration: string;
|
|
8
|
+
k6Out?: string;
|
|
9
|
+
k6ApiToken?: string;
|
|
10
|
+
n8nApiBaseUrl: string;
|
|
11
|
+
tags?: K6Tag[];
|
|
12
|
+
resultsWebhook?: {
|
|
13
|
+
url: string;
|
|
14
|
+
authHeader: string;
|
|
15
|
+
};
|
|
16
|
+
appMetricsPolling?: {
|
|
17
|
+
enabled: boolean;
|
|
18
|
+
intervalMs?: number;
|
|
19
|
+
};
|
|
20
|
+
};
|
|
21
|
+
export type K6RunOpts = {
|
|
22
|
+
scenarioRunName: string;
|
|
23
|
+
};
|
|
24
|
+
export declare class K6Executor {
|
|
25
|
+
private readonly opts;
|
|
26
|
+
private readonly handleSummaryScript;
|
|
27
|
+
constructor(opts: K6ExecutorOpts);
|
|
28
|
+
executeTestScenario(scenario: Scenario, { scenarioRunName }: K6RunOpts): Promise<void>;
|
|
29
|
+
sendTestReport(testReport: unknown): Promise<void>;
|
|
30
|
+
private augmentSummaryScript;
|
|
31
|
+
private loadEndOfTestSummary;
|
|
32
|
+
private resolveK6ExecutablePath;
|
|
33
|
+
}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.K6Executor = void 0;
|
|
7
|
+
const fs_1 = __importDefault(require("fs"));
|
|
8
|
+
const strict_1 = __importDefault(require("node:assert/strict"));
|
|
9
|
+
const path_1 = __importDefault(require("path"));
|
|
10
|
+
const zx_1 = require("zx");
|
|
11
|
+
const app_metrics_poller_1 = require("../test-execution/app-metrics-poller");
|
|
12
|
+
const test_report_1 = require("../test-execution/test-report");
|
|
13
|
+
class K6Executor {
|
|
14
|
+
constructor(opts) {
|
|
15
|
+
this.opts = opts;
|
|
16
|
+
this.handleSummaryScript = `
|
|
17
|
+
import { textSummary } from 'https://jslib.k6.io/k6-summary/0.0.2/index.js';
|
|
18
|
+
export function handleSummary(data) {
|
|
19
|
+
return {
|
|
20
|
+
stdout: textSummary(data),
|
|
21
|
+
'{{scenarioName}}.summary.json': JSON.stringify(data),
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
`;
|
|
25
|
+
}
|
|
26
|
+
async executeTestScenario(scenario, { scenarioRunName }) {
|
|
27
|
+
const augmentedTestScriptPath = this.augmentSummaryScript(scenario, scenarioRunName);
|
|
28
|
+
const runDirPath = path_1.default.dirname(augmentedTestScriptPath);
|
|
29
|
+
const flags = [
|
|
30
|
+
['--quiet'],
|
|
31
|
+
['--duration', this.opts.duration],
|
|
32
|
+
['--vus', this.opts.vus],
|
|
33
|
+
];
|
|
34
|
+
if (this.opts.k6Out) {
|
|
35
|
+
flags.push(['--out', this.opts.k6Out]);
|
|
36
|
+
}
|
|
37
|
+
else if (!this.opts.resultsWebhook && this.opts.k6ApiToken) {
|
|
38
|
+
flags.push(['--out', 'cloud']);
|
|
39
|
+
}
|
|
40
|
+
const flattedFlags = flags.flat(2);
|
|
41
|
+
const k6ExecutablePath = await this.resolveK6ExecutablePath();
|
|
42
|
+
let metricsPoller;
|
|
43
|
+
if (this.opts.appMetricsPolling?.enabled) {
|
|
44
|
+
const metricsUrl = `${this.opts.n8nApiBaseUrl}/metrics`;
|
|
45
|
+
const intervalMs = this.opts.appMetricsPolling.intervalMs ?? 5000;
|
|
46
|
+
metricsPoller = new app_metrics_poller_1.AppMetricsPoller(metricsUrl, intervalMs);
|
|
47
|
+
metricsPoller.start();
|
|
48
|
+
console.log(`Started polling app metrics from ${metricsUrl} every ${intervalMs}ms`);
|
|
49
|
+
}
|
|
50
|
+
try {
|
|
51
|
+
await (0, zx_1.$)({
|
|
52
|
+
cwd: runDirPath,
|
|
53
|
+
env: {
|
|
54
|
+
API_BASE_URL: this.opts.n8nApiBaseUrl,
|
|
55
|
+
DATA_TABLE_ID: scenario.dataTableId,
|
|
56
|
+
K6_CLOUD_TOKEN: this.opts.k6ApiToken,
|
|
57
|
+
},
|
|
58
|
+
stdio: 'inherit',
|
|
59
|
+
}) `${k6ExecutablePath} run ${flattedFlags} ${augmentedTestScriptPath}`;
|
|
60
|
+
}
|
|
61
|
+
finally {
|
|
62
|
+
if (metricsPoller) {
|
|
63
|
+
metricsPoller.stop();
|
|
64
|
+
console.log('Stopped polling app metrics');
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
console.log('\n');
|
|
68
|
+
if (this.opts.resultsWebhook) {
|
|
69
|
+
const endOfTestSummary = this.loadEndOfTestSummary(runDirPath, scenarioRunName);
|
|
70
|
+
const appMetricsData = metricsPoller?.getMetricsData();
|
|
71
|
+
const testReport = (0, test_report_1.buildTestReport)(scenario, endOfTestSummary, [
|
|
72
|
+
...(this.opts.tags ?? []),
|
|
73
|
+
{ name: 'Vus', value: this.opts.vus.toString() },
|
|
74
|
+
{ name: 'Duration', value: this.opts.duration.toString() },
|
|
75
|
+
], appMetricsData);
|
|
76
|
+
await this.sendTestReport(testReport);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
async sendTestReport(testReport) {
|
|
80
|
+
(0, strict_1.default)(this.opts.resultsWebhook);
|
|
81
|
+
const response = await fetch(this.opts.resultsWebhook.url, {
|
|
82
|
+
method: 'POST',
|
|
83
|
+
body: JSON.stringify(testReport),
|
|
84
|
+
headers: {
|
|
85
|
+
Authorization: this.opts.resultsWebhook.authHeader,
|
|
86
|
+
'Content-Type': 'application/json',
|
|
87
|
+
},
|
|
88
|
+
});
|
|
89
|
+
if (!response.ok) {
|
|
90
|
+
console.warn(`Failed to send test summary: ${response.status} ${await response.text()}`);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
augmentSummaryScript(scenario, scenarioRunName) {
|
|
94
|
+
const fullTestScriptPath = path_1.default.join(scenario.scenarioDirPath, scenario.scriptPath);
|
|
95
|
+
const testScript = fs_1.default.readFileSync(fullTestScriptPath, 'utf8');
|
|
96
|
+
const summaryScript = this.handleSummaryScript.replace('{{scenarioName}}', scenarioRunName);
|
|
97
|
+
const augmentedTestScript = `${testScript}\n\n${summaryScript}`;
|
|
98
|
+
const tempFilePath = (0, zx_1.tmpfile)(`${scenarioRunName}.js`, augmentedTestScript);
|
|
99
|
+
return tempFilePath;
|
|
100
|
+
}
|
|
101
|
+
loadEndOfTestSummary(dir, scenarioRunName) {
|
|
102
|
+
const summaryReportPath = path_1.default.join(dir, `${scenarioRunName}.summary.json`);
|
|
103
|
+
const summaryReport = fs_1.default.readFileSync(summaryReportPath, 'utf8');
|
|
104
|
+
try {
|
|
105
|
+
return JSON.parse(summaryReport);
|
|
106
|
+
}
|
|
107
|
+
catch (error) {
|
|
108
|
+
throw new Error(`Failed to parse the summary report at ${summaryReportPath}`);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
async resolveK6ExecutablePath() {
|
|
112
|
+
const k6ExecutablePath = await (0, zx_1.which)(this.opts.k6ExecutablePath, { nothrow: true });
|
|
113
|
+
if (!k6ExecutablePath) {
|
|
114
|
+
throw new Error('Could not find k6 executable based on your `PATH`. Please ensure k6 is available in your system and add it to your `PATH` or specify the path to the k6 executable using the `K6_PATH` environment variable.');
|
|
115
|
+
}
|
|
116
|
+
return k6ExecutablePath;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
exports.K6Executor = K6Executor;
|
|
120
|
+
//# sourceMappingURL=k6-executor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"k6-executor.js","sourceRoot":"","sources":["../../src/test-execution/k6-executor.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
type TrendStat = 'avg' | 'min' | 'med' | 'max' | 'p(90)' | 'p(95)';
|
|
2
|
+
type MetricType = 'trend' | 'rate' | 'counter';
|
|
3
|
+
type MetricContains = 'time' | 'default' | 'data';
|
|
4
|
+
interface TrendValues {
|
|
5
|
+
avg: number;
|
|
6
|
+
min: number;
|
|
7
|
+
med: number;
|
|
8
|
+
max: number;
|
|
9
|
+
'p(90)': number;
|
|
10
|
+
'p(95)': number;
|
|
11
|
+
}
|
|
12
|
+
interface RateValues {
|
|
13
|
+
rate: number;
|
|
14
|
+
passes: number;
|
|
15
|
+
fails: number;
|
|
16
|
+
}
|
|
17
|
+
interface CounterValues {
|
|
18
|
+
count: number;
|
|
19
|
+
rate: number;
|
|
20
|
+
}
|
|
21
|
+
interface K6TrendMetric {
|
|
22
|
+
type: 'trend';
|
|
23
|
+
contains: 'time';
|
|
24
|
+
values: TrendValues;
|
|
25
|
+
}
|
|
26
|
+
interface RateMetric {
|
|
27
|
+
type: 'rate';
|
|
28
|
+
contains: 'default';
|
|
29
|
+
values: RateValues;
|
|
30
|
+
}
|
|
31
|
+
interface K6CounterMetric {
|
|
32
|
+
type: 'counter';
|
|
33
|
+
contains: MetricContains;
|
|
34
|
+
values: CounterValues;
|
|
35
|
+
}
|
|
36
|
+
interface Options {
|
|
37
|
+
summaryTrendStats: TrendStat[];
|
|
38
|
+
summaryTimeUnit: string;
|
|
39
|
+
noColor: boolean;
|
|
40
|
+
}
|
|
41
|
+
interface State {
|
|
42
|
+
isStdOutTTY: boolean;
|
|
43
|
+
isStdErrTTY: boolean;
|
|
44
|
+
testRunDurationMs: number;
|
|
45
|
+
}
|
|
46
|
+
interface Metrics {
|
|
47
|
+
http_req_tls_handshaking: K6TrendMetric;
|
|
48
|
+
checks: RateMetric;
|
|
49
|
+
http_req_sending: K6TrendMetric;
|
|
50
|
+
http_reqs: K6CounterMetric;
|
|
51
|
+
http_req_blocked: K6TrendMetric;
|
|
52
|
+
data_received: K6CounterMetric;
|
|
53
|
+
iterations: K6CounterMetric;
|
|
54
|
+
http_req_waiting: K6TrendMetric;
|
|
55
|
+
http_req_receiving: K6TrendMetric;
|
|
56
|
+
'http_req_duration{expected_response:true}': K6TrendMetric;
|
|
57
|
+
iteration_duration: K6TrendMetric;
|
|
58
|
+
http_req_connecting: K6TrendMetric;
|
|
59
|
+
http_req_failed: RateMetric;
|
|
60
|
+
http_req_duration: K6TrendMetric;
|
|
61
|
+
data_sent: K6CounterMetric;
|
|
62
|
+
}
|
|
63
|
+
interface K6Check {
|
|
64
|
+
name: string;
|
|
65
|
+
path: string;
|
|
66
|
+
id: string;
|
|
67
|
+
passes: number;
|
|
68
|
+
fails: number;
|
|
69
|
+
}
|
|
70
|
+
interface RootGroup {
|
|
71
|
+
name: string;
|
|
72
|
+
path: string;
|
|
73
|
+
id: string;
|
|
74
|
+
groups: unknown[];
|
|
75
|
+
checks: K6Check[];
|
|
76
|
+
}
|
|
77
|
+
interface K6EndOfTestSummary {
|
|
78
|
+
options: Options;
|
|
79
|
+
state: State;
|
|
80
|
+
metrics: Metrics;
|
|
81
|
+
root_group: RootGroup;
|
|
82
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"k6-summary.js","sourceRoot":"","sources":["../../src/test-execution/k6-summary.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export declare class PrometheusMetricsParser {
|
|
2
|
+
static extractMetricValues(metricsData: string[], metricName: string): number[];
|
|
3
|
+
static calculateMetricStats(metricsData: string[], metricName: string): {
|
|
4
|
+
max: number;
|
|
5
|
+
avg: number;
|
|
6
|
+
min: number;
|
|
7
|
+
count: number;
|
|
8
|
+
} | null;
|
|
9
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PrometheusMetricsParser = void 0;
|
|
4
|
+
class PrometheusMetricsParser {
|
|
5
|
+
static extractMetricValues(metricsData, metricName) {
|
|
6
|
+
const values = [];
|
|
7
|
+
for (const metricsText of metricsData) {
|
|
8
|
+
const lines = metricsText.split('\n');
|
|
9
|
+
for (const line of lines) {
|
|
10
|
+
if (line.startsWith('#') || line.trim() === '') {
|
|
11
|
+
continue;
|
|
12
|
+
}
|
|
13
|
+
if (line.startsWith(metricName)) {
|
|
14
|
+
const parts = line.split(/\s+/);
|
|
15
|
+
if (parts.length >= 2) {
|
|
16
|
+
const value = parseFloat(parts[parts.length - 1]);
|
|
17
|
+
if (!isNaN(value)) {
|
|
18
|
+
values.push(value);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
return values;
|
|
25
|
+
}
|
|
26
|
+
static calculateMetricStats(metricsData, metricName) {
|
|
27
|
+
const values = this.extractMetricValues(metricsData, metricName);
|
|
28
|
+
if (values.length === 0) {
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
const max = Math.max(...values);
|
|
32
|
+
const min = Math.min(...values);
|
|
33
|
+
const sum = values.reduce((a, b) => a + b, 0);
|
|
34
|
+
const avg = sum / values.length;
|
|
35
|
+
return {
|
|
36
|
+
max,
|
|
37
|
+
min,
|
|
38
|
+
avg,
|
|
39
|
+
count: values.length,
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
exports.PrometheusMetricsParser = PrometheusMetricsParser;
|
|
44
|
+
//# sourceMappingURL=prometheus-metrics-parser.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prometheus-metrics-parser.js","sourceRoot":"","sources":["../../src/test-execution/prometheus-metrics-parser.ts"],"names":[],"mappings":";;;AAGA,MAAa,uBAAuB;IAInC,MAAM,CAAC,mBAAmB,CAAC,WAAqB,EAAE,UAAkB;QACnE,MAAM,MAAM,GAAa,EAAE,CAAC;QAE5B,KAAK,MAAM,WAAW,IAAI,WAAW,EAAE,CAAC;YACvC,MAAM,KAAK,GAAG,WAAW,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;YAEtC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;gBAE1B,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;oBAChD,SAAS;gBACV,CAAC;gBAKD,IAAI,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;oBACjC,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;oBAChC,IAAI,KAAK,CAAC,MAAM,IAAI,CAAC,EAAE,CAAC;wBACvB,MAAM,KAAK,GAAG,UAAU,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC;wBAClD,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC;4BACnB,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;wBACpB,CAAC;oBACF,CAAC;gBACF,CAAC;YACF,CAAC;QACF,CAAC;QAED,OAAO,MAAM,CAAC;IACf,CAAC;IAKD,MAAM,CAAC,oBAAoB,CAC1B,WAAqB,EACrB,UAAkB;QAElB,MAAM,MAAM,GAAG,IAAI,CAAC,mBAAmB,CAAC,WAAW,EAAE,UAAU,CAAC,CAAC;QAEjE,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACzB,OAAO,IAAI,CAAC;QACb,CAAC;QAED,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC,CAAC;QAChC,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC,CAAC;QAChC,MAAM,GAAG,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;QAC9C,MAAM,GAAG,GAAG,GAAG,GAAG,MAAM,CAAC,MAAM,CAAC;QAEhC,OAAO;YACN,GAAG;YACH,GAAG;YACH,GAAG;YACH,KAAK,EAAE,MAAM,CAAC,MAAM;SACpB,CAAC;IACH,CAAC;CACD;AA3DD,0DA2DC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { AuthenticatedN8nApiClient } from '../n8n-api-client/authenticated-n8n-api-client';
|
|
2
|
+
import type { LoadableScenarioData } from '../scenario/scenario-data-loader';
|
|
3
|
+
export declare class ScenarioDataImporter {
|
|
4
|
+
private readonly workflowApiClient;
|
|
5
|
+
private readonly credentialApiClient;
|
|
6
|
+
private readonly dataTableApiClient;
|
|
7
|
+
private readonly projectApiClient;
|
|
8
|
+
constructor(n8nApiClient: AuthenticatedN8nApiClient);
|
|
9
|
+
private replaceValuesInObject;
|
|
10
|
+
importTestScenarioData(data: LoadableScenarioData): Promise<{
|
|
11
|
+
dataTableId: string | undefined;
|
|
12
|
+
}>;
|
|
13
|
+
private importCredentials;
|
|
14
|
+
private importDataTable;
|
|
15
|
+
private importWorkflow;
|
|
16
|
+
private findExistingCredentials;
|
|
17
|
+
private findExistingWorkflows;
|
|
18
|
+
private getBenchmarkCredentialName;
|
|
19
|
+
private getBenchmarkWorkflowName;
|
|
20
|
+
private getBenchmarkDataTableName;
|
|
21
|
+
}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ScenarioDataImporter = void 0;
|
|
4
|
+
const credentials_api_client_1 = require("../n8n-api-client/credentials-api-client");
|
|
5
|
+
const data_table_api_client_1 = require("../n8n-api-client/data-table-api-client");
|
|
6
|
+
const project_api_client_1 = require("../n8n-api-client/project-api-client");
|
|
7
|
+
const workflows_api_client_1 = require("../n8n-api-client/workflows-api-client");
|
|
8
|
+
class ScenarioDataImporter {
|
|
9
|
+
constructor(n8nApiClient) {
|
|
10
|
+
this.workflowApiClient = new workflows_api_client_1.WorkflowApiClient(n8nApiClient);
|
|
11
|
+
this.credentialApiClient = new credentials_api_client_1.CredentialApiClient(n8nApiClient);
|
|
12
|
+
this.dataTableApiClient = new data_table_api_client_1.DataTableApiClient(n8nApiClient);
|
|
13
|
+
this.projectApiClient = new project_api_client_1.ProjectApiClient(n8nApiClient);
|
|
14
|
+
}
|
|
15
|
+
replaceValuesInObject(obj, searchText, targetText) {
|
|
16
|
+
if (Array.isArray(obj)) {
|
|
17
|
+
obj.map((item) => this.replaceValuesInObject(item, searchText, targetText));
|
|
18
|
+
}
|
|
19
|
+
else if (typeof obj === 'object' && obj !== null) {
|
|
20
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
21
|
+
if (typeof value === 'string' && value === searchText) {
|
|
22
|
+
obj[key] = targetText;
|
|
23
|
+
}
|
|
24
|
+
else {
|
|
25
|
+
this.replaceValuesInObject(value, searchText, targetText);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
async importTestScenarioData(data) {
|
|
31
|
+
const existingWorkflows = await this.workflowApiClient.getAllWorkflows();
|
|
32
|
+
const existingCredentials = await this.credentialApiClient.getAllCredentials();
|
|
33
|
+
const existingDataTables = await this.dataTableApiClient.getAllDataTables();
|
|
34
|
+
for (const credential of data.credentials) {
|
|
35
|
+
const createdCredential = await this.importCredentials({ existingCredentials, credential });
|
|
36
|
+
for (const workflow of data.workflows) {
|
|
37
|
+
this.replaceValuesInObject(workflow, credential.id, createdCredential.id);
|
|
38
|
+
this.replaceValuesInObject(workflow, credential.name, createdCredential.name);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
for (const workflow of data.workflows) {
|
|
42
|
+
await this.importWorkflow({ existingWorkflows, workflow });
|
|
43
|
+
}
|
|
44
|
+
let dataTableId;
|
|
45
|
+
if (data.dataTable) {
|
|
46
|
+
dataTableId = await this.importDataTable({ existingDataTables, dataTable: data.dataTable });
|
|
47
|
+
}
|
|
48
|
+
return { dataTableId };
|
|
49
|
+
}
|
|
50
|
+
async importCredentials(opts) {
|
|
51
|
+
const existingCredentials = this.findExistingCredentials(opts.existingCredentials, opts.credential);
|
|
52
|
+
if (existingCredentials.length > 0) {
|
|
53
|
+
for (const toDelete of existingCredentials) {
|
|
54
|
+
await this.credentialApiClient.deleteCredential(toDelete.id);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return await this.credentialApiClient.createCredential({
|
|
58
|
+
...opts.credential,
|
|
59
|
+
name: this.getBenchmarkCredentialName(opts.credential),
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
async importDataTable(opts) {
|
|
63
|
+
const { existingDataTables, dataTable } = opts;
|
|
64
|
+
const projectId = await this.projectApiClient.getPersonalProject();
|
|
65
|
+
const existingTable = existingDataTables.find((dt) => dt.name === this.getBenchmarkDataTableName(dataTable));
|
|
66
|
+
if (existingTable) {
|
|
67
|
+
await this.dataTableApiClient.deleteDataTable(projectId, existingTable.id);
|
|
68
|
+
}
|
|
69
|
+
const { id } = await this.dataTableApiClient.createDataTable(projectId, {
|
|
70
|
+
...dataTable,
|
|
71
|
+
name: this.getBenchmarkDataTableName(dataTable),
|
|
72
|
+
});
|
|
73
|
+
return id;
|
|
74
|
+
}
|
|
75
|
+
async importWorkflow(opts) {
|
|
76
|
+
const existingWorkflows = this.findExistingWorkflows(opts.existingWorkflows, opts.workflow);
|
|
77
|
+
if (existingWorkflows.length > 0) {
|
|
78
|
+
for (const toDelete of existingWorkflows) {
|
|
79
|
+
await this.workflowApiClient.archiveWorkflow(toDelete.id);
|
|
80
|
+
await this.workflowApiClient.deleteWorkflow(toDelete.id);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
const createdWorkflow = await this.workflowApiClient.createWorkflow({
|
|
84
|
+
...opts.workflow,
|
|
85
|
+
name: this.getBenchmarkWorkflowName(opts.workflow),
|
|
86
|
+
});
|
|
87
|
+
return await this.workflowApiClient.activateWorkflow(createdWorkflow);
|
|
88
|
+
}
|
|
89
|
+
findExistingCredentials(existingCredentials, credentialToImport) {
|
|
90
|
+
const benchmarkCredentialName = this.getBenchmarkCredentialName(credentialToImport);
|
|
91
|
+
return existingCredentials.filter((existingCredential) => existingCredential.name === benchmarkCredentialName);
|
|
92
|
+
}
|
|
93
|
+
findExistingWorkflows(existingWorkflows, workflowToImport) {
|
|
94
|
+
const benchmarkWorkflowName = this.getBenchmarkWorkflowName(workflowToImport);
|
|
95
|
+
return existingWorkflows.filter((existingWorkflow) => existingWorkflow.name === benchmarkWorkflowName);
|
|
96
|
+
}
|
|
97
|
+
getBenchmarkCredentialName(credential) {
|
|
98
|
+
return `[BENCHMARK] ${credential.name}`;
|
|
99
|
+
}
|
|
100
|
+
getBenchmarkWorkflowName(workflow) {
|
|
101
|
+
return `[BENCHMARK] ${workflow.name}`;
|
|
102
|
+
}
|
|
103
|
+
getBenchmarkDataTableName(dataTable) {
|
|
104
|
+
return `[BENCHMARK] ${dataTable.name}`;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
exports.ScenarioDataImporter = ScenarioDataImporter;
|
|
108
|
+
//# sourceMappingURL=scenario-data-importer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scenario-data-importer.js","sourceRoot":"","sources":["../../src/test-execution/scenario-data-importer.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { N8nApiClient } from '../n8n-api-client/n8n-api-client';
|
|
2
|
+
import type { ScenarioDataFileLoader } from '../scenario/scenario-data-loader';
|
|
3
|
+
import type { Scenario } from '../types/scenario';
|
|
4
|
+
import type { K6Executor } from './k6-executor';
|
|
5
|
+
export declare class ScenarioRunner {
|
|
6
|
+
private readonly n8nClient;
|
|
7
|
+
private readonly dataLoader;
|
|
8
|
+
private readonly k6Executor;
|
|
9
|
+
private readonly ownerConfig;
|
|
10
|
+
private readonly scenarioPrefix;
|
|
11
|
+
constructor(n8nClient: N8nApiClient, dataLoader: ScenarioDataFileLoader, k6Executor: K6Executor, ownerConfig: {
|
|
12
|
+
email: string;
|
|
13
|
+
password: string;
|
|
14
|
+
}, scenarioPrefix: string);
|
|
15
|
+
runManyScenarios(scenarios: Scenario[]): Promise<void>;
|
|
16
|
+
private runSingleTestScenario;
|
|
17
|
+
private formTestScenarioRunName;
|
|
18
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ScenarioRunner = void 0;
|
|
4
|
+
const zx_1 = require("zx");
|
|
5
|
+
const authenticated_n8n_api_client_1 = require("../n8n-api-client/authenticated-n8n-api-client");
|
|
6
|
+
const scenario_data_importer_1 = require("../test-execution/scenario-data-importer");
|
|
7
|
+
class ScenarioRunner {
|
|
8
|
+
constructor(n8nClient, dataLoader, k6Executor, ownerConfig, scenarioPrefix) {
|
|
9
|
+
this.n8nClient = n8nClient;
|
|
10
|
+
this.dataLoader = dataLoader;
|
|
11
|
+
this.k6Executor = k6Executor;
|
|
12
|
+
this.ownerConfig = ownerConfig;
|
|
13
|
+
this.scenarioPrefix = scenarioPrefix;
|
|
14
|
+
}
|
|
15
|
+
async runManyScenarios(scenarios) {
|
|
16
|
+
console.log(`Waiting for n8n ${this.n8nClient.apiBaseUrl} to become online`);
|
|
17
|
+
await this.n8nClient.waitForInstanceToBecomeOnline();
|
|
18
|
+
console.log('Setting up owner');
|
|
19
|
+
await this.n8nClient.setupOwnerIfNeeded(this.ownerConfig);
|
|
20
|
+
const authenticatedN8nClient = await authenticated_n8n_api_client_1.AuthenticatedN8nApiClient.createUsingUsernameAndPassword(this.n8nClient, this.ownerConfig);
|
|
21
|
+
const testDataImporter = new scenario_data_importer_1.ScenarioDataImporter(authenticatedN8nClient);
|
|
22
|
+
for (const scenario of scenarios) {
|
|
23
|
+
await this.runSingleTestScenario(testDataImporter, scenario);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
async runSingleTestScenario(testDataImporter, scenario) {
|
|
27
|
+
const scenarioRunName = this.formTestScenarioRunName(scenario);
|
|
28
|
+
console.log('Running scenario:', scenarioRunName);
|
|
29
|
+
console.log('Loading and importing data');
|
|
30
|
+
const testData = await this.dataLoader.loadDataForScenario(scenario);
|
|
31
|
+
const { dataTableId } = await testDataImporter.importTestScenarioData(testData);
|
|
32
|
+
await (0, zx_1.sleep)(1000);
|
|
33
|
+
console.log('Executing scenario script');
|
|
34
|
+
await this.k6Executor.executeTestScenario({
|
|
35
|
+
...scenario,
|
|
36
|
+
dataTableId,
|
|
37
|
+
}, {
|
|
38
|
+
scenarioRunName,
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
formTestScenarioRunName(scenario) {
|
|
42
|
+
return `${this.scenarioPrefix}-${scenario.name}`;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
exports.ScenarioRunner = ScenarioRunner;
|
|
46
|
+
//# sourceMappingURL=scenario-runner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scenario-runner.js","sourceRoot":"","sources":["../../src/test-execution/scenario-runner.ts"],"names":[],"mappings":";;;AAAA,2BAA2B;AAE3B,gGAA0F;AAG1F,oFAA+E;AAQ/E,MAAa,cAAc;IAC1B,YACkB,SAAuB,EACvB,UAAkC,EAClC,UAAsB,EACtB,WAGhB,EACgB,cAAsB;QAPtB,cAAS,GAAT,SAAS,CAAc;QACvB,eAAU,GAAV,UAAU,CAAwB;QAClC,eAAU,GAAV,UAAU,CAAY;QACtB,gBAAW,GAAX,WAAW,CAG3B;QACgB,mBAAc,GAAd,cAAc,CAAQ;IACrC,CAAC;IAEJ,KAAK,CAAC,gBAAgB,CAAC,SAAqB;QAC3C,OAAO,CAAC,GAAG,CAAC,mBAAmB,IAAI,CAAC,SAAS,CAAC,UAAU,mBAAmB,CAAC,CAAC;QAC7E,MAAM,IAAI,CAAC,SAAS,CAAC,6BAA6B,EAAE,CAAC;QAErD,OAAO,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAC;QAChC,MAAM,IAAI,CAAC,SAAS,CAAC,kBAAkB,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QAE1D,MAAM,sBAAsB,GAAG,MAAM,wDAAyB,CAAC,8BAA8B,CAC5F,IAAI,CAAC,SAAS,EACd,IAAI,CAAC,WAAW,CAChB,CAAC;QACF,MAAM,gBAAgB,GAAG,IAAI,6CAAoB,CAAC,sBAAsB,CAAC,CAAC;QAE1E,KAAK,MAAM,QAAQ,IAAI,SAAS,EAAE,CAAC;YAClC,MAAM,IAAI,CAAC,qBAAqB,CAAC,gBAAgB,EAAE,QAAQ,CAAC,CAAC;QAC9D,CAAC;IACF,CAAC;IAEO,KAAK,CAAC,qBAAqB,CAAC,gBAAsC,EAAE,QAAkB;QAC7F,MAAM,eAAe,GAAG,IAAI,CAAC,uBAAuB,CAAC,QAAQ,CAAC,CAAC;QAC/D,OAAO,CAAC,GAAG,CAAC,mBAAmB,EAAE,eAAe,CAAC,CAAC;QAElD,OAAO,CAAC,GAAG,CAAC,4BAA4B,CAAC,CAAC;QAC1C,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,mBAAmB,CAAC,QAAQ,CAAC,CAAC;QACrE,MAAM,EAAE,WAAW,EAAE,GAAG,MAAM,gBAAgB,CAAC,sBAAsB,CAAC,QAAQ,CAAC,CAAC;QAIhF,MAAM,IAAA,UAAK,EAAC,IAAI,CAAC,CAAC;QAElB,OAAO,CAAC,GAAG,CAAC,2BAA2B,CAAC,CAAC;QACzC,MAAM,IAAI,CAAC,UAAU,CAAC,mBAAmB,CACxC;YACC,GAAG,QAAQ;YACX,WAAW;SACX,EACD;YACC,eAAe;SACf,CACD,CAAC;IACH,CAAC;IAOO,uBAAuB,CAAC,QAAkB;QACjD,OAAO,GAAG,IAAI,CAAC,cAAc,IAAI,QAAQ,CAAC,IAAI,EAAE,CAAC;IAClD,CAAC;CACD;AA9DD,wCA8DC"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import type { Scenario } from '../types/scenario';
|
|
2
|
+
export type K6Tag = {
|
|
3
|
+
name: string;
|
|
4
|
+
value: string;
|
|
5
|
+
};
|
|
6
|
+
export type Check = {
|
|
7
|
+
name: string;
|
|
8
|
+
passes: number;
|
|
9
|
+
fails: number;
|
|
10
|
+
};
|
|
11
|
+
export type CounterMetric = {
|
|
12
|
+
type: 'counter';
|
|
13
|
+
count: number;
|
|
14
|
+
rate: number;
|
|
15
|
+
};
|
|
16
|
+
export type TrendMetric = {
|
|
17
|
+
type: 'trend';
|
|
18
|
+
'p(95)': number;
|
|
19
|
+
avg: number;
|
|
20
|
+
min: number;
|
|
21
|
+
med: number;
|
|
22
|
+
max: number;
|
|
23
|
+
'p(90)': number;
|
|
24
|
+
};
|
|
25
|
+
export type AppMetricStats = {
|
|
26
|
+
max: number;
|
|
27
|
+
avg: number;
|
|
28
|
+
min: number;
|
|
29
|
+
count: number;
|
|
30
|
+
};
|
|
31
|
+
export type AppMetricsReport = {
|
|
32
|
+
heapSizeTotal?: AppMetricStats;
|
|
33
|
+
heapSizeUsed?: AppMetricStats;
|
|
34
|
+
externalMemory?: AppMetricStats;
|
|
35
|
+
eventLoopLag?: AppMetricStats;
|
|
36
|
+
};
|
|
37
|
+
export type TestReport = {
|
|
38
|
+
runId: string;
|
|
39
|
+
ts: string;
|
|
40
|
+
scenarioName: string;
|
|
41
|
+
tags: K6Tag[];
|
|
42
|
+
metrics: {
|
|
43
|
+
iterations: CounterMetric;
|
|
44
|
+
dataReceived: CounterMetric;
|
|
45
|
+
dataSent: CounterMetric;
|
|
46
|
+
httpRequests: CounterMetric;
|
|
47
|
+
httpRequestDuration: TrendMetric;
|
|
48
|
+
httpRequestSending: TrendMetric;
|
|
49
|
+
httpRequestReceiving: TrendMetric;
|
|
50
|
+
httpRequestWaiting: TrendMetric;
|
|
51
|
+
};
|
|
52
|
+
checks: Check[];
|
|
53
|
+
appMetrics?: AppMetricsReport;
|
|
54
|
+
};
|
|
55
|
+
export declare function buildAppMetricsReport(metricsData: string[]): AppMetricsReport;
|
|
56
|
+
export declare function buildTestReport(scenario: Scenario, endOfTestSummary: K6EndOfTestSummary, tags: K6Tag[], appMetricsData?: string[]): TestReport;
|