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
@@ -62,12 +62,32 @@ require_relative "decision_agent/auth/rbac_config"
62
62
  require_relative "decision_agent/auth/permission_checker"
63
63
  require_relative "decision_agent/auth/access_audit_logger"
64
64
 
65
+ require_relative "decision_agent/data_enrichment/config"
66
+ require_relative "decision_agent/data_enrichment/client"
67
+ require_relative "decision_agent/data_enrichment/cache_adapter"
68
+ require_relative "decision_agent/data_enrichment/cache/memory_adapter"
69
+ require_relative "decision_agent/data_enrichment/circuit_breaker"
70
+ require_relative "decision_agent/data_enrichment/errors"
71
+
72
+ require_relative "decision_agent/simulation"
73
+
74
+ require_relative "decision_agent/explainability/condition_trace"
75
+ require_relative "decision_agent/explainability/rule_trace"
76
+ require_relative "decision_agent/explainability/trace_collector"
77
+ require_relative "decision_agent/explainability/explainability_result"
78
+
65
79
  module DecisionAgent
66
80
  # Global RBAC configuration
67
81
  @rbac_config = Auth::RbacConfig.new
82
+ # Global data enrichment configuration
83
+ @data_enrichment_config = DataEnrichment::Config.new
84
+ @data_enrichment_client = nil
85
+ @permission_checker = nil
86
+ @permission_checker_mutex = Mutex.new
87
+ @data_enrichment_client_mutex = Mutex.new
68
88
 
69
89
  class << self
70
- attr_reader :rbac_config
90
+ attr_reader :rbac_config, :data_enrichment_config
71
91
 
72
92
  # Configure RBAC adapter
73
93
  # @param adapter_type [Symbol] :default, :devise_cancan, :pundit, or :custom
@@ -85,15 +105,53 @@ module DecisionAgent
85
105
  elsif adapter_type
86
106
  @rbac_config.use(adapter_type, **options)
87
107
  end
108
+ # Initialize permission checker at configuration time (thread-safe write-once pattern)
109
+ @permission_checker = Auth::PermissionChecker.new(adapter: @rbac_config.adapter)
88
110
  @rbac_config
89
111
  end
90
112
 
91
113
  # Get the configured permission checker
114
+ # Thread-safe: uses double-checked locking for lazy initialization fallback
92
115
  def permission_checker
93
- @permission_checker ||= Auth::PermissionChecker.new(adapter: @rbac_config.adapter)
116
+ return @permission_checker if @permission_checker
117
+
118
+ @permission_checker_mutex.synchronize do
119
+ @permission_checker ||= Auth::PermissionChecker.new(adapter: @rbac_config.adapter)
120
+ end
94
121
  end
95
122
 
96
123
  # Set a custom permission checker
97
124
  attr_writer :permission_checker
125
+
126
+ # Configure data enrichment endpoints
127
+ # @yield [DataEnrichment::Config] Configuration block
128
+ # @example
129
+ # DecisionAgent.configure_data_enrichment do |config|
130
+ # config.add_endpoint(:credit_bureau,
131
+ # url: "https://api.creditbureau.com/v1/score",
132
+ # method: :post,
133
+ # auth: { type: :api_key, header: "X-API-Key" },
134
+ # cache: { ttl: 3600, adapter: :memory }
135
+ # )
136
+ # end
137
+ def configure_data_enrichment
138
+ yield @data_enrichment_config if block_given?
139
+ # Initialize client at configuration time (thread-safe write-once pattern)
140
+ @data_enrichment_client = DataEnrichment::Client.new(config: @data_enrichment_config)
141
+ @data_enrichment_config
142
+ end
143
+
144
+ # Get the data enrichment client
145
+ # Thread-safe: uses double-checked locking for lazy initialization fallback
146
+ def data_enrichment_client
147
+ return @data_enrichment_client if @data_enrichment_client
148
+
149
+ @data_enrichment_client_mutex.synchronize do
150
+ @data_enrichment_client ||= DataEnrichment::Client.new(config: @data_enrichment_config)
151
+ end
152
+ end
153
+
154
+ # Set a custom data enrichment client
155
+ attr_writer :data_enrichment_client
98
156
  end
99
157
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: decision_agent
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 1.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sam Aswin
@@ -23,6 +23,20 @@ dependencies:
23
23
  - - "~>"
24
24
  - !ruby/object:Gem::Version
25
25
  version: '3.1'
26
+ - !ruby/object:Gem::Dependency
27
+ name: csv
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - "~>"
31
+ - !ruby/object:Gem::Version
32
+ version: '3.3'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '3.3'
26
40
  - !ruby/object:Gem::Dependency
27
41
  name: json-canonicalization
28
42
  requirement: !ruby/object:Gem::Requirement
@@ -37,6 +51,34 @@ dependencies:
37
51
  - - "~>"
38
52
  - !ruby/object:Gem::Version
39
53
  version: '1.0'
54
+ - !ruby/object:Gem::Dependency
55
+ name: nokogiri
56
+ requirement: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - "~>"
59
+ - !ruby/object:Gem::Version
60
+ version: '1.15'
61
+ type: :runtime
62
+ prerelease: false
63
+ version_requirements: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - "~>"
66
+ - !ruby/object:Gem::Version
67
+ version: '1.15'
68
+ - !ruby/object:Gem::Dependency
69
+ name: parslet
70
+ requirement: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - "~>"
73
+ - !ruby/object:Gem::Version
74
+ version: '2.0'
75
+ type: :runtime
76
+ prerelease: false
77
+ version_requirements: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - "~>"
80
+ - !ruby/object:Gem::Version
81
+ version: '2.0'
40
82
  - !ruby/object:Gem::Dependency
41
83
  name: roo
42
84
  requirement: !ruby/object:Gem::Requirement
@@ -135,6 +177,20 @@ dependencies:
135
177
  - - "~>"
136
178
  - !ruby/object:Gem::Version
137
179
  version: '1.60'
180
+ - !ruby/object:Gem::Dependency
181
+ name: webmock
182
+ requirement: !ruby/object:Gem::Requirement
183
+ requirements:
184
+ - - "~>"
185
+ - !ruby/object:Gem::Version
186
+ version: '3.18'
187
+ type: :development
188
+ prerelease: false
189
+ version_requirements: !ruby/object:Gem::Requirement
190
+ requirements:
191
+ - - "~>"
192
+ - !ruby/object:Gem::Version
193
+ version: '3.18'
138
194
  description: A production-grade decision agent that provides deterministic rule evaluation,
139
195
  conflict resolution, and full audit replay capabilities. Framework-agnostic and
140
196
  AI-optional.
@@ -173,7 +229,32 @@ files:
173
229
  - lib/decision_agent/auth/session_manager.rb
174
230
  - lib/decision_agent/auth/user.rb
175
231
  - lib/decision_agent/context.rb
232
+ - lib/decision_agent/data_enrichment/cache/memory_adapter.rb
233
+ - lib/decision_agent/data_enrichment/cache_adapter.rb
234
+ - lib/decision_agent/data_enrichment/circuit_breaker.rb
235
+ - lib/decision_agent/data_enrichment/client.rb
236
+ - lib/decision_agent/data_enrichment/config.rb
237
+ - lib/decision_agent/data_enrichment/errors.rb
176
238
  - lib/decision_agent/decision.rb
239
+ - lib/decision_agent/dmn/adapter.rb
240
+ - lib/decision_agent/dmn/cache.rb
241
+ - lib/decision_agent/dmn/decision_graph.rb
242
+ - lib/decision_agent/dmn/decision_tree.rb
243
+ - lib/decision_agent/dmn/errors.rb
244
+ - lib/decision_agent/dmn/exporter.rb
245
+ - lib/decision_agent/dmn/feel/evaluator.rb
246
+ - lib/decision_agent/dmn/feel/functions.rb
247
+ - lib/decision_agent/dmn/feel/parser.rb
248
+ - lib/decision_agent/dmn/feel/simple_parser.rb
249
+ - lib/decision_agent/dmn/feel/transformer.rb
250
+ - lib/decision_agent/dmn/feel/types.rb
251
+ - lib/decision_agent/dmn/importer.rb
252
+ - lib/decision_agent/dmn/model.rb
253
+ - lib/decision_agent/dmn/parser.rb
254
+ - lib/decision_agent/dmn/testing.rb
255
+ - lib/decision_agent/dmn/validator.rb
256
+ - lib/decision_agent/dmn/versioning.rb
257
+ - lib/decision_agent/dmn/visualizer.rb
177
258
  - lib/decision_agent/dsl/condition_evaluator.rb
178
259
  - lib/decision_agent/dsl/rule_parser.rb
179
260
  - lib/decision_agent/dsl/schema_validator.rb
@@ -181,8 +262,13 @@ files:
181
262
  - lib/decision_agent/evaluation.rb
182
263
  - lib/decision_agent/evaluation_validator.rb
183
264
  - lib/decision_agent/evaluators/base.rb
265
+ - lib/decision_agent/evaluators/dmn_evaluator.rb
184
266
  - lib/decision_agent/evaluators/json_rule_evaluator.rb
185
267
  - lib/decision_agent/evaluators/static_evaluator.rb
268
+ - lib/decision_agent/explainability/condition_trace.rb
269
+ - lib/decision_agent/explainability/explainability_result.rb
270
+ - lib/decision_agent/explainability/rule_trace.rb
271
+ - lib/decision_agent/explainability/trace_collector.rb
186
272
  - lib/decision_agent/monitoring/alert_manager.rb
187
273
  - lib/decision_agent/monitoring/dashboard/public/dashboard.css
188
274
  - lib/decision_agent/monitoring/dashboard/public/dashboard.js
@@ -200,6 +286,15 @@ files:
200
286
  - lib/decision_agent/scoring/max_weight.rb
201
287
  - lib/decision_agent/scoring/threshold.rb
202
288
  - lib/decision_agent/scoring/weighted_average.rb
289
+ - lib/decision_agent/simulation.rb
290
+ - lib/decision_agent/simulation/errors.rb
291
+ - lib/decision_agent/simulation/impact_analyzer.rb
292
+ - lib/decision_agent/simulation/monte_carlo_simulator.rb
293
+ - lib/decision_agent/simulation/replay_engine.rb
294
+ - lib/decision_agent/simulation/scenario_engine.rb
295
+ - lib/decision_agent/simulation/scenario_library.rb
296
+ - lib/decision_agent/simulation/shadow_test_engine.rb
297
+ - lib/decision_agent/simulation/what_if_analyzer.rb
203
298
  - lib/decision_agent/testing/batch_test_importer.rb
204
299
  - lib/decision_agent/testing/batch_test_runner.rb
205
300
  - lib/decision_agent/testing/test_coverage_analyzer.rb
@@ -210,12 +305,21 @@ files:
210
305
  - lib/decision_agent/versioning/adapter.rb
211
306
  - lib/decision_agent/versioning/file_storage_adapter.rb
212
307
  - lib/decision_agent/versioning/version_manager.rb
308
+ - lib/decision_agent/web/dmn_editor.rb
213
309
  - lib/decision_agent/web/middleware/auth_middleware.rb
214
310
  - lib/decision_agent/web/middleware/permission_middleware.rb
215
311
  - lib/decision_agent/web/public/app.js
216
312
  - lib/decision_agent/web/public/batch_testing.html
313
+ - lib/decision_agent/web/public/dmn-editor.css
314
+ - lib/decision_agent/web/public/dmn-editor.html
315
+ - lib/decision_agent/web/public/dmn-editor.js
217
316
  - lib/decision_agent/web/public/index.html
218
317
  - lib/decision_agent/web/public/login.html
318
+ - lib/decision_agent/web/public/simulation.html
319
+ - lib/decision_agent/web/public/simulation_impact.html
320
+ - lib/decision_agent/web/public/simulation_replay.html
321
+ - lib/decision_agent/web/public/simulation_shadow.html
322
+ - lib/decision_agent/web/public/simulation_whatif.html
219
323
  - lib/decision_agent/web/public/styles.css
220
324
  - lib/decision_agent/web/public/users.html
221
325
  - lib/decision_agent/web/server.rb
@@ -234,66 +338,6 @@ files:
234
338
  - lib/generators/decision_agent/install/templates/performance_metric.rb
235
339
  - lib/generators/decision_agent/install/templates/rule.rb
236
340
  - lib/generators/decision_agent/install/templates/rule_version.rb
237
- - spec/ab_testing/ab_test_assignment_spec.rb
238
- - spec/ab_testing/ab_test_manager_spec.rb
239
- - spec/ab_testing/ab_test_spec.rb
240
- - spec/ab_testing/ab_testing_agent_spec.rb
241
- - spec/ab_testing/storage/adapter_spec.rb
242
- - spec/ab_testing/storage/memory_adapter_spec.rb
243
- - spec/activerecord_thread_safety_spec.rb
244
- - spec/advanced_operators_spec.rb
245
- - spec/agent_spec.rb
246
- - spec/api_contract_spec.rb
247
- - spec/audit_adapters_spec.rb
248
- - spec/auth/access_audit_logger_spec.rb
249
- - spec/auth/authenticator_spec.rb
250
- - spec/auth/password_reset_spec.rb
251
- - spec/auth/permission_checker_spec.rb
252
- - spec/auth/permission_spec.rb
253
- - spec/auth/rbac_adapter_spec.rb
254
- - spec/auth/rbac_config_spec.rb
255
- - spec/auth/role_spec.rb
256
- - spec/auth/session_manager_spec.rb
257
- - spec/auth/session_spec.rb
258
- - spec/auth/user_spec.rb
259
- - spec/comprehensive_edge_cases_spec.rb
260
- - spec/context_spec.rb
261
- - spec/decision_agent_spec.rb
262
- - spec/decision_spec.rb
263
- - spec/dsl/condition_evaluator_spec.rb
264
- - spec/dsl_validation_spec.rb
265
- - spec/edge_cases_spec.rb
266
- - spec/evaluation_spec.rb
267
- - spec/evaluation_validator_spec.rb
268
- - spec/examples.txt
269
- - spec/examples/feedback_aware_evaluator_spec.rb
270
- - spec/issue_verification_spec.rb
271
- - spec/json_rule_evaluator_spec.rb
272
- - spec/monitoring/alert_manager_spec.rb
273
- - spec/monitoring/metrics_collector_spec.rb
274
- - spec/monitoring/monitored_agent_spec.rb
275
- - spec/monitoring/prometheus_exporter_spec.rb
276
- - spec/monitoring/storage/activerecord_adapter_spec.rb
277
- - spec/monitoring/storage/base_adapter_spec.rb
278
- - spec/monitoring/storage/memory_adapter_spec.rb
279
- - spec/performance_optimizations_spec.rb
280
- - spec/replay_edge_cases_spec.rb
281
- - spec/replay_spec.rb
282
- - spec/rfc8785_canonicalization_spec.rb
283
- - spec/scoring_spec.rb
284
- - spec/spec_helper.rb
285
- - spec/testing/batch_test_importer_spec.rb
286
- - spec/testing/batch_test_runner_spec.rb
287
- - spec/testing/test_coverage_analyzer_spec.rb
288
- - spec/testing/test_result_comparator_spec.rb
289
- - spec/testing/test_scenario_spec.rb
290
- - spec/thread_safety_spec.rb
291
- - spec/thread_safety_spec.rb.broken
292
- - spec/versioning/adapter_spec.rb
293
- - spec/versioning_spec.rb
294
- - spec/web/middleware/auth_middleware_spec.rb
295
- - spec/web/middleware/permission_middleware_spec.rb
296
- - spec/web_ui_rack_spec.rb
297
341
  homepage: https://github.com/samaswin/decision_agent
298
342
  licenses:
299
343
  - MIT
@@ -1,253 +0,0 @@
1
- require "spec_helper"
2
- require "decision_agent/ab_testing/ab_test_assignment"
3
-
4
- RSpec.describe DecisionAgent::ABTesting::ABTestAssignment do
5
- describe "#initialize" do
6
- it "creates an assignment with required fields" do
7
- assignment = described_class.new(
8
- ab_test_id: "test_1",
9
- variant: :champion,
10
- version_id: "v1"
11
- )
12
-
13
- expect(assignment.ab_test_id).to eq("test_1")
14
- expect(assignment.variant).to eq(:champion)
15
- expect(assignment.version_id).to eq("v1")
16
- expect(assignment.timestamp).to be_a(Time)
17
- end
18
-
19
- it "accepts optional user_id" do
20
- assignment = described_class.new(
21
- ab_test_id: "test_1",
22
- variant: :champion,
23
- version_id: "v1",
24
- user_id: "user_123"
25
- )
26
-
27
- expect(assignment.user_id).to eq("user_123")
28
- end
29
-
30
- it "accepts optional timestamp" do
31
- custom_time = Time.new(2024, 1, 1, 12, 0, 0, "+00:00")
32
- assignment = described_class.new(
33
- ab_test_id: "test_1",
34
- variant: :champion,
35
- version_id: "v1",
36
- timestamp: custom_time
37
- )
38
-
39
- expect(assignment.timestamp).to eq(custom_time)
40
- end
41
-
42
- it "accepts optional decision_result and confidence" do
43
- assignment = described_class.new(
44
- ab_test_id: "test_1",
45
- variant: :champion,
46
- version_id: "v1",
47
- decision_result: "approve",
48
- confidence: 0.95
49
- )
50
-
51
- expect(assignment.decision_result).to eq("approve")
52
- expect(assignment.confidence).to eq(0.95)
53
- end
54
-
55
- it "accepts optional context" do
56
- context = { user_type: "premium", region: "us" }
57
- assignment = described_class.new(
58
- ab_test_id: "test_1",
59
- variant: :champion,
60
- version_id: "v1",
61
- context: context
62
- )
63
-
64
- expect(assignment.context).to eq(context)
65
- end
66
-
67
- it "defaults context to empty hash" do
68
- assignment = described_class.new(
69
- ab_test_id: "test_1",
70
- variant: :champion,
71
- version_id: "v1"
72
- )
73
-
74
- expect(assignment.context).to eq({})
75
- end
76
-
77
- it "accepts optional id" do
78
- assignment = described_class.new(
79
- ab_test_id: "test_1",
80
- variant: :champion,
81
- version_id: "v1",
82
- id: "assign_123"
83
- )
84
-
85
- expect(assignment.id).to eq("assign_123")
86
- end
87
-
88
- it "raises error if ab_test_id is nil" do
89
- expect do
90
- described_class.new(
91
- ab_test_id: nil,
92
- variant: :champion,
93
- version_id: "v1"
94
- )
95
- end.to raise_error(DecisionAgent::ValidationError, /AB test ID is required/)
96
- end
97
-
98
- it "raises error if variant is nil" do
99
- expect do
100
- described_class.new(
101
- ab_test_id: "test_1",
102
- variant: nil,
103
- version_id: "v1"
104
- )
105
- end.to raise_error(DecisionAgent::ValidationError, /Variant is required/)
106
- end
107
-
108
- it "raises error if version_id is nil" do
109
- expect do
110
- described_class.new(
111
- ab_test_id: "test_1",
112
- variant: :champion,
113
- version_id: nil
114
- )
115
- end.to raise_error(DecisionAgent::ValidationError, /Version ID is required/)
116
- end
117
-
118
- it "raises error if variant is not :champion or :challenger" do
119
- expect do
120
- described_class.new(
121
- ab_test_id: "test_1",
122
- variant: :invalid,
123
- version_id: "v1"
124
- )
125
- end.to raise_error(DecisionAgent::ValidationError, /Variant must be :champion or :challenger/)
126
- end
127
-
128
- it "raises error if confidence is negative" do
129
- expect do
130
- described_class.new(
131
- ab_test_id: "test_1",
132
- variant: :champion,
133
- version_id: "v1",
134
- confidence: -0.1
135
- )
136
- end.to raise_error(DecisionAgent::ValidationError, /Confidence must be between 0 and 1/)
137
- end
138
-
139
- it "raises error if confidence is greater than 1" do
140
- expect do
141
- described_class.new(
142
- ab_test_id: "test_1",
143
- variant: :champion,
144
- version_id: "v1",
145
- confidence: 1.5
146
- )
147
- end.to raise_error(DecisionAgent::ValidationError, /Confidence must be between 0 and 1/)
148
- end
149
-
150
- it "accepts confidence of 0" do
151
- assignment = described_class.new(
152
- ab_test_id: "test_1",
153
- variant: :champion,
154
- version_id: "v1",
155
- confidence: 0.0
156
- )
157
-
158
- expect(assignment.confidence).to eq(0.0)
159
- end
160
-
161
- it "accepts confidence of 1" do
162
- assignment = described_class.new(
163
- ab_test_id: "test_1",
164
- variant: :champion,
165
- version_id: "v1",
166
- confidence: 1.0
167
- )
168
-
169
- expect(assignment.confidence).to eq(1.0)
170
- end
171
-
172
- it "accepts challenger variant" do
173
- assignment = described_class.new(
174
- ab_test_id: "test_1",
175
- variant: :challenger,
176
- version_id: "v2"
177
- )
178
-
179
- expect(assignment.variant).to eq(:challenger)
180
- end
181
- end
182
-
183
- describe "#record_decision" do
184
- let(:assignment) do
185
- described_class.new(
186
- ab_test_id: "test_1",
187
- variant: :champion,
188
- version_id: "v1"
189
- )
190
- end
191
-
192
- it "updates decision_result and confidence" do
193
- assignment.record_decision("approve", 0.95)
194
-
195
- expect(assignment.decision_result).to eq("approve")
196
- expect(assignment.confidence).to eq(0.95)
197
- end
198
-
199
- it "can update multiple times" do
200
- assignment.record_decision("approve", 0.95)
201
- assignment.record_decision("reject", 0.85)
202
-
203
- expect(assignment.decision_result).to eq("reject")
204
- expect(assignment.confidence).to eq(0.85)
205
- end
206
- end
207
-
208
- describe "#to_h" do
209
- it "converts assignment to hash with all fields" do
210
- assignment = described_class.new(
211
- ab_test_id: "test_1",
212
- variant: :champion,
213
- version_id: "v1",
214
- id: "assign_123",
215
- user_id: "user_456",
216
- decision_result: "approve",
217
- confidence: 0.95,
218
- context: { region: "us" },
219
- timestamp: Time.new(2024, 1, 1, 12, 0, 0, "+00:00")
220
- )
221
-
222
- hash = assignment.to_h
223
-
224
- expect(hash).to eq({
225
- id: "assign_123",
226
- ab_test_id: "test_1",
227
- user_id: "user_456",
228
- variant: :champion,
229
- version_id: "v1",
230
- timestamp: Time.new(2024, 1, 1, 12, 0, 0, "+00:00"),
231
- decision_result: "approve",
232
- confidence: 0.95,
233
- context: { region: "us" }
234
- })
235
- end
236
-
237
- it "includes nil values in hash" do
238
- assignment = described_class.new(
239
- ab_test_id: "test_1",
240
- variant: :champion,
241
- version_id: "v1"
242
- )
243
-
244
- hash = assignment.to_h
245
-
246
- expect(hash[:id]).to be_nil
247
- expect(hash[:user_id]).to be_nil
248
- expect(hash[:decision_result]).to be_nil
249
- expect(hash[:confidence]).to be_nil
250
- expect(hash[:context]).to eq({})
251
- end
252
- end
253
- end