@loadstrike/loadstrike-sdk 0.1.10001 → 1.0.10801

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/dist/esm/sinks.js CHANGED
@@ -235,13 +235,8 @@ export class MemoryReportingSink {
235
235
  SaveRealtimeMetrics(metrics) {
236
236
  this.saveRealtimeMetrics(metrics);
237
237
  }
238
- saveFinalStats(result) {
239
- this.finalResults.push(cloneNodeStats(result));
240
- }
241
- SaveFinalStats(result) {
242
- this.saveFinalStats(result);
243
- }
244
238
  saveRunResult(result) {
239
+ this.finalResults.push(cloneRunResult(result));
245
240
  this.runResults.push(deepCloneRecord(result));
246
241
  }
247
242
  SaveRunResult(result) {
@@ -283,13 +278,9 @@ export class ConsoleReportingSink {
283
278
  SaveRealtimeMetrics(metrics) {
284
279
  this.saveRealtimeMetrics(metrics);
285
280
  }
286
- saveFinalStats(result) {
281
+ saveRunResult(result) {
287
282
  this.writeLine(`final requests=${result.allRequestCount} ok=${result.allOkCount} fail=${result.allFailCount} failedThresholds=${result.failedThresholds}`);
288
283
  }
289
- SaveFinalStats(result) {
290
- this.saveFinalStats(result);
291
- }
292
- saveRunResult(_result) { }
293
284
  SaveRunResult(result) {
294
285
  this.saveRunResult(result);
295
286
  }
@@ -358,17 +349,6 @@ export class CompositeReportingSink {
358
349
  async SaveRealtimeMetrics(metrics) {
359
350
  await this.saveRealtimeMetrics(metrics);
360
351
  }
361
- async saveFinalStats(result) {
362
- for (const sink of this.sinks) {
363
- const saveFinalStats = sink.saveFinalStats ?? sink.SaveFinalStats;
364
- if (saveFinalStats) {
365
- await saveFinalStats.call(sink, result);
366
- }
367
- }
368
- }
369
- async SaveFinalStats(result) {
370
- await this.saveFinalStats(result);
371
- }
372
352
  async saveRunResult(result) {
373
353
  for (const sink of this.sinks) {
374
354
  const saveRunResult = sink.saveRunResult ?? sink.SaveRunResult;
@@ -454,12 +434,6 @@ export class InfluxDbReportingSink {
454
434
  async SaveRealtimeMetrics(metrics) {
455
435
  await this.saveRealtimeMetrics(metrics);
456
436
  }
457
- async saveFinalStats(result) {
458
- await this.persistEvents(createFinalStatsEvents(this.getSession(), result));
459
- }
460
- async SaveFinalStats(result) {
461
- await this.saveFinalStats(result);
462
- }
463
437
  async saveRunResult(result) {
464
438
  await this.persistEvents(createRunResultEvents(this.getSession(), result));
465
439
  }
@@ -569,12 +543,6 @@ export class GrafanaLokiReportingSink {
569
543
  async SaveRealtimeMetrics(metrics) {
570
544
  await this.saveRealtimeMetrics(metrics);
571
545
  }
572
- async saveFinalStats(result) {
573
- await this.persistEvents(createFinalStatsEvents(this.getSession(), result));
574
- }
575
- async SaveFinalStats(result) {
576
- await this.saveFinalStats(result);
577
- }
578
546
  async saveRunResult(result) {
579
547
  await this.persistEvents(createRunResultEvents(this.getSession(), result));
580
548
  }
@@ -720,12 +688,6 @@ export class TimescaleDbReportingSink {
720
688
  async SaveRealtimeMetrics(metrics) {
721
689
  await this.saveRealtimeMetrics(metrics);
722
690
  }
723
- async saveFinalStats(result) {
724
- await this.persistEvents(createFinalStatsEvents(this.getSession(), result));
725
- }
726
- async SaveFinalStats(result) {
727
- await this.saveFinalStats(result);
728
- }
729
691
  async saveRunResult(result) {
730
692
  await this.persistEvents(createRunResultEvents(this.getSession(), result));
731
693
  }
@@ -1001,12 +963,6 @@ export class DatadogReportingSink {
1001
963
  async SaveRealtimeMetrics(metrics) {
1002
964
  await this.saveRealtimeMetrics(metrics);
1003
965
  }
1004
- async saveFinalStats(result) {
1005
- await this.persistEvents(createFinalStatsEvents(this.getSession(), result));
1006
- }
1007
- async SaveFinalStats(result) {
1008
- await this.saveFinalStats(result);
1009
- }
1010
966
  async saveRunResult(result) {
1011
967
  await this.persistEvents(createRunResultEvents(this.getSession(), result));
1012
968
  }
@@ -1129,12 +1085,6 @@ export class SplunkReportingSink {
1129
1085
  async SaveRealtimeMetrics(metrics) {
1130
1086
  await this.saveRealtimeMetrics(metrics);
1131
1087
  }
1132
- async saveFinalStats(result) {
1133
- await this.persistEvents(createFinalStatsEvents(this.getSession(), result));
1134
- }
1135
- async SaveFinalStats(result) {
1136
- await this.saveFinalStats(result);
1137
- }
1138
1088
  async saveRunResult(result) {
1139
1089
  await this.persistEvents(createRunResultEvents(this.getSession(), result));
1140
1090
  }
@@ -1237,12 +1187,6 @@ export class OtelCollectorReportingSink {
1237
1187
  async SaveRealtimeMetrics(metrics) {
1238
1188
  await this.saveRealtimeMetrics(metrics);
1239
1189
  }
1240
- async saveFinalStats(result) {
1241
- await this.persistEvents(createFinalStatsEvents(this.getSession(), result));
1242
- }
1243
- async SaveFinalStats(result) {
1244
- await this.saveFinalStats(result);
1245
- }
1246
1190
  async saveRunResult(result) {
1247
1191
  await this.persistEvents(createRunResultEvents(this.getSession(), result));
1248
1192
  }
@@ -1356,6 +1300,7 @@ function createFinalStatsEvents(session, stats) {
1356
1300
  function createRunResultEvents(session, result) {
1357
1301
  const occurredUtc = new Date();
1358
1302
  const events = [
1303
+ ...createFinalStatsEvents(session, runResultToNodeStats(result)),
1359
1304
  createReportingEvent(session, occurredUtc, "run.result.final", null, null, {
1360
1305
  phase: "final",
1361
1306
  entity: "run-result"
@@ -1450,7 +1395,8 @@ function createStepEvents(session, occurredUtc, phase, scenario) {
1450
1395
  return events;
1451
1396
  }
1452
1397
  function createStatusCodeEvents(session, occurredUtc, phase, scenarioName, stepName, resultKind, statusCodes) {
1453
- return statusCodes.map((statusCode) => createReportingEvent(session, occurredUtc, `status-code.${phase}`, scenarioName, stepName, {
1398
+ const normalizedStatusCodes = Array.isArray(statusCodes) ? statusCodes : [];
1399
+ return normalizedStatusCodes.map((statusCode) => createReportingEvent(session, occurredUtc, `status-code.${phase}`, scenarioName, stepName, {
1454
1400
  phase,
1455
1401
  entity: stepName ? "step-status-code" : "scenario-status-code",
1456
1402
  result_kind: resultKind,
@@ -1583,30 +1529,63 @@ function createReportingEvent(session, occurredUtc, eventType, scenarioName, ste
1583
1529
  };
1584
1530
  }
1585
1531
  function addMeasurementFields(fields, prefix, measurement) {
1586
- fields[`${prefix}_request_count`] = measurement.request.count;
1587
- fields[`${prefix}_request_percent`] = measurement.request.percent;
1588
- fields[`${prefix}_request_rps`] = measurement.request.rps;
1589
- fields[`${prefix}_latency_min_ms`] = measurement.latency.minMs;
1590
- fields[`${prefix}_latency_mean_ms`] = measurement.latency.meanMs;
1591
- fields[`${prefix}_latency_max_ms`] = measurement.latency.maxMs;
1592
- fields[`${prefix}_latency_p50_ms`] = measurement.latency.percent50;
1593
- fields[`${prefix}_latency_p75_ms`] = measurement.latency.percent75;
1594
- fields[`${prefix}_latency_p95_ms`] = measurement.latency.percent95;
1595
- fields[`${prefix}_latency_p99_ms`] = measurement.latency.percent99;
1596
- fields[`${prefix}_latency_std_dev`] = measurement.latency.stdDev;
1597
- fields[`${prefix}_latency_le_800_count`] = measurement.latency.latencyCount.lessOrEq800;
1598
- fields[`${prefix}_latency_gt_800_lt_1200_count`] = measurement.latency.latencyCount.more800Less1200;
1599
- fields[`${prefix}_latency_ge_1200_count`] = measurement.latency.latencyCount.moreOrEq1200;
1600
- fields[`${prefix}_bytes_all`] = measurement.dataTransfer.allBytes;
1601
- fields[`${prefix}_bytes_min`] = measurement.dataTransfer.minBytes;
1602
- fields[`${prefix}_bytes_mean`] = measurement.dataTransfer.meanBytes;
1603
- fields[`${prefix}_bytes_max`] = measurement.dataTransfer.maxBytes;
1604
- fields[`${prefix}_bytes_p50`] = measurement.dataTransfer.percent50;
1605
- fields[`${prefix}_bytes_p75`] = measurement.dataTransfer.percent75;
1606
- fields[`${prefix}_bytes_p95`] = measurement.dataTransfer.percent95;
1607
- fields[`${prefix}_bytes_p99`] = measurement.dataTransfer.percent99;
1608
- fields[`${prefix}_bytes_std_dev`] = measurement.dataTransfer.stdDev;
1609
- fields[`${prefix}_status_code_count`] = measurement.statusCodes.length;
1532
+ const request = measurement?.request ?? { count: 0, percent: 0, rps: 0 };
1533
+ const latency = measurement?.latency ?? {
1534
+ minMs: 0,
1535
+ meanMs: 0,
1536
+ maxMs: 0,
1537
+ percent50: 0,
1538
+ percent75: 0,
1539
+ percent95: 0,
1540
+ percent99: 0,
1541
+ stdDev: 0,
1542
+ latencyCount: {
1543
+ lessOrEq800: 0,
1544
+ more800Less1200: 0,
1545
+ moreOrEq1200: 0
1546
+ }
1547
+ };
1548
+ const latencyCount = latency.latencyCount ?? {
1549
+ lessOrEq800: 0,
1550
+ more800Less1200: 0,
1551
+ moreOrEq1200: 0
1552
+ };
1553
+ const dataTransfer = measurement?.dataTransfer ?? {
1554
+ allBytes: 0,
1555
+ minBytes: 0,
1556
+ meanBytes: 0,
1557
+ maxBytes: 0,
1558
+ percent50: 0,
1559
+ percent75: 0,
1560
+ percent95: 0,
1561
+ percent99: 0,
1562
+ stdDev: 0
1563
+ };
1564
+ const statusCodes = Array.isArray(measurement?.statusCodes) ? measurement.statusCodes : [];
1565
+ fields[`${prefix}_request_count`] = request.count ?? 0;
1566
+ fields[`${prefix}_request_percent`] = request.percent ?? 0;
1567
+ fields[`${prefix}_request_rps`] = request.rps ?? 0;
1568
+ fields[`${prefix}_latency_min_ms`] = latency.minMs ?? 0;
1569
+ fields[`${prefix}_latency_mean_ms`] = latency.meanMs ?? 0;
1570
+ fields[`${prefix}_latency_max_ms`] = latency.maxMs ?? 0;
1571
+ fields[`${prefix}_latency_p50_ms`] = latency.percent50 ?? 0;
1572
+ fields[`${prefix}_latency_p75_ms`] = latency.percent75 ?? 0;
1573
+ fields[`${prefix}_latency_p95_ms`] = latency.percent95 ?? 0;
1574
+ fields[`${prefix}_latency_p99_ms`] = latency.percent99 ?? 0;
1575
+ fields[`${prefix}_latency_std_dev`] = latency.stdDev ?? 0;
1576
+ fields[`${prefix}_latency_le_800_count`] = latencyCount.lessOrEq800 ?? 0;
1577
+ fields[`${prefix}_latency_gt_800_lt_1200_count`] = latencyCount.more800Less1200 ?? 0;
1578
+ fields[`${prefix}_latency_ge_1200_count`] = latencyCount.moreOrEq1200 ?? 0;
1579
+ fields[`${prefix}_bytes_all`] = dataTransfer.allBytes ?? 0;
1580
+ fields[`${prefix}_bytes_min`] = dataTransfer.minBytes ?? 0;
1581
+ fields[`${prefix}_bytes_mean`] = dataTransfer.meanBytes ?? 0;
1582
+ fields[`${prefix}_bytes_max`] = dataTransfer.maxBytes ?? 0;
1583
+ fields[`${prefix}_bytes_p50`] = dataTransfer.percent50 ?? 0;
1584
+ fields[`${prefix}_bytes_p75`] = dataTransfer.percent75 ?? 0;
1585
+ fields[`${prefix}_bytes_p95`] = dataTransfer.percent95 ?? 0;
1586
+ fields[`${prefix}_bytes_p99`] = dataTransfer.percent99 ?? 0;
1587
+ fields[`${prefix}_bytes_std_dev`] = dataTransfer.stdDev ?? 0;
1588
+ fields[`${prefix}_status_code_count`] = statusCodes.length;
1610
1589
  }
1611
1590
  function buildInfluxWriteUri(options) {
1612
1591
  const query = new URLSearchParams({
@@ -2578,28 +2557,28 @@ function cloneSessionStartInfo(session) {
2578
2557
  };
2579
2558
  }
2580
2559
  function cloneNodeInfo(nodeInfo) {
2581
- return { ...nodeInfo };
2560
+ return { ...(nodeInfo ?? {}) };
2582
2561
  }
2583
2562
  function cloneTestInfo(testInfo) {
2584
- return { ...testInfo };
2563
+ return { ...(testInfo ?? {}) };
2585
2564
  }
2586
2565
  function cloneMetricStats(metrics) {
2587
2566
  return {
2588
- counters: metrics.counters.map((value) => ({ ...value })),
2589
- gauges: metrics.gauges.map((value) => ({ ...value })),
2590
- durationMs: metrics.durationMs
2567
+ counters: (metrics?.counters ?? []).map((value) => ({ ...value })),
2568
+ gauges: (metrics?.gauges ?? []).map((value) => ({ ...value })),
2569
+ durationMs: metrics?.durationMs ?? 0
2591
2570
  };
2592
2571
  }
2593
2572
  function cloneScenarioStats(value) {
2594
2573
  return {
2595
2574
  ...value,
2596
- ok: deepCloneRecord(value.ok),
2597
- fail: deepCloneRecord(value.fail),
2598
- loadSimulationStats: { ...value.loadSimulationStats },
2599
- stepStats: value.stepStats.map((step) => ({
2575
+ ok: deepCloneRecord(value?.ok ?? {}),
2576
+ fail: deepCloneRecord(value?.fail ?? {}),
2577
+ loadSimulationStats: { ...(value?.loadSimulationStats ?? {}) },
2578
+ stepStats: (value?.stepStats ?? []).map((step) => ({
2600
2579
  ...step,
2601
- ok: deepCloneRecord(step.ok),
2602
- fail: deepCloneRecord(step.fail)
2580
+ ok: deepCloneRecord(step?.ok ?? {}),
2581
+ fail: deepCloneRecord(step?.fail ?? {})
2603
2582
  }))
2604
2583
  };
2605
2584
  }
@@ -2608,20 +2587,91 @@ function cloneNodeStats(result) {
2608
2587
  ...result,
2609
2588
  nodeInfo: cloneNodeInfo(result.nodeInfo),
2610
2589
  testInfo: cloneTestInfo(result.testInfo),
2611
- thresholds: result.thresholds.map((value) => ({ ...value })),
2612
- thresholdResults: result.thresholdResults.map((value) => ({ ...value })),
2590
+ thresholds: (result.thresholds ?? []).map((value) => ({ ...value })),
2591
+ thresholdResults: (result.thresholdResults ?? []).map((value) => ({ ...value })),
2613
2592
  metrics: cloneMetricStats(result.metrics),
2614
- metricValues: result.metricValues.map((value) => ({ ...value })),
2615
- scenarioStats: result.scenarioStats.map((value) => cloneScenarioStats(value)),
2616
- stepStats: result.stepStats.map((step) => ({
2593
+ metricValues: Array.isArray(result.metricValues) ? result.metricValues.map((value) => ({ ...value })) : [],
2594
+ scenarioStats: (result.scenarioStats ?? []).map((value) => cloneScenarioStats(value)),
2595
+ stepStats: (result.stepStats ?? []).map((step) => ({
2596
+ ...step,
2597
+ ok: deepCloneRecord(step?.ok ?? {}),
2598
+ fail: deepCloneRecord(step?.fail ?? {})
2599
+ })),
2600
+ pluginsData: (result.pluginsData ?? []).map((plugin) => new LoadStrikePluginDataModel(plugin.pluginName, (plugin.tables ?? []).map((table) => new LoadStrikePluginDataTableModel(table.tableName, [...(table.headers ?? [])], (table.rows ?? []).map((row) => deepCloneRecord(row)))), [...(plugin.hints ?? [])])),
2601
+ disabledSinks: [...(result.disabledSinks ?? [])],
2602
+ sinkErrors: (result.sinkErrors ?? []).map((value) => ({ ...value })),
2603
+ reportFiles: [...(result.reportFiles ?? [])],
2604
+ logFiles: [...(result.logFiles ?? [])]
2605
+ };
2606
+ }
2607
+ function cloneRunResult(result) {
2608
+ return {
2609
+ ...result,
2610
+ nodeInfo: cloneNodeInfo(result.nodeInfo),
2611
+ testInfo: cloneTestInfo(result.testInfo),
2612
+ thresholds: (result.thresholds ?? []).map((value) => ({ ...value })),
2613
+ thresholdResults: (result.thresholdResults ?? []).map((value) => ({ ...value })),
2614
+ metricStats: cloneMetricStats(result.metricStats),
2615
+ metrics: Array.isArray(result.metrics) ? result.metrics.map((value) => ({ ...value })) : [],
2616
+ scenarioStats: (result.scenarioStats ?? []).map((value) => cloneScenarioStats(value)),
2617
+ stepStats: (result.stepStats ?? []).map((step) => ({
2617
2618
  ...step,
2618
- ok: deepCloneRecord(step.ok),
2619
- fail: deepCloneRecord(step.fail)
2619
+ ok: deepCloneRecord(step?.ok ?? {}),
2620
+ fail: deepCloneRecord(step?.fail ?? {})
2620
2621
  })),
2621
- pluginsData: result.pluginsData.map((plugin) => new LoadStrikePluginDataModel(plugin.pluginName, plugin.tables.map((table) => new LoadStrikePluginDataTableModel(table.tableName, [...table.headers], table.rows.map((row) => deepCloneRecord(row)))), [...plugin.hints])),
2622
- disabledSinks: [...result.disabledSinks],
2623
- sinkErrors: result.sinkErrors.map((value) => ({ ...value })),
2624
- reportFiles: [...result.reportFiles]
2622
+ scenarioDurationsMs: { ...(result.scenarioDurationsMs ?? {}) },
2623
+ pluginsData: (result.pluginsData ?? []).map((plugin) => new LoadStrikePluginDataModel(plugin.pluginName, (plugin.tables ?? []).map((table) => new LoadStrikePluginDataTableModel(table.tableName, [...(table.headers ?? [])], (table.rows ?? []).map((row) => deepCloneRecord(row)))), [...(plugin.hints ?? [])])),
2624
+ disabledSinks: [...(result.disabledSinks ?? [])],
2625
+ sinkErrors: (result.sinkErrors ?? []).map((value) => ({ ...value })),
2626
+ reportFiles: [...(result.reportFiles ?? [])],
2627
+ logFiles: [...(result.logFiles ?? [])],
2628
+ correlationRows: (result.correlationRows ?? []).map((row) => deepCloneRecord(row)),
2629
+ failedCorrelationRows: (result.failedCorrelationRows ?? []).map((row) => deepCloneRecord(row))
2630
+ };
2631
+ }
2632
+ function runResultToNodeStats(result) {
2633
+ return {
2634
+ startedUtc: result.startedUtc,
2635
+ completedUtc: result.completedUtc,
2636
+ allBytes: result.allBytes ?? 0,
2637
+ allRequestCount: result.allRequestCount ?? 0,
2638
+ allOkCount: result.allOkCount ?? 0,
2639
+ allFailCount: result.allFailCount ?? 0,
2640
+ failedThresholds: result.failedThresholds ?? 0,
2641
+ durationMs: result.durationMs ?? 0,
2642
+ nodeInfo: cloneNodeInfo(result.nodeInfo),
2643
+ testInfo: cloneTestInfo(result.testInfo),
2644
+ thresholds: (result.thresholds ?? []).map((value) => ({ ...value })),
2645
+ thresholdResults: (result.thresholdResults ?? []).map((value) => ({ ...value })),
2646
+ metrics: cloneMetricStats(result.metricStats),
2647
+ metricValues: Array.isArray(result.metrics) ? result.metrics.map((value) => ({ ...value })) : [],
2648
+ scenarioStats: (result.scenarioStats ?? []).map((value) => cloneScenarioStats(value)),
2649
+ stepStats: (result.stepStats ?? []).map((step) => ({
2650
+ ...step,
2651
+ ok: deepCloneRecord(step?.ok ?? {}),
2652
+ fail: deepCloneRecord(step?.fail ?? {})
2653
+ })),
2654
+ pluginsData: (result.pluginsData ?? []).map((plugin) => new LoadStrikePluginDataModel(plugin.pluginName, (plugin.tables ?? []).map((table) => new LoadStrikePluginDataTableModel(table.tableName, [...(table.headers ?? [])], (table.rows ?? []).map((row) => deepCloneRecord(row)))), [...(plugin.hints ?? [])])),
2655
+ disabledSinks: [...(result.disabledSinks ?? [])],
2656
+ sinkErrors: (result.sinkErrors ?? []).map((value) => ({ ...value })),
2657
+ reportFiles: [...(result.reportFiles ?? [])],
2658
+ logFiles: [...(result.logFiles ?? [])],
2659
+ findScenarioStats: (scenarioName) => result.scenarioStats.find((value) => value.scenarioName === scenarioName),
2660
+ getScenarioStats: (scenarioName) => {
2661
+ const scenario = result.scenarioStats.find((value) => value.scenarioName === scenarioName);
2662
+ if (!scenario) {
2663
+ throw new Error(`Scenario stats not found: ${scenarioName}`);
2664
+ }
2665
+ return scenario;
2666
+ },
2667
+ FindScenarioStats: (scenarioName) => result.scenarioStats.find((value) => value.scenarioName === scenarioName),
2668
+ GetScenarioStats: (scenarioName) => {
2669
+ const scenario = result.scenarioStats.find((value) => value.scenarioName === scenarioName);
2670
+ if (!scenario) {
2671
+ throw new Error(`Scenario stats not found: ${scenarioName}`);
2672
+ }
2673
+ return scenario;
2674
+ }
2625
2675
  };
2626
2676
  }
2627
2677
  function deepCloneRecord(value) {
@@ -2655,3 +2705,47 @@ async function postWithTimeout(fetchImpl, url, init, timeoutMs, sinkName) {
2655
2705
  clearTimeout(timer);
2656
2706
  }
2657
2707
  }
2708
+ export const __loadstrikeTestExports = {
2709
+ GrafanaLokiReportingSink,
2710
+ InfluxDbReportingSink,
2711
+ OtelCollectorReportingSink,
2712
+ SplunkReportingSink,
2713
+ TimescaleDbReportingSink,
2714
+ cleanNullableText,
2715
+ cloneBaseContext,
2716
+ cloneMetricStats,
2717
+ cloneNodeStats,
2718
+ cloneScenarioStats,
2719
+ cloneSessionStartInfo,
2720
+ createFinalStatsEvents,
2721
+ createRealtimeStatsEvents,
2722
+ createRunResultEvents,
2723
+ createReportingEvent,
2724
+ mergeDatadogOptions,
2725
+ mergeInfluxOptions,
2726
+ mergeGrafanaLokiOptions,
2727
+ mergeOtelCollectorOptions,
2728
+ mergeSplunkOptions,
2729
+ mergeTimescaleDbOptions,
2730
+ normalizeConfigKey,
2731
+ normalizePath,
2732
+ normalizePluginFieldName,
2733
+ normalizePluginFieldValue,
2734
+ normalizeStringMap,
2735
+ optionNumber,
2736
+ optionString,
2737
+ pickBooleanValue,
2738
+ pickRecordValue,
2739
+ postWithTimeout,
2740
+ quoteIdentifier,
2741
+ replaceLiteral,
2742
+ resolveConfigSection,
2743
+ resolveTimeoutMs,
2744
+ sanitizeGrafanaLabelKey,
2745
+ sanitizeGrafanaLabelValue,
2746
+ sinkSessionMetadataFromContext,
2747
+ toOtelAnyValue,
2748
+ trimTrailingSlashes,
2749
+ tryReadText,
2750
+ validateIdentifier
2751
+ };
@@ -211,6 +211,9 @@ class AzureEventHubsEndpointDefinitionModel extends TrafficEndpointDefinitionMod
211
211
  super.Validate();
212
212
  requireNonEmptyString(this.ConnectionString, "ConnectionString must be provided for Azure Event Hubs endpoint.");
213
213
  requireNonEmptyString(this.EventHubName, "EventHubName must be provided for Azure Event Hubs endpoint.");
214
+ if (this.PartitionCount != null && this.PartitionCount < 0) {
215
+ throw new RangeError("PartitionCount must be zero or greater when configured.");
216
+ }
214
217
  }
215
218
  }
216
219
  class DelegateStreamEndpointDefinitionModel extends TrafficEndpointDefinitionModel {
@@ -614,6 +617,12 @@ function initializeAzureEventHubsEndpointDefinitionModel(target, initial) {
614
617
  if (hasAnyEndpointField(raw, ["PartitionId", "partitionId"])) {
615
618
  target.PartitionId = pickOptionalEndpointString(raw, "PartitionId", "partitionId");
616
619
  }
620
+ if (hasAnyEndpointField(raw, ["PartitionKey", "partitionKey"])) {
621
+ target.PartitionKey = pickOptionalEndpointString(raw, "PartitionKey", "partitionKey");
622
+ }
623
+ if (hasAnyEndpointField(raw, ["PartitionCount", "partitionCount"])) {
624
+ target.PartitionCount = pickOptionalEndpointNumber(raw, "PartitionCount", "partitionCount");
625
+ }
617
626
  }
618
627
  function initializeDelegateStreamEndpointDefinitionModel(target, initial) {
619
628
  const raw = asRecordOrEmpty(initial);
@@ -704,10 +713,9 @@ function validateHttpAuthOptionsModel(target) {
704
713
  }
705
714
  return;
706
715
  case "oauth2clientcredentials":
707
- if (!target.OAuth2ClientCredentials) {
708
- throw new Error("OAuth2ClientCredentials must be provided for OAuth2 client-credentials auth.");
709
- }
710
- toHttpOAuth2ClientCredentialsOptionsModel(target.OAuth2ClientCredentials).Validate();
716
+ requireNonEmptyString(target.TokenUrl ?? target.OAuth2ClientCredentials?.TokenEndpoint, "TokenEndpoint must be provided for OAuth2 client credentials flow.");
717
+ requireNonEmptyString(target.ClientId ?? target.OAuth2ClientCredentials?.ClientId, "ClientId must be provided for OAuth2 client credentials flow.");
718
+ requireNonEmptyString(target.ClientSecret ?? target.OAuth2ClientCredentials?.ClientSecret, "ClientSecret must be provided for OAuth2 client credentials flow.");
711
719
  return;
712
720
  default:
713
721
  throw new RangeError("Unsupported HTTP auth type.");
@@ -897,8 +905,7 @@ const protocolBus = new class InMemoryProtocolBus {
897
905
  produceEventHub(endpoint, payload) {
898
906
  const options = endpoint.azureEventHubs ?? {};
899
907
  const hubName = optionString(options, "EventHubName", "eventHubName") || endpoint.name;
900
- const partitionId = optionString(options, "PartitionId", "partitionId")
901
- || partitionFromKey(optionString(options, "PartitionKey", "partitionKey"), optionNumber(options, "PartitionCount", "partitionCount") || 4);
908
+ const partitionId = resolveEventHubProducePartitionId(options) || "";
902
909
  const rows = this.eventHubs.get(hubName) ?? [];
903
910
  rows.push({
904
911
  partitionId,
@@ -1124,10 +1131,10 @@ class HttpEndpointAdapter extends CallbackAdapter {
1124
1131
  body.set("grant_type", "client_credentials");
1125
1132
  body.set("client_id", clientId);
1126
1133
  body.set("client_secret", clientSecret);
1127
- const scopes = Array.isArray(oauthOptions?.scopes)
1128
- ? oauthOptions.scopes.map((x) => String(x).trim()).filter((x) => x.length > 0)
1129
- : Array.isArray(auth?.scopes)
1130
- ? auth.scopes.map((x) => String(x).trim()).filter((x) => x.length > 0)
1134
+ const scopes = Array.isArray(auth?.scopes)
1135
+ ? auth.scopes.map((x) => String(x).trim()).filter((x) => x.length > 0)
1136
+ : Array.isArray(oauthOptions?.scopes)
1137
+ ? oauthOptions.scopes.map((x) => String(x).trim()).filter((x) => x.length > 0)
1131
1138
  : [];
1132
1139
  const scopeText = typeof auth?.scope === "string" && auth.scope.trim()
1133
1140
  ? auth.scope.trim()
@@ -1139,8 +1146,8 @@ class HttpEndpointAdapter extends CallbackAdapter {
1139
1146
  body.set("audience", auth.audience.trim());
1140
1147
  }
1141
1148
  for (const [key, value] of Object.entries({
1142
- ...(auth?.additionalFormFields ?? {}),
1143
- ...(oauthOptions?.additionalFormFields ?? {})
1149
+ ...(oauthOptions?.additionalFormFields ?? {}),
1150
+ ...(auth?.additionalFormFields ?? {})
1144
1151
  })) {
1145
1152
  if (key.trim() && typeof value === "string") {
1146
1153
  body.set(key.trim(), value);
@@ -1566,13 +1573,15 @@ class AzureEventHubsEndpointAdapter extends CallbackAdapter {
1566
1573
  const options = this.endpoint.azureEventHubs ?? {};
1567
1574
  const resolved = prepareProducedPayload(this.endpoint, payload);
1568
1575
  const wire = toWirePayload(resolved, this.endpoint);
1569
- const batch = await producer.createBatch();
1576
+ const partitionId = resolveEventHubProducePartitionId(options);
1577
+ const batch = partitionId
1578
+ ? await producer.createBatch({ partitionId })
1579
+ : await producer.createBatch();
1570
1580
  const eventData = {
1571
1581
  body: Buffer.from(wire.body),
1572
1582
  contentType: wire.contentType,
1573
1583
  properties: { ...wire.headers }
1574
1584
  };
1575
- const partitionId = optionString(options, "PartitionId", "partitionId");
1576
1585
  if (partitionId) {
1577
1586
  eventData.partitionId = partitionId;
1578
1587
  }
@@ -1798,6 +1807,10 @@ const AZURE_EVENT_HUBS_ENDPOINT_FLAT_KEYS = [
1798
1807
  "consumerGroup",
1799
1808
  "PartitionId",
1800
1809
  "partitionId",
1810
+ "PartitionKey",
1811
+ "partitionKey",
1812
+ "PartitionCount",
1813
+ "partitionCount",
1801
1814
  "StartFromEarliest",
1802
1815
  "startFromEarliest"
1803
1816
  ];
@@ -1910,7 +1923,11 @@ function resolveEndpointKind(raw) {
1910
1923
  "EventHubName",
1911
1924
  "eventHubName",
1912
1925
  "PartitionId",
1913
- "partitionId"
1926
+ "partitionId",
1927
+ "PartitionKey",
1928
+ "partitionKey",
1929
+ "PartitionCount",
1930
+ "partitionCount"
1914
1931
  ])) {
1915
1932
  return "AzureEventHubs";
1916
1933
  }
@@ -2411,6 +2428,10 @@ function validateAzureEventHubsEndpoint(endpoint, mode, hasModeDelegate) {
2411
2428
  }
2412
2429
  requireNonEmptyString(optionString(options, "ConnectionString", "connectionString"), "ConnectionString must be provided for Azure Event Hubs endpoint.");
2413
2430
  requireNonEmptyString(optionString(options, "EventHubName", "eventHubName"), "EventHubName must be provided for Azure Event Hubs endpoint.");
2431
+ const partitionCount = optionNumber(options, "PartitionCount", "partitionCount");
2432
+ if (partitionCount < 0) {
2433
+ throw new RangeError("PartitionCount must be zero or greater when configured.");
2434
+ }
2414
2435
  }
2415
2436
  function validatePushDiffusionEndpoint(endpoint, mode) {
2416
2437
  const options = endpoint.pushDiffusion;
@@ -2810,6 +2831,17 @@ function partitionFromKey(value, partitionCount) {
2810
2831
  const normalized = Math.abs(hash) % count;
2811
2832
  return String(normalized);
2812
2833
  }
2834
+ function resolveEventHubProducePartitionId(options) {
2835
+ const explicitPartitionId = optionString(options, "PartitionId", "partitionId");
2836
+ if (explicitPartitionId) {
2837
+ return explicitPartitionId;
2838
+ }
2839
+ const partitionKey = optionString(options, "PartitionKey", "partitionKey");
2840
+ if (!partitionKey) {
2841
+ return undefined;
2842
+ }
2843
+ return partitionFromKey(partitionKey, optionNumber(options, "PartitionCount", "partitionCount") || 4);
2844
+ }
2813
2845
  function attachPayloadHelpers(payload) {
2814
2846
  const target = {
2815
2847
  ...payload,
@@ -2992,10 +3024,10 @@ function deserializeBrokerPayloadBody(body, contentType, messagePayloadType) {
2992
3024
  function extractTrackingValue(payload, selector) {
2993
3025
  const normalized = selector.trim().toLowerCase();
2994
3026
  if (normalized.startsWith("header:")) {
2995
- const headerName = selector.slice("header:".length).trim().toLowerCase();
3027
+ const headerName = selector.slice("header:".length).trim();
2996
3028
  for (const [key, value] of Object.entries(payload.headers ?? {})) {
2997
3029
  const resolved = String(value ?? "").trim();
2998
- if (key.toLowerCase() === headerName && resolved) {
3030
+ if (key === headerName && resolved) {
2999
3031
  return resolved;
3000
3032
  }
3001
3033
  }
@@ -3656,3 +3688,57 @@ function payloadBodyAsUtf8(body) {
3656
3688
  return String(body);
3657
3689
  }
3658
3690
  }
3691
+ export const __loadstrikeTestExports = {
3692
+ AzureEventHubsEndpointAdapter,
3693
+ AzureEventHubsEndpointDefinition,
3694
+ AzureEventHubsEndpointDefinitionModel,
3695
+ CallbackAdapter,
3696
+ DelegateStreamEndpointDefinition,
3697
+ DelegateStreamEndpointDefinitionModel,
3698
+ HttpEndpointAdapter,
3699
+ NatsEndpointDefinition,
3700
+ NatsEndpointDefinitionModel,
3701
+ PushDiffusionEndpointDefinition,
3702
+ PushDiffusionEndpointDefinitionModel,
3703
+ RabbitMqEndpointDefinition,
3704
+ RabbitMqEndpointDefinitionModel,
3705
+ RedisStreamsEndpointAdapter,
3706
+ RedisStreamsEndpointDefinition,
3707
+ RedisStreamsEndpointDefinitionModel,
3708
+ bufferToUint8Array,
3709
+ applyHttpAuthHeaders,
3710
+ buildHttpRequestBody,
3711
+ buildConfluentKafkaClientOptions,
3712
+ buildKafkaClientOptions,
3713
+ canonicalizeHttpResponseSource,
3714
+ createDelegateRequestEndpointView,
3715
+ createNatsHeaders,
3716
+ createRedisStreamPayload,
3717
+ deserializeBrokerPayloadBody,
3718
+ extractTrackingValue,
3719
+ fromKafkaHeaders,
3720
+ headerValue,
3721
+ attachDotNetTrackingPayloadAliases,
3722
+ attachPayloadHelpers,
3723
+ attachStructuredProducedMessageRequestAliases,
3724
+ inferLegacyHttpResponseSource,
3725
+ injectTrackingValue,
3726
+ mapConfluentKafkaSaslMechanism,
3727
+ mapConfluentKafkaSecurityProtocol,
3728
+ mapKafkaSaslMechanism,
3729
+ parseBodyObject,
3730
+ partitionFromKey,
3731
+ payloadBodyAsUtf8,
3732
+ prepareProducedPayload,
3733
+ protocolBus: protocolBus,
3734
+ readFirstRedisStreamEntry,
3735
+ resolveConnectionMetadata,
3736
+ resolveKafkaOAuthBearerToken,
3737
+ serializePayloadBody,
3738
+ setJsonBodyValue,
3739
+ shouldUseConfluentKafkaClient,
3740
+ toHeaderRecord,
3741
+ toKafkaHeadersWithContentType,
3742
+ validateHttpEndpoint,
3743
+ validateTrackingSelectorPath
3744
+ };
@@ -60,6 +60,7 @@ export interface ClusterNodeStatsPayload {
60
60
  pluginsData?: unknown[];
61
61
  nodeInfo?: Record<string, unknown>;
62
62
  testInfo?: Record<string, unknown>;
63
+ logFiles?: string[];
63
64
  }
64
65
  export interface ClusterRunResultMessage {
65
66
  commandId: string;
@@ -110,3 +111,32 @@ export declare class DistributedClusterAgent {
110
111
  dispose(): Promise<void>;
111
112
  pollAndExecuteOnce(execute: (dispatch: ClusterScenarioDispatch) => Promise<ClusterNodeResult> | ClusterNodeResult): Promise<boolean>;
112
113
  }
114
+ declare function parseRunCommand(value: Record<string, unknown>): ClusterRunCommandMessage;
115
+ declare function parseRunResult(value: Record<string, unknown>): ClusterRunResultMessage;
116
+ declare function convertRunResult(result: ClusterRunResultMessage): ClusterNodeResult;
117
+ declare function sanitizeToken(value: string): string;
118
+ declare function buildWeightedCycle(weights: number[]): number[];
119
+ declare function stringOrDefault(value: unknown, fallback: string): string;
120
+ declare function numberOrDefault(value: unknown, fallback: number): number;
121
+ declare function recordOrUndefined(value: unknown): Record<string, unknown> | undefined;
122
+ declare function arrayOrUndefined(value: unknown): unknown[] | undefined;
123
+ declare function booleanOrDefault(value: unknown, fallback: boolean): boolean;
124
+ declare function sleep(ms: number): Promise<void>;
125
+ export declare const __loadstrikeTestExports: {
126
+ DistributedClusterAgent: typeof DistributedClusterAgent;
127
+ DistributedClusterCoordinator: typeof DistributedClusterCoordinator;
128
+ LocalClusterCoordinator: typeof LocalClusterCoordinator;
129
+ arrayOrUndefined: typeof arrayOrUndefined;
130
+ booleanOrDefault: typeof booleanOrDefault;
131
+ buildWeightedCycle: typeof buildWeightedCycle;
132
+ convertRunResult: typeof convertRunResult;
133
+ numberOrDefault: typeof numberOrDefault;
134
+ parseRunCommand: typeof parseRunCommand;
135
+ parseRunResult: typeof parseRunResult;
136
+ planAgentScenarioAssignments: typeof planAgentScenarioAssignments;
137
+ recordOrUndefined: typeof recordOrUndefined;
138
+ sanitizeToken: typeof sanitizeToken;
139
+ sleep: typeof sleep;
140
+ stringOrDefault: typeof stringOrDefault;
141
+ };
142
+ export {};