@testsmith/api-spector 0.7.6 → 0.7.8
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/bin/cli.js +4 -0
- package/out/main/chunks/{broker-client-5IyCTx40.js → broker-client-BYpWeOWT.js} +4 -1
- package/out/main/chunks/{flow-engine-BVzjz4lJ.js → flow-engine-BxwjEy37.js} +66 -1
- package/out/main/chunks/{handle-DRmKOL-s.js → handle-Crv1i9hb.js} +61 -0
- package/out/main/chunks/{import-B5T5wDu7.js → import-DTcZMRNu.js} +2 -2
- package/out/main/chunks/perf-report-ChaO3K0w.js +124 -0
- package/out/main/chunks/perf-run-uZBqnG3S.js +578 -0
- package/out/main/chunks/{request-collection-KGPuvN7o.js → request-collection-gkOYlWml.js} +68 -11
- package/out/main/chunks/{request-exec-CwJ8_OKC.js → request-exec-Cv2jkrO1.js} +458 -12
- package/out/main/chunks/run-concurrent-468tH0AX.js +26 -0
- package/out/main/chunks/{soap-handler-DTfFnzNY.js → soap-handler-Brr7BPWO.js} +2 -2
- package/out/main/contract.js +6 -3
- package/out/main/flow.js +8 -5
- package/out/main/index.js +1338 -179
- package/out/main/lib.js +6 -3
- package/out/main/perf-worker.js +45 -0
- package/out/main/perf.js +253 -0
- package/out/main/runner.js +181 -7
- package/out/main/socketio.js +172 -0
- package/out/main/wsdl.js +3 -3
- package/out/preload/index.js +127 -0
- package/out/renderer/assets/{GraphQLEditor-CDLI1OHR.js → GraphQLEditor-Dv2K4wdd.js} +59 -14
- package/out/renderer/assets/{SoapEditor-CSUFJk7c.js → SoapEditor-Cnz3M4Dz.js} +3 -3
- package/out/renderer/assets/ajv-CJPwFjiP.js +6718 -0
- package/out/renderer/assets/{codemirror-F6m3Hm1M.js → codemirror-CdIFBPs2.js} +1214 -1207
- package/out/renderer/assets/{index-8bbf24uI.js → index-CtCdFiRx.js} +16957 -13834
- package/out/renderer/assets/index-DkhdKzQJ.css +627 -0
- package/out/renderer/index.html +4 -4
- package/package.json +12 -6
- package/out/renderer/assets/ajv-2MAg6kGF.js +0 -6153
- package/out/renderer/assets/index-CYArpqm5.css +0 -627
|
@@ -24,7 +24,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
24
24
|
const undici = require("undici");
|
|
25
25
|
const promises$1 = require("fs/promises");
|
|
26
26
|
const nodeTls = require("tls");
|
|
27
|
-
const handle = require("./handle-
|
|
27
|
+
const handle = require("./handle-Crv1i9hb.js");
|
|
28
28
|
const fs = require("node:fs");
|
|
29
29
|
const os = require("node:os");
|
|
30
30
|
const path = require("node:path");
|
|
@@ -35,6 +35,9 @@ const vm = require("node:vm");
|
|
|
35
35
|
const tv4 = require("tv4");
|
|
36
36
|
const jsonpathPlus = require("jsonpath-plus");
|
|
37
37
|
const xmldom = require("@xmldom/xmldom");
|
|
38
|
+
const mqtt = require("mqtt");
|
|
39
|
+
const WebSocket = require("ws");
|
|
40
|
+
const socket_ioClient = require("socket.io-client");
|
|
38
41
|
const http = require("node:http");
|
|
39
42
|
const Ajv = require("ajv");
|
|
40
43
|
function _interopNamespaceDefault(e) {
|
|
@@ -719,6 +722,11 @@ function registerSecretHandlers(ipc) {
|
|
|
719
722
|
handle.handleIpc(ipc, handle.IPC.secret.setMasterKey, (_e, value) => {
|
|
720
723
|
process.env[MASTER_KEY_ENV] = value;
|
|
721
724
|
});
|
|
725
|
+
handle.handleIpc(ipc, handle.IPC.secret.encryptWithMasterKey, (_e, plaintext) => {
|
|
726
|
+
const password = process.env[MASTER_KEY_ENV];
|
|
727
|
+
if (!password) throw new Error("Master password is not set in this process.");
|
|
728
|
+
return encryptSecret(plaintext, password);
|
|
729
|
+
});
|
|
722
730
|
handle.handleIpc(ipc, handle.IPC.secret.set, async (_e, ref, value) => {
|
|
723
731
|
const ss = getSafeStorage();
|
|
724
732
|
if (!ss?.isEncryptionAvailable()) {
|
|
@@ -737,6 +745,20 @@ function registerSecretHandlers(ipc) {
|
|
|
737
745
|
}
|
|
738
746
|
});
|
|
739
747
|
}
|
|
748
|
+
function encryptSecret(plaintext, password) {
|
|
749
|
+
const salt = crypto.randomBytes(16);
|
|
750
|
+
const iv = crypto.randomBytes(12);
|
|
751
|
+
const key = crypto.pbkdf2Sync(password, salt, 1e5, 32, "sha256");
|
|
752
|
+
const cipher = crypto.createCipheriv("aes-256-gcm", key, iv);
|
|
753
|
+
const ciphertext = Buffer.concat([cipher.update(plaintext, "utf8"), cipher.final()]);
|
|
754
|
+
const authTag = cipher.getAuthTag();
|
|
755
|
+
return {
|
|
756
|
+
secretEncrypted: Buffer.concat([ciphertext, authTag]).toString("base64"),
|
|
757
|
+
secretSalt: salt.toString("base64"),
|
|
758
|
+
secretIv: iv.toString("base64"),
|
|
759
|
+
secretHash: crypto.createHash("sha256").update(plaintext, "utf8").digest("hex").slice(0, 8)
|
|
760
|
+
};
|
|
761
|
+
}
|
|
740
762
|
function decryptSecret(encrypted, salt, iv, password) {
|
|
741
763
|
const saltBuf = Buffer.from(salt, "base64");
|
|
742
764
|
const ivBuf = Buffer.from(iv, "base64");
|
|
@@ -886,9 +908,9 @@ async function buildEnvVars(environment) {
|
|
|
886
908
|
}
|
|
887
909
|
return vars;
|
|
888
910
|
}
|
|
889
|
-
function mergeVars(envVars, collectionVars, globals2, localVars = {}, dynamicVars = {}) {
|
|
911
|
+
function mergeVars(envVars, collectionVars, globals2, localVars = {}, dynamicVars = {}, folderVars = {}) {
|
|
890
912
|
const out = {};
|
|
891
|
-
for (const src of [dynamicVars, globals2, collectionVars, envVars, localVars]) {
|
|
913
|
+
for (const src of [dynamicVars, globals2, collectionVars, folderVars, envVars, localVars]) {
|
|
892
914
|
for (const [k, v] of Object.entries(src ?? {})) {
|
|
893
915
|
if (v == null) continue;
|
|
894
916
|
out[k] = v;
|
|
@@ -1072,6 +1094,7 @@ class AssertionError extends Error {
|
|
|
1072
1094
|
}
|
|
1073
1095
|
function buildAt(ctx, testResults, consoleOutput) {
|
|
1074
1096
|
const { envVars, collectionVars, globals: globals2, localVars, piiMaskPatterns = [] } = ctx;
|
|
1097
|
+
const folderVars = ctx.folderVars ?? {};
|
|
1075
1098
|
const isSensitiveKey = (key) => piiMaskPatterns.some((p) => key.toLowerCase().includes(p.toLowerCase()));
|
|
1076
1099
|
function makeVarScope(store, scopeName) {
|
|
1077
1100
|
return {
|
|
@@ -1094,9 +1117,10 @@ function buildAt(ctx, testResults, consoleOutput) {
|
|
|
1094
1117
|
variables: makeVarScope(localVars, "variables"),
|
|
1095
1118
|
environment: makeVarScope(envVars, "environment"),
|
|
1096
1119
|
collectionVariables: makeVarScope(collectionVars, "collectionVariables"),
|
|
1120
|
+
folder: makeVarScope(folderVars, "folder"),
|
|
1097
1121
|
globals: makeVarScope(globals2, "globals"),
|
|
1098
1122
|
// Convenience: get/set across all scopes (local wins)
|
|
1099
|
-
variables_get: (key) => localVars[key] ?? envVars[key] ?? collectionVars[key] ?? globals2[key] ?? null,
|
|
1123
|
+
variables_get: (key) => localVars[key] ?? envVars[key] ?? folderVars[key] ?? collectionVars[key] ?? globals2[key] ?? null,
|
|
1100
1124
|
variables_set: (key, value) => {
|
|
1101
1125
|
localVars[key] = String(value);
|
|
1102
1126
|
},
|
|
@@ -1155,6 +1179,14 @@ function buildAt(ctx, testResults, consoleOutput) {
|
|
|
1155
1179
|
return String(binCode % 10 ** digits).padStart(digits, "0");
|
|
1156
1180
|
}
|
|
1157
1181
|
};
|
|
1182
|
+
const collected = ctx.messages ?? ctx.mqttMessages;
|
|
1183
|
+
if (collected) {
|
|
1184
|
+
sp.messages = collected.map((m) => ({
|
|
1185
|
+
...m,
|
|
1186
|
+
json: () => JSON.parse(m.payload),
|
|
1187
|
+
text: () => m.payload
|
|
1188
|
+
}));
|
|
1189
|
+
}
|
|
1158
1190
|
if (ctx.response) {
|
|
1159
1191
|
const resp = ctx.response;
|
|
1160
1192
|
let parsedJson = void 0;
|
|
@@ -1230,12 +1262,17 @@ async function runScript(code, ctx, timeoutMs = 5e3) {
|
|
|
1230
1262
|
const collectionCopy = { ...ctx.collectionVars };
|
|
1231
1263
|
const globalsCopy = { ...ctx.globals };
|
|
1232
1264
|
const localVarsCopy = { ...ctx.localVars };
|
|
1265
|
+
const folderCopy = { ...ctx.folderVars ?? {} };
|
|
1233
1266
|
const scriptCtx = {
|
|
1234
1267
|
envVars: envVarsCopy,
|
|
1235
1268
|
collectionVars: collectionCopy,
|
|
1236
1269
|
globals: globalsCopy,
|
|
1237
1270
|
localVars: localVarsCopy,
|
|
1271
|
+
folderVars: folderCopy,
|
|
1238
1272
|
response: ctx.response,
|
|
1273
|
+
data: ctx.data,
|
|
1274
|
+
mqttMessages: ctx.mqttMessages,
|
|
1275
|
+
messages: ctx.messages,
|
|
1239
1276
|
piiMaskPatterns: ctx.piiMaskPatterns
|
|
1240
1277
|
};
|
|
1241
1278
|
const sandbox = buildSandbox(scriptCtx, testResults, consoleOutput, faker);
|
|
@@ -1250,6 +1287,7 @@ async function runScript(code, ctx, timeoutMs = 5e3) {
|
|
|
1250
1287
|
updatedCollectionVars: collectionCopy,
|
|
1251
1288
|
updatedGlobals: globalsCopy,
|
|
1252
1289
|
updatedLocalVars: localVarsCopy,
|
|
1290
|
+
updatedFolderVars: folderCopy,
|
|
1253
1291
|
error: message
|
|
1254
1292
|
};
|
|
1255
1293
|
}
|
|
@@ -1259,7 +1297,8 @@ async function runScript(code, ctx, timeoutMs = 5e3) {
|
|
|
1259
1297
|
updatedEnvVars: envVarsCopy,
|
|
1260
1298
|
updatedCollectionVars: collectionCopy,
|
|
1261
1299
|
updatedGlobals: globalsCopy,
|
|
1262
|
-
updatedLocalVars: localVarsCopy
|
|
1300
|
+
updatedLocalVars: localVarsCopy,
|
|
1301
|
+
updatedFolderVars: folderCopy
|
|
1263
1302
|
};
|
|
1264
1303
|
}
|
|
1265
1304
|
async function evaluateExpression(code, ctx, timeoutMs = 5e3) {
|
|
@@ -1338,6 +1377,7 @@ class StreamFramer {
|
|
|
1338
1377
|
constructor(mode) {
|
|
1339
1378
|
this.mode = mode;
|
|
1340
1379
|
}
|
|
1380
|
+
mode;
|
|
1341
1381
|
carry = "";
|
|
1342
1382
|
/** Feed a decoded text chunk; returns the frames that completed. */
|
|
1343
1383
|
push(text) {
|
|
@@ -1500,6 +1540,314 @@ async function readStream(body, kind, startMs, opts = {}) {
|
|
|
1500
1540
|
}
|
|
1501
1541
|
return { events, text, close, firstEventMs: Math.max(firstEventMs, 0) };
|
|
1502
1542
|
}
|
|
1543
|
+
async function runMqttTest(cfg) {
|
|
1544
|
+
const messages = [];
|
|
1545
|
+
const collectMs = Math.max(0, Math.min(6e4, Math.floor(cfg.collectMs ?? 2e3)));
|
|
1546
|
+
const clientId = cfg.clientId?.trim() || `api-spector-test-${Date.now().toString(36)}-${Math.random().toString(16).slice(2, 10)}`;
|
|
1547
|
+
return new Promise((resolve2) => {
|
|
1548
|
+
let settled = false;
|
|
1549
|
+
let connected = false;
|
|
1550
|
+
let lastError = "";
|
|
1551
|
+
let client;
|
|
1552
|
+
let timer;
|
|
1553
|
+
const runAssertions = async (connectError) => {
|
|
1554
|
+
if (settled) return;
|
|
1555
|
+
settled = true;
|
|
1556
|
+
if (timer) clearTimeout(timer);
|
|
1557
|
+
try {
|
|
1558
|
+
client?.end(true);
|
|
1559
|
+
} catch {
|
|
1560
|
+
}
|
|
1561
|
+
if (connectError && messages.length === 0) {
|
|
1562
|
+
resolve2({ testResults: [], consoleOutput: [], error: connectError, messages, clientId });
|
|
1563
|
+
return;
|
|
1564
|
+
}
|
|
1565
|
+
const out = await runScript(cfg.script ?? "", {
|
|
1566
|
+
envVars: cfg.envVars ?? {},
|
|
1567
|
+
collectionVars: cfg.collectionVars ?? {},
|
|
1568
|
+
globals: cfg.globals ?? {},
|
|
1569
|
+
localVars: cfg.localVars ?? {},
|
|
1570
|
+
mqttMessages: messages
|
|
1571
|
+
});
|
|
1572
|
+
resolve2({ testResults: out.testResults, consoleOutput: out.consoleOutput, error: out.error, messages, clientId });
|
|
1573
|
+
};
|
|
1574
|
+
try {
|
|
1575
|
+
client = mqtt.connect(cfg.url, {
|
|
1576
|
+
clientId,
|
|
1577
|
+
username: cfg.username?.trim() || void 0,
|
|
1578
|
+
password: cfg.password ? cfg.password : void 0,
|
|
1579
|
+
clean: cfg.cleanSession !== false,
|
|
1580
|
+
reconnectPeriod: 0,
|
|
1581
|
+
connectTimeout: 15e3
|
|
1582
|
+
});
|
|
1583
|
+
} catch (err) {
|
|
1584
|
+
resolve2({ testResults: [], consoleOutput: [], error: err instanceof Error ? err.message : String(err), messages, clientId });
|
|
1585
|
+
return;
|
|
1586
|
+
}
|
|
1587
|
+
client.on("connect", () => {
|
|
1588
|
+
connected = true;
|
|
1589
|
+
const publish = () => {
|
|
1590
|
+
const topic = cfg.publishTopic?.trim();
|
|
1591
|
+
if (!topic) return;
|
|
1592
|
+
if (/[+#]/.test(topic)) return;
|
|
1593
|
+
client.publish(topic, cfg.publishPayload ?? "", { qos: cfg.qos ?? 0, retain: Boolean(cfg.retain) });
|
|
1594
|
+
};
|
|
1595
|
+
const sub = cfg.subscribeTopic?.trim();
|
|
1596
|
+
if (sub) {
|
|
1597
|
+
client.subscribe(sub, { qos: cfg.qos ?? 0 }, () => setTimeout(publish, 100));
|
|
1598
|
+
} else {
|
|
1599
|
+
publish();
|
|
1600
|
+
}
|
|
1601
|
+
timer = setTimeout(() => void runAssertions(), collectMs);
|
|
1602
|
+
});
|
|
1603
|
+
client.on("message", (topic, payload, packet) => {
|
|
1604
|
+
messages.push({ topic, payload: payload.toString("utf8"), qos: packet?.qos, retain: Boolean(packet?.retain), timestamp: Date.now() });
|
|
1605
|
+
});
|
|
1606
|
+
client.on("error", (err) => {
|
|
1607
|
+
const code = err?.code;
|
|
1608
|
+
lastError = [err?.message, code].filter(Boolean).join(" ");
|
|
1609
|
+
void runAssertions(err?.message || "Could not connect to the broker");
|
|
1610
|
+
});
|
|
1611
|
+
client.on("close", () => {
|
|
1612
|
+
if (settled) return;
|
|
1613
|
+
void runAssertions(connected ? `The test connection was dropped by the broker before the window finished${lastError ? ` [${lastError}]` : ""}. Common causes: publishing to a topic that contains a wildcard (+ or #), or another connection using the same client id. Check the broker's log.` : `Could not connect to the broker${lastError ? ` [${lastError}]` : ""}`);
|
|
1614
|
+
});
|
|
1615
|
+
});
|
|
1616
|
+
}
|
|
1617
|
+
function clampWindow(ms) {
|
|
1618
|
+
return Math.max(0, Math.min(6e4, Math.floor(ms ?? 3e3)));
|
|
1619
|
+
}
|
|
1620
|
+
async function assertOver(cfg, messages) {
|
|
1621
|
+
const out = await runScript(cfg.script ?? "", {
|
|
1622
|
+
envVars: cfg.envVars ?? {},
|
|
1623
|
+
collectionVars: cfg.collectionVars ?? {},
|
|
1624
|
+
globals: cfg.globals ?? {},
|
|
1625
|
+
localVars: cfg.localVars ?? {},
|
|
1626
|
+
messages
|
|
1627
|
+
});
|
|
1628
|
+
return { testResults: out.testResults, consoleOutput: out.consoleOutput, error: out.error, messages };
|
|
1629
|
+
}
|
|
1630
|
+
function errorResult(error, messages) {
|
|
1631
|
+
return { testResults: [], consoleOutput: [], error, messages };
|
|
1632
|
+
}
|
|
1633
|
+
function parseJsonObject(text, label) {
|
|
1634
|
+
if (!text || !text.trim()) return void 0;
|
|
1635
|
+
let parsed;
|
|
1636
|
+
try {
|
|
1637
|
+
parsed = JSON.parse(text);
|
|
1638
|
+
} catch {
|
|
1639
|
+
throw new Error(`${label} is not valid JSON`);
|
|
1640
|
+
}
|
|
1641
|
+
return parsed && typeof parsed === "object" ? parsed : void 0;
|
|
1642
|
+
}
|
|
1643
|
+
async function runWsTest(cfg) {
|
|
1644
|
+
const messages = [];
|
|
1645
|
+
const collectMs = clampWindow(cfg.collectMs);
|
|
1646
|
+
return new Promise((resolve2) => {
|
|
1647
|
+
let settled = false;
|
|
1648
|
+
let connected = false;
|
|
1649
|
+
let lastError = "";
|
|
1650
|
+
let ws;
|
|
1651
|
+
let timer;
|
|
1652
|
+
const finish = async (connectError) => {
|
|
1653
|
+
if (settled) return;
|
|
1654
|
+
settled = true;
|
|
1655
|
+
if (timer) clearTimeout(timer);
|
|
1656
|
+
try {
|
|
1657
|
+
ws?.close();
|
|
1658
|
+
} catch {
|
|
1659
|
+
}
|
|
1660
|
+
if (connectError && messages.length === 0) {
|
|
1661
|
+
resolve2(errorResult(connectError, messages));
|
|
1662
|
+
return;
|
|
1663
|
+
}
|
|
1664
|
+
resolve2(await assertOver(cfg, messages));
|
|
1665
|
+
};
|
|
1666
|
+
try {
|
|
1667
|
+
ws = new WebSocket(cfg.url, { headers: cfg.headers });
|
|
1668
|
+
} catch (err) {
|
|
1669
|
+
resolve2(errorResult(err instanceof Error ? err.message : String(err), messages));
|
|
1670
|
+
return;
|
|
1671
|
+
}
|
|
1672
|
+
ws.on("open", () => {
|
|
1673
|
+
connected = true;
|
|
1674
|
+
if (cfg.sendMessage) {
|
|
1675
|
+
try {
|
|
1676
|
+
ws.send(cfg.sendMessage);
|
|
1677
|
+
} catch {
|
|
1678
|
+
}
|
|
1679
|
+
}
|
|
1680
|
+
timer = setTimeout(() => void finish(), collectMs);
|
|
1681
|
+
});
|
|
1682
|
+
ws.on("message", (data) => {
|
|
1683
|
+
const text = data.toString();
|
|
1684
|
+
messages.push({ payload: text, data: text, timestamp: Date.now() });
|
|
1685
|
+
});
|
|
1686
|
+
ws.on("error", (err) => {
|
|
1687
|
+
lastError = err.message;
|
|
1688
|
+
void finish(err.message || "Could not connect to the server");
|
|
1689
|
+
});
|
|
1690
|
+
ws.on("close", () => {
|
|
1691
|
+
if (settled) return;
|
|
1692
|
+
void finish(connected ? `The WebSocket was closed before the window finished${lastError ? ` [${lastError}]` : ""}.` : `Could not connect to the server${lastError ? ` [${lastError}]` : ""}`);
|
|
1693
|
+
});
|
|
1694
|
+
});
|
|
1695
|
+
}
|
|
1696
|
+
function targetUrl(url, namespace) {
|
|
1697
|
+
const ns = (namespace ?? "").trim();
|
|
1698
|
+
if (!ns || ns === "/") return url;
|
|
1699
|
+
return url.replace(/\/+$/, "") + (ns.startsWith("/") ? ns : `/${ns}`);
|
|
1700
|
+
}
|
|
1701
|
+
async function runSocketIoTest(cfg) {
|
|
1702
|
+
const messages = [];
|
|
1703
|
+
const collectMs = clampWindow(cfg.collectMs);
|
|
1704
|
+
let auth;
|
|
1705
|
+
try {
|
|
1706
|
+
auth = parseJsonObject(cfg.auth, "Auth");
|
|
1707
|
+
} catch (e) {
|
|
1708
|
+
return errorResult(e instanceof Error ? e.message : String(e), messages);
|
|
1709
|
+
}
|
|
1710
|
+
let emitArgs = [];
|
|
1711
|
+
if (cfg.emitArgs && cfg.emitArgs.trim()) {
|
|
1712
|
+
try {
|
|
1713
|
+
const parsed = JSON.parse(cfg.emitArgs);
|
|
1714
|
+
emitArgs = Array.isArray(parsed) ? parsed : [parsed];
|
|
1715
|
+
} catch {
|
|
1716
|
+
return errorResult("Emit arguments are not valid JSON", messages);
|
|
1717
|
+
}
|
|
1718
|
+
}
|
|
1719
|
+
return new Promise((resolve2) => {
|
|
1720
|
+
let settled = false;
|
|
1721
|
+
let socket;
|
|
1722
|
+
let timer;
|
|
1723
|
+
const finish = async (connectError) => {
|
|
1724
|
+
if (settled) return;
|
|
1725
|
+
settled = true;
|
|
1726
|
+
if (timer) clearTimeout(timer);
|
|
1727
|
+
try {
|
|
1728
|
+
socket?.disconnect();
|
|
1729
|
+
} catch {
|
|
1730
|
+
}
|
|
1731
|
+
if (connectError && messages.length === 0) {
|
|
1732
|
+
resolve2(errorResult(connectError, messages));
|
|
1733
|
+
return;
|
|
1734
|
+
}
|
|
1735
|
+
resolve2(await assertOver(cfg, messages));
|
|
1736
|
+
};
|
|
1737
|
+
try {
|
|
1738
|
+
socket = socket_ioClient.io(targetUrl(cfg.url, cfg.namespace), {
|
|
1739
|
+
path: cfg.path?.trim() || "/socket.io",
|
|
1740
|
+
transports: ["websocket", "polling"],
|
|
1741
|
+
extraHeaders: cfg.headers,
|
|
1742
|
+
auth,
|
|
1743
|
+
forceNew: true,
|
|
1744
|
+
reconnection: false
|
|
1745
|
+
});
|
|
1746
|
+
} catch (err) {
|
|
1747
|
+
resolve2(errorResult(err instanceof Error ? err.message : String(err), messages));
|
|
1748
|
+
return;
|
|
1749
|
+
}
|
|
1750
|
+
socket.on("connect", () => {
|
|
1751
|
+
const ev = cfg.emitEvent?.trim();
|
|
1752
|
+
if (ev) setTimeout(() => {
|
|
1753
|
+
try {
|
|
1754
|
+
socket.emit(ev, ...emitArgs);
|
|
1755
|
+
} catch {
|
|
1756
|
+
}
|
|
1757
|
+
}, 100);
|
|
1758
|
+
timer = setTimeout(() => void finish(), collectMs);
|
|
1759
|
+
});
|
|
1760
|
+
socket.on("connect_error", (err) => {
|
|
1761
|
+
void finish(`Could not connect to the server${err.message ? ` [${err.message}]` : ""}`);
|
|
1762
|
+
});
|
|
1763
|
+
socket.onAny((event, ...args) => {
|
|
1764
|
+
const payload = JSON.stringify(args.length <= 1 ? args[0] ?? null : args);
|
|
1765
|
+
messages.push({ payload, event, args, timestamp: Date.now() });
|
|
1766
|
+
});
|
|
1767
|
+
});
|
|
1768
|
+
}
|
|
1769
|
+
const GQL_SUB_ID = "1";
|
|
1770
|
+
async function runGraphqlWsTest(cfg) {
|
|
1771
|
+
const messages = [];
|
|
1772
|
+
const collectMs = clampWindow(cfg.collectMs);
|
|
1773
|
+
let variables;
|
|
1774
|
+
let connectionParams;
|
|
1775
|
+
try {
|
|
1776
|
+
variables = parseJsonObject(cfg.variables, "Variables");
|
|
1777
|
+
connectionParams = parseJsonObject(cfg.connectionParams, "Connection params");
|
|
1778
|
+
} catch (e) {
|
|
1779
|
+
return errorResult(e instanceof Error ? e.message : String(e), messages);
|
|
1780
|
+
}
|
|
1781
|
+
return new Promise((resolve2) => {
|
|
1782
|
+
let settled = false;
|
|
1783
|
+
let connected = false;
|
|
1784
|
+
let lastError = "";
|
|
1785
|
+
let ws;
|
|
1786
|
+
let timer;
|
|
1787
|
+
const finish = async (connectError) => {
|
|
1788
|
+
if (settled) return;
|
|
1789
|
+
settled = true;
|
|
1790
|
+
if (timer) clearTimeout(timer);
|
|
1791
|
+
try {
|
|
1792
|
+
ws?.close();
|
|
1793
|
+
} catch {
|
|
1794
|
+
}
|
|
1795
|
+
if (connectError && messages.length === 0) {
|
|
1796
|
+
resolve2(errorResult(connectError, messages));
|
|
1797
|
+
return;
|
|
1798
|
+
}
|
|
1799
|
+
resolve2(await assertOver(cfg, messages));
|
|
1800
|
+
};
|
|
1801
|
+
try {
|
|
1802
|
+
ws = new WebSocket(cfg.url, "graphql-transport-ws", { headers: cfg.headers });
|
|
1803
|
+
} catch (err) {
|
|
1804
|
+
resolve2(errorResult(err instanceof Error ? err.message : String(err), messages));
|
|
1805
|
+
return;
|
|
1806
|
+
}
|
|
1807
|
+
ws.on("open", () => {
|
|
1808
|
+
connected = true;
|
|
1809
|
+
ws.send(JSON.stringify({ type: "connection_init", payload: connectionParams }));
|
|
1810
|
+
timer = setTimeout(() => void finish(), collectMs);
|
|
1811
|
+
});
|
|
1812
|
+
ws.on("message", (raw) => {
|
|
1813
|
+
let msg;
|
|
1814
|
+
try {
|
|
1815
|
+
msg = JSON.parse(raw.toString());
|
|
1816
|
+
} catch {
|
|
1817
|
+
return;
|
|
1818
|
+
}
|
|
1819
|
+
switch (msg.type) {
|
|
1820
|
+
case "connection_ack":
|
|
1821
|
+
ws.send(JSON.stringify({ id: GQL_SUB_ID, type: "subscribe", payload: { query: cfg.query, variables, operationName: cfg.operationName } }));
|
|
1822
|
+
break;
|
|
1823
|
+
case "next": {
|
|
1824
|
+
const payload = msg.payload;
|
|
1825
|
+
const data = payload?.data;
|
|
1826
|
+
messages.push({ payload: JSON.stringify(data ?? payload ?? null), data, errors: payload?.errors, timestamp: Date.now() });
|
|
1827
|
+
break;
|
|
1828
|
+
}
|
|
1829
|
+
case "error":
|
|
1830
|
+
lastError = JSON.stringify(msg.payload);
|
|
1831
|
+
void finish(`The subscription returned an error${lastError ? ` [${lastError}]` : ""}`);
|
|
1832
|
+
break;
|
|
1833
|
+
case "complete":
|
|
1834
|
+
void finish();
|
|
1835
|
+
break;
|
|
1836
|
+
case "ping":
|
|
1837
|
+
ws.send(JSON.stringify({ type: "pong" }));
|
|
1838
|
+
break;
|
|
1839
|
+
}
|
|
1840
|
+
});
|
|
1841
|
+
ws.on("error", (err) => {
|
|
1842
|
+
lastError = err.message;
|
|
1843
|
+
void finish(err.message || "Could not connect to the server");
|
|
1844
|
+
});
|
|
1845
|
+
ws.on("close", () => {
|
|
1846
|
+
if (settled) return;
|
|
1847
|
+
void finish(connected ? `The subscription connection was closed before the window finished${lastError ? ` [${lastError}]` : ""}.` : `Could not connect to the server${lastError ? ` [${lastError}]` : ""}`);
|
|
1848
|
+
});
|
|
1849
|
+
});
|
|
1850
|
+
}
|
|
1503
1851
|
const SIGNATURE = Buffer.from("NTLMSSP\0", "latin1");
|
|
1504
1852
|
const F_UNICODE = 1;
|
|
1505
1853
|
const F_OEM = 2;
|
|
@@ -1676,7 +2024,7 @@ async function buildAuthHeaders(auth, vars) {
|
|
|
1676
2024
|
const username = interpolate(auth.username ?? "", vars);
|
|
1677
2025
|
headers["Authorization"] = `Basic ${Buffer.from(`${username}:${password}`).toString("base64")}`;
|
|
1678
2026
|
}
|
|
1679
|
-
if (auth.type === "apikey" && auth.apiKeyIn === "header") {
|
|
2027
|
+
if (auth.type === "apikey" && (auth.apiKeyIn ?? "header") === "header") {
|
|
1680
2028
|
let value = auth.apiKeyValue ?? "";
|
|
1681
2029
|
if (!value && auth.apiKeySecretRef) value = await getSecret(auth.apiKeySecretRef) ?? "";
|
|
1682
2030
|
value = interpolate(value, vars);
|
|
@@ -2267,6 +2615,7 @@ async function resolveInlineSecrets(req) {
|
|
|
2267
2615
|
async function executeRunnerRequest(opts) {
|
|
2268
2616
|
const { req, collectionVars, envVars, globals: globals2, dispatcher, piiMaskPatterns, proxy, tls, onScriptOutput } = opts;
|
|
2269
2617
|
let { localVars } = opts;
|
|
2618
|
+
const folderVars = opts.folderVars ?? {};
|
|
2270
2619
|
applyRequestDefaults(req);
|
|
2271
2620
|
const base = {
|
|
2272
2621
|
requestId: req.id,
|
|
@@ -2277,13 +2626,99 @@ async function executeRunnerRequest(opts) {
|
|
|
2277
2626
|
};
|
|
2278
2627
|
const dynamicVars = await buildDynamicVars();
|
|
2279
2628
|
if (localVars && Object.keys(localVars).length > 0) {
|
|
2280
|
-
localVars = resolveDataRow(localVars, mergeVars(envVars, collectionVars, globals2, {}, dynamicVars));
|
|
2629
|
+
localVars = resolveDataRow(localVars, mergeVars(envVars, collectionVars, globals2, {}, dynamicVars, folderVars));
|
|
2281
2630
|
}
|
|
2282
|
-
let vars = mergeVars(envVars, collectionVars, globals2, localVars, dynamicVars);
|
|
2631
|
+
let vars = mergeVars(envVars, collectionVars, globals2, localVars, dynamicVars, folderVars);
|
|
2283
2632
|
let updatedEnvVars = { ...envVars };
|
|
2284
2633
|
let updatedCollectionVars = { ...collectionVars };
|
|
2285
2634
|
let updatedGlobals = { ...globals2 };
|
|
2635
|
+
let updatedFolderVars = { ...folderVars };
|
|
2286
2636
|
let preScriptError;
|
|
2637
|
+
if (req.protocol === "mqtt") {
|
|
2638
|
+
const m = req.mqtt ?? {};
|
|
2639
|
+
const istr = (s) => s ? interpolate(s, vars) : void 0;
|
|
2640
|
+
const mqttStart = Date.now();
|
|
2641
|
+
const testRes = await runMqttTest({
|
|
2642
|
+
url: interpolate(req.url, vars),
|
|
2643
|
+
clientId: istr(m.clientId),
|
|
2644
|
+
username: istr(m.username),
|
|
2645
|
+
password: m.password ? interpolate(m.password, vars) : void 0,
|
|
2646
|
+
cleanSession: m.cleanSession !== false,
|
|
2647
|
+
subscribeTopic: istr(m.subscribeTopic),
|
|
2648
|
+
qos: m.qos,
|
|
2649
|
+
publishTopic: istr(m.publishTopic),
|
|
2650
|
+
publishPayload: istr(m.publishPayload),
|
|
2651
|
+
retain: m.retain,
|
|
2652
|
+
collectMs: m.collectMs ?? 2e3,
|
|
2653
|
+
script: m.testScript ?? "",
|
|
2654
|
+
envVars,
|
|
2655
|
+
collectionVars,
|
|
2656
|
+
globals: globals2,
|
|
2657
|
+
localVars
|
|
2658
|
+
});
|
|
2659
|
+
const status = testRes.error ? "error" : testRes.testResults.length === 0 ? "skipped" : testRes.testResults.every((t) => t.passed) ? "passed" : "failed";
|
|
2660
|
+
return {
|
|
2661
|
+
result: {
|
|
2662
|
+
...base,
|
|
2663
|
+
status,
|
|
2664
|
+
durationMs: Date.now() - mqttStart,
|
|
2665
|
+
testResults: testRes.testResults,
|
|
2666
|
+
consoleOutput: testRes.consoleOutput,
|
|
2667
|
+
error: testRes.error
|
|
2668
|
+
},
|
|
2669
|
+
updatedEnvVars,
|
|
2670
|
+
updatedCollectionVars,
|
|
2671
|
+
updatedGlobals,
|
|
2672
|
+
updatedLocalVars: localVars,
|
|
2673
|
+
updatedFolderVars
|
|
2674
|
+
};
|
|
2675
|
+
}
|
|
2676
|
+
if (req.protocol === "websocket" || req.protocol === "socketio") {
|
|
2677
|
+
const rtHeaders = {};
|
|
2678
|
+
for (const h of req.headers ?? []) if (h.enabled && h.key) rtHeaders[interpolate(h.key, vars)] = interpolate(h.value, vars);
|
|
2679
|
+
const istr = (s) => s ? interpolate(s, vars) : void 0;
|
|
2680
|
+
const rtStart = Date.now();
|
|
2681
|
+
const testRes = req.protocol === "websocket" ? await runWsTest({
|
|
2682
|
+
url: interpolate(req.url, vars),
|
|
2683
|
+
headers: rtHeaders,
|
|
2684
|
+
sendMessage: istr(req.websocket?.sendMessage),
|
|
2685
|
+
collectMs: req.websocket?.collectMs ?? 3e3,
|
|
2686
|
+
script: req.websocket?.testScript ?? "",
|
|
2687
|
+
envVars,
|
|
2688
|
+
collectionVars,
|
|
2689
|
+
globals: globals2,
|
|
2690
|
+
localVars
|
|
2691
|
+
}) : await runSocketIoTest({
|
|
2692
|
+
url: interpolate(req.url, vars),
|
|
2693
|
+
namespace: req.socketio?.namespace,
|
|
2694
|
+
auth: istr(req.socketio?.auth),
|
|
2695
|
+
headers: rtHeaders,
|
|
2696
|
+
emitEvent: istr(req.socketio?.emitEvent),
|
|
2697
|
+
emitArgs: istr(req.socketio?.emitArgs),
|
|
2698
|
+
collectMs: req.socketio?.collectMs ?? 3e3,
|
|
2699
|
+
script: req.socketio?.testScript ?? "",
|
|
2700
|
+
envVars,
|
|
2701
|
+
collectionVars,
|
|
2702
|
+
globals: globals2,
|
|
2703
|
+
localVars
|
|
2704
|
+
});
|
|
2705
|
+
const status = testRes.error ? "error" : testRes.testResults.length === 0 ? "skipped" : testRes.testResults.every((t) => t.passed) ? "passed" : "failed";
|
|
2706
|
+
return {
|
|
2707
|
+
result: {
|
|
2708
|
+
...base,
|
|
2709
|
+
status,
|
|
2710
|
+
durationMs: Date.now() - rtStart,
|
|
2711
|
+
testResults: testRes.testResults,
|
|
2712
|
+
consoleOutput: testRes.consoleOutput,
|
|
2713
|
+
error: testRes.error
|
|
2714
|
+
},
|
|
2715
|
+
updatedEnvVars,
|
|
2716
|
+
updatedCollectionVars,
|
|
2717
|
+
updatedGlobals,
|
|
2718
|
+
updatedLocalVars: localVars,
|
|
2719
|
+
updatedFolderVars
|
|
2720
|
+
};
|
|
2721
|
+
}
|
|
2287
2722
|
let inlineSecrets = {};
|
|
2288
2723
|
try {
|
|
2289
2724
|
inlineSecrets = await resolveInlineSecrets(req);
|
|
@@ -2298,7 +2733,8 @@ async function executeRunnerRequest(opts) {
|
|
|
2298
2733
|
updatedEnvVars,
|
|
2299
2734
|
updatedCollectionVars,
|
|
2300
2735
|
updatedGlobals,
|
|
2301
|
-
updatedLocalVars: localVars
|
|
2736
|
+
updatedLocalVars: localVars,
|
|
2737
|
+
updatedFolderVars
|
|
2302
2738
|
};
|
|
2303
2739
|
}
|
|
2304
2740
|
if (Object.keys(inlineSecrets).length) vars = { ...vars, ...inlineSecrets };
|
|
@@ -2308,6 +2744,7 @@ async function executeRunnerRequest(opts) {
|
|
|
2308
2744
|
collectionVars: { ...collectionVars },
|
|
2309
2745
|
globals: { ...globals2 },
|
|
2310
2746
|
localVars: { ...localVars },
|
|
2747
|
+
folderVars: { ...updatedFolderVars },
|
|
2311
2748
|
piiMaskPatterns
|
|
2312
2749
|
});
|
|
2313
2750
|
preScriptError = r.error;
|
|
@@ -2315,10 +2752,11 @@ async function executeRunnerRequest(opts) {
|
|
|
2315
2752
|
updatedEnvVars = r.updatedEnvVars;
|
|
2316
2753
|
updatedCollectionVars = r.updatedCollectionVars;
|
|
2317
2754
|
updatedGlobals = r.updatedGlobals;
|
|
2755
|
+
updatedFolderVars = r.updatedFolderVars;
|
|
2318
2756
|
patchGlobals(r.updatedGlobals);
|
|
2319
2757
|
await persistGlobals();
|
|
2320
2758
|
onScriptOutput?.("pre", r.consoleOutput, r.error);
|
|
2321
|
-
vars = mergeVars(updatedEnvVars, updatedCollectionVars, updatedGlobals, localVars, dynamicVars);
|
|
2759
|
+
vars = mergeVars(updatedEnvVars, updatedCollectionVars, updatedGlobals, localVars, dynamicVars, updatedFolderVars);
|
|
2322
2760
|
if (Object.keys(inlineSecrets).length) vars = { ...vars, ...inlineSecrets };
|
|
2323
2761
|
}
|
|
2324
2762
|
const resolvedUrl = buildUrl(req.url, req.params, vars);
|
|
@@ -2347,6 +2785,7 @@ async function executeRunnerRequest(opts) {
|
|
|
2347
2785
|
collectionVars: updatedCollectionVars,
|
|
2348
2786
|
globals: updatedGlobals,
|
|
2349
2787
|
localVars,
|
|
2788
|
+
folderVars: updatedFolderVars,
|
|
2350
2789
|
response: scriptResponse,
|
|
2351
2790
|
piiMaskPatterns
|
|
2352
2791
|
});
|
|
@@ -2356,6 +2795,7 @@ async function executeRunnerRequest(opts) {
|
|
|
2356
2795
|
updatedEnvVars = r.updatedEnvVars;
|
|
2357
2796
|
updatedCollectionVars = r.updatedCollectionVars;
|
|
2358
2797
|
updatedGlobals = r.updatedGlobals;
|
|
2798
|
+
updatedFolderVars = r.updatedFolderVars;
|
|
2359
2799
|
localVars = r.updatedLocalVars;
|
|
2360
2800
|
patchGlobals(r.updatedGlobals);
|
|
2361
2801
|
await persistGlobals();
|
|
@@ -2395,7 +2835,8 @@ async function executeRunnerRequest(opts) {
|
|
|
2395
2835
|
updatedEnvVars,
|
|
2396
2836
|
updatedCollectionVars,
|
|
2397
2837
|
updatedGlobals,
|
|
2398
|
-
updatedLocalVars: localVars
|
|
2838
|
+
updatedLocalVars: localVars,
|
|
2839
|
+
updatedFolderVars
|
|
2399
2840
|
};
|
|
2400
2841
|
} catch (err) {
|
|
2401
2842
|
return {
|
|
@@ -2409,7 +2850,8 @@ async function executeRunnerRequest(opts) {
|
|
|
2409
2850
|
updatedEnvVars,
|
|
2410
2851
|
updatedCollectionVars,
|
|
2411
2852
|
updatedGlobals,
|
|
2412
|
-
updatedLocalVars: localVars
|
|
2853
|
+
updatedLocalVars: localVars,
|
|
2854
|
+
updatedFolderVars
|
|
2413
2855
|
};
|
|
2414
2856
|
}
|
|
2415
2857
|
}
|
|
@@ -2491,7 +2933,11 @@ exports.registerSecretProvider = registerSecretProvider;
|
|
|
2491
2933
|
exports.registeredSchemes = registeredSchemes;
|
|
2492
2934
|
exports.resolveDataRow = resolveDataRow;
|
|
2493
2935
|
exports.resolveExternalSecret = resolveExternalSecret;
|
|
2936
|
+
exports.runGraphqlWsTest = runGraphqlWsTest;
|
|
2937
|
+
exports.runMqttTest = runMqttTest;
|
|
2494
2938
|
exports.runScript = runScript;
|
|
2939
|
+
exports.runSocketIoTest = runSocketIoTest;
|
|
2940
|
+
exports.runWsTest = runWsTest;
|
|
2495
2941
|
exports.setGlobals = setGlobals;
|
|
2496
2942
|
exports.setSecretsConfig = setSecretsConfig;
|
|
2497
2943
|
exports.syntheticHttpFailure = syntheticHttpFailure;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
async function runPlanConcurrent(concurrency, items, isBarrier, run) {
|
|
3
|
+
const n = Math.max(1, Math.floor(concurrency));
|
|
4
|
+
const pool = [];
|
|
5
|
+
const settleOne = async () => {
|
|
6
|
+
const done = await Promise.race(pool.map((x) => x.p));
|
|
7
|
+
const pi = pool.findIndex((x) => x.i === done);
|
|
8
|
+
if (pi >= 0) pool.splice(pi, 1);
|
|
9
|
+
};
|
|
10
|
+
const drain = async () => {
|
|
11
|
+
while (pool.length) await settleOne();
|
|
12
|
+
};
|
|
13
|
+
for (let i = 0; i < items.length; i++) {
|
|
14
|
+
const item = items[i];
|
|
15
|
+
if (isBarrier(item)) {
|
|
16
|
+
await drain();
|
|
17
|
+
await run(item, i);
|
|
18
|
+
} else {
|
|
19
|
+
const idx = i;
|
|
20
|
+
pool.push({ p: run(item, idx).then(() => idx), i: idx });
|
|
21
|
+
if (pool.length >= n) await settleOne();
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
await drain();
|
|
25
|
+
}
|
|
26
|
+
exports.runPlanConcurrent = runPlanConcurrent;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
const handle = require("./handle-
|
|
2
|
+
const handle = require("./handle-Crv1i9hb.js");
|
|
3
3
|
const https = require("node:https");
|
|
4
4
|
const http = require("node:http");
|
|
5
5
|
const xmldom = require("@xmldom/xmldom");
|
|
@@ -313,7 +313,7 @@ function registerSoapHandlers(ipc) {
|
|
|
313
313
|
handle.handleIpc(ipc, handle.IPC.wsdl.import, async (_event, opts) => {
|
|
314
314
|
const { validateWsdlImport } = await Promise.resolve().then(() => require("./ipc-validate-sqUFqRmE.js"));
|
|
315
315
|
validateWsdlImport(opts);
|
|
316
|
-
const { importWsdl } = await Promise.resolve().then(() => require("./import-
|
|
316
|
+
const { importWsdl } = await Promise.resolve().then(() => require("./import-DTcZMRNu.js"));
|
|
317
317
|
const wsdlText = opts.xml ?? (opts.url ? await fetchUrl(opts.url) : "");
|
|
318
318
|
if (!wsdlText) throw new Error("wsdl:import requires either `url` or `xml`");
|
|
319
319
|
return importWsdl(wsdlText, { name: opts.name, existingMockPorts: opts.existingMockPorts });
|