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.
Files changed (55) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +71 -7
  3. data/README.md +70 -42
  4. data/docs/client/quick_start.md +279 -0
  5. data/docs/client.md +825 -0
  6. data/docs/configuration.md +550 -0
  7. data/docs/grpc.md +50 -25
  8. data/docs/testing.md +118 -28
  9. data/docs/workers.md +982 -0
  10. data/exe/busybee +6 -0
  11. data/lib/busybee/cli.rb +173 -0
  12. data/lib/busybee/client/error_handling.rb +37 -0
  13. data/lib/busybee/client/job_operations.rb +236 -0
  14. data/lib/busybee/client/message_operations.rb +84 -0
  15. data/lib/busybee/client/process_operations.rb +108 -0
  16. data/lib/busybee/client/variable_operations.rb +64 -0
  17. data/lib/busybee/client.rb +87 -0
  18. data/lib/busybee/configure.rb +290 -0
  19. data/lib/busybee/credentials/camunda_cloud.rb +58 -0
  20. data/lib/busybee/credentials/insecure.rb +24 -0
  21. data/lib/busybee/credentials/oauth.rb +157 -0
  22. data/lib/busybee/credentials/tls.rb +43 -0
  23. data/lib/busybee/credentials.rb +200 -0
  24. data/lib/busybee/defaults.rb +20 -0
  25. data/lib/busybee/error.rb +50 -0
  26. data/lib/busybee/grpc/error.rb +60 -0
  27. data/lib/busybee/grpc.rb +2 -2
  28. data/lib/busybee/job.rb +219 -0
  29. data/lib/busybee/job_stream.rb +85 -0
  30. data/lib/busybee/logging.rb +61 -0
  31. data/lib/busybee/railtie.rb +113 -0
  32. data/lib/busybee/runner/hybrid.rb +64 -0
  33. data/lib/busybee/runner/multi.rb +101 -0
  34. data/lib/busybee/runner/polling.rb +54 -0
  35. data/lib/busybee/runner/streaming.rb +159 -0
  36. data/lib/busybee/runner.rb +97 -0
  37. data/lib/busybee/runtime_config.rb +184 -0
  38. data/lib/busybee/serialization.rb +100 -0
  39. data/lib/busybee/testing/activated_job.rb +33 -8
  40. data/lib/busybee/testing/helpers/execution.rb +139 -0
  41. data/lib/busybee/testing/helpers/support.rb +78 -0
  42. data/lib/busybee/testing/helpers.rb +56 -66
  43. data/lib/busybee/testing/matchers/complete_job.rb +55 -0
  44. data/lib/busybee/testing/matchers/fail_job.rb +75 -0
  45. data/lib/busybee/testing/matchers/have_activated.rb +1 -1
  46. data/lib/busybee/testing/matchers/have_available_jobs.rb +44 -0
  47. data/lib/busybee/testing/matchers/throw_bpmn_error_on.rb +72 -0
  48. data/lib/busybee/testing.rb +5 -33
  49. data/lib/busybee/version.rb +1 -1
  50. data/lib/busybee/worker/configuration.rb +287 -0
  51. data/lib/busybee/worker/dsl.rb +187 -0
  52. data/lib/busybee/worker/shutdown.rb +27 -0
  53. data/lib/busybee/worker.rb +130 -0
  54. data/lib/busybee.rb +134 -2
  55. metadata +80 -3
data/docs/testing.md CHANGED
@@ -1,6 +1,8 @@
1
- # Testing BPMN Workflows with Busybee
1
+ # Testing BPMN Workflows
2
2
 
3
- Busybee provides RSpec helpers and matchers for testing BPMN workflows against Zeebe. This testing module makes it easy to write integration tests that deploy processes, create instances, activate jobs, and verify workflow behavior.
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
- # ZEEBE_ADDRESS=localhost:26500
39
- # ZEEBE_USERNAME=demo
40
- # ZEEBE_PASSWORD=demo
40
+ # CLUSTER_ADDRESS=localhost:26500
41
41
 
42
42
  # Or configure explicitly
43
- Busybee::Testing.configure do |config|
44
- config.address = "localhost:26500"
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
- ### Configuration Options
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
- | Option | Environment Variable | Default | Description |
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: Verify then complete
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