@opensip-cli/simulation 0.1.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/LICENSE +202 -0
- package/NOTICE +8 -0
- package/README.md +31 -0
- package/dist/__tests__/internal-surface.test.d.ts +9 -0
- package/dist/__tests__/internal-surface.test.d.ts.map +1 -0
- package/dist/__tests__/internal-surface.test.js +38 -0
- package/dist/__tests__/internal-surface.test.js.map +1 -0
- package/dist/__tests__/plugin-loader.test.d.ts +2 -0
- package/dist/__tests__/plugin-loader.test.d.ts.map +1 -0
- package/dist/__tests__/plugin-loader.test.js +64 -0
- package/dist/__tests__/plugin-loader.test.js.map +1 -0
- package/dist/__tests__/public-api.test.d.ts +15 -0
- package/dist/__tests__/public-api.test.d.ts.map +1 -0
- package/dist/__tests__/public-api.test.js +73 -0
- package/dist/__tests__/public-api.test.js.map +1 -0
- package/dist/__tests__/registry-cross-kind.test.d.ts +6 -0
- package/dist/__tests__/registry-cross-kind.test.d.ts.map +1 -0
- package/dist/__tests__/registry-cross-kind.test.js +86 -0
- package/dist/__tests__/registry-cross-kind.test.js.map +1 -0
- package/dist/__tests__/scenario-execution.test.d.ts +10 -0
- package/dist/__tests__/scenario-execution.test.d.ts.map +1 -0
- package/dist/__tests__/scenario-execution.test.js +92 -0
- package/dist/__tests__/scenario-execution.test.js.map +1 -0
- package/dist/__tests__/sim-capability-contract.test.d.ts +10 -0
- package/dist/__tests__/sim-capability-contract.test.d.ts.map +1 -0
- package/dist/__tests__/sim-capability-contract.test.js +131 -0
- package/dist/__tests__/sim-capability-contract.test.js.map +1 -0
- package/dist/__tests__/sim-config-schema.test.d.ts +5 -0
- package/dist/__tests__/sim-config-schema.test.d.ts.map +1 -0
- package/dist/__tests__/sim-config-schema.test.js +23 -0
- package/dist/__tests__/sim-config-schema.test.js.map +1 -0
- package/dist/__tests__/sim-failure-signal.test.d.ts +10 -0
- package/dist/__tests__/sim-failure-signal.test.d.ts.map +1 -0
- package/dist/__tests__/sim-failure-signal.test.js +86 -0
- package/dist/__tests__/sim-failure-signal.test.js.map +1 -0
- package/dist/__tests__/surviving-surface-edges.test.d.ts +10 -0
- package/dist/__tests__/surviving-surface-edges.test.d.ts.map +1 -0
- package/dist/__tests__/surviving-surface-edges.test.js +122 -0
- package/dist/__tests__/surviving-surface-edges.test.js.map +1 -0
- package/dist/__tests__/test-utils/targets.d.ts +26 -0
- package/dist/__tests__/test-utils/targets.d.ts.map +1 -0
- package/dist/__tests__/test-utils/targets.js +38 -0
- package/dist/__tests__/test-utils/targets.js.map +1 -0
- package/dist/__tests__/test-utils/with-sim-scope.d.ts +12 -0
- package/dist/__tests__/test-utils/with-sim-scope.d.ts.map +1 -0
- package/dist/__tests__/test-utils/with-sim-scope.js +22 -0
- package/dist/__tests__/test-utils/with-sim-scope.js.map +1 -0
- package/dist/__tests__/tool-live-view.test.d.ts +20 -0
- package/dist/__tests__/tool-live-view.test.d.ts.map +1 -0
- package/dist/__tests__/tool-live-view.test.js +146 -0
- package/dist/__tests__/tool-live-view.test.js.map +1 -0
- package/dist/__tests__/tool-show-mode.test.d.ts +10 -0
- package/dist/__tests__/tool-show-mode.test.d.ts.map +1 -0
- package/dist/__tests__/tool-show-mode.test.js +156 -0
- package/dist/__tests__/tool-show-mode.test.js.map +1 -0
- package/dist/__tests__/tool.test.d.ts +15 -0
- package/dist/__tests__/tool.test.d.ts.map +1 -0
- package/dist/__tests__/tool.test.js +250 -0
- package/dist/__tests__/tool.test.js.map +1 -0
- package/dist/cli/__tests__/sim-config.test.d.ts +2 -0
- package/dist/cli/__tests__/sim-config.test.d.ts.map +1 -0
- package/dist/cli/__tests__/sim-config.test.js +94 -0
- package/dist/cli/__tests__/sim-config.test.js.map +1 -0
- package/dist/cli/__tests__/sim-runner.test.d.ts +23 -0
- package/dist/cli/__tests__/sim-runner.test.d.ts.map +1 -0
- package/dist/cli/__tests__/sim-runner.test.js +188 -0
- package/dist/cli/__tests__/sim-runner.test.js.map +1 -0
- package/dist/cli/__tests__/sim-worker.test.d.ts +12 -0
- package/dist/cli/__tests__/sim-worker.test.d.ts.map +1 -0
- package/dist/cli/__tests__/sim-worker.test.js +69 -0
- package/dist/cli/__tests__/sim-worker.test.js.map +1 -0
- package/dist/cli/__tests__/sim.test.d.ts +2 -0
- package/dist/cli/__tests__/sim.test.d.ts.map +1 -0
- package/dist/cli/__tests__/sim.test.js +232 -0
- package/dist/cli/__tests__/sim.test.js.map +1 -0
- package/dist/cli/sim-config-schema.d.ts +30 -0
- package/dist/cli/sim-config-schema.d.ts.map +1 -0
- package/dist/cli/sim-config-schema.js +33 -0
- package/dist/cli/sim-config-schema.js.map +1 -0
- package/dist/cli/sim-config.d.ts +22 -0
- package/dist/cli/sim-config.d.ts.map +1 -0
- package/dist/cli/sim-config.js +73 -0
- package/dist/cli/sim-config.js.map +1 -0
- package/dist/cli/sim-runner.d.ts +60 -0
- package/dist/cli/sim-runner.d.ts.map +1 -0
- package/dist/cli/sim-runner.js +175 -0
- package/dist/cli/sim-runner.js.map +1 -0
- package/dist/cli/sim-worker.d.ts +24 -0
- package/dist/cli/sim-worker.d.ts.map +1 -0
- package/dist/cli/sim-worker.js +65 -0
- package/dist/cli/sim-worker.js.map +1 -0
- package/dist/cli/sim.d.ts +56 -0
- package/dist/cli/sim.d.ts.map +1 -0
- package/dist/cli/sim.js +265 -0
- package/dist/cli/sim.js.map +1 -0
- package/dist/framework/__tests__/assertions.test.d.ts +2 -0
- package/dist/framework/__tests__/assertions.test.d.ts.map +1 -0
- package/dist/framework/__tests__/assertions.test.js +122 -0
- package/dist/framework/__tests__/assertions.test.js.map +1 -0
- package/dist/framework/__tests__/misc-coverage.test.d.ts +18 -0
- package/dist/framework/__tests__/misc-coverage.test.d.ts.map +1 -0
- package/dist/framework/__tests__/misc-coverage.test.js +142 -0
- package/dist/framework/__tests__/misc-coverage.test.js.map +1 -0
- package/dist/framework/__tests__/resolve-metric.test.d.ts +12 -0
- package/dist/framework/__tests__/resolve-metric.test.d.ts.map +1 -0
- package/dist/framework/__tests__/resolve-metric.test.js +124 -0
- package/dist/framework/__tests__/resolve-metric.test.js.map +1 -0
- package/dist/framework/__tests__/result-builder.test.d.ts +2 -0
- package/dist/framework/__tests__/result-builder.test.d.ts.map +1 -0
- package/dist/framework/__tests__/result-builder.test.js +180 -0
- package/dist/framework/__tests__/result-builder.test.js.map +1 -0
- package/dist/framework/__tests__/scenario-logger.test.d.ts +9 -0
- package/dist/framework/__tests__/scenario-logger.test.d.ts.map +1 -0
- package/dist/framework/__tests__/scenario-logger.test.js +110 -0
- package/dist/framework/__tests__/scenario-logger.test.js.map +1 -0
- package/dist/framework/__tests__/validation.test.d.ts +9 -0
- package/dist/framework/__tests__/validation.test.d.ts.map +1 -0
- package/dist/framework/__tests__/validation.test.js +126 -0
- package/dist/framework/__tests__/validation.test.js.map +1 -0
- package/dist/framework/assertions.d.ts +106 -0
- package/dist/framework/assertions.d.ts.map +1 -0
- package/dist/framework/assertions.js +256 -0
- package/dist/framework/assertions.js.map +1 -0
- package/dist/framework/execution/__tests__/execution-engine.test.d.ts +9 -0
- package/dist/framework/execution/__tests__/execution-engine.test.d.ts.map +1 -0
- package/dist/framework/execution/__tests__/execution-engine.test.js +138 -0
- package/dist/framework/execution/__tests__/execution-engine.test.js.map +1 -0
- package/dist/framework/execution/__tests__/fault-builders.test.d.ts +5 -0
- package/dist/framework/execution/__tests__/fault-builders.test.d.ts.map +1 -0
- package/dist/framework/execution/__tests__/fault-builders.test.js +23 -0
- package/dist/framework/execution/__tests__/fault-builders.test.js.map +1 -0
- package/dist/framework/execution/__tests__/fault-model.test.d.ts +6 -0
- package/dist/framework/execution/__tests__/fault-model.test.d.ts.map +1 -0
- package/dist/framework/execution/__tests__/fault-model.test.js +64 -0
- package/dist/framework/execution/__tests__/fault-model.test.js.map +1 -0
- package/dist/framework/execution/__tests__/http-target.test.d.ts +5 -0
- package/dist/framework/execution/__tests__/http-target.test.d.ts.map +1 -0
- package/dist/framework/execution/__tests__/http-target.test.js +36 -0
- package/dist/framework/execution/__tests__/http-target.test.js.map +1 -0
- package/dist/framework/execution/__tests__/run-load-window.test.d.ts +7 -0
- package/dist/framework/execution/__tests__/run-load-window.test.d.ts.map +1 -0
- package/dist/framework/execution/__tests__/run-load-window.test.js +89 -0
- package/dist/framework/execution/__tests__/run-load-window.test.js.map +1 -0
- package/dist/framework/execution/execution-engine.d.ts +55 -0
- package/dist/framework/execution/execution-engine.d.ts.map +1 -0
- package/dist/framework/execution/execution-engine.js +111 -0
- package/dist/framework/execution/execution-engine.js.map +1 -0
- package/dist/framework/execution/fault-builders.d.ts +26 -0
- package/dist/framework/execution/fault-builders.d.ts.map +1 -0
- package/dist/framework/execution/fault-builders.js +24 -0
- package/dist/framework/execution/fault-builders.js.map +1 -0
- package/dist/framework/execution/fault-model.d.ts +49 -0
- package/dist/framework/execution/fault-model.d.ts.map +1 -0
- package/dist/framework/execution/fault-model.js +108 -0
- package/dist/framework/execution/fault-model.js.map +1 -0
- package/dist/framework/execution/fault-spec.d.ts +40 -0
- package/dist/framework/execution/fault-spec.d.ts.map +1 -0
- package/dist/framework/execution/fault-spec.js +23 -0
- package/dist/framework/execution/fault-spec.js.map +1 -0
- package/dist/framework/execution/http-target.d.ts +34 -0
- package/dist/framework/execution/http-target.d.ts.map +1 -0
- package/dist/framework/execution/http-target.js +41 -0
- package/dist/framework/execution/http-target.js.map +1 -0
- package/dist/framework/execution/latency-tracker.d.ts +44 -0
- package/dist/framework/execution/latency-tracker.d.ts.map +1 -0
- package/dist/framework/execution/latency-tracker.js +76 -0
- package/dist/framework/execution/latency-tracker.js.map +1 -0
- package/dist/framework/execution/run-load-window.d.ts +45 -0
- package/dist/framework/execution/run-load-window.d.ts.map +1 -0
- package/dist/framework/execution/run-load-window.js +136 -0
- package/dist/framework/execution/run-load-window.js.map +1 -0
- package/dist/framework/execution/scenario-aborted-error.d.ts +14 -0
- package/dist/framework/execution/scenario-aborted-error.d.ts.map +1 -0
- package/dist/framework/execution/scenario-aborted-error.js +24 -0
- package/dist/framework/execution/scenario-aborted-error.js.map +1 -0
- package/dist/framework/execution/target.d.ts +35 -0
- package/dist/framework/execution/target.d.ts.map +1 -0
- package/dist/framework/execution/target.js +21 -0
- package/dist/framework/execution/target.js.map +1 -0
- package/dist/framework/registry.d.ts +67 -0
- package/dist/framework/registry.d.ts.map +1 -0
- package/dist/framework/registry.js +113 -0
- package/dist/framework/registry.js.map +1 -0
- package/dist/framework/resolve-metric.d.ts +79 -0
- package/dist/framework/resolve-metric.d.ts.map +1 -0
- package/dist/framework/resolve-metric.js +135 -0
- package/dist/framework/resolve-metric.js.map +1 -0
- package/dist/framework/result-builder.d.ts +72 -0
- package/dist/framework/result-builder.d.ts.map +1 -0
- package/dist/framework/result-builder.js +214 -0
- package/dist/framework/result-builder.js.map +1 -0
- package/dist/framework/result-renderers.d.ts +37 -0
- package/dist/framework/result-renderers.d.ts.map +1 -0
- package/dist/framework/result-renderers.js +62 -0
- package/dist/framework/result-renderers.js.map +1 -0
- package/dist/framework/runnable-scenario.d.ts +37 -0
- package/dist/framework/runnable-scenario.d.ts.map +1 -0
- package/dist/framework/runnable-scenario.js +9 -0
- package/dist/framework/runnable-scenario.js.map +1 -0
- package/dist/framework/scenario-executor-result.d.ts +44 -0
- package/dist/framework/scenario-executor-result.d.ts.map +1 -0
- package/dist/framework/scenario-executor-result.js +13 -0
- package/dist/framework/scenario-executor-result.js.map +1 -0
- package/dist/framework/scenario-logger.d.ts +18 -0
- package/dist/framework/scenario-logger.d.ts.map +1 -0
- package/dist/framework/scenario-logger.js +39 -0
- package/dist/framework/scenario-logger.js.map +1 -0
- package/dist/framework/scenario-metric-key.d.ts +24 -0
- package/dist/framework/scenario-metric-key.d.ts.map +1 -0
- package/dist/framework/scenario-metric-key.js +17 -0
- package/dist/framework/scenario-metric-key.js.map +1 -0
- package/dist/framework/validation.d.ts +83 -0
- package/dist/framework/validation.d.ts.map +1 -0
- package/dist/framework/validation.js +93 -0
- package/dist/framework/validation.js.map +1 -0
- package/dist/index.d.ts +35 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +53 -0
- package/dist/index.js.map +1 -0
- package/dist/internal.d.ts +19 -0
- package/dist/internal.d.ts.map +1 -0
- package/dist/internal.js +16 -0
- package/dist/internal.js.map +1 -0
- package/dist/kinds/chaos/__tests__/executor.test.d.ts +6 -0
- package/dist/kinds/chaos/__tests__/executor.test.d.ts.map +1 -0
- package/dist/kinds/chaos/__tests__/executor.test.js +117 -0
- package/dist/kinds/chaos/__tests__/executor.test.js.map +1 -0
- package/dist/kinds/chaos/config.d.ts +48 -0
- package/dist/kinds/chaos/config.d.ts.map +1 -0
- package/dist/kinds/chaos/config.js +15 -0
- package/dist/kinds/chaos/config.js.map +1 -0
- package/dist/kinds/chaos/define.d.ts +32 -0
- package/dist/kinds/chaos/define.d.ts.map +1 -0
- package/dist/kinds/chaos/define.js +111 -0
- package/dist/kinds/chaos/define.js.map +1 -0
- package/dist/kinds/chaos/executor.d.ts +21 -0
- package/dist/kinds/chaos/executor.d.ts.map +1 -0
- package/dist/kinds/chaos/executor.js +107 -0
- package/dist/kinds/chaos/executor.js.map +1 -0
- package/dist/kinds/chaos/result.d.ts +43 -0
- package/dist/kinds/chaos/result.d.ts.map +1 -0
- package/dist/kinds/chaos/result.js +12 -0
- package/dist/kinds/chaos/result.js.map +1 -0
- package/dist/kinds/load/__tests__/executor.test.d.ts +6 -0
- package/dist/kinds/load/__tests__/executor.test.d.ts.map +1 -0
- package/dist/kinds/load/__tests__/executor.test.js +75 -0
- package/dist/kinds/load/__tests__/executor.test.js.map +1 -0
- package/dist/kinds/load/config.d.ts +30 -0
- package/dist/kinds/load/config.d.ts.map +1 -0
- package/dist/kinds/load/config.js +9 -0
- package/dist/kinds/load/config.js.map +1 -0
- package/dist/kinds/load/define.d.ts +47 -0
- package/dist/kinds/load/define.d.ts.map +1 -0
- package/dist/kinds/load/define.js +76 -0
- package/dist/kinds/load/define.js.map +1 -0
- package/dist/kinds/load/executor.d.ts +16 -0
- package/dist/kinds/load/executor.d.ts.map +1 -0
- package/dist/kinds/load/executor.js +90 -0
- package/dist/kinds/load/executor.js.map +1 -0
- package/dist/kinds/load/result.d.ts +21 -0
- package/dist/kinds/load/result.d.ts.map +1 -0
- package/dist/kinds/load/result.js +10 -0
- package/dist/kinds/load/result.js.map +1 -0
- package/dist/persistence/session-payload.d.ts +32 -0
- package/dist/persistence/session-payload.d.ts.map +1 -0
- package/dist/persistence/session-payload.js +46 -0
- package/dist/persistence/session-payload.js.map +1 -0
- package/dist/persistence/session-replay.d.ts +13 -0
- package/dist/persistence/session-replay.d.ts.map +1 -0
- package/dist/persistence/session-replay.js +76 -0
- package/dist/persistence/session-replay.js.map +1 -0
- package/dist/persistence/session-replay.test.d.ts +10 -0
- package/dist/persistence/session-replay.test.d.ts.map +1 -0
- package/dist/persistence/session-replay.test.js +169 -0
- package/dist/persistence/session-replay.test.js.map +1 -0
- package/dist/plugins/__tests__/loader-scenarios.test.d.ts +12 -0
- package/dist/plugins/__tests__/loader-scenarios.test.d.ts.map +1 -0
- package/dist/plugins/__tests__/loader-scenarios.test.js +61 -0
- package/dist/plugins/__tests__/loader-scenarios.test.js.map +1 -0
- package/dist/plugins/__tests__/scenario-package-discovery.test.d.ts +2 -0
- package/dist/plugins/__tests__/scenario-package-discovery.test.d.ts.map +1 -0
- package/dist/plugins/__tests__/scenario-package-discovery.test.js +43 -0
- package/dist/plugins/__tests__/scenario-package-discovery.test.js.map +1 -0
- package/dist/plugins/loader.d.ts +32 -0
- package/dist/plugins/loader.d.ts.map +1 -0
- package/dist/plugins/loader.js +119 -0
- package/dist/plugins/loader.js.map +1 -0
- package/dist/plugins/scenario-package-discovery.d.ts +23 -0
- package/dist/plugins/scenario-package-discovery.d.ts.map +1 -0
- package/dist/plugins/scenario-package-discovery.js +42 -0
- package/dist/plugins/scenario-package-discovery.js.map +1 -0
- package/dist/plugins/types.d.ts +24 -0
- package/dist/plugins/types.d.ts.map +1 -0
- package/dist/plugins/types.js +18 -0
- package/dist/plugins/types.js.map +1 -0
- package/dist/recipes/__tests__/recipes.test.d.ts +2 -0
- package/dist/recipes/__tests__/recipes.test.d.ts.map +1 -0
- package/dist/recipes/__tests__/recipes.test.js +515 -0
- package/dist/recipes/__tests__/recipes.test.js.map +1 -0
- package/dist/recipes/built-in-recipes.d.ts +16 -0
- package/dist/recipes/built-in-recipes.d.ts.map +1 -0
- package/dist/recipes/built-in-recipes.js +37 -0
- package/dist/recipes/built-in-recipes.js.map +1 -0
- package/dist/recipes/define-recipe.d.ts +29 -0
- package/dist/recipes/define-recipe.d.ts.map +1 -0
- package/dist/recipes/define-recipe.js +68 -0
- package/dist/recipes/define-recipe.js.map +1 -0
- package/dist/recipes/registry.d.ts +54 -0
- package/dist/recipes/registry.d.ts.map +1 -0
- package/dist/recipes/registry.js +89 -0
- package/dist/recipes/registry.js.map +1 -0
- package/dist/recipes/service.d.ts +61 -0
- package/dist/recipes/service.d.ts.map +1 -0
- package/dist/recipes/service.js +205 -0
- package/dist/recipes/service.js.map +1 -0
- package/dist/recipes/types.d.ts +62 -0
- package/dist/recipes/types.d.ts.map +1 -0
- package/dist/recipes/types.js +10 -0
- package/dist/recipes/types.js.map +1 -0
- package/dist/scaffold/examples.d.ts +22 -0
- package/dist/scaffold/examples.d.ts.map +1 -0
- package/dist/scaffold/examples.js +91 -0
- package/dist/scaffold/examples.js.map +1 -0
- package/dist/scope-augmentation.d.ts +70 -0
- package/dist/scope-augmentation.d.ts.map +1 -0
- package/dist/scope-augmentation.js +28 -0
- package/dist/scope-augmentation.js.map +1 -0
- package/dist/tool.d.ts +21 -0
- package/dist/tool.d.ts.map +1 -0
- package/dist/tool.js +347 -0
- package/dist/tool.js.map +1 -0
- package/dist/types/base-types.d.ts +35 -0
- package/dist/types/base-types.d.ts.map +1 -0
- package/dist/types/base-types.js +8 -0
- package/dist/types/base-types.js.map +1 -0
- package/dist/types/framework-types.d.ts +62 -0
- package/dist/types/framework-types.d.ts.map +1 -0
- package/dist/types/framework-types.js +15 -0
- package/dist/types/framework-types.js.map +1 -0
- package/dist/types/kind-types.d.ts +24 -0
- package/dist/types/kind-types.d.ts.map +1 -0
- package/dist/types/kind-types.js +22 -0
- package/dist/types/kind-types.js.map +1 -0
- package/dist/types/workload.d.ts +30 -0
- package/dist/types/workload.d.ts.map +1 -0
- package/dist/types/workload.js +20 -0
- package/dist/types/workload.js.map +1 -0
- package/package.json +120 -0
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { ASSERTIONS, evaluateAssertion, evaluateOperator, getOperatorDescription, } from '../assertions.js';
|
|
3
|
+
describe('ASSERTIONS factories', () => {
|
|
4
|
+
it.each([
|
|
5
|
+
['lowErrorRate', () => ASSERTIONS.lowErrorRate(), 'error_rate', 'lt', 0.05],
|
|
6
|
+
['lowErrorRate(custom)', () => ASSERTIONS.lowErrorRate(0.01), 'error_rate', 'lt', 0.01],
|
|
7
|
+
['zeroErrors', () => ASSERTIONS.zeroErrors(), 'error_rate', 'eq', 0],
|
|
8
|
+
['lowLatency(default)', () => ASSERTIONS.lowLatency(), 'p95_latency_ms', 'lt', 500],
|
|
9
|
+
[
|
|
10
|
+
'lowLatency(p99,1000)',
|
|
11
|
+
() => ASSERTIONS.lowLatency('p99', 1000),
|
|
12
|
+
'p99_latency_ms',
|
|
13
|
+
'lt',
|
|
14
|
+
1000,
|
|
15
|
+
],
|
|
16
|
+
['avgLatency', () => ASSERTIONS.avgLatency(), 'avg_latency_ms', 'lt', 200],
|
|
17
|
+
['avgLatency(custom)', () => ASSERTIONS.avgLatency(50), 'avg_latency_ms', 'lt', 50],
|
|
18
|
+
['maxLatency', () => ASSERTIONS.maxLatency(), 'max_latency_ms', 'lt', 2000],
|
|
19
|
+
['maxLatency(custom)', () => ASSERTIONS.maxLatency(5000), 'max_latency_ms', 'lt', 5000],
|
|
20
|
+
['minThroughput', () => ASSERTIONS.minThroughput(100), 'requests_per_second', 'gte', 100],
|
|
21
|
+
['maxThroughput', () => ASSERTIONS.maxThroughput(50), 'requests_per_second', 'lte', 50],
|
|
22
|
+
['highSuccessRate(default)', () => ASSERTIONS.highSuccessRate(), 'success_rate', 'gte', 0.95],
|
|
23
|
+
[
|
|
24
|
+
'highSuccessRate(custom)',
|
|
25
|
+
() => ASSERTIONS.highSuccessRate(0.99),
|
|
26
|
+
'success_rate',
|
|
27
|
+
'gte',
|
|
28
|
+
0.99,
|
|
29
|
+
],
|
|
30
|
+
['perfectSuccessRate', () => ASSERTIONS.perfectSuccessRate(), 'success_rate', 'eq', 1],
|
|
31
|
+
['memoryUsage', () => ASSERTIONS.memoryUsage(512), 'memory_mb', 'lt', 512],
|
|
32
|
+
['cpuUsage', () => ASSERTIONS.cpuUsage(80), 'cpu_percent', 'lt', 80],
|
|
33
|
+
])('%s produces expected ScenarioAssertion', (_name, factory, metric, operator, value) => {
|
|
34
|
+
const a = factory();
|
|
35
|
+
expect(a.metric).toBe(metric);
|
|
36
|
+
expect(a.operator).toBe(operator);
|
|
37
|
+
expect(a.value).toBe(value);
|
|
38
|
+
expect(typeof a.message).toBe('string');
|
|
39
|
+
expect((a.message ?? '').length).toBeGreaterThan(0);
|
|
40
|
+
});
|
|
41
|
+
it('custom() builds an assertion with a user-supplied metric', () => {
|
|
42
|
+
const a = ASSERTIONS.custom('total_requests', 'gt', 42, 'total_requests must be > 42');
|
|
43
|
+
expect(a.metric).toBe('total_requests');
|
|
44
|
+
expect(a.operator).toBe('gt');
|
|
45
|
+
expect(a.value).toBe(42);
|
|
46
|
+
expect(a.message).toBe('total_requests must be > 42');
|
|
47
|
+
});
|
|
48
|
+
it('custom() defaults the message when omitted', () => {
|
|
49
|
+
const a = ASSERTIONS.custom('p50_latency_ms', 'lt', 100);
|
|
50
|
+
expect(a.message).toBe('p50_latency_ms lt 100');
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
describe('evaluateOperator', () => {
|
|
54
|
+
it.each([
|
|
55
|
+
[1, 'lt', 2, true],
|
|
56
|
+
[2, 'lt', 2, false],
|
|
57
|
+
[2, 'lte', 2, true],
|
|
58
|
+
[3, 'lte', 2, false],
|
|
59
|
+
[3, 'gt', 2, true],
|
|
60
|
+
[2, 'gt', 2, false],
|
|
61
|
+
[2, 'gte', 2, true],
|
|
62
|
+
[1, 'gte', 2, false],
|
|
63
|
+
[5, 'eq', 5, true],
|
|
64
|
+
[5, 'eq', 6, false],
|
|
65
|
+
[5, 'neq', 6, true],
|
|
66
|
+
[5, 'neq', 5, false],
|
|
67
|
+
])('evaluateOperator(%d, %s, %d) === %s', (a, op, b, expected) => {
|
|
68
|
+
expect(evaluateOperator(a, op, b)).toBe(expected);
|
|
69
|
+
});
|
|
70
|
+
it('returns false for unknown operators', () => {
|
|
71
|
+
expect(evaluateOperator(1, 'unknown', 2)).toBe(false);
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
describe('evaluateAssertion', () => {
|
|
75
|
+
const make = (operator, value) => ({
|
|
76
|
+
metric: 'error_rate',
|
|
77
|
+
operator,
|
|
78
|
+
value,
|
|
79
|
+
message: 'x',
|
|
80
|
+
});
|
|
81
|
+
it.each([
|
|
82
|
+
['lt', 5, 3, true],
|
|
83
|
+
['lt', 5, 5, false],
|
|
84
|
+
['lte', 5, 5, true],
|
|
85
|
+
['lte', 5, 6, false],
|
|
86
|
+
['gt', 5, 6, true],
|
|
87
|
+
['gt', 5, 5, false],
|
|
88
|
+
['gte', 5, 5, true],
|
|
89
|
+
['gte', 5, 4, false],
|
|
90
|
+
['eq', 5, 5, true],
|
|
91
|
+
['eq', 5, 6, false],
|
|
92
|
+
['neq', 5, 6, true],
|
|
93
|
+
['neq', 5, 5, false],
|
|
94
|
+
])('evaluateAssertion(op=%s, expected=%d, actual=%d) === %s', (op, expected, actual, want) => {
|
|
95
|
+
expect(evaluateAssertion(make(op, expected), actual)).toBe(want);
|
|
96
|
+
});
|
|
97
|
+
it('returns false for unknown operator on the assertion', () => {
|
|
98
|
+
const bad = {
|
|
99
|
+
metric: 'error_rate',
|
|
100
|
+
operator: 'unknown',
|
|
101
|
+
value: 0,
|
|
102
|
+
message: 'x',
|
|
103
|
+
};
|
|
104
|
+
expect(evaluateAssertion(bad, 0)).toBe(false);
|
|
105
|
+
});
|
|
106
|
+
});
|
|
107
|
+
describe('getOperatorDescription', () => {
|
|
108
|
+
it.each([
|
|
109
|
+
['lt', 'less than'],
|
|
110
|
+
['lte', 'at most'],
|
|
111
|
+
['gt', 'greater than'],
|
|
112
|
+
['gte', 'at least'],
|
|
113
|
+
['eq', 'equal to'],
|
|
114
|
+
['neq', 'not equal to'],
|
|
115
|
+
])('getOperatorDescription(%s) === %s', (op, want) => {
|
|
116
|
+
expect(getOperatorDescription(op)).toBe(want);
|
|
117
|
+
});
|
|
118
|
+
it('returns the operator string for unknown operators', () => {
|
|
119
|
+
expect(getOperatorDescription('unknown')).toBe('unknown');
|
|
120
|
+
});
|
|
121
|
+
});
|
|
122
|
+
//# sourceMappingURL=assertions.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"assertions.test.js","sourceRoot":"","sources":["../../../src/framework/__tests__/assertions.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Coverage gap-fillers for small utility surfaces.
|
|
3
|
+
*
|
|
4
|
+
* Each test here targets a real-world failure or behavior, not just
|
|
5
|
+
* raw line coverage. Concretely:
|
|
6
|
+
* - core's Registry<T> name-collision guard (with nameCollisionMode:
|
|
7
|
+
* 'throw') prevents inconsistent dual-key state when two different
|
|
8
|
+
* ids share a name — without the throw, the last writer wins on
|
|
9
|
+
* `byName` while both ids remain in `byId`. This is the contract
|
|
10
|
+
* the simulation scenario registry depends on.
|
|
11
|
+
* - LatencyTracker's reset() must clear the running sum, otherwise a
|
|
12
|
+
* subsequent record() would carry stale state.
|
|
13
|
+
* - renderScenarioResultView's exhaustiveness branch fires when an
|
|
14
|
+
* unknown `kind` reaches the renderer; tests force this with a
|
|
15
|
+
* hand-rolled cast to verify the runtime guard, not just the types.
|
|
16
|
+
*/
|
|
17
|
+
export {};
|
|
18
|
+
//# sourceMappingURL=misc-coverage.test.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"misc-coverage.test.d.ts","sourceRoot":"","sources":["../../../src/framework/__tests__/misc-coverage.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG"}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Coverage gap-fillers for small utility surfaces.
|
|
3
|
+
*
|
|
4
|
+
* Each test here targets a real-world failure or behavior, not just
|
|
5
|
+
* raw line coverage. Concretely:
|
|
6
|
+
* - core's Registry<T> name-collision guard (with nameCollisionMode:
|
|
7
|
+
* 'throw') prevents inconsistent dual-key state when two different
|
|
8
|
+
* ids share a name — without the throw, the last writer wins on
|
|
9
|
+
* `byName` while both ids remain in `byId`. This is the contract
|
|
10
|
+
* the simulation scenario registry depends on.
|
|
11
|
+
* - LatencyTracker's reset() must clear the running sum, otherwise a
|
|
12
|
+
* subsequent record() would carry stale state.
|
|
13
|
+
* - renderScenarioResultView's exhaustiveness branch fires when an
|
|
14
|
+
* unknown `kind` reaches the renderer; tests force this with a
|
|
15
|
+
* hand-rolled cast to verify the runtime guard, not just the types.
|
|
16
|
+
*/
|
|
17
|
+
import { Registry, ValidationError } from '@opensip-cli/core';
|
|
18
|
+
import { describe, expect, it } from 'vitest';
|
|
19
|
+
import { LatencyTracker } from '../execution/latency-tracker.js';
|
|
20
|
+
import { renderScenarioResultView } from '../result-renderers.js';
|
|
21
|
+
function makeScenarioRegistry() {
|
|
22
|
+
return new Registry({
|
|
23
|
+
module: 'test',
|
|
24
|
+
duplicatePolicy: 'silent-skip',
|
|
25
|
+
evtPrefix: 'test.registry',
|
|
26
|
+
nameCollisionMode: 'throw',
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
describe('Registry<T> with silent-skip + nameCollisionMode=throw — coverage edges', () => {
|
|
30
|
+
it('throws ValidationError on a name collision (two different ids, same name)', () => {
|
|
31
|
+
const reg = makeScenarioRegistry();
|
|
32
|
+
reg.register({ id: 'a', name: 'shared' });
|
|
33
|
+
expect(() => reg.register({ id: 'b', name: 'shared' })).toThrow(ValidationError);
|
|
34
|
+
});
|
|
35
|
+
it('skips silent duplicate when same id is registered twice', () => {
|
|
36
|
+
const reg = makeScenarioRegistry();
|
|
37
|
+
reg.register({ id: 'a', name: 'one' });
|
|
38
|
+
reg.register({ id: 'a', name: 'one' }); // skipped silently
|
|
39
|
+
expect(reg.size).toBe(1);
|
|
40
|
+
});
|
|
41
|
+
it('size() reflects current entries', () => {
|
|
42
|
+
const reg = makeScenarioRegistry();
|
|
43
|
+
expect(reg.size).toBe(0);
|
|
44
|
+
reg.register({ id: 'a', name: 'one' });
|
|
45
|
+
reg.register({ id: 'b', name: 'two' });
|
|
46
|
+
expect(reg.size).toBe(2);
|
|
47
|
+
});
|
|
48
|
+
it('clear() empties both byId and byName indices', () => {
|
|
49
|
+
const reg = makeScenarioRegistry();
|
|
50
|
+
reg.register({ id: 'a', name: 'one' });
|
|
51
|
+
reg.register({ id: 'b', name: 'two', tags: ['x'] });
|
|
52
|
+
reg.clear();
|
|
53
|
+
expect(reg.size).toBe(0);
|
|
54
|
+
expect(reg.get('a')).toBeUndefined();
|
|
55
|
+
expect(reg.get('one')).toBeUndefined();
|
|
56
|
+
expect(reg.getByTag('x')).toEqual([]);
|
|
57
|
+
});
|
|
58
|
+
it('getByTag returns matching items', () => {
|
|
59
|
+
const reg = makeScenarioRegistry();
|
|
60
|
+
reg.register({ id: 'a', name: 'one', tags: ['x', 'shared'] });
|
|
61
|
+
reg.register({ id: 'b', name: 'two', tags: ['shared'] });
|
|
62
|
+
reg.register({ id: 'c', name: 'three' });
|
|
63
|
+
const shared = reg.getByTag('shared');
|
|
64
|
+
expect(shared.map((i) => i.id).sort()).toEqual(['a', 'b']);
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
// ---------------------------------------------------------------------------
|
|
68
|
+
// LatencyTracker — reset() and average() with no samples
|
|
69
|
+
// ---------------------------------------------------------------------------
|
|
70
|
+
describe('LatencyTracker — reset and zero-sample edges', () => {
|
|
71
|
+
it('reset() clears samples, sum, and the sorted cache', () => {
|
|
72
|
+
const t = new LatencyTracker();
|
|
73
|
+
t.record(10);
|
|
74
|
+
t.record(20);
|
|
75
|
+
t.record(30);
|
|
76
|
+
expect(t.average).toBeCloseTo(20);
|
|
77
|
+
expect(t.count).toBe(3);
|
|
78
|
+
t.reset();
|
|
79
|
+
expect(t.count).toBe(0);
|
|
80
|
+
expect(t.average).toBe(0);
|
|
81
|
+
expect(t.getPercentile(50)).toBe(0);
|
|
82
|
+
// After reset, new samples behave as if fresh.
|
|
83
|
+
t.record(100);
|
|
84
|
+
expect(t.average).toBe(100);
|
|
85
|
+
expect(t.count).toBe(1);
|
|
86
|
+
});
|
|
87
|
+
it('average() returns 0 with no samples', () => {
|
|
88
|
+
const t = new LatencyTracker();
|
|
89
|
+
expect(t.average).toBe(0);
|
|
90
|
+
});
|
|
91
|
+
it('getPercentile returns the single sample with one record', () => {
|
|
92
|
+
const t = new LatencyTracker();
|
|
93
|
+
t.record(42);
|
|
94
|
+
expect(t.getPercentile(50)).toBe(42);
|
|
95
|
+
expect(t.getPercentile(99)).toBe(42);
|
|
96
|
+
});
|
|
97
|
+
it('getPercentile interpolates linearly between bracketing samples', () => {
|
|
98
|
+
const t = new LatencyTracker();
|
|
99
|
+
// 4 samples → index for p50 = 0.5 * 3 = 1.5 → interpolate between
|
|
100
|
+
// sorted[1]=20 and sorted[2]=30 with fraction 0.5 → 25.
|
|
101
|
+
t.record(40);
|
|
102
|
+
t.record(20);
|
|
103
|
+
t.record(10);
|
|
104
|
+
t.record(30);
|
|
105
|
+
expect(t.getPercentile(50)).toBeCloseTo(25);
|
|
106
|
+
// p0 and p100 land exactly on the endpoints (lower === upper path).
|
|
107
|
+
expect(t.getPercentile(0)).toBe(10);
|
|
108
|
+
expect(t.getPercentile(100)).toBe(40);
|
|
109
|
+
});
|
|
110
|
+
it('getLatencySnapshot reports avg + percentiles together', () => {
|
|
111
|
+
const t = new LatencyTracker();
|
|
112
|
+
t.record(10);
|
|
113
|
+
t.record(20);
|
|
114
|
+
t.record(30);
|
|
115
|
+
const snap = t.getLatencySnapshot();
|
|
116
|
+
expect(snap.avgLatencyMs).toBeCloseTo(20);
|
|
117
|
+
expect(snap.p50LatencyMs).toBeCloseTo(20);
|
|
118
|
+
expect(snap.p95LatencyMs).toBeGreaterThanOrEqual(snap.p50LatencyMs);
|
|
119
|
+
expect(snap.p99LatencyMs).toBeGreaterThanOrEqual(snap.p95LatencyMs);
|
|
120
|
+
});
|
|
121
|
+
});
|
|
122
|
+
// ---------------------------------------------------------------------------
|
|
123
|
+
// renderScenarioResultView — exhaustiveness guard
|
|
124
|
+
// ---------------------------------------------------------------------------
|
|
125
|
+
describe('renderScenarioResultView — exhaustiveness guard', () => {
|
|
126
|
+
it('throws when called with an unknown kind (runtime safety net)', () => {
|
|
127
|
+
// Hand-rolled cast — the union doesn't permit "future-kind" at compile
|
|
128
|
+
// time, but the runtime exhaustiveness probe is what protects callers
|
|
129
|
+
// when a future variant slips past the type system (e.g., crossing a
|
|
130
|
+
// serialization boundary).
|
|
131
|
+
const future = {
|
|
132
|
+
kind: 'future-kind',
|
|
133
|
+
scenarioId: 's1',
|
|
134
|
+
passed: true,
|
|
135
|
+
durationMs: 1,
|
|
136
|
+
signals: [],
|
|
137
|
+
outcome: {},
|
|
138
|
+
};
|
|
139
|
+
expect(() => renderScenarioResultView(future)).toThrow(/exhaustiveness/);
|
|
140
|
+
});
|
|
141
|
+
});
|
|
142
|
+
//# sourceMappingURL=misc-coverage.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"misc-coverage.test.js","sourceRoot":"","sources":["../../../src/framework/__tests__/misc-coverage.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Tests pinning the canonical metric resolver.
|
|
3
|
+
*
|
|
4
|
+
* Includes the symmetry regression: both `validateAssertions` (used by the
|
|
5
|
+
* execution-engine path) and `ScenarioResultBuilder.evaluateAssertions`
|
|
6
|
+
* (used by the kind executors) MUST produce the same actual value via
|
|
7
|
+
* `resolveMetric`. Before the unification, the two paths returned
|
|
8
|
+
* different values for `success_rate` on `totalRequests === 0` and
|
|
9
|
+
* disagreed on which keys were resolvable.
|
|
10
|
+
*/
|
|
11
|
+
export {};
|
|
12
|
+
//# sourceMappingURL=resolve-metric.test.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resolve-metric.test.d.ts","sourceRoot":"","sources":["../../../src/framework/__tests__/resolve-metric.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG"}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Tests pinning the canonical metric resolver.
|
|
3
|
+
*
|
|
4
|
+
* Includes the symmetry regression: both `validateAssertions` (used by the
|
|
5
|
+
* execution-engine path) and `ScenarioResultBuilder.evaluateAssertions`
|
|
6
|
+
* (used by the kind executors) MUST produce the same actual value via
|
|
7
|
+
* `resolveMetric`. Before the unification, the two paths returned
|
|
8
|
+
* different values for `success_rate` on `totalRequests === 0` and
|
|
9
|
+
* disagreed on which keys were resolvable.
|
|
10
|
+
*/
|
|
11
|
+
import { describe, expect, it } from 'vitest';
|
|
12
|
+
import { validateAssertions } from '../execution/execution-engine.js';
|
|
13
|
+
import { resolveMetric } from '../resolve-metric.js';
|
|
14
|
+
import { ScenarioResultBuilder, createEmptyMetrics } from '../result-builder.js';
|
|
15
|
+
const baseMetrics = (overrides = {}) => ({
|
|
16
|
+
...createEmptyMetrics(),
|
|
17
|
+
totalRequests: 100,
|
|
18
|
+
successfulRequests: 95,
|
|
19
|
+
failedRequests: 5,
|
|
20
|
+
avgLatencyMs: 40,
|
|
21
|
+
p50LatencyMs: 30,
|
|
22
|
+
p95LatencyMs: 70,
|
|
23
|
+
p99LatencyMs: 100,
|
|
24
|
+
errorsGenerated: 5,
|
|
25
|
+
...overrides,
|
|
26
|
+
});
|
|
27
|
+
/**
|
|
28
|
+
* Run an assertion through both metric-resolution paths and compare. The
|
|
29
|
+
* `eq` operator with the resolver's value as `expected` lets us read the
|
|
30
|
+
* canonical actual back out of the validateAssertions path's `failed[].actual`
|
|
31
|
+
* (we treat any failure with the same `actual` as success for the symmetry
|
|
32
|
+
* check).
|
|
33
|
+
*/
|
|
34
|
+
function actualFromValidateAssertions(metrics, metric) {
|
|
35
|
+
const a = {
|
|
36
|
+
metric,
|
|
37
|
+
operator: 'eq',
|
|
38
|
+
// Force a failure so we can read `failed[0].actual`.
|
|
39
|
+
value: Number.NaN,
|
|
40
|
+
message: 'symmetry probe',
|
|
41
|
+
};
|
|
42
|
+
const out = validateAssertions(metrics, [a]);
|
|
43
|
+
// NaN !== NaN ⇒ failed list always populated for this probe.
|
|
44
|
+
return out.failed[0]?.actual ?? Number.NaN;
|
|
45
|
+
}
|
|
46
|
+
function actualFromResultBuilder(metrics, metric, durationSeconds) {
|
|
47
|
+
const a = {
|
|
48
|
+
metric,
|
|
49
|
+
operator: 'eq',
|
|
50
|
+
value: Number.NaN,
|
|
51
|
+
message: 'symmetry probe',
|
|
52
|
+
};
|
|
53
|
+
const builder = ScenarioResultBuilder.create('probe').withMetrics(metrics);
|
|
54
|
+
const built = (durationSeconds === undefined ? builder : builder.withDuration(durationSeconds))
|
|
55
|
+
.evaluateAssertions([a])
|
|
56
|
+
.build();
|
|
57
|
+
return built.assertions.failed[0]?.actual ?? Number.NaN;
|
|
58
|
+
}
|
|
59
|
+
describe('resolveMetric symmetry', () => {
|
|
60
|
+
it('chaos recovery_rate produces the same value via both paths', () => {
|
|
61
|
+
const m = baseMetrics({ errorsGenerated: 10, failedRequests: 2 });
|
|
62
|
+
const expected = resolveMetric('recovery_rate', m);
|
|
63
|
+
expect(actualFromValidateAssertions(m, 'recovery_rate')).toBeCloseTo(expected);
|
|
64
|
+
expect(actualFromResultBuilder(m, 'recovery_rate')).toBeCloseTo(expected);
|
|
65
|
+
// And the value is the documented one (1 - 2/10 = 0.8).
|
|
66
|
+
expect(expected).toBeCloseTo(0.8);
|
|
67
|
+
});
|
|
68
|
+
it('recovery_rate is 1 when no errors generated, both paths agree', () => {
|
|
69
|
+
const m = baseMetrics({ errorsGenerated: 0, failedRequests: 0 });
|
|
70
|
+
expect(actualFromValidateAssertions(m, 'recovery_rate')).toBe(1);
|
|
71
|
+
expect(actualFromResultBuilder(m, 'recovery_rate')).toBe(1);
|
|
72
|
+
});
|
|
73
|
+
it('success_rate is 0 with zero requests, both paths agree (tightening)', () => {
|
|
74
|
+
const m = baseMetrics({ totalRequests: 0, successfulRequests: 0 });
|
|
75
|
+
expect(actualFromValidateAssertions(m, 'success_rate')).toBe(0);
|
|
76
|
+
expect(actualFromResultBuilder(m, 'success_rate')).toBe(0);
|
|
77
|
+
});
|
|
78
|
+
it.each([
|
|
79
|
+
['error_rate', { totalRequests: 200, failedRequests: 50 }, 0.25],
|
|
80
|
+
['success_rate', { totalRequests: 200, successfulRequests: 180 }, 0.9],
|
|
81
|
+
['p50_latency_ms', { p50LatencyMs: 33 }, 33],
|
|
82
|
+
['p95_latency_ms', { p95LatencyMs: 88 }, 88],
|
|
83
|
+
['p99_latency_ms', { p99LatencyMs: 200 }, 200],
|
|
84
|
+
['avg_latency_ms', { avgLatencyMs: 45 }, 45],
|
|
85
|
+
['total_requests', { totalRequests: 7 }, 7],
|
|
86
|
+
['failed_requests', { failedRequests: 4 }, 4],
|
|
87
|
+
])('%s resolves to %d via both paths', (metric, overrides, expected) => {
|
|
88
|
+
const m = baseMetrics(overrides);
|
|
89
|
+
expect(actualFromValidateAssertions(m, metric)).toBeCloseTo(expected);
|
|
90
|
+
expect(actualFromResultBuilder(m, metric)).toBeCloseTo(expected);
|
|
91
|
+
});
|
|
92
|
+
it('requests_per_second resolves via the result-builder path with duration', () => {
|
|
93
|
+
const m = baseMetrics({ totalRequests: 100 });
|
|
94
|
+
expect(actualFromResultBuilder(m, 'requests_per_second', 10)).toBeCloseTo(10);
|
|
95
|
+
// Without duration in the validate-assertions path, the value is 0
|
|
96
|
+
// (no duration plumbed through). Documented behaviour.
|
|
97
|
+
expect(actualFromValidateAssertions(m, 'requests_per_second')).toBe(0);
|
|
98
|
+
});
|
|
99
|
+
it('reserved keys (cpu_percent, memory_mb, max_latency_ms) resolve to 0', () => {
|
|
100
|
+
const m = baseMetrics();
|
|
101
|
+
expect(resolveMetric('cpu_percent', m)).toBe(0);
|
|
102
|
+
expect(resolveMetric('memory_mb', m)).toBe(0);
|
|
103
|
+
expect(resolveMetric('max_latency_ms', m)).toBe(0);
|
|
104
|
+
});
|
|
105
|
+
it('resolves the raw count metric keys directly off SimulationMetrics', () => {
|
|
106
|
+
const m = baseMetrics({
|
|
107
|
+
successfulRequests: 95,
|
|
108
|
+
failedRequests: 5,
|
|
109
|
+
errorsGenerated: 7,
|
|
110
|
+
totalRequests: 100,
|
|
111
|
+
});
|
|
112
|
+
expect(resolveMetric('successful_requests', m)).toBe(95);
|
|
113
|
+
expect(resolveMetric('failed_requests', m)).toBe(5);
|
|
114
|
+
expect(resolveMetric('errors_generated', m)).toBe(7);
|
|
115
|
+
expect(resolveMetric('total_requests', m)).toBe(100);
|
|
116
|
+
});
|
|
117
|
+
it('falls through to 0 for a key outside the supported set (type-bypass guard)', () => {
|
|
118
|
+
const m = baseMetrics();
|
|
119
|
+
// A caller that deliberately bypasses the compile-time narrowing hits the
|
|
120
|
+
// exhaustive default arm, which returns 0.
|
|
121
|
+
expect(resolveMetric('not-a-real-metric', m)).toBe(0);
|
|
122
|
+
});
|
|
123
|
+
});
|
|
124
|
+
//# sourceMappingURL=resolve-metric.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resolve-metric.test.js","sourceRoot":"","sources":["../../../src/framework/__tests__/resolve-metric.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"result-builder.test.d.ts","sourceRoot":"","sources":["../../../src/framework/__tests__/result-builder.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import { ValidationError, createSignal } from '@opensip-cli/core';
|
|
2
|
+
import { describe, expect, it } from 'vitest';
|
|
3
|
+
import { ASSERTIONS } from '../assertions.js';
|
|
4
|
+
import { ScenarioResultBuilder, createEmptyMetrics, mergeMetrics } from '../result-builder.js';
|
|
5
|
+
const baseMetrics = (overrides = {}) => ({
|
|
6
|
+
...createEmptyMetrics(),
|
|
7
|
+
totalRequests: 100,
|
|
8
|
+
successfulRequests: 99,
|
|
9
|
+
failedRequests: 1,
|
|
10
|
+
avgLatencyMs: 50,
|
|
11
|
+
p50LatencyMs: 40,
|
|
12
|
+
p95LatencyMs: 80,
|
|
13
|
+
p99LatencyMs: 120,
|
|
14
|
+
errorsGenerated: 0,
|
|
15
|
+
...overrides,
|
|
16
|
+
});
|
|
17
|
+
const sig = (overrides = {}) => createSignal({
|
|
18
|
+
source: 'simulation',
|
|
19
|
+
provider: 'opensip',
|
|
20
|
+
severity: 'medium',
|
|
21
|
+
category: 'warning',
|
|
22
|
+
ruleId: 'sim:test',
|
|
23
|
+
message: 'note',
|
|
24
|
+
...overrides,
|
|
25
|
+
});
|
|
26
|
+
describe('ScenarioResultBuilder', () => {
|
|
27
|
+
it('returns scenario id', () => {
|
|
28
|
+
expect(ScenarioResultBuilder.create('s1').getScenarioId()).toBe('s1');
|
|
29
|
+
});
|
|
30
|
+
it('builds a passed payload when all assertions hold', () => {
|
|
31
|
+
const out = ScenarioResultBuilder.create('s1')
|
|
32
|
+
.withMetrics(baseMetrics())
|
|
33
|
+
.withDuration(10)
|
|
34
|
+
.evaluateAssertions([ASSERTIONS.lowErrorRate(), ASSERTIONS.highSuccessRate(0.9)])
|
|
35
|
+
.build();
|
|
36
|
+
expect(out.passed).toBe(true);
|
|
37
|
+
expect(out.assertions.passed).toHaveLength(2);
|
|
38
|
+
expect(out.assertions.failed).toEqual([]);
|
|
39
|
+
});
|
|
40
|
+
it('records failed assertion with the actual value', () => {
|
|
41
|
+
const out = ScenarioResultBuilder.create('s1')
|
|
42
|
+
.withMetrics(baseMetrics({ failedRequests: 50 }))
|
|
43
|
+
.evaluateAssertions([ASSERTIONS.lowErrorRate()])
|
|
44
|
+
.build();
|
|
45
|
+
expect(out.passed).toBe(false);
|
|
46
|
+
expect(out.assertions.failed).toHaveLength(1);
|
|
47
|
+
expect(out.assertions.failed[0]?.actual).toBeCloseTo(0.5);
|
|
48
|
+
});
|
|
49
|
+
it('throws when evaluating assertions before metrics are set', () => {
|
|
50
|
+
expect(() => ScenarioResultBuilder.create('s1').evaluateAssertions([ASSERTIONS.lowErrorRate()])).toThrow(ValidationError);
|
|
51
|
+
});
|
|
52
|
+
it('throws on build() when metrics are not set', () => {
|
|
53
|
+
expect(() => ScenarioResultBuilder.create('s1').build()).toThrow(ValidationError);
|
|
54
|
+
});
|
|
55
|
+
it('addSignal / addSignals propagate to the payload', () => {
|
|
56
|
+
const out = ScenarioResultBuilder.create('s1')
|
|
57
|
+
.withMetrics(baseMetrics())
|
|
58
|
+
.addSignal(sig({ ruleId: 'sim:a' }))
|
|
59
|
+
.addSignals([sig({ ruleId: 'sim:b' }), sig({ ruleId: 'sim:c' })])
|
|
60
|
+
.build();
|
|
61
|
+
expect(out.signals.map((s) => s.ruleId)).toEqual(['sim:a', 'sim:b', 'sim:c']);
|
|
62
|
+
});
|
|
63
|
+
it('assertionPassed / assertionFailed allow manual record-keeping', () => {
|
|
64
|
+
const a = ASSERTIONS.lowErrorRate();
|
|
65
|
+
const out = ScenarioResultBuilder.create('s1')
|
|
66
|
+
.withMetrics(baseMetrics())
|
|
67
|
+
.assertionPassed(a)
|
|
68
|
+
.assertionFailed(a, 0.99)
|
|
69
|
+
.build();
|
|
70
|
+
expect(out.assertions.passed).toHaveLength(1);
|
|
71
|
+
expect(out.assertions.failed).toHaveLength(1);
|
|
72
|
+
});
|
|
73
|
+
describe('metric resolution', () => {
|
|
74
|
+
it('resolves error_rate from totalRequests / failedRequests', () => {
|
|
75
|
+
const out = ScenarioResultBuilder.create('s1')
|
|
76
|
+
.withMetrics(baseMetrics({ totalRequests: 200, failedRequests: 10 }))
|
|
77
|
+
.evaluateAssertions([ASSERTIONS.lowErrorRate(0.1)])
|
|
78
|
+
.build();
|
|
79
|
+
expect(out.passed).toBe(true);
|
|
80
|
+
});
|
|
81
|
+
it('resolves error_rate to 0 when totalRequests is 0', () => {
|
|
82
|
+
const out = ScenarioResultBuilder.create('s1')
|
|
83
|
+
.withMetrics(baseMetrics({ totalRequests: 0, failedRequests: 0 }))
|
|
84
|
+
.evaluateAssertions([ASSERTIONS.lowErrorRate()])
|
|
85
|
+
.build();
|
|
86
|
+
expect(out.passed).toBe(true);
|
|
87
|
+
});
|
|
88
|
+
it('resolves success_rate from successful / total', () => {
|
|
89
|
+
const out = ScenarioResultBuilder.create('s1')
|
|
90
|
+
.withMetrics(baseMetrics({ totalRequests: 100, successfulRequests: 100 }))
|
|
91
|
+
.evaluateAssertions([ASSERTIONS.perfectSuccessRate()])
|
|
92
|
+
.build();
|
|
93
|
+
expect(out.passed).toBe(true);
|
|
94
|
+
});
|
|
95
|
+
it('success_rate is 0 when no requests', () => {
|
|
96
|
+
const out = ScenarioResultBuilder.create('s1')
|
|
97
|
+
.withMetrics(baseMetrics({ totalRequests: 0 }))
|
|
98
|
+
.evaluateAssertions([ASSERTIONS.highSuccessRate(0.5)])
|
|
99
|
+
.build();
|
|
100
|
+
expect(out.passed).toBe(false);
|
|
101
|
+
});
|
|
102
|
+
it('resolves requests_per_second from totalRequests / duration', () => {
|
|
103
|
+
const out = ScenarioResultBuilder.create('s1')
|
|
104
|
+
.withMetrics(baseMetrics({ totalRequests: 100 }))
|
|
105
|
+
.withDuration(10)
|
|
106
|
+
.evaluateAssertions([ASSERTIONS.minThroughput(5)])
|
|
107
|
+
.build();
|
|
108
|
+
expect(out.passed).toBe(true); // 10 RPS >= 5
|
|
109
|
+
});
|
|
110
|
+
it('requests_per_second is 0 without duration', () => {
|
|
111
|
+
const out = ScenarioResultBuilder.create('s1')
|
|
112
|
+
.withMetrics(baseMetrics())
|
|
113
|
+
.evaluateAssertions([ASSERTIONS.minThroughput(1)])
|
|
114
|
+
.build();
|
|
115
|
+
expect(out.passed).toBe(false);
|
|
116
|
+
});
|
|
117
|
+
it('resolves direct latency fields from metrics', () => {
|
|
118
|
+
const out = ScenarioResultBuilder.create('s1')
|
|
119
|
+
.withMetrics(baseMetrics({ p95LatencyMs: 200 }))
|
|
120
|
+
.evaluateAssertions([ASSERTIONS.lowLatency('p95', 300)])
|
|
121
|
+
.build();
|
|
122
|
+
expect(out.passed).toBe(true);
|
|
123
|
+
});
|
|
124
|
+
it('returns 0 for unknown metrics', () => {
|
|
125
|
+
const out = ScenarioResultBuilder.create('s1')
|
|
126
|
+
.withMetrics(baseMetrics())
|
|
127
|
+
.evaluateAssertions([ASSERTIONS.custom('cpu_percent', 'lt', 100)])
|
|
128
|
+
.build();
|
|
129
|
+
expect(out.passed).toBe(true); // 0 < 100
|
|
130
|
+
});
|
|
131
|
+
});
|
|
132
|
+
});
|
|
133
|
+
describe('createEmptyMetrics', () => {
|
|
134
|
+
it('returns a zero-valued metrics object', () => {
|
|
135
|
+
const m = createEmptyMetrics();
|
|
136
|
+
expect(m.totalRequests).toBe(0);
|
|
137
|
+
expect(m.successfulRequests).toBe(0);
|
|
138
|
+
expect(m.failedRequests).toBe(0);
|
|
139
|
+
expect(m.avgLatencyMs).toBe(0);
|
|
140
|
+
});
|
|
141
|
+
});
|
|
142
|
+
describe('mergeMetrics', () => {
|
|
143
|
+
it('returns empty metrics for an empty list', () => {
|
|
144
|
+
expect(mergeMetrics([])).toEqual(createEmptyMetrics());
|
|
145
|
+
});
|
|
146
|
+
it('returns the single entry directly', () => {
|
|
147
|
+
const only = baseMetrics();
|
|
148
|
+
expect(mergeMetrics([only])).toBe(only);
|
|
149
|
+
});
|
|
150
|
+
it('sums totals and weights average latency', () => {
|
|
151
|
+
const a = baseMetrics({
|
|
152
|
+
totalRequests: 100,
|
|
153
|
+
successfulRequests: 100,
|
|
154
|
+
failedRequests: 0,
|
|
155
|
+
avgLatencyMs: 50,
|
|
156
|
+
});
|
|
157
|
+
const b = baseMetrics({
|
|
158
|
+
totalRequests: 100,
|
|
159
|
+
successfulRequests: 50,
|
|
160
|
+
failedRequests: 50,
|
|
161
|
+
avgLatencyMs: 150,
|
|
162
|
+
});
|
|
163
|
+
const merged = mergeMetrics([a, b]);
|
|
164
|
+
expect(merged.totalRequests).toBe(200);
|
|
165
|
+
expect(merged.successfulRequests).toBe(150);
|
|
166
|
+
expect(merged.failedRequests).toBe(50);
|
|
167
|
+
expect(merged.avgLatencyMs).toBeCloseTo(100); // weighted by totalRequests
|
|
168
|
+
});
|
|
169
|
+
it('takes the max for percentile latencies', () => {
|
|
170
|
+
const a = baseMetrics({ p99LatencyMs: 100 });
|
|
171
|
+
const b = baseMetrics({ p99LatencyMs: 250 });
|
|
172
|
+
expect(mergeMetrics([a, b]).p99LatencyMs).toBe(250);
|
|
173
|
+
});
|
|
174
|
+
it('returns 0 average latency when totalRequests is 0', () => {
|
|
175
|
+
const a = baseMetrics({ totalRequests: 0, avgLatencyMs: 100 });
|
|
176
|
+
const b = baseMetrics({ totalRequests: 0, avgLatencyMs: 200 });
|
|
177
|
+
expect(mergeMetrics([a, b]).avgLatencyMs).toBe(0);
|
|
178
|
+
});
|
|
179
|
+
});
|
|
180
|
+
//# sourceMappingURL=result-builder.test.js.map
|