@blokjs/runner 0.2.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/dist/Blok.d.ts +19 -0
- package/dist/Blok.js +184 -0
- package/dist/Blok.js.map +1 -0
- package/dist/BlokResponse.d.ts +16 -0
- package/dist/BlokResponse.js +28 -0
- package/dist/BlokResponse.js.map +1 -0
- package/dist/Configuration.d.ts +37 -0
- package/dist/Configuration.js +248 -0
- package/dist/Configuration.js.map +1 -0
- package/dist/ConfigurationResolver.d.ts +7 -0
- package/dist/ConfigurationResolver.js +15 -0
- package/dist/ConfigurationResolver.js.map +1 -0
- package/dist/DefaultLogger.d.ts +65 -0
- package/dist/DefaultLogger.js +101 -0
- package/dist/DefaultLogger.js.map +1 -0
- package/dist/LocalStorage.d.ts +7 -0
- package/dist/LocalStorage.js +56 -0
- package/dist/LocalStorage.js.map +1 -0
- package/dist/MemoryUsage.d.ts +22 -0
- package/dist/MemoryUsage.js +83 -0
- package/dist/MemoryUsage.js.map +1 -0
- package/dist/NodeMap.d.ts +7 -0
- package/dist/NodeMap.js +13 -0
- package/dist/NodeMap.js.map +1 -0
- package/dist/ResolverBase.d.ts +8 -0
- package/dist/ResolverBase.js +18 -0
- package/dist/ResolverBase.js.map +1 -0
- package/dist/Runner.d.ts +25 -0
- package/dist/Runner.js +32 -0
- package/dist/Runner.js.map +1 -0
- package/dist/RunnerNode.d.ts +9 -0
- package/dist/RunnerNode.js +8 -0
- package/dist/RunnerNode.js.map +1 -0
- package/dist/RunnerNodeBase.d.ts +4 -0
- package/dist/RunnerNodeBase.js +3 -0
- package/dist/RunnerNodeBase.js.map +1 -0
- package/dist/RunnerSteps.d.ts +14 -0
- package/dist/RunnerSteps.js +110 -0
- package/dist/RunnerSteps.js.map +1 -0
- package/dist/RuntimeAdapterNode.d.ts +19 -0
- package/dist/RuntimeAdapterNode.js +87 -0
- package/dist/RuntimeAdapterNode.js.map +1 -0
- package/dist/RuntimeRegistry.d.ts +61 -0
- package/dist/RuntimeRegistry.js +87 -0
- package/dist/RuntimeRegistry.js.map +1 -0
- package/dist/TriggerBase.d.ts +119 -0
- package/dist/TriggerBase.js +413 -0
- package/dist/TriggerBase.js.map +1 -0
- package/dist/adapters/BunRuntimeAdapter.d.ts +38 -0
- package/dist/adapters/BunRuntimeAdapter.js +169 -0
- package/dist/adapters/BunRuntimeAdapter.js.map +1 -0
- package/dist/adapters/DockerRuntimeAdapter.d.ts +85 -0
- package/dist/adapters/DockerRuntimeAdapter.js +298 -0
- package/dist/adapters/DockerRuntimeAdapter.js.map +1 -0
- package/dist/adapters/HttpRuntimeAdapter.d.ts +58 -0
- package/dist/adapters/HttpRuntimeAdapter.js +152 -0
- package/dist/adapters/HttpRuntimeAdapter.js.map +1 -0
- package/dist/adapters/NodeJsRuntimeAdapter.d.ts +23 -0
- package/dist/adapters/NodeJsRuntimeAdapter.js +67 -0
- package/dist/adapters/NodeJsRuntimeAdapter.js.map +1 -0
- package/dist/adapters/RuntimeAdapter.d.ts +42 -0
- package/dist/adapters/RuntimeAdapter.js +2 -0
- package/dist/adapters/RuntimeAdapter.js.map +1 -0
- package/dist/adapters/WasmRuntimeAdapter.d.ts +69 -0
- package/dist/adapters/WasmRuntimeAdapter.js +279 -0
- package/dist/adapters/WasmRuntimeAdapter.js.map +1 -0
- package/dist/cache/NodeResultCache.d.ts +286 -0
- package/dist/cache/NodeResultCache.js +499 -0
- package/dist/cache/NodeResultCache.js.map +1 -0
- package/dist/cache/index.d.ts +1 -0
- package/dist/cache/index.js +2 -0
- package/dist/cache/index.js.map +1 -0
- package/dist/cost/CostEstimator.d.ts +57 -0
- package/dist/cost/CostEstimator.js +171 -0
- package/dist/cost/CostEstimator.js.map +1 -0
- package/dist/cost/index.d.ts +4 -0
- package/dist/cost/index.js +3 -0
- package/dist/cost/index.js.map +1 -0
- package/dist/cost/pricing.d.ts +24 -0
- package/dist/cost/pricing.js +169 -0
- package/dist/cost/pricing.js.map +1 -0
- package/dist/defineNode.d.ts +155 -0
- package/dist/defineNode.js +191 -0
- package/dist/defineNode.js.map +1 -0
- package/dist/graphql/GraphQLSchemaGenerator.d.ts +129 -0
- package/dist/graphql/GraphQLSchemaGenerator.js +425 -0
- package/dist/graphql/GraphQLSchemaGenerator.js.map +1 -0
- package/dist/hmr/FileWatcher.d.ts +62 -0
- package/dist/hmr/FileWatcher.js +185 -0
- package/dist/hmr/FileWatcher.js.map +1 -0
- package/dist/hmr/HmrDevConsole.d.ts +13 -0
- package/dist/hmr/HmrDevConsole.js +46 -0
- package/dist/hmr/HmrDevConsole.js.map +1 -0
- package/dist/hmr/HotReloadManager.d.ts +84 -0
- package/dist/hmr/HotReloadManager.js +195 -0
- package/dist/hmr/HotReloadManager.js.map +1 -0
- package/dist/hmr/index.d.ts +39 -0
- package/dist/hmr/index.js +38 -0
- package/dist/hmr/index.js.map +1 -0
- package/dist/index.d.ts +107 -0
- package/dist/index.js +107 -0
- package/dist/index.js.map +1 -0
- package/dist/integrations/APMIntegration.d.ts +141 -0
- package/dist/integrations/APMIntegration.js +212 -0
- package/dist/integrations/APMIntegration.js.map +1 -0
- package/dist/integrations/AzureMonitorIntegration.d.ts +118 -0
- package/dist/integrations/AzureMonitorIntegration.js +254 -0
- package/dist/integrations/AzureMonitorIntegration.js.map +1 -0
- package/dist/integrations/CloudWatchIntegration.d.ts +135 -0
- package/dist/integrations/CloudWatchIntegration.js +293 -0
- package/dist/integrations/CloudWatchIntegration.js.map +1 -0
- package/dist/integrations/SentryIntegration.d.ts +153 -0
- package/dist/integrations/SentryIntegration.js +200 -0
- package/dist/integrations/SentryIntegration.js.map +1 -0
- package/dist/integrations/index.d.ts +19 -0
- package/dist/integrations/index.js +16 -0
- package/dist/integrations/index.js.map +1 -0
- package/dist/marketplace/RuntimeAutoScaler.d.ts +148 -0
- package/dist/marketplace/RuntimeAutoScaler.js +366 -0
- package/dist/marketplace/RuntimeAutoScaler.js.map +1 -0
- package/dist/marketplace/RuntimeCatalog.d.ts +174 -0
- package/dist/marketplace/RuntimeCatalog.js +339 -0
- package/dist/marketplace/RuntimeCatalog.js.map +1 -0
- package/dist/marketplace/RuntimeDiscovery.d.ts +86 -0
- package/dist/marketplace/RuntimeDiscovery.js +219 -0
- package/dist/marketplace/RuntimeDiscovery.js.map +1 -0
- package/dist/marketplace/RuntimeHealthMonitor.d.ts +100 -0
- package/dist/marketplace/RuntimeHealthMonitor.js +241 -0
- package/dist/marketplace/RuntimeHealthMonitor.js.map +1 -0
- package/dist/marketplace/RuntimeMetricsDashboard.d.ts +113 -0
- package/dist/marketplace/RuntimeMetricsDashboard.js +293 -0
- package/dist/marketplace/RuntimeMetricsDashboard.js.map +1 -0
- package/dist/monitoring/CircuitBreaker.d.ts +107 -0
- package/dist/monitoring/CircuitBreaker.js +238 -0
- package/dist/monitoring/CircuitBreaker.js.map +1 -0
- package/dist/monitoring/DistributedTracer.d.ts +125 -0
- package/dist/monitoring/DistributedTracer.js +230 -0
- package/dist/monitoring/DistributedTracer.js.map +1 -0
- package/dist/monitoring/HealthCheck.d.ts +54 -0
- package/dist/monitoring/HealthCheck.js +102 -0
- package/dist/monitoring/HealthCheck.js.map +1 -0
- package/dist/monitoring/PerformanceProfiler.d.ts +63 -0
- package/dist/monitoring/PerformanceProfiler.js +229 -0
- package/dist/monitoring/PerformanceProfiler.js.map +1 -0
- package/dist/monitoring/PrometheusBootstrap.d.ts +30 -0
- package/dist/monitoring/PrometheusBootstrap.js +71 -0
- package/dist/monitoring/PrometheusBootstrap.js.map +1 -0
- package/dist/monitoring/PrometheusMetricsBridge.d.ts +60 -0
- package/dist/monitoring/PrometheusMetricsBridge.js +216 -0
- package/dist/monitoring/PrometheusMetricsBridge.js.map +1 -0
- package/dist/monitoring/RateLimiter.d.ts +58 -0
- package/dist/monitoring/RateLimiter.js +128 -0
- package/dist/monitoring/RateLimiter.js.map +1 -0
- package/dist/monitoring/StructuredLogger.d.ts +131 -0
- package/dist/monitoring/StructuredLogger.js +207 -0
- package/dist/monitoring/StructuredLogger.js.map +1 -0
- package/dist/monitoring/TracingBootstrap.d.ts +69 -0
- package/dist/monitoring/TracingBootstrap.js +129 -0
- package/dist/monitoring/TracingBootstrap.js.map +1 -0
- package/dist/monitoring/TriggerMetricsCollector.d.ts +94 -0
- package/dist/monitoring/TriggerMetricsCollector.js +174 -0
- package/dist/monitoring/TriggerMetricsCollector.js.map +1 -0
- package/dist/monitoring/index.d.ts +9 -0
- package/dist/monitoring/index.js +10 -0
- package/dist/monitoring/index.js.map +1 -0
- package/dist/openapi/OpenAPIGenerator.d.ts +192 -0
- package/dist/openapi/OpenAPIGenerator.js +373 -0
- package/dist/openapi/OpenAPIGenerator.js.map +1 -0
- package/dist/openapi/index.d.ts +20 -0
- package/dist/openapi/index.js +20 -0
- package/dist/openapi/index.js.map +1 -0
- package/dist/security/ABAC.d.ts +224 -0
- package/dist/security/ABAC.js +380 -0
- package/dist/security/ABAC.js.map +1 -0
- package/dist/security/AuditLogger.d.ts +242 -0
- package/dist/security/AuditLogger.js +317 -0
- package/dist/security/AuditLogger.js.map +1 -0
- package/dist/security/AuthMiddleware.d.ts +163 -0
- package/dist/security/AuthMiddleware.js +274 -0
- package/dist/security/AuthMiddleware.js.map +1 -0
- package/dist/security/EncryptionAtRest.d.ts +206 -0
- package/dist/security/EncryptionAtRest.js +236 -0
- package/dist/security/EncryptionAtRest.js.map +1 -0
- package/dist/security/OAuthProvider.d.ts +334 -0
- package/dist/security/OAuthProvider.js +719 -0
- package/dist/security/OAuthProvider.js.map +1 -0
- package/dist/security/PIIDetector.d.ts +233 -0
- package/dist/security/PIIDetector.js +354 -0
- package/dist/security/PIIDetector.js.map +1 -0
- package/dist/security/RBAC.d.ts +143 -0
- package/dist/security/RBAC.js +285 -0
- package/dist/security/RBAC.js.map +1 -0
- package/dist/security/SecretManager.d.ts +652 -0
- package/dist/security/SecretManager.js +1146 -0
- package/dist/security/SecretManager.js.map +1 -0
- package/dist/security/TLSConfig.d.ts +305 -0
- package/dist/security/TLSConfig.js +550 -0
- package/dist/security/TLSConfig.js.map +1 -0
- package/dist/security/index.d.ts +79 -0
- package/dist/security/index.js +80 -0
- package/dist/security/index.js.map +1 -0
- package/dist/testing/TestHarness.d.ts +189 -0
- package/dist/testing/TestHarness.js +272 -0
- package/dist/testing/TestHarness.js.map +1 -0
- package/dist/testing/TestLogger.d.ts +103 -0
- package/dist/testing/TestLogger.js +153 -0
- package/dist/testing/TestLogger.js.map +1 -0
- package/dist/testing/WorkflowTestRunner.d.ts +172 -0
- package/dist/testing/WorkflowTestRunner.js +355 -0
- package/dist/testing/WorkflowTestRunner.js.map +1 -0
- package/dist/testing/index.d.ts +21 -0
- package/dist/testing/index.js +22 -0
- package/dist/testing/index.js.map +1 -0
- package/dist/tracing/InMemoryRunStore.d.ts +44 -0
- package/dist/tracing/InMemoryRunStore.js +341 -0
- package/dist/tracing/InMemoryRunStore.js.map +1 -0
- package/dist/tracing/PostgresRunStore.d.ts +82 -0
- package/dist/tracing/PostgresRunStore.js +640 -0
- package/dist/tracing/PostgresRunStore.js.map +1 -0
- package/dist/tracing/RunStore.d.ts +38 -0
- package/dist/tracing/RunStore.js +2 -0
- package/dist/tracing/RunStore.js.map +1 -0
- package/dist/tracing/RunTracker.d.ts +75 -0
- package/dist/tracing/RunTracker.js +374 -0
- package/dist/tracing/RunTracker.js.map +1 -0
- package/dist/tracing/SqliteRunStore.d.ts +53 -0
- package/dist/tracing/SqliteRunStore.js +703 -0
- package/dist/tracing/SqliteRunStore.js.map +1 -0
- package/dist/tracing/TraceRouter.d.ts +47 -0
- package/dist/tracing/TraceRouter.js +904 -0
- package/dist/tracing/TraceRouter.js.map +1 -0
- package/dist/tracing/TracingLogger.d.ts +21 -0
- package/dist/tracing/TracingLogger.js +62 -0
- package/dist/tracing/TracingLogger.js.map +1 -0
- package/dist/tracing/createStore.d.ts +30 -0
- package/dist/tracing/createStore.js +75 -0
- package/dist/tracing/createStore.js.map +1 -0
- package/dist/tracing/index.d.ts +13 -0
- package/dist/tracing/index.js +9 -0
- package/dist/tracing/index.js.map +1 -0
- package/dist/tracing/sanitize.d.ts +7 -0
- package/dist/tracing/sanitize.js +95 -0
- package/dist/tracing/sanitize.js.map +1 -0
- package/dist/tracing/types.d.ts +178 -0
- package/dist/tracing/types.js +3 -0
- package/dist/tracing/types.js.map +1 -0
- package/dist/types/Average.d.ts +11 -0
- package/dist/types/Average.js +2 -0
- package/dist/types/Average.js.map +1 -0
- package/dist/types/Condition.d.ts +8 -0
- package/dist/types/Condition.js +2 -0
- package/dist/types/Condition.js.map +1 -0
- package/dist/types/Conditions.d.ts +5 -0
- package/dist/types/Conditions.js +2 -0
- package/dist/types/Conditions.js.map +1 -0
- package/dist/types/Config.d.ts +12 -0
- package/dist/types/Config.js +2 -0
- package/dist/types/Config.js.map +1 -0
- package/dist/types/Flow.d.ts +5 -0
- package/dist/types/Flow.js +2 -0
- package/dist/types/Flow.js.map +1 -0
- package/dist/types/GlobalOptions.d.ts +11 -0
- package/dist/types/GlobalOptions.js +2 -0
- package/dist/types/GlobalOptions.js.map +1 -0
- package/dist/types/Inputs.d.ts +5 -0
- package/dist/types/Inputs.js +2 -0
- package/dist/types/Inputs.js.map +1 -0
- package/dist/types/JsonLikeObject.d.ts +3 -0
- package/dist/types/JsonLikeObject.js +2 -0
- package/dist/types/JsonLikeObject.js.map +1 -0
- package/dist/types/Mapper.d.ts +5 -0
- package/dist/types/Mapper.js +2 -0
- package/dist/types/Mapper.js.map +1 -0
- package/dist/types/Node.d.ts +10 -0
- package/dist/types/Node.js +2 -0
- package/dist/types/Node.js.map +1 -0
- package/dist/types/ParamsDictionary.d.ts +3 -0
- package/dist/types/ParamsDictionary.js +2 -0
- package/dist/types/ParamsDictionary.js.map +1 -0
- package/dist/types/Properties.d.ts +5 -0
- package/dist/types/Properties.js +2 -0
- package/dist/types/Properties.js.map +1 -0
- package/dist/types/Targets.d.ts +5 -0
- package/dist/types/Targets.js +2 -0
- package/dist/types/Targets.js.map +1 -0
- package/dist/types/Trigger.d.ts +5 -0
- package/dist/types/Trigger.js +2 -0
- package/dist/types/Trigger.js.map +1 -0
- package/dist/types/TriggerHttp.d.ts +7 -0
- package/dist/types/TriggerHttp.js +2 -0
- package/dist/types/TriggerHttp.js.map +1 -0
- package/dist/types/TriggerResponse.d.ts +6 -0
- package/dist/types/TriggerResponse.js +2 -0
- package/dist/types/TriggerResponse.js.map +1 -0
- package/dist/types/Triggers.d.ts +5 -0
- package/dist/types/Triggers.js +2 -0
- package/dist/types/Triggers.js.map +1 -0
- package/dist/types/TryCatch.d.ts +6 -0
- package/dist/types/TryCatch.js +2 -0
- package/dist/types/TryCatch.js.map +1 -0
- package/dist/visualization/NodeDependencyGraph.d.ts +76 -0
- package/dist/visualization/NodeDependencyGraph.js +418 -0
- package/dist/visualization/NodeDependencyGraph.js.map +1 -0
- package/dist/visualization/WorkflowVisualizer.d.ts +144 -0
- package/dist/visualization/WorkflowVisualizer.js +446 -0
- package/dist/visualization/WorkflowVisualizer.js.map +1 -0
- package/package.json +95 -0
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import { GlobalLogger } from "@blokjs/shared";
|
|
2
|
+
/**
|
|
3
|
+
* TestLogger - A logger that captures log output for testing.
|
|
4
|
+
*
|
|
5
|
+
* Extends GlobalLogger to be compatible with the Context.logger interface
|
|
6
|
+
* while storing all log entries in memory for assertion and inspection.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```typescript
|
|
10
|
+
* const logger = new TestLogger();
|
|
11
|
+
* logger.info("User created");
|
|
12
|
+
* logger.warn("Rate limit approaching");
|
|
13
|
+
*
|
|
14
|
+
* // Assert specific messages were logged
|
|
15
|
+
* logger.assertLogged("User created", "info");
|
|
16
|
+
* logger.assertLogged(/rate limit/i, "warn");
|
|
17
|
+
*
|
|
18
|
+
* // Inspect all logs
|
|
19
|
+
* const errors = logger.getLogsByLevel("error");
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
export class TestLogger extends GlobalLogger {
|
|
23
|
+
entries;
|
|
24
|
+
constructor() {
|
|
25
|
+
super();
|
|
26
|
+
this.entries = [];
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Log an info-level message.
|
|
30
|
+
*/
|
|
31
|
+
info(message) {
|
|
32
|
+
this.addEntry("info", message);
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Log a warning-level message.
|
|
36
|
+
*/
|
|
37
|
+
warn(message) {
|
|
38
|
+
this.addEntry("warn", message);
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Log a debug-level message.
|
|
42
|
+
*/
|
|
43
|
+
debug(message) {
|
|
44
|
+
this.addEntry("debug", message);
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Implementation of GlobalLogger.log - logs at info level.
|
|
48
|
+
*/
|
|
49
|
+
log(message) {
|
|
50
|
+
this.addEntry("info", message);
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Implementation of GlobalLogger.logLevel.
|
|
54
|
+
*/
|
|
55
|
+
logLevel(level, message) {
|
|
56
|
+
this.addEntry(level, message);
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Implementation of GlobalLogger.error.
|
|
60
|
+
*/
|
|
61
|
+
error(message, _stack = "") {
|
|
62
|
+
this.addEntry("error", message);
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Get all logged entries.
|
|
66
|
+
*/
|
|
67
|
+
getLogs() {
|
|
68
|
+
return this.entries.map((entry) => entry.message);
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Get all log entries with full metadata.
|
|
72
|
+
*/
|
|
73
|
+
getEntries() {
|
|
74
|
+
return [...this.entries];
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Get log entries filtered by level.
|
|
78
|
+
*
|
|
79
|
+
* @param level - The log level to filter by ("info", "warn", "error", "debug")
|
|
80
|
+
* @returns Array of matching log entries
|
|
81
|
+
*/
|
|
82
|
+
getLogsByLevel(level) {
|
|
83
|
+
return this.entries.filter((entry) => entry.level === level);
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Assert that a specific message was logged.
|
|
87
|
+
*
|
|
88
|
+
* @param message - String or RegExp to match against log messages
|
|
89
|
+
* @param level - Optional level to restrict the search to
|
|
90
|
+
* @throws Error if no matching log entry is found
|
|
91
|
+
*/
|
|
92
|
+
assertLogged(message, level) {
|
|
93
|
+
const searchEntries = level ? this.getLogsByLevel(level) : this.entries;
|
|
94
|
+
const found = searchEntries.some((entry) => {
|
|
95
|
+
if (typeof message === "string") {
|
|
96
|
+
return entry.message.includes(message);
|
|
97
|
+
}
|
|
98
|
+
return message.test(entry.message);
|
|
99
|
+
});
|
|
100
|
+
if (!found) {
|
|
101
|
+
const levelInfo = level ? ` at level "${level}"` : "";
|
|
102
|
+
const loggedMessages = searchEntries.map((e) => ` [${e.level}] ${e.message}`).join("\n");
|
|
103
|
+
throw new Error(`Expected log message matching ${message}${levelInfo} but none was found.\n` +
|
|
104
|
+
`Logged messages:\n${loggedMessages || " (none)"}`);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Assert that a specific message was NOT logged.
|
|
109
|
+
*
|
|
110
|
+
* @param message - String or RegExp to match against log messages
|
|
111
|
+
* @param level - Optional level to restrict the search to
|
|
112
|
+
* @throws Error if a matching log entry is found
|
|
113
|
+
*/
|
|
114
|
+
assertNotLogged(message, level) {
|
|
115
|
+
const searchEntries = level ? this.getLogsByLevel(level) : this.entries;
|
|
116
|
+
const found = searchEntries.some((entry) => {
|
|
117
|
+
if (typeof message === "string") {
|
|
118
|
+
return entry.message.includes(message);
|
|
119
|
+
}
|
|
120
|
+
return message.test(entry.message);
|
|
121
|
+
});
|
|
122
|
+
if (found) {
|
|
123
|
+
const levelInfo = level ? ` at level "${level}"` : "";
|
|
124
|
+
throw new Error(`Expected log message matching ${message}${levelInfo} to NOT be present, but it was found.`);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Clear all captured log entries.
|
|
129
|
+
*/
|
|
130
|
+
clear() {
|
|
131
|
+
this.entries = [];
|
|
132
|
+
this.logs = [];
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Get the total number of log entries.
|
|
136
|
+
*/
|
|
137
|
+
get count() {
|
|
138
|
+
return this.entries.length;
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Internal helper to add a log entry.
|
|
142
|
+
*/
|
|
143
|
+
addEntry(level, message) {
|
|
144
|
+
const entry = {
|
|
145
|
+
level,
|
|
146
|
+
message,
|
|
147
|
+
timestamp: Date.now(),
|
|
148
|
+
};
|
|
149
|
+
this.entries.push(entry);
|
|
150
|
+
this.logs.push(message);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
//# sourceMappingURL=TestLogger.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TestLogger.js","sourceRoot":"","sources":["../../src/testing/TestLogger.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import type { Context } from "@blokjs/shared";
|
|
2
|
+
import BlokService from "../Blok";
|
|
3
|
+
import type { FunctionNode } from "../defineNode";
|
|
4
|
+
import type { TestContextOverrides, TestResult } from "./TestHarness";
|
|
5
|
+
/**
|
|
6
|
+
* Configuration options for the WorkflowTestRunner.
|
|
7
|
+
*/
|
|
8
|
+
export interface WorkflowTestConfig {
|
|
9
|
+
/** Timeout in milliseconds for the entire workflow execution. Default: 30000 */
|
|
10
|
+
timeout?: number;
|
|
11
|
+
/** Whether to print execution details to the console. Default: false */
|
|
12
|
+
verbose?: boolean;
|
|
13
|
+
/** When true, all nodes without explicit implementations will be auto-mocked. Default: false */
|
|
14
|
+
mockAllNodes?: boolean;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Result of a workflow test execution.
|
|
18
|
+
*/
|
|
19
|
+
export interface WorkflowTestResult {
|
|
20
|
+
/** Whether the entire workflow executed successfully */
|
|
21
|
+
success: boolean;
|
|
22
|
+
/** Final output of the workflow */
|
|
23
|
+
output: any;
|
|
24
|
+
/** Ordered trace of node executions */
|
|
25
|
+
trace: ExecutionTrace[];
|
|
26
|
+
/** Total execution duration in milliseconds */
|
|
27
|
+
durationMs: number;
|
|
28
|
+
/** Per-node results keyed by node name */
|
|
29
|
+
nodeResults: Map<string, TestResult<any>>;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* A record of a single node execution within a workflow.
|
|
33
|
+
*/
|
|
34
|
+
export interface ExecutionTrace {
|
|
35
|
+
/** Name of the node that was executed */
|
|
36
|
+
nodeName: string;
|
|
37
|
+
/** The index of this step in the workflow */
|
|
38
|
+
stepIndex: number;
|
|
39
|
+
/** Input data provided to the node */
|
|
40
|
+
input: any;
|
|
41
|
+
/** Output data returned by the node */
|
|
42
|
+
output: any;
|
|
43
|
+
/** Execution duration in milliseconds */
|
|
44
|
+
durationMs: number;
|
|
45
|
+
/** Whether this node executed successfully */
|
|
46
|
+
success: boolean;
|
|
47
|
+
/** Error message if the node failed */
|
|
48
|
+
error?: string;
|
|
49
|
+
/** Timestamp when this node started execution */
|
|
50
|
+
timestamp: number;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Options for a single workflow execution.
|
|
54
|
+
*/
|
|
55
|
+
export interface WorkflowExecuteOptions {
|
|
56
|
+
/** HTTP headers to populate in the context request */
|
|
57
|
+
headers?: Record<string, string>;
|
|
58
|
+
/** Query parameters to populate in the context request */
|
|
59
|
+
query?: Record<string, string>;
|
|
60
|
+
/** Path parameters to populate in the context request */
|
|
61
|
+
params?: Record<string, string>;
|
|
62
|
+
/** Additional context overrides */
|
|
63
|
+
contextOverrides?: TestContextOverrides;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* WorkflowTestRunner - For testing complete workflows.
|
|
67
|
+
*
|
|
68
|
+
* Allows you to register real or mock node implementations and execute
|
|
69
|
+
* workflow definitions in a controlled test environment. Captures a full
|
|
70
|
+
* execution trace showing which nodes ran, in what order, with what data.
|
|
71
|
+
*
|
|
72
|
+
* @example
|
|
73
|
+
* ```typescript
|
|
74
|
+
* import { WorkflowTestRunner } from "@blokjs/runner";
|
|
75
|
+
* import { defineNode } from "@blokjs/runner";
|
|
76
|
+
* import { z } from "zod";
|
|
77
|
+
*
|
|
78
|
+
* const runner = new WorkflowTestRunner({ verbose: true });
|
|
79
|
+
*
|
|
80
|
+
* // Register a real node
|
|
81
|
+
* runner.registerNode("validate-input", ValidateInputNode);
|
|
82
|
+
*
|
|
83
|
+
* // Register a mock for an external API call
|
|
84
|
+
* runner.mockNode("fetch-user", async (input) => {
|
|
85
|
+
* return { user: { id: input.userId, name: "Test User" } };
|
|
86
|
+
* });
|
|
87
|
+
*
|
|
88
|
+
* // Load and execute workflow
|
|
89
|
+
* runner.loadWorkflow({
|
|
90
|
+
* name: "get-user",
|
|
91
|
+
* steps: [
|
|
92
|
+
* { name: "step1", node: "validate-input", inputs: { userId: "abc-123" } },
|
|
93
|
+
* { name: "step2", node: "fetch-user", inputs: { userId: "${response.data.userId}" } },
|
|
94
|
+
* ],
|
|
95
|
+
* });
|
|
96
|
+
*
|
|
97
|
+
* const result = await runner.execute({ userId: "abc-123" });
|
|
98
|
+
* console.log(result.trace); // See execution order and data flow
|
|
99
|
+
* ```
|
|
100
|
+
*/
|
|
101
|
+
export declare class WorkflowTestRunner {
|
|
102
|
+
private config;
|
|
103
|
+
private nodes;
|
|
104
|
+
private workflow;
|
|
105
|
+
private trace;
|
|
106
|
+
private nodeResults;
|
|
107
|
+
constructor(config?: WorkflowTestConfig);
|
|
108
|
+
/**
|
|
109
|
+
* Register a real node implementation for use in the workflow.
|
|
110
|
+
*
|
|
111
|
+
* @param name - The node name as referenced in the workflow steps
|
|
112
|
+
* @param node - A BlokService instance or a FunctionNode from defineNode()
|
|
113
|
+
*/
|
|
114
|
+
registerNode(name: string, node: BlokService<any> | FunctionNode<any, any>): void;
|
|
115
|
+
/**
|
|
116
|
+
* Register a mock node that executes the provided handler function.
|
|
117
|
+
*
|
|
118
|
+
* Use this to simulate external API calls, database queries, or any
|
|
119
|
+
* node whose real implementation you want to skip during testing.
|
|
120
|
+
*
|
|
121
|
+
* @param name - The node name as referenced in the workflow steps
|
|
122
|
+
* @param handler - Async function that receives (input, ctx) and returns output
|
|
123
|
+
*/
|
|
124
|
+
mockNode(name: string, handler: (input: any, ctx: Context) => Promise<any>): void;
|
|
125
|
+
/**
|
|
126
|
+
* Load a workflow definition from a JSON object or JSON string.
|
|
127
|
+
*
|
|
128
|
+
* The workflow should have a `steps` array where each step defines:
|
|
129
|
+
* - `name`: step identifier
|
|
130
|
+
* - `node`: the node type to execute
|
|
131
|
+
* - `inputs`: input data for the node (optional)
|
|
132
|
+
*
|
|
133
|
+
* @param workflow - Workflow definition object or JSON string
|
|
134
|
+
*/
|
|
135
|
+
loadWorkflow(workflow: object | string): void;
|
|
136
|
+
/**
|
|
137
|
+
* Execute the loaded workflow with the given input.
|
|
138
|
+
*
|
|
139
|
+
* Each step in the workflow is executed sequentially. The output of each
|
|
140
|
+
* step is fed into the context for subsequent steps. A full execution
|
|
141
|
+
* trace is captured for inspection.
|
|
142
|
+
*
|
|
143
|
+
* @param input - Input data (populates request.body in the context)
|
|
144
|
+
* @param options - Optional execution configuration
|
|
145
|
+
* @returns WorkflowTestResult with output, trace, and per-node results
|
|
146
|
+
* @throws Error if no workflow is loaded or if a required node is not registered
|
|
147
|
+
*/
|
|
148
|
+
execute(input: any, options?: WorkflowExecuteOptions): Promise<WorkflowTestResult>;
|
|
149
|
+
/**
|
|
150
|
+
* Get the execution trace from the most recent workflow run.
|
|
151
|
+
*
|
|
152
|
+
* @returns Array of ExecutionTrace entries in execution order
|
|
153
|
+
*/
|
|
154
|
+
getTrace(): ExecutionTrace[];
|
|
155
|
+
/**
|
|
156
|
+
* Reset the runner state (clears trace, node results, and loaded workflow).
|
|
157
|
+
* Registered nodes and mocks are preserved.
|
|
158
|
+
*/
|
|
159
|
+
reset(): void;
|
|
160
|
+
/**
|
|
161
|
+
* Fully reset the runner, including all registered nodes and mocks.
|
|
162
|
+
*/
|
|
163
|
+
resetAll(): void;
|
|
164
|
+
/**
|
|
165
|
+
* Internal: Execute all workflow steps sequentially.
|
|
166
|
+
*/
|
|
167
|
+
private executeSteps;
|
|
168
|
+
/**
|
|
169
|
+
* Internal: Execute a single workflow step.
|
|
170
|
+
*/
|
|
171
|
+
private executeStep;
|
|
172
|
+
}
|
|
@@ -0,0 +1,355 @@
|
|
|
1
|
+
import BlokService from "../Blok";
|
|
2
|
+
import BlokResponse from "../BlokResponse";
|
|
3
|
+
import { TestLogger } from "./TestLogger";
|
|
4
|
+
/**
|
|
5
|
+
* A mock node implementation wrapping a user-provided handler function.
|
|
6
|
+
*/
|
|
7
|
+
class MockNode extends BlokService {
|
|
8
|
+
handler;
|
|
9
|
+
constructor(name, handler) {
|
|
10
|
+
super();
|
|
11
|
+
this.name = name;
|
|
12
|
+
this.handler = handler;
|
|
13
|
+
}
|
|
14
|
+
async handle(ctx, inputs) {
|
|
15
|
+
const response = new BlokResponse();
|
|
16
|
+
try {
|
|
17
|
+
const result = await this.handler(inputs, ctx);
|
|
18
|
+
response.setSuccess(result);
|
|
19
|
+
}
|
|
20
|
+
catch (error) {
|
|
21
|
+
const { GlobalError } = await import("@blokjs/shared");
|
|
22
|
+
const globalError = new GlobalError(error instanceof Error ? error.message : String(error));
|
|
23
|
+
globalError.setCode(500);
|
|
24
|
+
globalError.setName(this.name);
|
|
25
|
+
response.setError(globalError);
|
|
26
|
+
}
|
|
27
|
+
return response;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* An auto-mock node that returns an empty object for any input.
|
|
32
|
+
*/
|
|
33
|
+
class AutoMockNode extends MockNode {
|
|
34
|
+
constructor(name) {
|
|
35
|
+
super(name, async () => ({}));
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* WorkflowTestRunner - For testing complete workflows.
|
|
40
|
+
*
|
|
41
|
+
* Allows you to register real or mock node implementations and execute
|
|
42
|
+
* workflow definitions in a controlled test environment. Captures a full
|
|
43
|
+
* execution trace showing which nodes ran, in what order, with what data.
|
|
44
|
+
*
|
|
45
|
+
* @example
|
|
46
|
+
* ```typescript
|
|
47
|
+
* import { WorkflowTestRunner } from "@blokjs/runner";
|
|
48
|
+
* import { defineNode } from "@blokjs/runner";
|
|
49
|
+
* import { z } from "zod";
|
|
50
|
+
*
|
|
51
|
+
* const runner = new WorkflowTestRunner({ verbose: true });
|
|
52
|
+
*
|
|
53
|
+
* // Register a real node
|
|
54
|
+
* runner.registerNode("validate-input", ValidateInputNode);
|
|
55
|
+
*
|
|
56
|
+
* // Register a mock for an external API call
|
|
57
|
+
* runner.mockNode("fetch-user", async (input) => {
|
|
58
|
+
* return { user: { id: input.userId, name: "Test User" } };
|
|
59
|
+
* });
|
|
60
|
+
*
|
|
61
|
+
* // Load and execute workflow
|
|
62
|
+
* runner.loadWorkflow({
|
|
63
|
+
* name: "get-user",
|
|
64
|
+
* steps: [
|
|
65
|
+
* { name: "step1", node: "validate-input", inputs: { userId: "abc-123" } },
|
|
66
|
+
* { name: "step2", node: "fetch-user", inputs: { userId: "${response.data.userId}" } },
|
|
67
|
+
* ],
|
|
68
|
+
* });
|
|
69
|
+
*
|
|
70
|
+
* const result = await runner.execute({ userId: "abc-123" });
|
|
71
|
+
* console.log(result.trace); // See execution order and data flow
|
|
72
|
+
* ```
|
|
73
|
+
*/
|
|
74
|
+
export class WorkflowTestRunner {
|
|
75
|
+
config;
|
|
76
|
+
nodes;
|
|
77
|
+
workflow;
|
|
78
|
+
trace;
|
|
79
|
+
nodeResults;
|
|
80
|
+
constructor(config) {
|
|
81
|
+
this.config = {
|
|
82
|
+
timeout: config?.timeout ?? 30000,
|
|
83
|
+
verbose: config?.verbose ?? false,
|
|
84
|
+
mockAllNodes: config?.mockAllNodes ?? false,
|
|
85
|
+
};
|
|
86
|
+
this.nodes = new Map();
|
|
87
|
+
this.workflow = null;
|
|
88
|
+
this.trace = [];
|
|
89
|
+
this.nodeResults = new Map();
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Register a real node implementation for use in the workflow.
|
|
93
|
+
*
|
|
94
|
+
* @param name - The node name as referenced in the workflow steps
|
|
95
|
+
* @param node - A BlokService instance or a FunctionNode from defineNode()
|
|
96
|
+
*/
|
|
97
|
+
registerNode(name, node) {
|
|
98
|
+
node.name = name;
|
|
99
|
+
this.nodes.set(name, node);
|
|
100
|
+
if (this.config.verbose) {
|
|
101
|
+
console.log(`[WorkflowTestRunner] Registered node: ${name}`);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Register a mock node that executes the provided handler function.
|
|
106
|
+
*
|
|
107
|
+
* Use this to simulate external API calls, database queries, or any
|
|
108
|
+
* node whose real implementation you want to skip during testing.
|
|
109
|
+
*
|
|
110
|
+
* @param name - The node name as referenced in the workflow steps
|
|
111
|
+
* @param handler - Async function that receives (input, ctx) and returns output
|
|
112
|
+
*/
|
|
113
|
+
mockNode(name, handler) {
|
|
114
|
+
const mockNodeInstance = new MockNode(name, handler);
|
|
115
|
+
this.nodes.set(name, mockNodeInstance);
|
|
116
|
+
if (this.config.verbose) {
|
|
117
|
+
console.log(`[WorkflowTestRunner] Mocked node: ${name}`);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Load a workflow definition from a JSON object or JSON string.
|
|
122
|
+
*
|
|
123
|
+
* The workflow should have a `steps` array where each step defines:
|
|
124
|
+
* - `name`: step identifier
|
|
125
|
+
* - `node`: the node type to execute
|
|
126
|
+
* - `inputs`: input data for the node (optional)
|
|
127
|
+
*
|
|
128
|
+
* @param workflow - Workflow definition object or JSON string
|
|
129
|
+
*/
|
|
130
|
+
loadWorkflow(workflow) {
|
|
131
|
+
if (typeof workflow === "string") {
|
|
132
|
+
this.workflow = JSON.parse(workflow);
|
|
133
|
+
}
|
|
134
|
+
else {
|
|
135
|
+
this.workflow = workflow;
|
|
136
|
+
}
|
|
137
|
+
if (!this.workflow.steps || !Array.isArray(this.workflow.steps)) {
|
|
138
|
+
throw new Error("Workflow must have a 'steps' array");
|
|
139
|
+
}
|
|
140
|
+
if (this.config.verbose) {
|
|
141
|
+
console.log(`[WorkflowTestRunner] Loaded workflow: ${this.workflow.name ?? "(unnamed)"} ` +
|
|
142
|
+
`with ${this.workflow.steps.length} steps`);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Execute the loaded workflow with the given input.
|
|
147
|
+
*
|
|
148
|
+
* Each step in the workflow is executed sequentially. The output of each
|
|
149
|
+
* step is fed into the context for subsequent steps. A full execution
|
|
150
|
+
* trace is captured for inspection.
|
|
151
|
+
*
|
|
152
|
+
* @param input - Input data (populates request.body in the context)
|
|
153
|
+
* @param options - Optional execution configuration
|
|
154
|
+
* @returns WorkflowTestResult with output, trace, and per-node results
|
|
155
|
+
* @throws Error if no workflow is loaded or if a required node is not registered
|
|
156
|
+
*/
|
|
157
|
+
async execute(input, options) {
|
|
158
|
+
if (!this.workflow) {
|
|
159
|
+
throw new Error("No workflow loaded. Call loadWorkflow() first.");
|
|
160
|
+
}
|
|
161
|
+
// Reset trace for this execution
|
|
162
|
+
this.trace = [];
|
|
163
|
+
this.nodeResults = new Map();
|
|
164
|
+
const logger = options?.contextOverrides?.logger ?? new TestLogger();
|
|
165
|
+
const workflowStartTime = performance.now();
|
|
166
|
+
// Build the initial context
|
|
167
|
+
const ctx = {
|
|
168
|
+
id: options?.contextOverrides?.id ?? `test-workflow-${Date.now()}`,
|
|
169
|
+
workflow_name: this.workflow.name ?? "test-workflow",
|
|
170
|
+
workflow_path: options?.contextOverrides?.workflow_path ?? "/test",
|
|
171
|
+
request: {
|
|
172
|
+
body: input ?? {},
|
|
173
|
+
headers: options?.headers ?? options?.contextOverrides?.request?.headers ?? {},
|
|
174
|
+
query: options?.query ?? options?.contextOverrides?.request?.query ?? {},
|
|
175
|
+
params: options?.params ?? options?.contextOverrides?.request?.params ?? {},
|
|
176
|
+
},
|
|
177
|
+
response: {
|
|
178
|
+
data: {},
|
|
179
|
+
error: null,
|
|
180
|
+
success: true,
|
|
181
|
+
contentType: "application/json",
|
|
182
|
+
},
|
|
183
|
+
error: options?.contextOverrides?.error ?? { message: [] },
|
|
184
|
+
logger: logger,
|
|
185
|
+
config: options?.contextOverrides?.config ?? {},
|
|
186
|
+
vars: options?.contextOverrides?.vars ?? {},
|
|
187
|
+
env: options?.contextOverrides?.env ?? {},
|
|
188
|
+
eventLogger: logger,
|
|
189
|
+
_PRIVATE_: {},
|
|
190
|
+
};
|
|
191
|
+
let workflowSuccess = true;
|
|
192
|
+
let workflowError = null;
|
|
193
|
+
// Create a timeout promise
|
|
194
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
195
|
+
setTimeout(() => {
|
|
196
|
+
reject(new Error(`Workflow execution timed out after ${this.config.timeout}ms`));
|
|
197
|
+
}, this.config.timeout);
|
|
198
|
+
});
|
|
199
|
+
try {
|
|
200
|
+
await Promise.race([this.executeSteps(ctx, this.workflow.steps), timeoutPromise]);
|
|
201
|
+
}
|
|
202
|
+
catch (error) {
|
|
203
|
+
workflowSuccess = false;
|
|
204
|
+
workflowError = error;
|
|
205
|
+
if (this.config.verbose) {
|
|
206
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
207
|
+
console.log(`[WorkflowTestRunner] Workflow failed: ${errorMsg}`);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
const workflowEndTime = performance.now();
|
|
211
|
+
return {
|
|
212
|
+
success: workflowSuccess,
|
|
213
|
+
output: workflowSuccess ? ctx.response?.data : workflowError,
|
|
214
|
+
trace: [...this.trace],
|
|
215
|
+
durationMs: workflowEndTime - workflowStartTime,
|
|
216
|
+
nodeResults: new Map(this.nodeResults),
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* Get the execution trace from the most recent workflow run.
|
|
221
|
+
*
|
|
222
|
+
* @returns Array of ExecutionTrace entries in execution order
|
|
223
|
+
*/
|
|
224
|
+
getTrace() {
|
|
225
|
+
return [...this.trace];
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* Reset the runner state (clears trace, node results, and loaded workflow).
|
|
229
|
+
* Registered nodes and mocks are preserved.
|
|
230
|
+
*/
|
|
231
|
+
reset() {
|
|
232
|
+
this.workflow = null;
|
|
233
|
+
this.trace = [];
|
|
234
|
+
this.nodeResults = new Map();
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* Fully reset the runner, including all registered nodes and mocks.
|
|
238
|
+
*/
|
|
239
|
+
resetAll() {
|
|
240
|
+
this.reset();
|
|
241
|
+
this.nodes = new Map();
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* Internal: Execute all workflow steps sequentially.
|
|
245
|
+
*/
|
|
246
|
+
async executeSteps(ctx, steps) {
|
|
247
|
+
for (let i = 0; i < steps.length; i++) {
|
|
248
|
+
const step = steps[i];
|
|
249
|
+
await this.executeStep(ctx, step, i);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* Internal: Execute a single workflow step.
|
|
254
|
+
*/
|
|
255
|
+
async executeStep(ctx, step, stepIndex) {
|
|
256
|
+
const nodeName = step.node;
|
|
257
|
+
let node = this.nodes.get(nodeName);
|
|
258
|
+
// Auto-mock if configured
|
|
259
|
+
if (!node && this.config.mockAllNodes) {
|
|
260
|
+
node = new AutoMockNode(nodeName);
|
|
261
|
+
this.nodes.set(nodeName, node);
|
|
262
|
+
if (this.config.verbose) {
|
|
263
|
+
console.log(`[WorkflowTestRunner] Auto-mocked node: ${nodeName}`);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
if (!node) {
|
|
267
|
+
throw new Error(`Node "${nodeName}" is not registered. ` +
|
|
268
|
+
`Call registerNode("${nodeName}", nodeImpl) or mockNode("${nodeName}", handler) before executing.`);
|
|
269
|
+
}
|
|
270
|
+
// Determine input: use step.inputs if defined, otherwise use current response data
|
|
271
|
+
const stepInput = step.inputs ?? ctx.response?.data ?? {};
|
|
272
|
+
if (this.config.verbose) {
|
|
273
|
+
console.log(`[WorkflowTestRunner] Step ${stepIndex}: executing "${step.name}" (node: ${nodeName})`);
|
|
274
|
+
}
|
|
275
|
+
const startTime = performance.now();
|
|
276
|
+
const traceEntry = {
|
|
277
|
+
nodeName: step.name,
|
|
278
|
+
stepIndex,
|
|
279
|
+
input: stepInput,
|
|
280
|
+
output: null,
|
|
281
|
+
durationMs: 0,
|
|
282
|
+
success: false,
|
|
283
|
+
timestamp: Date.now(),
|
|
284
|
+
};
|
|
285
|
+
try {
|
|
286
|
+
const response = (await node.handle(ctx, stepInput));
|
|
287
|
+
const endTime = performance.now();
|
|
288
|
+
traceEntry.durationMs = endTime - startTime;
|
|
289
|
+
if (response.success === false || response.error) {
|
|
290
|
+
traceEntry.success = false;
|
|
291
|
+
traceEntry.error = response.error?.toString() ?? "Unknown error";
|
|
292
|
+
traceEntry.output = null;
|
|
293
|
+
this.nodeResults.set(step.name, {
|
|
294
|
+
success: false,
|
|
295
|
+
data: null,
|
|
296
|
+
error: response.error,
|
|
297
|
+
context: ctx,
|
|
298
|
+
durationMs: traceEntry.durationMs,
|
|
299
|
+
logs: ctx.logger.getLogs?.() ?? [],
|
|
300
|
+
});
|
|
301
|
+
this.trace.push(traceEntry);
|
|
302
|
+
throw new Error(`Node "${step.name}" (${nodeName}) failed: ${response.error?.toString() ?? "Unknown error"}`);
|
|
303
|
+
}
|
|
304
|
+
// Update context response with node output
|
|
305
|
+
ctx.response = {
|
|
306
|
+
data: response.data,
|
|
307
|
+
error: null,
|
|
308
|
+
success: true,
|
|
309
|
+
contentType: response.contentType ?? "application/json",
|
|
310
|
+
};
|
|
311
|
+
// If step sets a var, store output in vars
|
|
312
|
+
if (step.set_var) {
|
|
313
|
+
if (!ctx.vars)
|
|
314
|
+
ctx.vars = {};
|
|
315
|
+
ctx.vars[step.name] = response.data;
|
|
316
|
+
}
|
|
317
|
+
traceEntry.success = true;
|
|
318
|
+
traceEntry.output = response.data;
|
|
319
|
+
this.nodeResults.set(step.name, {
|
|
320
|
+
success: true,
|
|
321
|
+
data: response.data,
|
|
322
|
+
error: null,
|
|
323
|
+
context: ctx,
|
|
324
|
+
durationMs: traceEntry.durationMs,
|
|
325
|
+
logs: ctx.logger.getLogs?.() ?? [],
|
|
326
|
+
});
|
|
327
|
+
}
|
|
328
|
+
catch (error) {
|
|
329
|
+
const endTime = performance.now();
|
|
330
|
+
if (!traceEntry.durationMs) {
|
|
331
|
+
traceEntry.durationMs = endTime - startTime;
|
|
332
|
+
}
|
|
333
|
+
if (!traceEntry.error) {
|
|
334
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
335
|
+
traceEntry.success = false;
|
|
336
|
+
traceEntry.error = errorMsg;
|
|
337
|
+
this.nodeResults.set(step.name, {
|
|
338
|
+
success: false,
|
|
339
|
+
data: null,
|
|
340
|
+
error,
|
|
341
|
+
context: ctx,
|
|
342
|
+
durationMs: traceEntry.durationMs,
|
|
343
|
+
logs: ctx.logger.getLogs?.() ?? [],
|
|
344
|
+
});
|
|
345
|
+
}
|
|
346
|
+
this.trace.push(traceEntry);
|
|
347
|
+
throw error;
|
|
348
|
+
}
|
|
349
|
+
this.trace.push(traceEntry);
|
|
350
|
+
if (this.config.verbose) {
|
|
351
|
+
console.log(`[WorkflowTestRunner] Step ${stepIndex}: "${step.name}" completed in ${traceEntry.durationMs.toFixed(2)}ms`);
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
//# sourceMappingURL=WorkflowTestRunner.js.map
|