decision_agent 0.2.0 → 1.0.1

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 (126) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +313 -8
  3. data/bin/decision_agent +104 -0
  4. data/lib/decision_agent/agent.rb +72 -1
  5. data/lib/decision_agent/context.rb +1 -0
  6. data/lib/decision_agent/data_enrichment/cache/memory_adapter.rb +86 -0
  7. data/lib/decision_agent/data_enrichment/cache_adapter.rb +49 -0
  8. data/lib/decision_agent/data_enrichment/circuit_breaker.rb +135 -0
  9. data/lib/decision_agent/data_enrichment/client.rb +220 -0
  10. data/lib/decision_agent/data_enrichment/config.rb +78 -0
  11. data/lib/decision_agent/data_enrichment/errors.rb +36 -0
  12. data/lib/decision_agent/decision.rb +102 -2
  13. data/lib/decision_agent/dmn/adapter.rb +135 -0
  14. data/lib/decision_agent/dmn/cache.rb +306 -0
  15. data/lib/decision_agent/dmn/decision_graph.rb +327 -0
  16. data/lib/decision_agent/dmn/decision_tree.rb +192 -0
  17. data/lib/decision_agent/dmn/errors.rb +30 -0
  18. data/lib/decision_agent/dmn/exporter.rb +217 -0
  19. data/lib/decision_agent/dmn/feel/evaluator.rb +819 -0
  20. data/lib/decision_agent/dmn/feel/functions.rb +420 -0
  21. data/lib/decision_agent/dmn/feel/parser.rb +349 -0
  22. data/lib/decision_agent/dmn/feel/simple_parser.rb +276 -0
  23. data/lib/decision_agent/dmn/feel/transformer.rb +372 -0
  24. data/lib/decision_agent/dmn/feel/types.rb +276 -0
  25. data/lib/decision_agent/dmn/importer.rb +77 -0
  26. data/lib/decision_agent/dmn/model.rb +197 -0
  27. data/lib/decision_agent/dmn/parser.rb +191 -0
  28. data/lib/decision_agent/dmn/testing.rb +333 -0
  29. data/lib/decision_agent/dmn/validator.rb +315 -0
  30. data/lib/decision_agent/dmn/versioning.rb +229 -0
  31. data/lib/decision_agent/dmn/visualizer.rb +513 -0
  32. data/lib/decision_agent/dsl/condition_evaluator.rb +984 -838
  33. data/lib/decision_agent/dsl/schema_validator.rb +53 -14
  34. data/lib/decision_agent/evaluators/dmn_evaluator.rb +308 -0
  35. data/lib/decision_agent/evaluators/json_rule_evaluator.rb +69 -9
  36. data/lib/decision_agent/explainability/condition_trace.rb +83 -0
  37. data/lib/decision_agent/explainability/explainability_result.rb +52 -0
  38. data/lib/decision_agent/explainability/rule_trace.rb +39 -0
  39. data/lib/decision_agent/explainability/trace_collector.rb +24 -0
  40. data/lib/decision_agent/monitoring/alert_manager.rb +5 -1
  41. data/lib/decision_agent/simulation/errors.rb +18 -0
  42. data/lib/decision_agent/simulation/impact_analyzer.rb +498 -0
  43. data/lib/decision_agent/simulation/monte_carlo_simulator.rb +635 -0
  44. data/lib/decision_agent/simulation/replay_engine.rb +486 -0
  45. data/lib/decision_agent/simulation/scenario_engine.rb +318 -0
  46. data/lib/decision_agent/simulation/scenario_library.rb +163 -0
  47. data/lib/decision_agent/simulation/shadow_test_engine.rb +287 -0
  48. data/lib/decision_agent/simulation/what_if_analyzer.rb +1002 -0
  49. data/lib/decision_agent/simulation.rb +17 -0
  50. data/lib/decision_agent/version.rb +1 -1
  51. data/lib/decision_agent/versioning/activerecord_adapter.rb +23 -8
  52. data/lib/decision_agent/web/dmn_editor.rb +426 -0
  53. data/lib/decision_agent/web/public/app.js +119 -0
  54. data/lib/decision_agent/web/public/dmn-editor.css +596 -0
  55. data/lib/decision_agent/web/public/dmn-editor.html +250 -0
  56. data/lib/decision_agent/web/public/dmn-editor.js +553 -0
  57. data/lib/decision_agent/web/public/index.html +52 -0
  58. data/lib/decision_agent/web/public/simulation.html +130 -0
  59. data/lib/decision_agent/web/public/simulation_impact.html +478 -0
  60. data/lib/decision_agent/web/public/simulation_replay.html +551 -0
  61. data/lib/decision_agent/web/public/simulation_shadow.html +546 -0
  62. data/lib/decision_agent/web/public/simulation_whatif.html +532 -0
  63. data/lib/decision_agent/web/public/styles.css +86 -0
  64. data/lib/decision_agent/web/server.rb +1059 -23
  65. data/lib/decision_agent.rb +60 -2
  66. metadata +105 -61
  67. data/spec/ab_testing/ab_test_assignment_spec.rb +0 -253
  68. data/spec/ab_testing/ab_test_manager_spec.rb +0 -612
  69. data/spec/ab_testing/ab_test_spec.rb +0 -270
  70. data/spec/ab_testing/ab_testing_agent_spec.rb +0 -481
  71. data/spec/ab_testing/storage/adapter_spec.rb +0 -64
  72. data/spec/ab_testing/storage/memory_adapter_spec.rb +0 -485
  73. data/spec/activerecord_thread_safety_spec.rb +0 -553
  74. data/spec/advanced_operators_spec.rb +0 -3150
  75. data/spec/agent_spec.rb +0 -289
  76. data/spec/api_contract_spec.rb +0 -430
  77. data/spec/audit_adapters_spec.rb +0 -92
  78. data/spec/auth/access_audit_logger_spec.rb +0 -394
  79. data/spec/auth/authenticator_spec.rb +0 -112
  80. data/spec/auth/password_reset_spec.rb +0 -294
  81. data/spec/auth/permission_checker_spec.rb +0 -207
  82. data/spec/auth/permission_spec.rb +0 -73
  83. data/spec/auth/rbac_adapter_spec.rb +0 -550
  84. data/spec/auth/rbac_config_spec.rb +0 -82
  85. data/spec/auth/role_spec.rb +0 -51
  86. data/spec/auth/session_manager_spec.rb +0 -172
  87. data/spec/auth/session_spec.rb +0 -112
  88. data/spec/auth/user_spec.rb +0 -130
  89. data/spec/comprehensive_edge_cases_spec.rb +0 -1777
  90. data/spec/context_spec.rb +0 -127
  91. data/spec/decision_agent_spec.rb +0 -96
  92. data/spec/decision_spec.rb +0 -423
  93. data/spec/dsl/condition_evaluator_spec.rb +0 -774
  94. data/spec/dsl_validation_spec.rb +0 -648
  95. data/spec/edge_cases_spec.rb +0 -353
  96. data/spec/evaluation_spec.rb +0 -364
  97. data/spec/evaluation_validator_spec.rb +0 -165
  98. data/spec/examples/feedback_aware_evaluator_spec.rb +0 -460
  99. data/spec/examples.txt +0 -1633
  100. data/spec/issue_verification_spec.rb +0 -759
  101. data/spec/json_rule_evaluator_spec.rb +0 -587
  102. data/spec/monitoring/alert_manager_spec.rb +0 -378
  103. data/spec/monitoring/metrics_collector_spec.rb +0 -499
  104. data/spec/monitoring/monitored_agent_spec.rb +0 -222
  105. data/spec/monitoring/prometheus_exporter_spec.rb +0 -242
  106. data/spec/monitoring/storage/activerecord_adapter_spec.rb +0 -498
  107. data/spec/monitoring/storage/base_adapter_spec.rb +0 -61
  108. data/spec/monitoring/storage/memory_adapter_spec.rb +0 -247
  109. data/spec/performance_optimizations_spec.rb +0 -486
  110. data/spec/replay_edge_cases_spec.rb +0 -699
  111. data/spec/replay_spec.rb +0 -210
  112. data/spec/rfc8785_canonicalization_spec.rb +0 -215
  113. data/spec/scoring_spec.rb +0 -225
  114. data/spec/spec_helper.rb +0 -60
  115. data/spec/testing/batch_test_importer_spec.rb +0 -693
  116. data/spec/testing/batch_test_runner_spec.rb +0 -307
  117. data/spec/testing/test_coverage_analyzer_spec.rb +0 -292
  118. data/spec/testing/test_result_comparator_spec.rb +0 -392
  119. data/spec/testing/test_scenario_spec.rb +0 -113
  120. data/spec/thread_safety_spec.rb +0 -482
  121. data/spec/thread_safety_spec.rb.broken +0 -878
  122. data/spec/versioning/adapter_spec.rb +0 -156
  123. data/spec/versioning_spec.rb +0 -1030
  124. data/spec/web/middleware/auth_middleware_spec.rb +0 -133
  125. data/spec/web/middleware/permission_middleware_spec.rb +0 -247
  126. data/spec/web_ui_rack_spec.rb +0 -1840
@@ -1,485 +0,0 @@
1
- require "spec_helper"
2
- require "decision_agent/ab_testing/storage/memory_adapter"
3
- require "decision_agent/ab_testing/ab_test"
4
- require "decision_agent/ab_testing/ab_test_assignment"
5
-
6
- RSpec.describe DecisionAgent::ABTesting::Storage::MemoryAdapter do
7
- let(:adapter) { described_class.new }
8
-
9
- describe "#initialize" do
10
- it "initializes with empty storage" do
11
- expect(adapter.test_count).to eq(0)
12
- expect(adapter.assignment_count).to eq(0)
13
- end
14
- end
15
-
16
- describe "#save_test" do
17
- it "saves a test and assigns an ID" do
18
- test = DecisionAgent::ABTesting::ABTest.new(
19
- name: "Test 1",
20
- champion_version_id: "v1",
21
- challenger_version_id: "v2"
22
- )
23
-
24
- saved = adapter.save_test(test)
25
- expect(saved.id).to eq(1)
26
- expect(saved.name).to eq("Test 1")
27
- end
28
-
29
- it "increments the test counter" do
30
- test1 = DecisionAgent::ABTesting::ABTest.new(
31
- name: "Test 1",
32
- champion_version_id: "v1",
33
- challenger_version_id: "v2"
34
- )
35
- test2 = DecisionAgent::ABTesting::ABTest.new(
36
- name: "Test 2",
37
- champion_version_id: "v1",
38
- challenger_version_id: "v2"
39
- )
40
-
41
- adapter.save_test(test1)
42
- adapter.save_test(test2)
43
-
44
- expect(adapter.test_count).to eq(2)
45
- end
46
- end
47
-
48
- describe "#get_test" do
49
- it "returns nil for non-existent test" do
50
- expect(adapter.get_test(999)).to be_nil
51
- end
52
-
53
- it "returns the saved test" do
54
- test = DecisionAgent::ABTesting::ABTest.new(
55
- name: "Test 1",
56
- champion_version_id: "v1",
57
- challenger_version_id: "v2"
58
- )
59
- saved = adapter.save_test(test)
60
-
61
- retrieved = adapter.get_test(saved.id)
62
- expect(retrieved).to be_a(DecisionAgent::ABTesting::ABTest)
63
- expect(retrieved.id).to eq(saved.id)
64
- expect(retrieved.name).to eq("Test 1")
65
- end
66
-
67
- it "converts string IDs to integers" do
68
- test = DecisionAgent::ABTesting::ABTest.new(
69
- name: "Test 1",
70
- champion_version_id: "v1",
71
- challenger_version_id: "v2"
72
- )
73
- saved = adapter.save_test(test)
74
-
75
- retrieved = adapter.get_test(saved.id.to_s)
76
- expect(retrieved).not_to be_nil
77
- expect(retrieved.id).to eq(saved.id)
78
- end
79
- end
80
-
81
- describe "#update_test" do
82
- let(:saved_test) do
83
- test = DecisionAgent::ABTesting::ABTest.new(
84
- name: "Original Name",
85
- champion_version_id: "v1",
86
- challenger_version_id: "v2",
87
- status: "scheduled"
88
- )
89
- adapter.save_test(test)
90
- end
91
-
92
- it "updates test attributes" do
93
- updated = adapter.update_test(saved_test.id, { status: "running" })
94
- expect(updated.status).to eq("running")
95
- expect(updated.name).to eq("Original Name") # Other attributes unchanged
96
- end
97
-
98
- it "raises error when test not found" do
99
- expect do
100
- adapter.update_test(999, { status: "running" })
101
- end.to raise_error(DecisionAgent::ABTesting::TestNotFoundError, /Test not found: 999/)
102
- end
103
-
104
- it "persists the update" do
105
- adapter.update_test(saved_test.id, { status: "running" })
106
- retrieved = adapter.get_test(saved_test.id)
107
- expect(retrieved.status).to eq("running")
108
- end
109
- end
110
-
111
- describe "#list_tests" do
112
- before do
113
- adapter.save_test(DecisionAgent::ABTesting::ABTest.new(
114
- name: "Scheduled Test",
115
- champion_version_id: "v1",
116
- challenger_version_id: "v2",
117
- status: "scheduled"
118
- ))
119
- adapter.save_test(DecisionAgent::ABTesting::ABTest.new(
120
- name: "Running Test",
121
- champion_version_id: "v1",
122
- challenger_version_id: "v2",
123
- status: "running"
124
- ))
125
- adapter.save_test(DecisionAgent::ABTesting::ABTest.new(
126
- name: "Another Running",
127
- champion_version_id: "v1",
128
- challenger_version_id: "v2",
129
- status: "running"
130
- ))
131
- end
132
-
133
- it "returns all tests when no filters" do
134
- tests = adapter.list_tests
135
- expect(tests.size).to eq(3)
136
- end
137
-
138
- it "filters by status" do
139
- tests = adapter.list_tests(status: "running")
140
- expect(tests.size).to eq(2)
141
- expect(tests.all? { |t| t.status == "running" }).to be true
142
- end
143
-
144
- it "returns empty array when no tests match status" do
145
- tests = adapter.list_tests(status: "completed")
146
- expect(tests).to eq([])
147
- end
148
-
149
- it "respects limit parameter" do
150
- tests = adapter.list_tests(limit: 2)
151
- expect(tests.size).to eq(2)
152
- end
153
-
154
- it "returns last N tests when limit specified" do
155
- tests = adapter.list_tests(limit: 2)
156
- # Should return the last 2 tests (most recently added)
157
- expect(tests.map(&:name)).to include("Running Test", "Another Running")
158
- end
159
-
160
- it "filters by status and limit together" do
161
- tests = adapter.list_tests(status: "running", limit: 1)
162
- expect(tests.size).to eq(1)
163
- expect(tests.first.status).to eq("running")
164
- end
165
- end
166
-
167
- describe "#save_assignment" do
168
- it "saves an assignment and assigns an ID" do
169
- assignment = DecisionAgent::ABTesting::ABTestAssignment.new(
170
- ab_test_id: 1,
171
- variant: :champion,
172
- version_id: "v1"
173
- )
174
-
175
- saved = adapter.save_assignment(assignment)
176
- expect(saved.id).to eq(1)
177
- expect(saved.ab_test_id).to eq(1)
178
- expect(saved.variant).to eq(:champion)
179
- end
180
-
181
- it "increments the assignment counter" do
182
- assignment1 = DecisionAgent::ABTesting::ABTestAssignment.new(
183
- ab_test_id: 1,
184
- variant: :champion,
185
- version_id: "v1"
186
- )
187
- assignment2 = DecisionAgent::ABTesting::ABTestAssignment.new(
188
- ab_test_id: 1,
189
- variant: :challenger,
190
- version_id: "v2"
191
- )
192
-
193
- adapter.save_assignment(assignment1)
194
- adapter.save_assignment(assignment2)
195
-
196
- expect(adapter.assignment_count).to eq(2)
197
- end
198
- end
199
-
200
- describe "#update_assignment" do
201
- let(:saved_assignment) do
202
- assignment = DecisionAgent::ABTesting::ABTestAssignment.new(
203
- ab_test_id: 1,
204
- variant: :champion,
205
- version_id: "v1"
206
- )
207
- adapter.save_assignment(assignment)
208
- end
209
-
210
- it "updates assignment attributes" do
211
- updated = adapter.update_assignment(saved_assignment.id, {
212
- decision_result: "approve",
213
- confidence: 0.85
214
- })
215
- expect(updated.decision_result).to eq("approve")
216
- expect(updated.confidence).to eq(0.85)
217
- end
218
-
219
- it "raises error when assignment not found" do
220
- expect do
221
- adapter.update_assignment(999, { decision_result: "approve" })
222
- end.to raise_error(StandardError, /Assignment not found: 999/)
223
- end
224
-
225
- it "persists the update" do
226
- adapter.update_assignment(saved_assignment.id, { decision_result: "reject" })
227
- # Get via get_assignments to verify persistence
228
- assignments = adapter.get_assignments(1)
229
- updated = assignments.find { |a| a.id == saved_assignment.id }
230
- expect(updated.decision_result).to eq("reject")
231
- end
232
- end
233
-
234
- describe "#get_assignments" do
235
- before do
236
- test1 = adapter.save_test(DecisionAgent::ABTesting::ABTest.new(
237
- name: "Test 1",
238
- champion_version_id: "v1",
239
- challenger_version_id: "v2"
240
- ))
241
- test2 = adapter.save_test(DecisionAgent::ABTesting::ABTest.new(
242
- name: "Test 2",
243
- champion_version_id: "v1",
244
- challenger_version_id: "v2"
245
- ))
246
-
247
- adapter.save_assignment(DecisionAgent::ABTesting::ABTestAssignment.new(
248
- ab_test_id: test1.id,
249
- variant: :champion,
250
- version_id: "v1"
251
- ))
252
- adapter.save_assignment(DecisionAgent::ABTesting::ABTestAssignment.new(
253
- ab_test_id: test1.id,
254
- variant: :challenger,
255
- version_id: "v2"
256
- ))
257
- adapter.save_assignment(DecisionAgent::ABTesting::ABTestAssignment.new(
258
- ab_test_id: test2.id,
259
- variant: :champion,
260
- version_id: "v1"
261
- ))
262
- end
263
-
264
- it "returns assignments for a specific test" do
265
- assignments = adapter.get_assignments(1)
266
- expect(assignments.size).to eq(2)
267
- expect(assignments.all? { |a| a.ab_test_id == 1 }).to be true
268
- end
269
-
270
- it "returns empty array when no assignments for test" do
271
- assignments = adapter.get_assignments(999)
272
- expect(assignments).to eq([])
273
- end
274
- end
275
-
276
- describe "#delete_test" do
277
- let(:test) do
278
- adapter.save_test(DecisionAgent::ABTesting::ABTest.new(
279
- name: "Test 1",
280
- champion_version_id: "v1",
281
- challenger_version_id: "v2"
282
- ))
283
- end
284
-
285
- before do
286
- adapter.save_assignment(DecisionAgent::ABTesting::ABTestAssignment.new(
287
- ab_test_id: test.id,
288
- variant: :champion,
289
- version_id: "v1"
290
- ))
291
- adapter.save_assignment(DecisionAgent::ABTesting::ABTestAssignment.new(
292
- ab_test_id: test.id,
293
- variant: :challenger,
294
- version_id: "v2"
295
- ))
296
- # Assignment for another test
297
- other_test = adapter.save_test(DecisionAgent::ABTesting::ABTest.new(
298
- name: "Test 2",
299
- champion_version_id: "v1",
300
- challenger_version_id: "v2"
301
- ))
302
- adapter.save_assignment(DecisionAgent::ABTesting::ABTestAssignment.new(
303
- ab_test_id: other_test.id,
304
- variant: :champion,
305
- version_id: "v1"
306
- ))
307
- end
308
-
309
- it "deletes the test" do
310
- expect(adapter.get_test(test.id)).not_to be_nil
311
- adapter.delete_test(test.id)
312
- expect(adapter.get_test(test.id)).to be_nil
313
- end
314
-
315
- it "deletes all assignments for the test" do
316
- expect(adapter.get_assignments(test.id).size).to eq(2)
317
- adapter.delete_test(test.id)
318
- expect(adapter.get_assignments(test.id).size).to eq(0)
319
- end
320
-
321
- it "does not delete assignments for other tests" do
322
- other_assignments_count = adapter.assignment_count - adapter.get_assignments(test.id).size
323
- adapter.delete_test(test.id)
324
- expect(adapter.assignment_count).to eq(other_assignments_count)
325
- end
326
-
327
- it "returns true" do
328
- result = adapter.delete_test(test.id)
329
- expect(result).to be true
330
- end
331
-
332
- it "converts string IDs to integers" do
333
- adapter.delete_test(test.id.to_s)
334
- expect(adapter.get_test(test.id)).to be_nil
335
- end
336
- end
337
-
338
- describe "#clear!" do
339
- before do
340
- adapter.save_test(DecisionAgent::ABTesting::ABTest.new(
341
- name: "Test 1",
342
- champion_version_id: "v1",
343
- challenger_version_id: "v2"
344
- ))
345
- adapter.save_assignment(DecisionAgent::ABTesting::ABTestAssignment.new(
346
- ab_test_id: 1,
347
- variant: :champion,
348
- version_id: "v1"
349
- ))
350
- end
351
-
352
- it "clears all tests" do
353
- expect(adapter.test_count).to eq(1)
354
- adapter.clear!
355
- expect(adapter.test_count).to eq(0)
356
- end
357
-
358
- it "clears all assignments" do
359
- expect(adapter.assignment_count).to eq(1)
360
- adapter.clear!
361
- expect(adapter.assignment_count).to eq(0)
362
- end
363
-
364
- it "resets test counter" do
365
- adapter.clear!
366
- test = adapter.save_test(DecisionAgent::ABTesting::ABTest.new(
367
- name: "New Test",
368
- champion_version_id: "v1",
369
- challenger_version_id: "v2"
370
- ))
371
- expect(test.id).to eq(1)
372
- end
373
-
374
- it "resets assignment counter" do
375
- adapter.clear!
376
- assignment = adapter.save_assignment(DecisionAgent::ABTesting::ABTestAssignment.new(
377
- ab_test_id: 1,
378
- variant: :champion,
379
- version_id: "v1"
380
- ))
381
- expect(assignment.id).to eq(1)
382
- end
383
- end
384
-
385
- describe "#test_count" do
386
- it "returns zero initially" do
387
- expect(adapter.test_count).to eq(0)
388
- end
389
-
390
- it "returns correct count after saving tests" do
391
- adapter.save_test(DecisionAgent::ABTesting::ABTest.new(
392
- name: "Test 1",
393
- champion_version_id: "v1",
394
- challenger_version_id: "v2"
395
- ))
396
- expect(adapter.test_count).to eq(1)
397
-
398
- adapter.save_test(DecisionAgent::ABTesting::ABTest.new(
399
- name: "Test 2",
400
- champion_version_id: "v1",
401
- challenger_version_id: "v2"
402
- ))
403
- expect(adapter.test_count).to eq(2)
404
- end
405
-
406
- it "reflects deletions" do
407
- test = adapter.save_test(DecisionAgent::ABTesting::ABTest.new(
408
- name: "Test 1",
409
- champion_version_id: "v1",
410
- challenger_version_id: "v2"
411
- ))
412
- adapter.save_test(DecisionAgent::ABTesting::ABTest.new(
413
- name: "Test 2",
414
- champion_version_id: "v1",
415
- challenger_version_id: "v2"
416
- ))
417
- adapter.delete_test(test.id)
418
- expect(adapter.test_count).to eq(1)
419
- end
420
- end
421
-
422
- describe "#assignment_count" do
423
- it "returns zero initially" do
424
- expect(adapter.assignment_count).to eq(0)
425
- end
426
-
427
- it "returns correct count after saving assignments" do
428
- adapter.save_assignment(DecisionAgent::ABTesting::ABTestAssignment.new(
429
- ab_test_id: 1,
430
- variant: :champion,
431
- version_id: "v1"
432
- ))
433
- expect(adapter.assignment_count).to eq(1)
434
-
435
- adapter.save_assignment(DecisionAgent::ABTesting::ABTestAssignment.new(
436
- ab_test_id: 1,
437
- variant: :challenger,
438
- version_id: "v2"
439
- ))
440
- expect(adapter.assignment_count).to eq(2)
441
- end
442
-
443
- it "reflects deletions" do
444
- test = adapter.save_test(DecisionAgent::ABTesting::ABTest.new(
445
- name: "Test 1",
446
- champion_version_id: "v1",
447
- challenger_version_id: "v2"
448
- ))
449
- adapter.save_assignment(DecisionAgent::ABTesting::ABTestAssignment.new(
450
- ab_test_id: test.id,
451
- variant: :champion,
452
- version_id: "v1"
453
- ))
454
- adapter.save_assignment(DecisionAgent::ABTesting::ABTestAssignment.new(
455
- ab_test_id: test.id,
456
- variant: :challenger,
457
- version_id: "v2"
458
- ))
459
- adapter.delete_test(test.id) # This deletes all assignments for the test
460
- expect(adapter.assignment_count).to eq(0)
461
- end
462
- end
463
-
464
- describe "thread safety" do
465
- it "handles concurrent access safely" do
466
- threads = []
467
- 10.times do
468
- threads << Thread.new do
469
- 10.times do
470
- test = adapter.save_test(DecisionAgent::ABTesting::ABTest.new(
471
- name: "Test",
472
- champion_version_id: "v1",
473
- challenger_version_id: "v2"
474
- ))
475
- adapter.get_test(test.id)
476
- adapter.list_tests
477
- end
478
- end
479
- end
480
-
481
- threads.each(&:join)
482
- expect(adapter.test_count).to eq(100)
483
- end
484
- end
485
- end