decision_agent 0.3.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 (115) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +272 -7
  3. data/lib/decision_agent/agent.rb +72 -1
  4. data/lib/decision_agent/context.rb +1 -0
  5. data/lib/decision_agent/data_enrichment/cache/memory_adapter.rb +86 -0
  6. data/lib/decision_agent/data_enrichment/cache_adapter.rb +49 -0
  7. data/lib/decision_agent/data_enrichment/circuit_breaker.rb +135 -0
  8. data/lib/decision_agent/data_enrichment/client.rb +220 -0
  9. data/lib/decision_agent/data_enrichment/config.rb +78 -0
  10. data/lib/decision_agent/data_enrichment/errors.rb +36 -0
  11. data/lib/decision_agent/decision.rb +102 -2
  12. data/lib/decision_agent/dmn/feel/evaluator.rb +28 -6
  13. data/lib/decision_agent/dsl/condition_evaluator.rb +982 -839
  14. data/lib/decision_agent/dsl/schema_validator.rb +51 -13
  15. data/lib/decision_agent/evaluators/dmn_evaluator.rb +106 -19
  16. data/lib/decision_agent/evaluators/json_rule_evaluator.rb +69 -9
  17. data/lib/decision_agent/explainability/condition_trace.rb +83 -0
  18. data/lib/decision_agent/explainability/explainability_result.rb +52 -0
  19. data/lib/decision_agent/explainability/rule_trace.rb +39 -0
  20. data/lib/decision_agent/explainability/trace_collector.rb +24 -0
  21. data/lib/decision_agent/monitoring/alert_manager.rb +5 -1
  22. data/lib/decision_agent/simulation/errors.rb +18 -0
  23. data/lib/decision_agent/simulation/impact_analyzer.rb +498 -0
  24. data/lib/decision_agent/simulation/monte_carlo_simulator.rb +635 -0
  25. data/lib/decision_agent/simulation/replay_engine.rb +486 -0
  26. data/lib/decision_agent/simulation/scenario_engine.rb +318 -0
  27. data/lib/decision_agent/simulation/scenario_library.rb +163 -0
  28. data/lib/decision_agent/simulation/shadow_test_engine.rb +287 -0
  29. data/lib/decision_agent/simulation/what_if_analyzer.rb +1002 -0
  30. data/lib/decision_agent/simulation.rb +17 -0
  31. data/lib/decision_agent/version.rb +1 -1
  32. data/lib/decision_agent/versioning/activerecord_adapter.rb +23 -8
  33. data/lib/decision_agent/web/public/app.js +119 -0
  34. data/lib/decision_agent/web/public/index.html +49 -0
  35. data/lib/decision_agent/web/public/simulation.html +130 -0
  36. data/lib/decision_agent/web/public/simulation_impact.html +478 -0
  37. data/lib/decision_agent/web/public/simulation_replay.html +551 -0
  38. data/lib/decision_agent/web/public/simulation_shadow.html +546 -0
  39. data/lib/decision_agent/web/public/simulation_whatif.html +532 -0
  40. data/lib/decision_agent/web/public/styles.css +65 -0
  41. data/lib/decision_agent/web/server.rb +594 -23
  42. data/lib/decision_agent.rb +60 -2
  43. metadata +53 -73
  44. data/spec/ab_testing/ab_test_assignment_spec.rb +0 -253
  45. data/spec/ab_testing/ab_test_manager_spec.rb +0 -612
  46. data/spec/ab_testing/ab_test_spec.rb +0 -270
  47. data/spec/ab_testing/ab_testing_agent_spec.rb +0 -655
  48. data/spec/ab_testing/storage/adapter_spec.rb +0 -64
  49. data/spec/ab_testing/storage/memory_adapter_spec.rb +0 -485
  50. data/spec/activerecord_thread_safety_spec.rb +0 -553
  51. data/spec/advanced_operators_spec.rb +0 -3150
  52. data/spec/agent_spec.rb +0 -289
  53. data/spec/api_contract_spec.rb +0 -430
  54. data/spec/audit_adapters_spec.rb +0 -92
  55. data/spec/auth/access_audit_logger_spec.rb +0 -394
  56. data/spec/auth/authenticator_spec.rb +0 -112
  57. data/spec/auth/password_reset_spec.rb +0 -294
  58. data/spec/auth/permission_checker_spec.rb +0 -207
  59. data/spec/auth/permission_spec.rb +0 -73
  60. data/spec/auth/rbac_adapter_spec.rb +0 -778
  61. data/spec/auth/rbac_config_spec.rb +0 -82
  62. data/spec/auth/role_spec.rb +0 -51
  63. data/spec/auth/session_manager_spec.rb +0 -172
  64. data/spec/auth/session_spec.rb +0 -112
  65. data/spec/auth/user_spec.rb +0 -130
  66. data/spec/comprehensive_edge_cases_spec.rb +0 -1777
  67. data/spec/context_spec.rb +0 -127
  68. data/spec/decision_agent_spec.rb +0 -96
  69. data/spec/decision_spec.rb +0 -423
  70. data/spec/dmn/decision_graph_spec.rb +0 -282
  71. data/spec/dmn/decision_tree_spec.rb +0 -203
  72. data/spec/dmn/feel/errors_spec.rb +0 -18
  73. data/spec/dmn/feel/functions_spec.rb +0 -400
  74. data/spec/dmn/feel/simple_parser_spec.rb +0 -274
  75. data/spec/dmn/feel/types_spec.rb +0 -176
  76. data/spec/dmn/feel_parser_spec.rb +0 -489
  77. data/spec/dmn/hit_policy_spec.rb +0 -202
  78. data/spec/dmn/integration_spec.rb +0 -226
  79. data/spec/dsl/condition_evaluator_spec.rb +0 -774
  80. data/spec/dsl_validation_spec.rb +0 -648
  81. data/spec/edge_cases_spec.rb +0 -353
  82. data/spec/evaluation_spec.rb +0 -364
  83. data/spec/evaluation_validator_spec.rb +0 -165
  84. data/spec/examples/feedback_aware_evaluator_spec.rb +0 -460
  85. data/spec/examples.txt +0 -1909
  86. data/spec/fixtures/dmn/complex_decision.dmn +0 -81
  87. data/spec/fixtures/dmn/invalid_structure.dmn +0 -31
  88. data/spec/fixtures/dmn/simple_decision.dmn +0 -40
  89. data/spec/issue_verification_spec.rb +0 -759
  90. data/spec/json_rule_evaluator_spec.rb +0 -587
  91. data/spec/monitoring/alert_manager_spec.rb +0 -378
  92. data/spec/monitoring/metrics_collector_spec.rb +0 -501
  93. data/spec/monitoring/monitored_agent_spec.rb +0 -225
  94. data/spec/monitoring/prometheus_exporter_spec.rb +0 -242
  95. data/spec/monitoring/storage/activerecord_adapter_spec.rb +0 -498
  96. data/spec/monitoring/storage/base_adapter_spec.rb +0 -61
  97. data/spec/monitoring/storage/memory_adapter_spec.rb +0 -247
  98. data/spec/performance_optimizations_spec.rb +0 -493
  99. data/spec/replay_edge_cases_spec.rb +0 -699
  100. data/spec/replay_spec.rb +0 -210
  101. data/spec/rfc8785_canonicalization_spec.rb +0 -215
  102. data/spec/scoring_spec.rb +0 -225
  103. data/spec/spec_helper.rb +0 -60
  104. data/spec/testing/batch_test_importer_spec.rb +0 -693
  105. data/spec/testing/batch_test_runner_spec.rb +0 -307
  106. data/spec/testing/test_coverage_analyzer_spec.rb +0 -292
  107. data/spec/testing/test_result_comparator_spec.rb +0 -392
  108. data/spec/testing/test_scenario_spec.rb +0 -113
  109. data/spec/thread_safety_spec.rb +0 -490
  110. data/spec/thread_safety_spec.rb.broken +0 -878
  111. data/spec/versioning/adapter_spec.rb +0 -156
  112. data/spec/versioning_spec.rb +0 -1030
  113. data/spec/web/middleware/auth_middleware_spec.rb +0 -133
  114. data/spec/web/middleware/permission_middleware_spec.rb +0 -247
  115. data/spec/web_ui_rack_spec.rb +0 -2134
@@ -1,282 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "spec_helper"
4
- require "decision_agent/dmn/decision_graph"
5
-
6
- RSpec.describe DecisionAgent::Dmn::DecisionGraph do
7
- describe "graph construction" do
8
- let(:graph) do
9
- described_class.new(id: "graph1", name: "Test Graph")
10
- end
11
-
12
- it "creates an empty graph" do
13
- expect(graph.decisions).to be_empty
14
- end
15
-
16
- it "adds decisions to the graph" do
17
- decision = DecisionAgent::Dmn::DecisionNode.new(
18
- id: "decision1",
19
- name: "First Decision"
20
- )
21
- graph.add_decision(decision)
22
-
23
- expect(graph.decisions["decision1"]).to eq(decision)
24
- end
25
-
26
- it "retrieves decisions by id" do
27
- decision = DecisionAgent::Dmn::DecisionNode.new(id: "decision1", name: "Test")
28
- graph.add_decision(decision)
29
-
30
- retrieved = graph.get_decision("decision1")
31
- expect(retrieved).to eq(decision)
32
- end
33
- end
34
-
35
- describe "decision dependencies" do
36
- let(:graph) do
37
- graph = described_class.new(id: "dep_graph", name: "Dependency Graph")
38
-
39
- # Create decisions
40
- decision1 = DecisionAgent::Dmn::DecisionNode.new(
41
- id: "base_rate",
42
- name: "Base Rate",
43
- decision_logic: 0.05
44
- )
45
-
46
- decision2 = DecisionAgent::Dmn::DecisionNode.new(
47
- id: "risk_adjustment",
48
- name: "Risk Adjustment",
49
- decision_logic: 0.02
50
- )
51
-
52
- decision3 = DecisionAgent::Dmn::DecisionNode.new(
53
- id: "final_rate",
54
- name: "Final Rate",
55
- decision_logic: ->(context) { context["base_rate"] + context["risk_adjustment"] }
56
- )
57
-
58
- # Add dependencies
59
- decision3.add_dependency("base_rate")
60
- decision3.add_dependency("risk_adjustment")
61
-
62
- graph.add_decision(decision1)
63
- graph.add_decision(decision2)
64
- graph.add_decision(decision3)
65
-
66
- graph
67
- end
68
-
69
- it "tracks decision dependencies" do
70
- decision = graph.get_decision("final_rate")
71
- expect(decision.information_requirements.length).to eq(2)
72
- expect(decision.depends_on?("base_rate")).to be true
73
- expect(decision.depends_on?("risk_adjustment")).to be true
74
- end
75
-
76
- it "evaluates decision with dependencies" do
77
- result = graph.evaluate("final_rate", {})
78
- expect(result).to eq(0.07) # 0.05 + 0.02
79
- end
80
- end
81
-
82
- describe "topological ordering" do
83
- let(:graph) do
84
- graph = described_class.new(id: "topo_graph", name: "Topological Graph")
85
-
86
- # Create a dependency chain: A -> B -> C
87
- decision_a = DecisionAgent::Dmn::DecisionNode.new(id: "a", name: "A", decision_logic: 1)
88
- decision_b = DecisionAgent::Dmn::DecisionNode.new(id: "b", name: "B", decision_logic: ->(ctx) { ctx["a"] + 1 })
89
- decision_c = DecisionAgent::Dmn::DecisionNode.new(id: "c", name: "C", decision_logic: ->(ctx) { ctx["b"] + 1 })
90
-
91
- decision_b.add_dependency("a")
92
- decision_c.add_dependency("b")
93
-
94
- graph.add_decision(decision_a)
95
- graph.add_decision(decision_b)
96
- graph.add_decision(decision_c)
97
-
98
- graph
99
- end
100
-
101
- it "returns decisions in topological order" do
102
- order = graph.topological_order
103
- expect(order.index("a")).to be < order.index("b")
104
- expect(order.index("b")).to be < order.index("c")
105
- end
106
-
107
- it "identifies root decisions" do
108
- roots = graph.root_decisions
109
- expect(roots).to eq(["a"])
110
- end
111
-
112
- it "identifies leaf decisions" do
113
- leaves = graph.leaf_decisions
114
- expect(leaves).to eq(["c"])
115
- end
116
- end
117
-
118
- describe "circular dependency detection" do
119
- it "detects circular dependencies" do
120
- graph = described_class.new(id: "circular", name: "Circular Graph")
121
-
122
- decision_a = DecisionAgent::Dmn::DecisionNode.new(id: "a", name: "A")
123
- decision_b = DecisionAgent::Dmn::DecisionNode.new(id: "b", name: "B")
124
-
125
- decision_a.add_dependency("b")
126
- decision_b.add_dependency("a")
127
-
128
- graph.add_decision(decision_a)
129
- graph.add_decision(decision_b)
130
-
131
- expect(graph.circular_dependencies?).to be true
132
- end
133
-
134
- it "raises error when evaluating circular dependencies" do
135
- graph = described_class.new(id: "circular", name: "Circular Graph")
136
-
137
- decision_a = DecisionAgent::Dmn::DecisionNode.new(id: "a", name: "A")
138
- decision_b = DecisionAgent::Dmn::DecisionNode.new(id: "b", name: "B")
139
-
140
- decision_a.add_dependency("b")
141
- decision_b.add_dependency("a")
142
-
143
- graph.add_decision(decision_a)
144
- graph.add_decision(decision_b)
145
-
146
- expect { graph.topological_order }.to raise_error(DecisionAgent::Dmn::DmnError, /Circular dependency/)
147
- end
148
- end
149
-
150
- describe "complex graph evaluation" do
151
- let(:graph) do
152
- # Build a loan approval graph
153
- # Decisions: income_check -> credit_check -> final_decision
154
- graph = described_class.new(id: "loan_graph", name: "Loan Approval Graph")
155
-
156
- income_check = DecisionAgent::Dmn::DecisionNode.new(
157
- id: "income_check",
158
- name: "Income Check",
159
- decision_logic: ->(ctx) { ctx["income"] >= 50_000 ? "sufficient" : "insufficient" }
160
- )
161
-
162
- credit_check = DecisionAgent::Dmn::DecisionNode.new(
163
- id: "credit_check",
164
- name: "Credit Check",
165
- decision_logic: ->(ctx) { ctx["credit_score"] >= 650 ? "good" : "poor" }
166
- )
167
-
168
- final_decision = DecisionAgent::Dmn::DecisionNode.new(
169
- id: "final_decision",
170
- name: "Final Decision",
171
- decision_logic: lambda do |ctx|
172
- if ctx["income_check"] == "sufficient" && ctx["credit_check"] == "good"
173
- "Approved"
174
- else
175
- "Rejected"
176
- end
177
- end
178
- )
179
-
180
- final_decision.add_dependency("income_check", "income_check")
181
- final_decision.add_dependency("credit_check", "credit_check")
182
-
183
- graph.add_decision(income_check)
184
- graph.add_decision(credit_check)
185
- graph.add_decision(final_decision)
186
-
187
- graph
188
- end
189
-
190
- it "evaluates graph with all dependencies for approved case" do
191
- context = { income: 60_000, credit_score: 700 }
192
- result = graph.evaluate("final_decision", context)
193
- expect(result).to eq("Approved")
194
- end
195
-
196
- it "evaluates graph with all dependencies for rejected case" do
197
- context = { income: 40_000, credit_score: 600 }
198
- result = graph.evaluate("final_decision", context)
199
- expect(result).to eq("Rejected")
200
- end
201
-
202
- it "evaluates all decisions in graph" do
203
- context = { income: 60_000, credit_score: 700 }
204
- results = graph.evaluate_all(context)
205
-
206
- expect(results["income_check"]).to eq("sufficient")
207
- expect(results["credit_check"]).to eq("good")
208
- expect(results["final_decision"]).to eq("Approved")
209
- end
210
- end
211
-
212
- describe "graph analysis" do
213
- let(:graph) do
214
- graph = described_class.new(id: "analysis", name: "Analysis Graph")
215
-
216
- d1 = DecisionAgent::Dmn::DecisionNode.new(id: "d1", name: "D1", decision_logic: 1)
217
- d2 = DecisionAgent::Dmn::DecisionNode.new(id: "d2", name: "D2", decision_logic: 2)
218
- d3 = DecisionAgent::Dmn::DecisionNode.new(id: "d3", name: "D3", decision_logic: 3)
219
-
220
- d3.add_dependency("d1")
221
- d3.add_dependency("d2")
222
-
223
- graph.add_decision(d1)
224
- graph.add_decision(d2)
225
- graph.add_decision(d3)
226
-
227
- graph
228
- end
229
-
230
- it "exports dependency graph structure" do
231
- dep_graph = graph.dependency_graph
232
- expect(dep_graph["d1"]).to eq([])
233
- expect(dep_graph["d2"]).to eq([])
234
- expect(dep_graph["d3"]).to contain_exactly("d1", "d2")
235
- end
236
-
237
- it "exports graph to hash representation" do
238
- hash = graph.to_h
239
- expect(hash[:id]).to eq("analysis")
240
- expect(hash[:name]).to eq("Analysis Graph")
241
- expect(hash[:decisions].keys).to contain_exactly("d1", "d2", "d3")
242
- expect(hash[:dependency_graph]).to be_a(Hash)
243
- end
244
- end
245
-
246
- describe DecisionAgent::Dmn::DecisionNode do
247
- it "creates decision node with basic attributes" do
248
- node = described_class.new(id: "test", name: "Test Decision")
249
- expect(node.id).to eq("test")
250
- expect(node.name).to eq("Test Decision")
251
- expect(node.information_requirements).to be_empty
252
- end
253
-
254
- it "adds dependencies" do
255
- node = described_class.new(id: "test", name: "Test")
256
- node.add_dependency("dep1", "variable1")
257
-
258
- expect(node.information_requirements.length).to eq(1)
259
- expect(node.information_requirements.first[:decision_id]).to eq("dep1")
260
- expect(node.information_requirements.first[:variable_name]).to eq("variable1")
261
- end
262
-
263
- it "checks if node depends on another decision" do
264
- node = described_class.new(id: "test", name: "Test")
265
- node.add_dependency("dep1")
266
-
267
- expect(node.depends_on?("dep1")).to be true
268
- expect(node.depends_on?("dep2")).to be false
269
- end
270
-
271
- it "resets evaluation state" do
272
- node = described_class.new(id: "test", name: "Test")
273
- node.value = "some value"
274
- node.evaluated = true
275
-
276
- node.reset!
277
-
278
- expect(node.value).to be_nil
279
- expect(node.evaluated).to be false
280
- end
281
- end
282
- end
@@ -1,203 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "spec_helper"
4
- require "decision_agent/dmn/decision_tree"
5
-
6
- RSpec.describe DecisionAgent::Dmn::DecisionTree do
7
- describe "basic tree structure" do
8
- let(:tree) do
9
- described_class.new(id: "tree1", name: "Loan Approval Tree")
10
- end
11
-
12
- it "creates a tree with root node" do
13
- expect(tree.root).to be_a(DecisionAgent::Dmn::TreeNode)
14
- expect(tree.root.id).to eq("root")
15
- end
16
-
17
- it "allows adding children to nodes" do
18
- child1 = DecisionAgent::Dmn::TreeNode.new(id: "child1", label: "Check Age")
19
- tree.root.add_child(child1)
20
-
21
- expect(tree.root.children).to include(child1)
22
- expect(child1.parent).to eq(tree.root)
23
- end
24
- end
25
-
26
- describe "tree evaluation" do
27
- let(:tree) do
28
- # Build a simple decision tree for loan approval
29
- tree = described_class.new(id: "loan_tree", name: "Loan Approval")
30
-
31
- # Root node
32
- root = tree.root
33
-
34
- # First level - check age
35
- age_check = DecisionAgent::Dmn::TreeNode.new(
36
- id: "age_check",
37
- label: "Age >= 18?",
38
- condition: "age >= 18"
39
- )
40
- root.add_child(age_check)
41
-
42
- # Second level under age check - check credit score
43
- good_credit = DecisionAgent::Dmn::TreeNode.new(
44
- id: "good_credit",
45
- label: "Credit Score >= 650?",
46
- condition: "credit_score >= 650"
47
- )
48
- age_check.add_child(good_credit)
49
-
50
- # Leaf nodes - decisions
51
- approved = DecisionAgent::Dmn::TreeNode.new(
52
- id: "approved",
53
- label: "Approved",
54
- decision: "Approved"
55
- )
56
- good_credit.add_child(approved)
57
-
58
- rejected_credit = DecisionAgent::Dmn::TreeNode.new(
59
- id: "rejected_credit",
60
- label: "Rejected - Poor Credit",
61
- decision: "Rejected - Poor Credit"
62
- )
63
- good_credit.add_child(rejected_credit)
64
-
65
- # Rejected for age
66
- rejected_age = DecisionAgent::Dmn::TreeNode.new(
67
- id: "rejected_age",
68
- label: "Rejected - Too Young",
69
- decision: "Rejected - Too Young"
70
- )
71
- root.add_child(rejected_age)
72
-
73
- tree
74
- end
75
-
76
- it "evaluates tree with context matching approved path" do
77
- context = { age: 25, credit_score: 700 }
78
- result = tree.evaluate(context)
79
- expect(result).to eq("Approved")
80
- end
81
-
82
- it "evaluates tree with poor credit" do
83
- context = { age: 25, credit_score: 600 }
84
- result = tree.evaluate(context)
85
- expect(result).to eq("Rejected - Poor Credit")
86
- end
87
-
88
- it "evaluates tree with age too young" do
89
- context = { age: 16, credit_score: 700 }
90
- result = tree.evaluate(context)
91
- expect(result).to eq("Rejected - Too Young")
92
- end
93
-
94
- it "returns nil when no path matches" do
95
- context = {}
96
- result = tree.evaluate(context)
97
- expect(result).to be_nil
98
- end
99
- end
100
-
101
- describe "tree serialization" do
102
- let(:tree_hash) do
103
- {
104
- id: "test_tree",
105
- name: "Test Tree",
106
- root: {
107
- id: "root",
108
- label: "Root",
109
- condition: nil,
110
- decision: nil,
111
- children: [
112
- {
113
- id: "node1",
114
- label: "Node 1",
115
- condition: "x > 5",
116
- decision: nil,
117
- children: [
118
- {
119
- id: "leaf1",
120
- label: "Leaf 1",
121
- condition: nil,
122
- decision: "Result A",
123
- children: []
124
- }
125
- ]
126
- }
127
- ]
128
- }
129
- }
130
- end
131
-
132
- it "converts tree to hash" do
133
- tree = described_class.new(id: "tree1", name: "Tree 1")
134
- node1 = DecisionAgent::Dmn::TreeNode.new(id: "node1", condition: "x > 5")
135
- tree.root.add_child(node1)
136
-
137
- hash = tree.to_h
138
- expect(hash[:id]).to eq("tree1")
139
- expect(hash[:name]).to eq("Tree 1")
140
- expect(hash[:root][:children].length).to eq(1)
141
- end
142
-
143
- it "builds tree from hash" do
144
- tree = described_class.from_hash(tree_hash)
145
-
146
- expect(tree.id).to eq("test_tree")
147
- expect(tree.name).to eq("Test Tree")
148
- expect(tree.root.children.length).to eq(1)
149
- expect(tree.root.children.first.id).to eq("node1")
150
- end
151
- end
152
-
153
- describe "tree analysis" do
154
- let(:tree) do
155
- tree = described_class.new(id: "analysis_tree", name: "Analysis Tree")
156
-
157
- level1 = DecisionAgent::Dmn::TreeNode.new(id: "level1")
158
- tree.root.add_child(level1)
159
-
160
- level2a = DecisionAgent::Dmn::TreeNode.new(id: "level2a")
161
- level2b = DecisionAgent::Dmn::TreeNode.new(id: "level2b")
162
- level1.add_child(level2a)
163
- level1.add_child(level2b)
164
-
165
- leaf1 = DecisionAgent::Dmn::TreeNode.new(id: "leaf1", decision: "Result 1")
166
- leaf2 = DecisionAgent::Dmn::TreeNode.new(id: "leaf2", decision: "Result 2")
167
- level2a.add_child(leaf1)
168
- level2b.add_child(leaf2)
169
-
170
- tree
171
- end
172
-
173
- it "collects all leaf nodes" do
174
- leaves = tree.leaf_nodes
175
- expect(leaves.length).to eq(2)
176
- expect(leaves.map(&:id)).to include("leaf1", "leaf2")
177
- end
178
-
179
- it "calculates tree depth" do
180
- expect(tree.depth).to eq(3) # root -> level1 -> level2 -> leaf (depth 3)
181
- end
182
-
183
- it "collects all paths from root to leaves" do
184
- paths = tree.paths
185
- expect(paths.length).to eq(2)
186
- expect(paths.first.length).to be >= 3
187
- end
188
- end
189
-
190
- describe DecisionAgent::Dmn::TreeNode do
191
- it "identifies leaf nodes correctly" do
192
- node = DecisionAgent::Dmn::TreeNode.new(id: "test", decision: "Decision")
193
- expect(node.leaf?).to be true
194
- end
195
-
196
- it "identifies non-leaf nodes correctly" do
197
- node = DecisionAgent::Dmn::TreeNode.new(id: "test")
198
- child = DecisionAgent::Dmn::TreeNode.new(id: "child")
199
- node.add_child(child)
200
- expect(node.leaf?).to be false
201
- end
202
- end
203
- end
@@ -1,18 +0,0 @@
1
- require "spec_helper"
2
- require "decision_agent/dmn/errors"
3
-
4
- RSpec.describe "FEEL Errors" do
5
- describe DecisionAgent::Dmn::FeelParseError do
6
- it "creates error with message only" do
7
- error = DecisionAgent::Dmn::FeelParseError.new("Parse failed")
8
- expect(error.message).to eq("Parse failed")
9
- end
10
- end
11
-
12
- describe DecisionAgent::Dmn::FeelEvaluationError do
13
- it "creates error with message only" do
14
- error = DecisionAgent::Dmn::FeelEvaluationError.new("Evaluation failed")
15
- expect(error.message).to eq("Evaluation failed")
16
- end
17
- end
18
- end