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,778 +0,0 @@
1
- require "spec_helper"
2
- require_relative "../../lib/decision_agent/auth/rbac_adapter"
3
- require_relative "../../lib/decision_agent/auth/user"
4
- require_relative "../../lib/decision_agent/auth/role"
5
-
6
- RSpec.describe DecisionAgent::Auth::RbacAdapter do
7
- describe "base class" do
8
- let(:adapter) { described_class.new }
9
- let(:user) { double("User") }
10
-
11
- describe "#can?" do
12
- it "raises NotImplementedError" do
13
- expect { adapter.can?(user, :read) }.to raise_error(NotImplementedError, /must implement #can\?/)
14
- end
15
- end
16
-
17
- describe "#has_role?" do
18
- it "raises NotImplementedError" do
19
- expect { adapter.has_role?(user, :admin) }.to raise_error(NotImplementedError, /must implement #has_role\?/)
20
- end
21
- end
22
-
23
- describe "#active?" do
24
- it "returns false for nil user" do
25
- expect(adapter.active?(nil)).to be false
26
- end
27
-
28
- it "returns true for user with active? method returning true" do
29
- user = double("User", active?: true)
30
- expect(adapter.active?(user)).to be true
31
- end
32
-
33
- it "returns false for user with active? method returning false" do
34
- user = double("User", active?: false)
35
- expect(adapter.active?(user)).to be false
36
- end
37
-
38
- it "returns true for user without active? method" do
39
- user = double("User")
40
- expect(adapter.active?(user)).to be true
41
- end
42
- end
43
-
44
- describe "#user_id" do
45
- it "returns nil for nil user" do
46
- expect(adapter.user_id(nil)).to be_nil
47
- end
48
-
49
- it "returns user.id when user responds to id" do
50
- user = double("User", id: "user123")
51
- expect(adapter.user_id(user)).to eq("user123")
52
- end
53
-
54
- it "returns user.to_s when user doesn't respond to id" do
55
- user = double("User", to_s: "user_string")
56
- expect(adapter.user_id(user)).to eq("user_string")
57
- end
58
- end
59
-
60
- describe "#user_email" do
61
- it "returns nil for nil user" do
62
- expect(adapter.user_email(nil)).to be_nil
63
- end
64
-
65
- it "returns user.email when user responds to email" do
66
- user = double("User", email: "user@example.com")
67
- expect(adapter.user_email(user)).to eq("user@example.com")
68
- end
69
-
70
- it "returns nil when user doesn't respond to email" do
71
- user = double("User")
72
- expect(adapter.user_email(user)).to be_nil
73
- end
74
- end
75
- end
76
-
77
- describe DecisionAgent::Auth::DefaultAdapter do
78
- let(:adapter) { described_class.new }
79
-
80
- describe "#can?" do
81
- it "returns false for nil user" do
82
- expect(adapter.can?(nil, :read)).to be false
83
- end
84
-
85
- it "returns false for inactive user" do
86
- user = double("User", active: false, roles: [])
87
- expect(adapter.can?(user, :read)).to be false
88
- end
89
-
90
- it "returns true when user has role with permission" do
91
- user = DecisionAgent::Auth::User.new(
92
- id: "user1",
93
- email: "user@example.com",
94
- password: "password123"
95
- )
96
- user.assign_role(:admin)
97
-
98
- expect(adapter.can?(user, :read)).to be true
99
- expect(adapter.can?(user, :write)).to be true
100
- expect(adapter.can?(user, :manage_users)).to be true
101
- end
102
-
103
- it "returns false when user doesn't have role with permission" do
104
- user = DecisionAgent::Auth::User.new(
105
- id: "user1",
106
- email: "user@example.com",
107
- password: "password123"
108
- )
109
- user.assign_role(:guest)
110
-
111
- expect(adapter.can?(user, :manage_users)).to be false
112
- end
113
-
114
- it "returns true when user has multiple roles and one has permission" do
115
- user = DecisionAgent::Auth::User.new(
116
- id: "user1",
117
- email: "user@example.com",
118
- password: "password123"
119
- )
120
- user.assign_role(:guest)
121
- user.assign_role(:editor)
122
-
123
- expect(adapter.can?(user, :write)).to be true
124
- end
125
- end
126
-
127
- describe "#has_role?" do
128
- it "returns false for nil user" do
129
- expect(adapter.has_role?(nil, :admin)).to be false
130
- end
131
-
132
- it "returns true when user has role" do
133
- user = DecisionAgent::Auth::User.new(
134
- id: "user1",
135
- email: "user@example.com",
136
- password: "password123"
137
- )
138
- user.assign_role(:admin)
139
-
140
- expect(adapter.has_role?(user, :admin)).to be true
141
- expect(adapter.has_role?(user, :guest)).to be false
142
- end
143
-
144
- it "handles string role names" do
145
- user = DecisionAgent::Auth::User.new(
146
- id: "user1",
147
- email: "user@example.com",
148
- password: "password123"
149
- )
150
- user.assign_role(:admin)
151
-
152
- expect(adapter.has_role?(user, "admin")).to be true
153
- end
154
- end
155
-
156
- describe "#active?" do
157
- it "returns false for nil user" do
158
- expect(adapter.active?(nil)).to be false
159
- end
160
-
161
- it "returns user.active when user responds to active" do
162
- user = double("User", active: true)
163
- expect(adapter.active?(user)).to be true
164
-
165
- user = double("User", active: false)
166
- expect(adapter.active?(user)).to be false
167
- end
168
-
169
- it "returns true when user doesn't respond to active" do
170
- user = double("User")
171
- expect(adapter.active?(user)).to be true
172
- end
173
- end
174
-
175
- describe "#extract_roles" do
176
- it "extracts roles from user.roles" do
177
- user = double("User", roles: %i[admin editor])
178
- roles = adapter.send(:extract_roles, user)
179
- expect(roles).to eq(%i[admin editor])
180
- end
181
-
182
- it "extracts role from user.role (singular)" do
183
- user = double("User", role: :admin)
184
- roles = adapter.send(:extract_roles, user)
185
- expect(roles).to eq([:admin])
186
- end
187
-
188
- it "returns empty array when user has no roles" do
189
- user = double("User")
190
- roles = adapter.send(:extract_roles, user)
191
- expect(roles).to eq([])
192
- end
193
-
194
- it "handles array of roles" do
195
- user = double("User", roles: %w[admin editor])
196
- roles = adapter.send(:extract_roles, user)
197
- expect(roles).to eq(%i[admin editor])
198
- end
199
- end
200
- end
201
-
202
- describe DecisionAgent::Auth::DeviseCanCanAdapter do
203
- let(:adapter) { described_class.new }
204
-
205
- describe "#initialize" do
206
- it "initializes without ability_class" do
207
- adapter = described_class.new
208
- expect(adapter.instance_variable_get(:@ability_class)).to be_nil
209
- end
210
-
211
- it "initializes with ability_class" do
212
- ability_class = Class.new
213
- adapter = described_class.new(ability_class: ability_class)
214
- expect(adapter.instance_variable_get(:@ability_class)).to eq(ability_class)
215
- end
216
- end
217
-
218
- describe "#can?" do
219
- it "returns false for nil user" do
220
- expect(adapter.can?(nil, :read)).to be false
221
- end
222
-
223
- it "returns false for inactive user" do
224
- user = double("User", active_for_authentication?: false)
225
- expect(adapter.can?(user, :read)).to be false
226
- end
227
-
228
- it "uses user.can? when available" do
229
- user = double("User", active_for_authentication?: true)
230
- allow(user).to receive(:can?).with(:read, Object).and_return(true)
231
-
232
- expect(adapter.can?(user, :read)).to be true
233
- expect(user).to have_received(:can?).with(:read, Object)
234
- end
235
-
236
- it "uses ability_class when provided" do
237
- ability_instance = double("Ability", can?: true)
238
- ability_class = double("AbilityClass", new: ability_instance)
239
- adapter = described_class.new(ability_class: ability_class)
240
- user = double("User", active_for_authentication?: true)
241
-
242
- expect(adapter.can?(user, :read)).to be true
243
- expect(ability_class).to have_received(:new).with(user)
244
- end
245
-
246
- it "returns false when neither user.can? nor ability_class available" do
247
- user = double("User", active_for_authentication?: true)
248
- expect(adapter.can?(user, :read)).to be false
249
- end
250
-
251
- it "maps permissions to CanCanCan actions" do
252
- user = double("User", active_for_authentication?: true)
253
- allow(user).to receive(:can?).and_return(true)
254
-
255
- adapter.can?(user, :read)
256
- expect(user).to have_received(:can?).with(:read, Object)
257
-
258
- adapter.can?(user, :write)
259
- expect(user).to have_received(:can?).with(:create, Object)
260
-
261
- adapter.can?(user, :delete)
262
- expect(user).to have_received(:can?).with(:destroy, Object)
263
- end
264
- end
265
-
266
- describe "#has_role?" do
267
- it "returns false for nil user" do
268
- expect(adapter.has_role?(nil, :admin)).to be false
269
- end
270
-
271
- it "returns false for inactive user" do
272
- user = double("User", active_for_authentication?: false)
273
- expect(adapter.has_role?(user, :admin)).to be false
274
- end
275
-
276
- it "uses user.has_role? when available" do
277
- user = double("User", active_for_authentication?: true, has_role?: true)
278
- expect(adapter.has_role?(user, :admin)).to be true
279
- end
280
-
281
- it "checks user.roles when has_role? not available" do
282
- role = double("Role", name: :admin)
283
- user = double("User", active_for_authentication?: true, roles: [role])
284
- expect(adapter.has_role?(user, :admin)).to be true
285
- end
286
-
287
- it "returns false when no role methods available" do
288
- user = double("User", active_for_authentication?: true)
289
- expect(adapter.has_role?(user, :admin)).to be false
290
- end
291
- end
292
-
293
- describe "#active?" do
294
- it "uses active_for_authentication? when available" do
295
- user = double("User", active_for_authentication?: true)
296
- expect(adapter.active?(user)).to be true
297
- end
298
-
299
- it "falls back to active? when active_for_authentication? not available" do
300
- user = double("User", active?: true)
301
- expect(adapter.active?(user)).to be true
302
- end
303
-
304
- it "returns true when neither method available" do
305
- user = double("User")
306
- expect(adapter.active?(user)).to be true
307
- end
308
- end
309
-
310
- describe "#map_permission_to_action" do
311
- it "maps known permissions" do
312
- mapping = adapter.send(:map_permission_to_action, :read)
313
- expect(mapping).to eq(:read)
314
-
315
- mapping = adapter.send(:map_permission_to_action, :write)
316
- expect(mapping).to eq(:create)
317
-
318
- mapping = adapter.send(:map_permission_to_action, :delete)
319
- expect(mapping).to eq(:destroy)
320
-
321
- mapping = adapter.send(:map_permission_to_action, :manage_users)
322
- expect(mapping).to eq(:manage)
323
- end
324
-
325
- it "returns permission as-is for unknown permissions" do
326
- mapping = adapter.send(:map_permission_to_action, :custom_permission)
327
- expect(mapping).to eq(:custom_permission)
328
- end
329
- end
330
- end
331
-
332
- describe DecisionAgent::Auth::PunditAdapter do
333
- let(:adapter) { described_class.new }
334
-
335
- describe "#can?" do
336
- it "returns false for nil user" do
337
- expect(adapter.can?(nil, :read)).to be false
338
- end
339
-
340
- it "returns false for inactive user" do
341
- user = double("User", active?: false)
342
- expect(adapter.can?(user, :read)).to be false
343
- end
344
-
345
- it "returns false when no resource provided" do
346
- user = double("User", active?: true)
347
- expect(adapter.can?(user, :read)).to be false
348
- end
349
-
350
- it "uses resource.policy_class when available" do
351
- policy = double("Policy", show: true)
352
- policy_class = double("PolicyClass", new: policy)
353
- resource = double("Resource", policy_class: policy_class)
354
- user = double("User", active?: true)
355
-
356
- expect(adapter.can?(user, :read, resource)).to be true
357
- expect(policy_class).to have_received(:new).with(user, resource)
358
- end
359
-
360
- it "infers policy class from resource class name" do
361
- policy = double("Policy", show: true)
362
- policy_class = double("PolicyClass")
363
- allow(policy_class).to receive(:new).with(anything, anything).and_return(policy)
364
- allow(Object).to receive(:const_defined?).with("TestResourcePolicy").and_return(true)
365
- allow(Object).to receive(:const_get).with("TestResourcePolicy").and_return(policy_class)
366
-
367
- resource = double("TestResource", class: double(name: "TestResource"))
368
- user = double("User", active?: true)
369
-
370
- expect(adapter.can?(user, :read, resource)).to be true
371
- end
372
-
373
- it "returns false when policy class doesn't exist" do
374
- resource = double("TestResource", class: double(name: "TestResource"))
375
- user = double("User", active?: true)
376
-
377
- allow(Object).to receive(:const_defined?).with("TestResourcePolicy").and_return(false)
378
-
379
- expect(adapter.can?(user, :read, resource)).to be false
380
- end
381
- end
382
-
383
- describe "#has_role?" do
384
- it "returns false for nil user" do
385
- expect(adapter.has_role?(nil, :admin)).to be false
386
- end
387
-
388
- it "returns false for inactive user" do
389
- user = double("User", active?: false)
390
- expect(adapter.has_role?(user, :admin)).to be false
391
- end
392
-
393
- it "uses user.has_role? when available" do
394
- user = double("User", active?: true, has_role?: true)
395
- expect(adapter.has_role?(user, :admin)).to be true
396
- end
397
-
398
- it "checks user.roles when has_role? not available" do
399
- role = double("Role", name: :admin, to_s: "admin")
400
- user = double("User", active?: true, roles: [role])
401
- expect(adapter.has_role?(user, :admin)).to be true
402
- end
403
- end
404
-
405
- describe "#map_permission_to_action" do
406
- it "maps known permissions" do
407
- mapping = adapter.send(:map_permission_to_action, :read)
408
- expect(mapping).to eq(:show)
409
-
410
- mapping = adapter.send(:map_permission_to_action, :write)
411
- expect(mapping).to eq(:create)
412
-
413
- mapping = adapter.send(:map_permission_to_action, :delete)
414
- expect(mapping).to eq(:destroy)
415
- end
416
- end
417
- end
418
-
419
- describe DecisionAgent::Auth::CustomAdapter do
420
- describe "#initialize" do
421
- it "initializes with procs" do
422
- can_proc = ->(_u, _p, _r) { true }
423
- has_role_proc = ->(_u, _r) { true }
424
- active_proc = ->(_u) { true }
425
- user_id_proc = ->(_u) { "id" }
426
- user_email_proc = ->(_u) { "email" }
427
-
428
- adapter = described_class.new(
429
- can_proc: can_proc,
430
- has_role_proc: has_role_proc,
431
- active_proc: active_proc,
432
- user_id_proc: user_id_proc,
433
- user_email_proc: user_email_proc
434
- )
435
-
436
- expect(adapter.instance_variable_get(:@can_proc)).to eq(can_proc)
437
- expect(adapter.instance_variable_get(:@has_role_proc)).to eq(has_role_proc)
438
- expect(adapter.instance_variable_get(:@active_proc)).to eq(active_proc)
439
- expect(adapter.instance_variable_get(:@user_id_proc)).to eq(user_id_proc)
440
- expect(adapter.instance_variable_get(:@user_email_proc)).to eq(user_email_proc)
441
- end
442
- end
443
-
444
- describe "#can?" do
445
- it "returns false for nil user" do
446
- adapter = described_class.new(can_proc: ->(_u, _p, _r) { true })
447
- expect(adapter.can?(nil, :read)).to be false
448
- end
449
-
450
- it "returns false for inactive user" do
451
- adapter = described_class.new(
452
- can_proc: ->(_u, _p, _r) { true },
453
- active_proc: ->(_u) { false }
454
- )
455
- user = double("User")
456
- expect(adapter.can?(user, :read)).to be false
457
- end
458
-
459
- it "calls can_proc when provided" do
460
- can_proc = ->(_u, p, _r) { p == :read }
461
- adapter = described_class.new(can_proc: can_proc, active_proc: ->(_u) { true })
462
- user = double("User")
463
-
464
- expect(adapter.can?(user, :read)).to be true
465
- expect(adapter.can?(user, :write)).to be false
466
- end
467
-
468
- it "raises error when can_proc not provided" do
469
- adapter = described_class.new(active_proc: ->(_u) { true })
470
- user = double("User")
471
-
472
- expect { adapter.can?(user, :read) }.to raise_error(NotImplementedError, /requires can_proc/)
473
- end
474
- end
475
-
476
- describe "#has_role?" do
477
- it "returns false for nil user" do
478
- adapter = described_class.new(has_role_proc: ->(_u, _r) { true })
479
- expect(adapter.has_role?(nil, :admin)).to be false
480
- end
481
-
482
- it "calls has_role_proc when provided" do
483
- has_role_proc = ->(_u, r) { r == :admin }
484
- adapter = described_class.new(has_role_proc: has_role_proc)
485
- user = double("User")
486
-
487
- expect(adapter.has_role?(user, :admin)).to be true
488
- expect(adapter.has_role?(user, :guest)).to be false
489
- end
490
-
491
- it "raises error when has_role_proc not provided" do
492
- adapter = described_class.new
493
- user = double("User")
494
-
495
- expect { adapter.has_role?(user, :admin) }.to raise_error(NotImplementedError, /requires has_role_proc/)
496
- end
497
- end
498
-
499
- describe "#active?" do
500
- it "calls active_proc when provided" do
501
- active_proc = ->(u) { u == "active_user" }
502
- adapter = described_class.new(active_proc: active_proc)
503
-
504
- expect(adapter.active?("active_user")).to be true
505
- expect(adapter.active?("inactive_user")).to be false
506
- end
507
-
508
- it "falls back to super when active_proc not provided" do
509
- adapter = described_class.new
510
- user = double("User", active?: true)
511
-
512
- expect(adapter.active?(user)).to be true
513
- end
514
- end
515
-
516
- describe "#user_id" do
517
- it "calls user_id_proc when provided" do
518
- user_id_proc = ->(_u) { "custom_id" }
519
- adapter = described_class.new(user_id_proc: user_id_proc)
520
- user = double("User")
521
-
522
- expect(adapter.user_id(user)).to eq("custom_id")
523
- end
524
-
525
- it "falls back to super when user_id_proc not provided" do
526
- adapter = described_class.new
527
- user = double("User", id: "user123")
528
-
529
- expect(adapter.user_id(user)).to eq("user123")
530
- end
531
- end
532
-
533
- describe "#user_email" do
534
- it "calls user_email_proc when provided" do
535
- user_email_proc = ->(_u) { "custom@example.com" }
536
- adapter = described_class.new(user_email_proc: user_email_proc)
537
- user = double("User")
538
-
539
- expect(adapter.user_email(user)).to eq("custom@example.com")
540
- end
541
-
542
- it "falls back to super when user_email_proc not provided" do
543
- adapter = described_class.new
544
- user = double("User", email: "user@example.com")
545
-
546
- expect(adapter.user_email(user)).to eq("user@example.com")
547
- end
548
- end
549
- end
550
-
551
- describe "Integration tests with real User and Role objects" do
552
- let(:adapter) { DecisionAgent::Auth::DefaultAdapter.new }
553
-
554
- describe "with real User and Role objects" do
555
- it "checks permissions using real Role class" do
556
- user = DecisionAgent::Auth::User.new(
557
- id: "user1",
558
- email: "admin@example.com",
559
- password: "password123",
560
- roles: [:admin]
561
- )
562
-
563
- expect(adapter.can?(user, :read)).to be true
564
- expect(adapter.can?(user, :write)).to be true
565
- expect(adapter.can?(user, :delete)).to be true
566
- expect(adapter.can?(user, :manage_users)).to be true
567
- expect(adapter.can?(user, :audit)).to be true
568
- end
569
-
570
- it "checks permissions for editor role" do
571
- user = DecisionAgent::Auth::User.new(
572
- id: "user2",
573
- email: "editor@example.com",
574
- password: "password123",
575
- roles: [:editor]
576
- )
577
-
578
- expect(adapter.can?(user, :read)).to be true
579
- expect(adapter.can?(user, :write)).to be true
580
- expect(adapter.can?(user, :delete)).to be false
581
- expect(adapter.can?(user, :manage_users)).to be false
582
- expect(adapter.can?(user, :audit)).to be false
583
- end
584
-
585
- it "checks permissions for viewer role" do
586
- user = DecisionAgent::Auth::User.new(
587
- id: "user3",
588
- email: "viewer@example.com",
589
- password: "password123",
590
- roles: [:viewer]
591
- )
592
-
593
- expect(adapter.can?(user, :read)).to be true
594
- expect(adapter.can?(user, :write)).to be false
595
- expect(adapter.can?(user, :delete)).to be false
596
- expect(adapter.can?(user, :manage_users)).to be false
597
- end
598
-
599
- it "checks permissions for approver role" do
600
- user = DecisionAgent::Auth::User.new(
601
- id: "user4",
602
- email: "approver@example.com",
603
- password: "password123",
604
- roles: [:approver]
605
- )
606
-
607
- expect(adapter.can?(user, :read)).to be true
608
- expect(adapter.can?(user, :approve)).to be true
609
- expect(adapter.can?(user, :write)).to be false
610
- expect(adapter.can?(user, :delete)).to be false
611
- end
612
-
613
- it "checks permissions for auditor role" do
614
- user = DecisionAgent::Auth::User.new(
615
- id: "user5",
616
- email: "auditor@example.com",
617
- password: "password123",
618
- roles: [:auditor]
619
- )
620
-
621
- expect(adapter.can?(user, :read)).to be true
622
- expect(adapter.can?(user, :audit)).to be true
623
- expect(adapter.can?(user, :write)).to be false
624
- expect(adapter.can?(user, :delete)).to be false
625
- end
626
-
627
- it "checks permissions for user with multiple roles" do
628
- user = DecisionAgent::Auth::User.new(
629
- id: "user6",
630
- email: "multi@example.com",
631
- password: "password123",
632
- roles: %i[viewer editor]
633
- )
634
-
635
- # Should have permissions from both roles
636
- expect(adapter.can?(user, :read)).to be true # from viewer
637
- expect(adapter.can?(user, :write)).to be true # from editor
638
- expect(adapter.can?(user, :delete)).to be false
639
- end
640
-
641
- it "checks role membership using real Role class" do
642
- admin_user = DecisionAgent::Auth::User.new(
643
- id: "admin1",
644
- email: "admin@example.com",
645
- password: "password123",
646
- roles: [:admin]
647
- )
648
-
649
- expect(adapter.has_role?(admin_user, :admin)).to be true
650
- expect(adapter.has_role?(admin_user, :editor)).to be false
651
- expect(adapter.has_role?(admin_user, :viewer)).to be false
652
-
653
- editor_user = DecisionAgent::Auth::User.new(
654
- id: "editor1",
655
- email: "editor@example.com",
656
- password: "password123",
657
- roles: [:editor]
658
- )
659
-
660
- expect(adapter.has_role?(editor_user, :editor)).to be true
661
- expect(adapter.has_role?(editor_user, :admin)).to be false
662
- end
663
-
664
- it "checks role membership for users with multiple roles" do
665
- user = DecisionAgent::Auth::User.new(
666
- id: "multi1",
667
- email: "multi@example.com",
668
- password: "password123",
669
- roles: %i[admin editor]
670
- )
671
-
672
- expect(adapter.has_role?(user, :admin)).to be true
673
- expect(adapter.has_role?(user, :editor)).to be true
674
- expect(adapter.has_role?(user, :viewer)).to be false
675
- end
676
-
677
- it "handles dynamic role assignment" do
678
- user = DecisionAgent::Auth::User.new(
679
- id: "dynamic1",
680
- email: "dynamic@example.com",
681
- password: "password123",
682
- roles: []
683
- )
684
-
685
- expect(adapter.has_role?(user, :admin)).to be false
686
- expect(adapter.can?(user, :read)).to be false
687
-
688
- # Assign role dynamically
689
- user.assign_role(:viewer)
690
-
691
- expect(adapter.has_role?(user, :viewer)).to be true
692
- expect(adapter.can?(user, :read)).to be true
693
- expect(adapter.can?(user, :write)).to be false
694
- end
695
-
696
- it "handles role removal" do
697
- user = DecisionAgent::Auth::User.new(
698
- id: "remove1",
699
- email: "remove@example.com",
700
- password: "password123",
701
- roles: %i[admin editor]
702
- )
703
-
704
- expect(adapter.has_role?(user, :admin)).to be true
705
- expect(adapter.can?(user, :manage_users)).to be true
706
-
707
- # Remove admin role
708
- user.remove_role(:admin)
709
-
710
- expect(adapter.has_role?(user, :admin)).to be false
711
- expect(adapter.has_role?(user, :editor)).to be true
712
- expect(adapter.can?(user, :manage_users)).to be false
713
- expect(adapter.can?(user, :write)).to be true # Still has editor role
714
- end
715
-
716
- it "checks active status with real User objects" do
717
- active_user = DecisionAgent::Auth::User.new(
718
- id: "active1",
719
- email: "active@example.com",
720
- password: "password123",
721
- roles: [:admin],
722
- active: true
723
- )
724
-
725
- expect(adapter.active?(active_user)).to be true
726
- expect(adapter.can?(active_user, :read)).to be true
727
-
728
- inactive_user = DecisionAgent::Auth::User.new(
729
- id: "inactive1",
730
- email: "inactive@example.com",
731
- password: "password123",
732
- roles: [:admin],
733
- active: false
734
- )
735
-
736
- expect(adapter.active?(inactive_user)).to be false
737
- expect(adapter.can?(inactive_user, :read)).to be false # Inactive users can't do anything
738
- end
739
-
740
- it "gets user ID and email from real User objects" do
741
- user = DecisionAgent::Auth::User.new(
742
- id: "userid123",
743
- email: "real@example.com",
744
- password: "password123",
745
- roles: [:admin]
746
- )
747
-
748
- expect(adapter.user_id(user)).to eq("userid123")
749
- expect(adapter.user_email(user)).to eq("real@example.com")
750
- end
751
-
752
- it "verifies permission checking integrates with real Role.has_permission? method" do
753
- # Test that the adapter correctly uses Role.has_permission? for all defined roles
754
- roles_to_test = DecisionAgent::Auth::Role.all
755
-
756
- roles_to_test.each do |role|
757
- user = DecisionAgent::Auth::User.new(
758
- id: "role_test_#{role}",
759
- email: "#{role}@example.com",
760
- password: "password123",
761
- roles: [role]
762
- )
763
-
764
- # Get expected permissions from Role class
765
- expected_permissions = DecisionAgent::Auth::Role.permissions_for(role)
766
-
767
- # Verify adapter returns same permissions
768
- all_permissions = %i[read write delete approve deploy manage_users audit]
769
- all_permissions.each do |permission|
770
- expected = expected_permissions.include?(permission)
771
- actual = adapter.can?(user, permission)
772
- expect(actual).to eq(expected), "Role #{role} permission #{permission} mismatch: expected #{expected}, got #{actual}"
773
- end
774
- end
775
- end
776
- end
777
- end
778
- end