busybee 0.1.0 → 0.3.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +71 -7
- data/README.md +70 -42
- data/docs/client/quick_start.md +279 -0
- data/docs/client.md +825 -0
- data/docs/configuration.md +550 -0
- data/docs/grpc.md +50 -25
- data/docs/testing.md +118 -28
- data/docs/workers.md +982 -0
- data/exe/busybee +6 -0
- data/lib/busybee/cli.rb +173 -0
- data/lib/busybee/client/error_handling.rb +37 -0
- data/lib/busybee/client/job_operations.rb +236 -0
- data/lib/busybee/client/message_operations.rb +84 -0
- data/lib/busybee/client/process_operations.rb +108 -0
- data/lib/busybee/client/variable_operations.rb +64 -0
- data/lib/busybee/client.rb +87 -0
- data/lib/busybee/configure.rb +290 -0
- data/lib/busybee/credentials/camunda_cloud.rb +58 -0
- data/lib/busybee/credentials/insecure.rb +24 -0
- data/lib/busybee/credentials/oauth.rb +157 -0
- data/lib/busybee/credentials/tls.rb +43 -0
- data/lib/busybee/credentials.rb +200 -0
- data/lib/busybee/defaults.rb +20 -0
- data/lib/busybee/error.rb +50 -0
- data/lib/busybee/grpc/error.rb +60 -0
- data/lib/busybee/grpc.rb +2 -2
- data/lib/busybee/job.rb +219 -0
- data/lib/busybee/job_stream.rb +85 -0
- data/lib/busybee/logging.rb +61 -0
- data/lib/busybee/railtie.rb +113 -0
- data/lib/busybee/runner/hybrid.rb +64 -0
- data/lib/busybee/runner/multi.rb +101 -0
- data/lib/busybee/runner/polling.rb +54 -0
- data/lib/busybee/runner/streaming.rb +159 -0
- data/lib/busybee/runner.rb +97 -0
- data/lib/busybee/runtime_config.rb +184 -0
- data/lib/busybee/serialization.rb +100 -0
- data/lib/busybee/testing/activated_job.rb +33 -8
- data/lib/busybee/testing/helpers/execution.rb +139 -0
- data/lib/busybee/testing/helpers/support.rb +78 -0
- data/lib/busybee/testing/helpers.rb +56 -66
- data/lib/busybee/testing/matchers/complete_job.rb +55 -0
- data/lib/busybee/testing/matchers/fail_job.rb +75 -0
- data/lib/busybee/testing/matchers/have_activated.rb +1 -1
- data/lib/busybee/testing/matchers/have_available_jobs.rb +44 -0
- data/lib/busybee/testing/matchers/throw_bpmn_error_on.rb +72 -0
- data/lib/busybee/testing.rb +5 -33
- data/lib/busybee/version.rb +1 -1
- data/lib/busybee/worker/configuration.rb +287 -0
- data/lib/busybee/worker/dsl.rb +187 -0
- data/lib/busybee/worker/shutdown.rb +27 -0
- data/lib/busybee/worker.rb +130 -0
- data/lib/busybee.rb +134 -2
- metadata +80 -3
data/docs/testing.md
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
# Testing BPMN Workflows
|
|
1
|
+
# Testing BPMN Workflows
|
|
2
2
|
|
|
3
|
-
Busybee provides RSpec helpers and matchers for testing BPMN workflows against Zeebe. This testing module
|
|
3
|
+
Busybee provides RSpec helpers and matchers for testing BPMN workflows against a running Zeebe instance. This testing module lets you deploy processes, create instances, activate jobs, and verify that your workflow definitions route work correctly.
|
|
4
|
+
|
|
5
|
+
This document covers **workflow-level testing** — verifying that your BPMN process definitions behave as expected. If you're looking to **unit test your worker classes** (the Ruby code that handles individual jobs), see [Workers: Testing Workers](workers.md#testing-workers). The two complement each other: workflow tests verify the orchestration, worker tests verify the business logic.
|
|
4
6
|
|
|
5
7
|
## Installation
|
|
6
8
|
|
|
@@ -35,27 +37,28 @@ Configure the Zeebe connection. Busybee reads from environment variables by defa
|
|
|
35
37
|
|
|
36
38
|
```ruby
|
|
37
39
|
# Use environment variables (recommended)
|
|
38
|
-
#
|
|
39
|
-
# ZEEBE_USERNAME=demo
|
|
40
|
-
# ZEEBE_PASSWORD=demo
|
|
40
|
+
# CLUSTER_ADDRESS=localhost:26500
|
|
41
41
|
|
|
42
42
|
# Or configure explicitly
|
|
43
|
-
Busybee
|
|
44
|
-
config.
|
|
45
|
-
config.username = "demo"
|
|
46
|
-
config.password = "demo"
|
|
47
|
-
config.activate_request_timeout = 2000 # milliseconds, default: 1000
|
|
43
|
+
Busybee.configure do |config|
|
|
44
|
+
config.cluster_address = "localhost:26500"
|
|
48
45
|
end
|
|
49
46
|
```
|
|
50
47
|
|
|
51
|
-
|
|
48
|
+
Job activation timeouts used by the testing helpers default to `Busybee.default_job_request_timeout` (see [Configuration](configuration.md)). You can override per-call or globally:
|
|
49
|
+
|
|
50
|
+
```ruby
|
|
51
|
+
# Per-call: pass timeout directly to the helper
|
|
52
|
+
job = activate_job("my-task", timeout: 2000)
|
|
53
|
+
jobs = activate_jobs("my-task", max_jobs: 5, timeout: 2000)
|
|
54
|
+
|
|
55
|
+
# Globally: configure a shorter default for your test environment
|
|
56
|
+
Busybee.configure do |config|
|
|
57
|
+
config.default_job_request_timeout = 2000 # milliseconds, default: 60_000
|
|
58
|
+
end
|
|
59
|
+
```
|
|
52
60
|
|
|
53
|
-
|
|
54
|
-
|--------|---------------------|---------|-------------|
|
|
55
|
-
| `address` | `ZEEBE_ADDRESS` | `"localhost:26500"` | Zeebe gateway gRPC address |
|
|
56
|
-
| `username` | `ZEEBE_USERNAME` | `"demo"` | Zeebe authentication username |
|
|
57
|
-
| `password` | `ZEEBE_PASSWORD` | `"demo"` | Zeebe authentication password |
|
|
58
|
-
| `activate_request_timeout` | - | `1000` | Timeout in milliseconds for job activation requests |
|
|
61
|
+
For authenticated cluster connections (TLS, OAuth, Camunda Cloud), configure credentials via `Busybee.configure` in your Rails `config/environments/test.rb` or equivalent. See [Providing Credentials](client.md#providing-credentials) for details.
|
|
59
62
|
|
|
60
63
|
## Helper Methods
|
|
61
64
|
|
|
@@ -145,14 +148,47 @@ after do
|
|
|
145
148
|
end
|
|
146
149
|
```
|
|
147
150
|
|
|
151
|
+
#### `with_activated_job_instance(job_type)`
|
|
152
|
+
|
|
153
|
+
Activates a job, yields it, and automatically completes it when the block exits. Must be called within a `with_process_instance` block. This ensures cleanup even if tests fail.
|
|
154
|
+
|
|
155
|
+
**Parameters:**
|
|
156
|
+
- `job_type` (String) - Job type to activate
|
|
157
|
+
|
|
158
|
+
**Yields:** `ActivatedJob` instance
|
|
159
|
+
|
|
160
|
+
**Note:** If the job is already completed or failed within the block, the cleanup completion is silently ignored.
|
|
161
|
+
|
|
162
|
+
**Examples:**
|
|
163
|
+
|
|
164
|
+
```ruby
|
|
165
|
+
with_process_instance("order-fulfillment") do
|
|
166
|
+
with_activated_job_instance("process-order") do |job|
|
|
167
|
+
# Test job without worrying about cleanup
|
|
168
|
+
expect(job.variables["order_id"]).to eq("123")
|
|
169
|
+
client.update_job_retries(job.key, 5)
|
|
170
|
+
# Job is automatically completed after block
|
|
171
|
+
end
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
# Test that completes the job explicitly
|
|
175
|
+
with_process_instance("order-fulfillment") do
|
|
176
|
+
with_activated_job_instance("process-order") do |job|
|
|
177
|
+
job.mark_completed(result: "success")
|
|
178
|
+
# Cleanup completion is safely ignored since job already completed
|
|
179
|
+
end
|
|
180
|
+
end
|
|
181
|
+
```
|
|
182
|
+
|
|
148
183
|
### Job Activation
|
|
149
184
|
|
|
150
|
-
#### `activate_job(type)`
|
|
185
|
+
#### `activate_job(type, timeout: nil)`
|
|
151
186
|
|
|
152
187
|
Activates a single job of the specified type. Raises `Busybee::Testing::NoJobAvailable` if no job is available.
|
|
153
188
|
|
|
154
189
|
**Parameters:**
|
|
155
190
|
- `type` (String) - Job type to activate
|
|
191
|
+
- `timeout` (Integer, ActiveSupport::Duration, optional) - Request timeout in milliseconds. Defaults to `Busybee.default_job_request_timeout` (60,000ms)
|
|
156
192
|
|
|
157
193
|
**Returns:** `ActivatedJob` instance
|
|
158
194
|
|
|
@@ -164,15 +200,19 @@ Activates a single job of the specified type. Raises `Busybee::Testing::NoJobAva
|
|
|
164
200
|
job = activate_job("process-payment")
|
|
165
201
|
expect(job.variables["amount"]).to eq(99.99)
|
|
166
202
|
job.mark_completed(payment_status: "success")
|
|
203
|
+
|
|
204
|
+
# With custom timeout for faster cleanup loops
|
|
205
|
+
job = activate_job("process-order", timeout: 100)
|
|
167
206
|
```
|
|
168
207
|
|
|
169
|
-
#### `activate_jobs(type, max_jobs:)`
|
|
208
|
+
#### `activate_jobs(type, max_jobs:, timeout: nil)`
|
|
170
209
|
|
|
171
210
|
Activates multiple jobs of the specified type.
|
|
172
211
|
|
|
173
212
|
**Parameters:**
|
|
174
213
|
- `type` (String) - Job type to activate
|
|
175
214
|
- `max_jobs` (Integer) - Maximum number of jobs to activate
|
|
215
|
+
- `timeout` (Integer, ActiveSupport::Duration, optional) - Request timeout in milliseconds. Defaults to `Busybee.default_job_request_timeout` (60,000ms)
|
|
176
216
|
|
|
177
217
|
**Returns:** Enumerator of `ActivatedJob` instances
|
|
178
218
|
|
|
@@ -292,6 +332,34 @@ job.headers #=> {"priority" => "high"}
|
|
|
292
332
|
job.retries #=> 3
|
|
293
333
|
```
|
|
294
334
|
|
|
335
|
+
#### Variables and Headers
|
|
336
|
+
|
|
337
|
+
The `variables` and `headers` accessors return frozen `HashWithIndifferentAccess` objects with method-style access:
|
|
338
|
+
|
|
339
|
+
```ruby
|
|
340
|
+
job = activate_job("my-task")
|
|
341
|
+
|
|
342
|
+
# Indifferent access - use symbol or string keys interchangeably
|
|
343
|
+
job.variables[:order_id] #=> "123"
|
|
344
|
+
job.variables["order_id"] #=> "123"
|
|
345
|
+
|
|
346
|
+
# Method-style access - access keys as methods
|
|
347
|
+
job.variables.order_id #=> "123"
|
|
348
|
+
job.variables.total #=> 99.99
|
|
349
|
+
|
|
350
|
+
# Works with camelCase keys from Zeebe (snake_case methods)
|
|
351
|
+
# If Zeebe sends {"customerId": "abc"}, access via:
|
|
352
|
+
job.variables.customer_id #=> "abc"
|
|
353
|
+
|
|
354
|
+
# Nested hashes also support method access
|
|
355
|
+
job.variables.order.line_items.first.product_name
|
|
356
|
+
|
|
357
|
+
# Variables are frozen (immutable) to prevent accidental modification
|
|
358
|
+
job.variables[:foo] = "bar" #=> raises FrozenError
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
This matches the behavior of `Busybee::Job` used in production code, making it easy to write tests that mirror real job handling.
|
|
362
|
+
|
|
295
363
|
### Expectation Methods
|
|
296
364
|
|
|
297
365
|
These methods verify job state and return `self` for chaining:
|
|
@@ -460,6 +528,26 @@ expect(activate_job("process-order"))
|
|
|
460
528
|
.and_complete(processed: true, processed_at: Time.now.iso8601)
|
|
461
529
|
```
|
|
462
530
|
|
|
531
|
+
### `have_available_jobs`
|
|
532
|
+
|
|
533
|
+
Matcher to check if jobs are available for activation. Primarily used in negated form to verify that no jobs exist.
|
|
534
|
+
|
|
535
|
+
**Aliases:** `have_an_available_job`
|
|
536
|
+
|
|
537
|
+
**Example:**
|
|
538
|
+
|
|
539
|
+
```ruby
|
|
540
|
+
# Verify jobs are available
|
|
541
|
+
expect { activate_job("process-order") }.to have_available_jobs
|
|
542
|
+
|
|
543
|
+
# More commonly: verify NO jobs are available (most common usage)
|
|
544
|
+
expect { activate_job("process-order") }.not_to have_available_jobs
|
|
545
|
+
|
|
546
|
+
# Use case: verify signal didn't create instances
|
|
547
|
+
client.broadcast_signal("non-existent-signal")
|
|
548
|
+
expect { activate_job("process-order") }.not_to have_an_available_job
|
|
549
|
+
```
|
|
550
|
+
|
|
463
551
|
## Complete Workflow Example
|
|
464
552
|
|
|
465
553
|
Here's a complete example testing an order fulfillment workflow:
|
|
@@ -634,18 +722,20 @@ cancel_instance(key) # Easy to forget in error paths
|
|
|
634
722
|
Assert expected inputs before completing jobs:
|
|
635
723
|
|
|
636
724
|
```ruby
|
|
637
|
-
# Good:
|
|
638
|
-
job = activate_job("send-email")
|
|
639
|
-
expect(job).to have_received_variables(
|
|
640
|
-
recipient: "user@example.com",
|
|
641
|
-
template: "order_confirmation"
|
|
642
|
-
)
|
|
643
|
-
job.mark_completed(sent_at: Time.now.iso8601)
|
|
644
|
-
|
|
645
|
-
# Also Good: fluent style
|
|
725
|
+
# Good: fluent style — verify and complete in one chain
|
|
646
726
|
activate_job("send-email")
|
|
647
727
|
.expect_variables(recipient: "user@example.com")
|
|
648
728
|
.and_complete(sent_at: Time.now.iso8601)
|
|
729
|
+
|
|
730
|
+
# Also Good: separate verification and completion — useful when you need to
|
|
731
|
+
# activate multiple jobs of the same type and distinguish between them
|
|
732
|
+
jobs = activate_jobs("send-comms", max_jobs: 2)
|
|
733
|
+
.group_by { |j| j.headers["medium"] }
|
|
734
|
+
.transform_values(&:first)
|
|
735
|
+
|
|
736
|
+
expect(jobs["sms"]).to have_received_variables(template: "order_sms")
|
|
737
|
+
expect(jobs["email"]).to have_received_variables(template: "order_confirmation")
|
|
738
|
+
jobs.each_value(&:mark_completed)
|
|
649
739
|
```
|
|
650
740
|
|
|
651
741
|
## Troubleshooting
|