decision_agent 0.3.0 → 1.1.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 (220) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +234 -14
  3. data/lib/decision_agent/ab_testing/ab_test.rb +5 -1
  4. data/lib/decision_agent/ab_testing/ab_test_assignment.rb +2 -0
  5. data/lib/decision_agent/ab_testing/ab_test_manager.rb +2 -0
  6. data/lib/decision_agent/ab_testing/ab_testing_agent.rb +2 -0
  7. data/lib/decision_agent/ab_testing/storage/activerecord_adapter.rb +2 -13
  8. data/lib/decision_agent/ab_testing/storage/adapter.rb +2 -0
  9. data/lib/decision_agent/ab_testing/storage/memory_adapter.rb +2 -0
  10. data/lib/decision_agent/agent.rb +78 -9
  11. data/lib/decision_agent/audit/adapter.rb +2 -0
  12. data/lib/decision_agent/audit/logger_adapter.rb +2 -0
  13. data/lib/decision_agent/audit/null_adapter.rb +2 -0
  14. data/lib/decision_agent/auth/access_audit_logger.rb +2 -0
  15. data/lib/decision_agent/auth/authenticator.rb +2 -0
  16. data/lib/decision_agent/auth/password_reset_manager.rb +2 -0
  17. data/lib/decision_agent/auth/password_reset_token.rb +2 -0
  18. data/lib/decision_agent/auth/permission.rb +2 -0
  19. data/lib/decision_agent/auth/permission_checker.rb +2 -0
  20. data/lib/decision_agent/auth/rbac_adapter.rb +2 -0
  21. data/lib/decision_agent/auth/rbac_config.rb +2 -0
  22. data/lib/decision_agent/auth/role.rb +2 -0
  23. data/lib/decision_agent/auth/session.rb +2 -0
  24. data/lib/decision_agent/auth/session_manager.rb +2 -0
  25. data/lib/decision_agent/auth/user.rb +2 -0
  26. data/lib/decision_agent/context.rb +14 -0
  27. data/lib/decision_agent/decision.rb +113 -4
  28. data/lib/decision_agent/dmn/adapter.rb +2 -0
  29. data/lib/decision_agent/dmn/cache.rb +2 -2
  30. data/lib/decision_agent/dmn/decision_graph.rb +7 -7
  31. data/lib/decision_agent/dmn/decision_tree.rb +16 -8
  32. data/lib/decision_agent/dmn/errors.rb +2 -0
  33. data/lib/decision_agent/dmn/exporter.rb +2 -0
  34. data/lib/decision_agent/dmn/feel/evaluator.rb +130 -114
  35. data/lib/decision_agent/dmn/feel/functions.rb +2 -0
  36. data/lib/decision_agent/dmn/feel/parser.rb +2 -0
  37. data/lib/decision_agent/dmn/feel/simple_parser.rb +98 -77
  38. data/lib/decision_agent/dmn/feel/transformer.rb +56 -102
  39. data/lib/decision_agent/dmn/feel/types.rb +2 -0
  40. data/lib/decision_agent/dmn/importer.rb +2 -0
  41. data/lib/decision_agent/dmn/model.rb +2 -4
  42. data/lib/decision_agent/dmn/parser.rb +2 -0
  43. data/lib/decision_agent/dmn/testing.rb +3 -2
  44. data/lib/decision_agent/dmn/validator.rb +5 -3
  45. data/lib/decision_agent/dmn/visualizer.rb +7 -6
  46. data/lib/decision_agent/dsl/condition_evaluator.rb +242 -1375
  47. data/lib/decision_agent/dsl/helpers/cache_helpers.rb +82 -0
  48. data/lib/decision_agent/dsl/helpers/comparison_helpers.rb +98 -0
  49. data/lib/decision_agent/dsl/helpers/date_helpers.rb +91 -0
  50. data/lib/decision_agent/dsl/helpers/geospatial_helpers.rb +85 -0
  51. data/lib/decision_agent/dsl/helpers/operator_evaluation_helpers.rb +160 -0
  52. data/lib/decision_agent/dsl/helpers/parameter_parsing_helpers.rb +206 -0
  53. data/lib/decision_agent/dsl/helpers/template_helpers.rb +39 -0
  54. data/lib/decision_agent/dsl/helpers/utility_helpers.rb +45 -0
  55. data/lib/decision_agent/dsl/operators/base.rb +70 -0
  56. data/lib/decision_agent/dsl/operators/basic_comparison_operators.rb +80 -0
  57. data/lib/decision_agent/dsl/operators/collection_operators.rb +60 -0
  58. data/lib/decision_agent/dsl/operators/date_arithmetic_operators.rb +206 -0
  59. data/lib/decision_agent/dsl/operators/date_time_operators.rb +47 -0
  60. data/lib/decision_agent/dsl/operators/duration_operators.rb +149 -0
  61. data/lib/decision_agent/dsl/operators/financial_operators.rb +237 -0
  62. data/lib/decision_agent/dsl/operators/geospatial_operators.rb +106 -0
  63. data/lib/decision_agent/dsl/operators/mathematical_operators.rb +234 -0
  64. data/lib/decision_agent/dsl/operators/moving_window_operators.rb +135 -0
  65. data/lib/decision_agent/dsl/operators/numeric_operators.rb +120 -0
  66. data/lib/decision_agent/dsl/operators/rate_operators.rb +65 -0
  67. data/lib/decision_agent/dsl/operators/statistical_aggregations.rb +187 -0
  68. data/lib/decision_agent/dsl/operators/string_aggregations.rb +84 -0
  69. data/lib/decision_agent/dsl/operators/string_operators.rb +72 -0
  70. data/lib/decision_agent/dsl/operators/time_component_operators.rb +72 -0
  71. data/lib/decision_agent/dsl/rule_parser.rb +2 -0
  72. data/lib/decision_agent/dsl/schema_validator.rb +37 -14
  73. data/lib/decision_agent/errors.rb +2 -0
  74. data/lib/decision_agent/evaluation.rb +14 -2
  75. data/lib/decision_agent/evaluators/base.rb +2 -0
  76. data/lib/decision_agent/evaluators/dmn_evaluator.rb +108 -19
  77. data/lib/decision_agent/evaluators/json_rule_evaluator.rb +56 -11
  78. data/lib/decision_agent/evaluators/static_evaluator.rb +2 -0
  79. data/lib/decision_agent/explainability/condition_trace.rb +85 -0
  80. data/lib/decision_agent/explainability/explainability_result.rb +50 -0
  81. data/lib/decision_agent/explainability/rule_trace.rb +41 -0
  82. data/lib/decision_agent/explainability/trace_collector.rb +26 -0
  83. data/lib/decision_agent/monitoring/alert_manager.rb +7 -16
  84. data/lib/decision_agent/monitoring/dashboard_server.rb +383 -250
  85. data/lib/decision_agent/monitoring/metrics_collector.rb +2 -0
  86. data/lib/decision_agent/monitoring/monitored_agent.rb +2 -0
  87. data/lib/decision_agent/monitoring/prometheus_exporter.rb +3 -1
  88. data/lib/decision_agent/replay/replay.rb +4 -1
  89. data/lib/decision_agent/scoring/base.rb +2 -0
  90. data/lib/decision_agent/scoring/consensus.rb +2 -0
  91. data/lib/decision_agent/scoring/max_weight.rb +2 -0
  92. data/lib/decision_agent/scoring/threshold.rb +2 -0
  93. data/lib/decision_agent/scoring/weighted_average.rb +2 -0
  94. data/lib/decision_agent/simulation/errors.rb +20 -0
  95. data/lib/decision_agent/simulation/impact_analyzer.rb +500 -0
  96. data/lib/decision_agent/simulation/monte_carlo_simulator.rb +638 -0
  97. data/lib/decision_agent/simulation/replay_engine.rb +488 -0
  98. data/lib/decision_agent/simulation/scenario_engine.rb +320 -0
  99. data/lib/decision_agent/simulation/scenario_library.rb +165 -0
  100. data/lib/decision_agent/simulation/shadow_test_engine.rb +274 -0
  101. data/lib/decision_agent/simulation/what_if_analyzer.rb +1008 -0
  102. data/lib/decision_agent/simulation.rb +19 -0
  103. data/lib/decision_agent/testing/batch_test_importer.rb +6 -2
  104. data/lib/decision_agent/testing/batch_test_runner.rb +5 -2
  105. data/lib/decision_agent/testing/test_coverage_analyzer.rb +2 -0
  106. data/lib/decision_agent/testing/test_result_comparator.rb +2 -0
  107. data/lib/decision_agent/testing/test_scenario.rb +2 -0
  108. data/lib/decision_agent/version.rb +3 -1
  109. data/lib/decision_agent/versioning/activerecord_adapter.rb +108 -43
  110. data/lib/decision_agent/versioning/adapter.rb +9 -0
  111. data/lib/decision_agent/versioning/file_storage_adapter.rb +19 -6
  112. data/lib/decision_agent/versioning/version_manager.rb +9 -0
  113. data/lib/decision_agent/web/dmn_editor/serialization.rb +74 -0
  114. data/lib/decision_agent/web/dmn_editor/xml_builder.rb +107 -0
  115. data/lib/decision_agent/web/dmn_editor.rb +8 -67
  116. data/lib/decision_agent/web/middleware/auth_middleware.rb +2 -0
  117. data/lib/decision_agent/web/middleware/permission_middleware.rb +3 -1
  118. data/lib/decision_agent/web/public/app.js +186 -26
  119. data/lib/decision_agent/web/public/batch_testing.html +80 -6
  120. data/lib/decision_agent/web/public/dmn-editor.html +2 -2
  121. data/lib/decision_agent/web/public/dmn-editor.js +74 -8
  122. data/lib/decision_agent/web/public/index.html +69 -3
  123. data/lib/decision_agent/web/public/login.html +1 -1
  124. data/lib/decision_agent/web/public/sample_batch.csv +11 -0
  125. data/lib/decision_agent/web/public/sample_impact.csv +11 -0
  126. data/lib/decision_agent/web/public/sample_replay.csv +11 -0
  127. data/lib/decision_agent/web/public/sample_rules.json +118 -0
  128. data/lib/decision_agent/web/public/sample_shadow.csv +11 -0
  129. data/lib/decision_agent/web/public/sample_whatif.csv +11 -0
  130. data/lib/decision_agent/web/public/simulation.html +146 -0
  131. data/lib/decision_agent/web/public/simulation_impact.html +495 -0
  132. data/lib/decision_agent/web/public/simulation_replay.html +547 -0
  133. data/lib/decision_agent/web/public/simulation_shadow.html +561 -0
  134. data/lib/decision_agent/web/public/simulation_whatif.html +549 -0
  135. data/lib/decision_agent/web/public/styles.css +65 -0
  136. data/lib/decision_agent/web/public/users.html +1 -1
  137. data/lib/decision_agent/web/rack_helpers.rb +106 -0
  138. data/lib/decision_agent/web/rack_request_helpers.rb +196 -0
  139. data/lib/decision_agent/web/server.rb +2126 -1374
  140. data/lib/decision_agent.rb +19 -1
  141. data/lib/generators/decision_agent/install/install_generator.rb +2 -0
  142. data/lib/generators/decision_agent/install/templates/ab_test_assignment_model.rb +2 -0
  143. data/lib/generators/decision_agent/install/templates/ab_test_model.rb +2 -0
  144. data/lib/generators/decision_agent/install/templates/ab_testing_migration.rb +2 -0
  145. data/lib/generators/decision_agent/install/templates/migration.rb +2 -0
  146. data/lib/generators/decision_agent/install/templates/rule.rb +2 -0
  147. data/lib/generators/decision_agent/install/templates/rule_version.rb +2 -0
  148. metadata +103 -89
  149. data/spec/ab_testing/ab_test_assignment_spec.rb +0 -253
  150. data/spec/ab_testing/ab_test_manager_spec.rb +0 -612
  151. data/spec/ab_testing/ab_test_spec.rb +0 -270
  152. data/spec/ab_testing/ab_testing_agent_spec.rb +0 -655
  153. data/spec/ab_testing/storage/adapter_spec.rb +0 -64
  154. data/spec/ab_testing/storage/memory_adapter_spec.rb +0 -485
  155. data/spec/activerecord_thread_safety_spec.rb +0 -553
  156. data/spec/advanced_operators_spec.rb +0 -3150
  157. data/spec/agent_spec.rb +0 -289
  158. data/spec/api_contract_spec.rb +0 -430
  159. data/spec/audit_adapters_spec.rb +0 -92
  160. data/spec/auth/access_audit_logger_spec.rb +0 -394
  161. data/spec/auth/authenticator_spec.rb +0 -112
  162. data/spec/auth/password_reset_spec.rb +0 -294
  163. data/spec/auth/permission_checker_spec.rb +0 -207
  164. data/spec/auth/permission_spec.rb +0 -73
  165. data/spec/auth/rbac_adapter_spec.rb +0 -778
  166. data/spec/auth/rbac_config_spec.rb +0 -82
  167. data/spec/auth/role_spec.rb +0 -51
  168. data/spec/auth/session_manager_spec.rb +0 -172
  169. data/spec/auth/session_spec.rb +0 -112
  170. data/spec/auth/user_spec.rb +0 -130
  171. data/spec/comprehensive_edge_cases_spec.rb +0 -1777
  172. data/spec/context_spec.rb +0 -127
  173. data/spec/decision_agent_spec.rb +0 -96
  174. data/spec/decision_spec.rb +0 -423
  175. data/spec/dmn/decision_graph_spec.rb +0 -282
  176. data/spec/dmn/decision_tree_spec.rb +0 -203
  177. data/spec/dmn/feel/errors_spec.rb +0 -18
  178. data/spec/dmn/feel/functions_spec.rb +0 -400
  179. data/spec/dmn/feel/simple_parser_spec.rb +0 -274
  180. data/spec/dmn/feel/types_spec.rb +0 -176
  181. data/spec/dmn/feel_parser_spec.rb +0 -489
  182. data/spec/dmn/hit_policy_spec.rb +0 -202
  183. data/spec/dmn/integration_spec.rb +0 -226
  184. data/spec/dsl/condition_evaluator_spec.rb +0 -774
  185. data/spec/dsl_validation_spec.rb +0 -648
  186. data/spec/edge_cases_spec.rb +0 -353
  187. data/spec/evaluation_spec.rb +0 -364
  188. data/spec/evaluation_validator_spec.rb +0 -165
  189. data/spec/examples/feedback_aware_evaluator_spec.rb +0 -460
  190. data/spec/examples.txt +0 -1909
  191. data/spec/fixtures/dmn/complex_decision.dmn +0 -81
  192. data/spec/fixtures/dmn/invalid_structure.dmn +0 -31
  193. data/spec/fixtures/dmn/simple_decision.dmn +0 -40
  194. data/spec/issue_verification_spec.rb +0 -759
  195. data/spec/json_rule_evaluator_spec.rb +0 -587
  196. data/spec/monitoring/alert_manager_spec.rb +0 -378
  197. data/spec/monitoring/metrics_collector_spec.rb +0 -501
  198. data/spec/monitoring/monitored_agent_spec.rb +0 -225
  199. data/spec/monitoring/prometheus_exporter_spec.rb +0 -242
  200. data/spec/monitoring/storage/activerecord_adapter_spec.rb +0 -498
  201. data/spec/monitoring/storage/base_adapter_spec.rb +0 -61
  202. data/spec/monitoring/storage/memory_adapter_spec.rb +0 -247
  203. data/spec/performance_optimizations_spec.rb +0 -493
  204. data/spec/replay_edge_cases_spec.rb +0 -699
  205. data/spec/replay_spec.rb +0 -210
  206. data/spec/rfc8785_canonicalization_spec.rb +0 -215
  207. data/spec/scoring_spec.rb +0 -225
  208. data/spec/spec_helper.rb +0 -60
  209. data/spec/testing/batch_test_importer_spec.rb +0 -693
  210. data/spec/testing/batch_test_runner_spec.rb +0 -307
  211. data/spec/testing/test_coverage_analyzer_spec.rb +0 -292
  212. data/spec/testing/test_result_comparator_spec.rb +0 -392
  213. data/spec/testing/test_scenario_spec.rb +0 -113
  214. data/spec/thread_safety_spec.rb +0 -490
  215. data/spec/thread_safety_spec.rb.broken +0 -878
  216. data/spec/versioning/adapter_spec.rb +0 -156
  217. data/spec/versioning_spec.rb +0 -1030
  218. data/spec/web/middleware/auth_middleware_spec.rb +0 -133
  219. data/spec/web/middleware/permission_middleware_spec.rb +0 -247
  220. data/spec/web_ui_rack_spec.rb +0 -2134
@@ -1,270 +0,0 @@
1
- require "spec_helper"
2
- require "decision_agent/ab_testing/ab_test"
3
-
4
- RSpec.describe DecisionAgent::ABTesting::ABTest do
5
- describe "#initialize" do
6
- it "creates a valid A/B test with default values" do
7
- test = described_class.new(
8
- name: "Test A vs B",
9
- champion_version_id: "v1",
10
- challenger_version_id: "v2"
11
- )
12
-
13
- expect(test.name).to eq("Test A vs B")
14
- expect(test.champion_version_id).to eq("v1")
15
- expect(test.challenger_version_id).to eq("v2")
16
- expect(test.traffic_split).to eq({ champion: 90, challenger: 10 })
17
- expect(test.status).to eq("scheduled")
18
- end
19
-
20
- it "accepts custom traffic split as hash" do
21
- test = described_class.new(
22
- name: "Custom Split",
23
- champion_version_id: "v1",
24
- challenger_version_id: "v2",
25
- traffic_split: { champion: 70, challenger: 30 }
26
- )
27
-
28
- expect(test.traffic_split).to eq({ champion: 70, challenger: 30 })
29
- end
30
-
31
- it "accepts custom traffic split as array" do
32
- test = described_class.new(
33
- name: "Array Split",
34
- champion_version_id: "v1",
35
- challenger_version_id: "v2",
36
- traffic_split: [80, 20]
37
- )
38
-
39
- expect(test.traffic_split).to eq({ champion: 80, challenger: 20 })
40
- end
41
-
42
- it "raises error if traffic split doesn't sum to 100" do
43
- expect do
44
- described_class.new(
45
- name: "Bad Split",
46
- champion_version_id: "v1",
47
- challenger_version_id: "v2",
48
- traffic_split: { champion: 60, challenger: 30 }
49
- )
50
- end.to raise_error(DecisionAgent::ValidationError, /must sum to 100/)
51
- end
52
-
53
- it "raises error if champion and challenger are the same" do
54
- expect do
55
- described_class.new(
56
- name: "Same Versions",
57
- champion_version_id: "v1",
58
- challenger_version_id: "v1"
59
- )
60
- end.to raise_error(DecisionAgent::ValidationError, /must be different/)
61
- end
62
-
63
- it "raises error if name is empty" do
64
- expect do
65
- described_class.new(
66
- name: "",
67
- champion_version_id: "v1",
68
- challenger_version_id: "v2"
69
- )
70
- end.to raise_error(DecisionAgent::ValidationError, /name is required/)
71
- end
72
- end
73
-
74
- describe "#assign_variant" do
75
- let(:test) do
76
- described_class.new(
77
- name: "Test",
78
- champion_version_id: "v1",
79
- challenger_version_id: "v2",
80
- traffic_split: { champion: 90, challenger: 10 },
81
- status: "running",
82
- id: 123
83
- )
84
- end
85
-
86
- it "assigns champion or challenger based on traffic split" do
87
- assignments = 1000.times.map { test.assign_variant }
88
- champion_count = assignments.count { |v| v == :champion }
89
- challenger_count = assignments.count { |v| v == :challenger }
90
-
91
- # With 90/10 split, expect roughly 900/100
92
- expect(champion_count).to be_between(850, 950)
93
- expect(challenger_count).to be_between(50, 150)
94
- end
95
-
96
- it "assigns same variant to same user consistently" do
97
- user_id = "user_123"
98
- variants = 10.times.map { test.assign_variant(user_id: user_id) }
99
-
100
- expect(variants.uniq.size).to eq(1)
101
- end
102
-
103
- it "assigns different users to different variants based on split" do
104
- assignments = 1000.times.map { |i| test.assign_variant(user_id: "user_#{i}") }
105
- champion_count = assignments.count { |v| v == :champion }
106
- challenger_count = assignments.count { |v| v == :challenger }
107
-
108
- expect(champion_count).to be_between(850, 950)
109
- expect(challenger_count).to be_between(50, 150)
110
- end
111
-
112
- it "raises error if test is not running" do
113
- test = described_class.new(
114
- name: "Not Running",
115
- champion_version_id: "v1",
116
- challenger_version_id: "v2",
117
- status: "completed"
118
- )
119
-
120
- expect do
121
- test.assign_variant
122
- end.to raise_error(DecisionAgent::ABTesting::TestNotRunningError)
123
- end
124
- end
125
-
126
- describe "#version_for_variant" do
127
- let(:test) do
128
- described_class.new(
129
- name: "Test",
130
- champion_version_id: "champion_v1",
131
- challenger_version_id: "challenger_v2"
132
- )
133
- end
134
-
135
- it "returns champion version ID for :champion variant" do
136
- expect(test.version_for_variant(:champion)).to eq("champion_v1")
137
- end
138
-
139
- it "returns challenger version ID for :challenger variant" do
140
- expect(test.version_for_variant(:challenger)).to eq("challenger_v2")
141
- end
142
-
143
- it "raises error for invalid variant" do
144
- expect do
145
- test.version_for_variant(:invalid)
146
- end.to raise_error(ArgumentError, /Invalid variant/)
147
- end
148
- end
149
-
150
- describe "#running?" do
151
- it "returns true when status is running and within date range" do
152
- test = described_class.new(
153
- name: "Test",
154
- champion_version_id: "v1",
155
- challenger_version_id: "v2",
156
- status: "running",
157
- start_date: Time.now.utc - 3600,
158
- end_date: Time.now.utc + 3600
159
- )
160
-
161
- expect(test.running?).to be true
162
- end
163
-
164
- it "returns false when status is not running" do
165
- test = described_class.new(
166
- name: "Test",
167
- champion_version_id: "v1",
168
- challenger_version_id: "v2",
169
- status: "completed"
170
- )
171
-
172
- expect(test.running?).to be false
173
- end
174
-
175
- it "returns false when start date is in future" do
176
- test = described_class.new(
177
- name: "Test",
178
- champion_version_id: "v1",
179
- challenger_version_id: "v2",
180
- status: "running",
181
- start_date: Time.now.utc + 3600
182
- )
183
-
184
- expect(test.running?).to be false
185
- end
186
-
187
- it "returns false when end date has passed" do
188
- test = described_class.new(
189
- name: "Test",
190
- champion_version_id: "v1",
191
- challenger_version_id: "v2",
192
- status: "running",
193
- start_date: Time.now.utc - 7200,
194
- end_date: Time.now.utc - 3600
195
- )
196
-
197
- expect(test.running?).to be false
198
- end
199
- end
200
-
201
- describe "status transitions" do
202
- it "can start a scheduled test" do
203
- test = described_class.new(
204
- name: "Test",
205
- champion_version_id: "v1",
206
- challenger_version_id: "v2",
207
- status: "scheduled"
208
- )
209
-
210
- expect { test.start! }.not_to raise_error
211
- expect(test.status).to eq("running")
212
- end
213
-
214
- it "can complete a running test" do
215
- test = described_class.new(
216
- name: "Test",
217
- champion_version_id: "v1",
218
- challenger_version_id: "v2",
219
- status: "running"
220
- )
221
-
222
- expect { test.complete! }.not_to raise_error
223
- expect(test.status).to eq("completed")
224
- end
225
-
226
- it "can cancel a test" do
227
- test = described_class.new(
228
- name: "Test",
229
- champion_version_id: "v1",
230
- challenger_version_id: "v2",
231
- status: "running"
232
- )
233
-
234
- expect { test.cancel! }.not_to raise_error
235
- expect(test.status).to eq("cancelled")
236
- end
237
-
238
- it "raises error when trying invalid status transition" do
239
- test = described_class.new(
240
- name: "Test",
241
- champion_version_id: "v1",
242
- challenger_version_id: "v2",
243
- status: "completed"
244
- )
245
-
246
- expect do
247
- test.start!
248
- end.to raise_error(DecisionAgent::ABTesting::InvalidStatusTransitionError)
249
- end
250
- end
251
-
252
- describe "#to_h" do
253
- it "returns hash representation" do
254
- test = described_class.new(
255
- name: "Test",
256
- champion_version_id: "v1",
257
- challenger_version_id: "v2",
258
- id: 123
259
- )
260
-
261
- hash = test.to_h
262
-
263
- expect(hash[:id]).to eq(123)
264
- expect(hash[:name]).to eq("Test")
265
- expect(hash[:champion_version_id]).to eq("v1")
266
- expect(hash[:challenger_version_id]).to eq("v2")
267
- expect(hash[:status]).to eq("scheduled")
268
- end
269
- end
270
- end