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,82 +0,0 @@
1
- require "spec_helper"
2
-
3
- RSpec.describe DecisionAgent::Auth::RbacConfig do
4
- let(:config) { described_class.new }
5
-
6
- describe "#initialize" do
7
- it "initializes with nil adapter, authenticator, and user_store" do
8
- expect(config.instance_variable_get(:@adapter)).to be_nil
9
- expect(config.authenticator).to be_nil
10
- expect(config.user_store).to be_nil
11
- end
12
- end
13
-
14
- describe "#use" do
15
- it "configures default adapter" do
16
- config.use(:default)
17
- expect(config.adapter).to be_a(DecisionAgent::Auth::DefaultAdapter)
18
- end
19
-
20
- it "configures devise_cancan adapter" do
21
- config.use(:devise_cancan)
22
- expect(config.adapter).to be_a(DecisionAgent::Auth::DeviseCanCanAdapter)
23
- end
24
-
25
- it "configures pundit adapter" do
26
- config.use(:pundit)
27
- expect(config.adapter).to be_a(DecisionAgent::Auth::PunditAdapter)
28
- end
29
-
30
- it "configures custom adapter" do
31
- config.use(:custom)
32
- expect(config.adapter).to be_a(DecisionAgent::Auth::CustomAdapter)
33
- end
34
-
35
- it "raises error for unknown adapter type" do
36
- expect do
37
- config.use(:unknown)
38
- end.to raise_error(ArgumentError, /Unknown adapter type/)
39
- end
40
-
41
- it "returns self for chaining" do
42
- result = config.use(:default)
43
- expect(result).to eq(config)
44
- end
45
- end
46
-
47
- describe "#adapter=" do
48
- it "sets custom adapter instance" do
49
- custom_adapter = DecisionAgent::Auth::DefaultAdapter.new
50
- config.adapter = custom_adapter
51
- expect(config.adapter).to eq(custom_adapter)
52
- end
53
-
54
- it "raises error for non-RbacAdapter instance" do
55
- expect do
56
- config.adapter = "not an adapter"
57
- end.to raise_error(ArgumentError, /must be an instance of DecisionAgent::Auth::RbacAdapter/)
58
- end
59
- end
60
-
61
- describe "#adapter" do
62
- it "returns configured adapter" do
63
- config.use(:default)
64
- expect(config.adapter).to be_a(DecisionAgent::Auth::DefaultAdapter)
65
- end
66
-
67
- it "returns default adapter if none configured" do
68
- expect(config.adapter).to be_a(DecisionAgent::Auth::DefaultAdapter)
69
- end
70
- end
71
-
72
- describe "#configured?" do
73
- it "returns false when no adapter configured" do
74
- expect(config.configured?).to be false
75
- end
76
-
77
- it "returns true when adapter configured" do
78
- config.use(:default)
79
- expect(config.configured?).to be true
80
- end
81
- end
82
- end
@@ -1,51 +0,0 @@
1
- require "spec_helper"
2
-
3
- RSpec.describe DecisionAgent::Auth::Role do
4
- describe ".all" do
5
- it "returns all role symbols" do
6
- roles = DecisionAgent::Auth::Role.all
7
- expect(roles).to include(:admin, :editor, :viewer, :auditor, :approver)
8
- end
9
- end
10
-
11
- describe ".exists?" do
12
- it "returns true for valid roles" do
13
- expect(DecisionAgent::Auth::Role.exists?(:admin)).to be true
14
- expect(DecisionAgent::Auth::Role.exists?(:editor)).to be true
15
- end
16
-
17
- it "returns false for invalid roles" do
18
- expect(DecisionAgent::Auth::Role.exists?(:invalid)).to be false
19
- end
20
- end
21
-
22
- describe ".permissions_for" do
23
- it "returns permissions for admin role" do
24
- permissions = DecisionAgent::Auth::Role.permissions_for(:admin)
25
- expect(permissions).to include(:read, :write, :delete, :approve, :deploy, :manage_users, :audit)
26
- end
27
-
28
- it "returns permissions for editor role" do
29
- permissions = DecisionAgent::Auth::Role.permissions_for(:editor)
30
- expect(permissions).to include(:read, :write)
31
- end
32
-
33
- it "returns permissions for viewer role" do
34
- permissions = DecisionAgent::Auth::Role.permissions_for(:viewer)
35
- expect(permissions).to include(:read)
36
- end
37
-
38
- it "returns empty array for invalid role" do
39
- permissions = DecisionAgent::Auth::Role.permissions_for(:invalid)
40
- expect(permissions).to eq([])
41
- end
42
- end
43
-
44
- describe ".has_permission?" do
45
- it "returns true if role has permission" do
46
- expect(DecisionAgent::Auth::Role.has_permission?(:admin, :write)).to be true
47
- expect(DecisionAgent::Auth::Role.has_permission?(:editor, :write)).to be true
48
- expect(DecisionAgent::Auth::Role.has_permission?(:viewer, :write)).to be false
49
- end
50
- end
51
- end
@@ -1,172 +0,0 @@
1
- require "spec_helper"
2
-
3
- RSpec.describe DecisionAgent::Auth::SessionManager do
4
- let(:manager) { DecisionAgent::Auth::SessionManager.new }
5
-
6
- describe "#initialize" do
7
- it "initializes with empty sessions" do
8
- expect(manager.count).to eq(0)
9
- end
10
- end
11
-
12
- describe "#create_session" do
13
- it "creates a new session" do
14
- session = manager.create_session("user123")
15
- expect(session).to be_a(DecisionAgent::Auth::Session)
16
- expect(session.user_id).to eq("user123")
17
- end
18
-
19
- it "stores the session" do
20
- session = manager.create_session("user123")
21
- retrieved = manager.get_session(session.token)
22
- expect(retrieved).to eq(session)
23
- end
24
-
25
- it "accepts custom expiration time" do
26
- session = manager.create_session("user123", expires_in: 7200)
27
- expect(session.expires_at).to be > Time.now.utc + 7100
28
- end
29
- end
30
-
31
- describe "#get_session" do
32
- it "returns session for valid token" do
33
- session = manager.create_session("user123")
34
- retrieved = manager.get_session(session.token)
35
- expect(retrieved).to eq(session)
36
- end
37
-
38
- it "returns nil for invalid token" do
39
- expect(manager.get_session("invalid_token")).to be_nil
40
- end
41
-
42
- it "returns nil for expired session" do
43
- session = manager.create_session("user123", expires_in: -1)
44
- expect(manager.get_session(session.token)).to be_nil
45
- end
46
- end
47
-
48
- describe "#delete_session" do
49
- it "deletes a session" do
50
- session = manager.create_session("user123")
51
- manager.delete_session(session.token)
52
- expect(manager.get_session(session.token)).to be_nil
53
- end
54
-
55
- it "does not raise error for non-existent session" do
56
- expect { manager.delete_session("nonexistent") }.not_to raise_error
57
- end
58
- end
59
-
60
- describe "#delete_user_sessions" do
61
- it "deletes all sessions for a user" do
62
- session1 = manager.create_session("user123")
63
- session2 = manager.create_session("user123")
64
- session3 = manager.create_session("user456")
65
-
66
- manager.delete_user_sessions("user123")
67
-
68
- expect(manager.get_session(session1.token)).to be_nil
69
- expect(manager.get_session(session2.token)).to be_nil
70
- expect(manager.get_session(session3.token)).to eq(session3) # Other user's session still exists
71
- end
72
-
73
- it "does not raise error for user with no sessions" do
74
- expect { manager.delete_user_sessions("nonexistent") }.not_to raise_error
75
- end
76
- end
77
-
78
- describe "#cleanup_expired_sessions" do
79
- it "removes expired sessions" do
80
- # Create expired session
81
- expired_session = manager.create_session("user123", expires_in: -1)
82
- # Create valid session
83
- valid_session = manager.create_session("user456", expires_in: 3600)
84
-
85
- expect(manager.count).to eq(2)
86
-
87
- # Force cleanup by setting last_cleanup far in the past and creating another session
88
- manager.instance_variable_set(:@last_cleanup, Time.now - 400) # More than 300 seconds
89
- manager.create_session("user789", expires_in: 3600)
90
-
91
- # Expired session should be cleaned up
92
- expect(manager.get_session(expired_session.token)).to be_nil
93
- expect(manager.get_session(valid_session.token)).to eq(valid_session)
94
- end
95
-
96
- it "only runs cleanup after cleanup_interval" do
97
- manager = DecisionAgent::Auth::SessionManager.new
98
-
99
- # Create an expired session
100
- expired_session = manager.create_session("user123", expires_in: -1)
101
- expect(manager.count).to eq(1)
102
-
103
- # The cleanup_expired_sessions is called during create_session, but it checks the interval
104
- # Let's test by manually setting last_cleanup to far in the past
105
- manager.instance_variable_set(:@last_cleanup, Time.now - 400) # More than 300 seconds
106
- manager.create_session("user456", expires_in: 3600)
107
-
108
- # The expired session should be cleaned up now
109
- expect(manager.get_session(expired_session.token)).to be_nil
110
- end
111
- end
112
-
113
- describe "#count" do
114
- it "returns zero initially" do
115
- expect(manager.count).to eq(0)
116
- end
117
-
118
- it "returns correct count after creating sessions" do
119
- manager.create_session("user123")
120
- expect(manager.count).to eq(1)
121
-
122
- manager.create_session("user123")
123
- expect(manager.count).to eq(2)
124
-
125
- manager.create_session("user456")
126
- expect(manager.count).to eq(3)
127
- end
128
-
129
- it "reflects deletions" do
130
- session1 = manager.create_session("user123")
131
- manager.create_session("user123")
132
- expect(manager.count).to eq(2)
133
-
134
- manager.delete_session(session1.token)
135
- expect(manager.count).to eq(1)
136
-
137
- manager.delete_user_sessions("user123")
138
- expect(manager.count).to eq(0)
139
- end
140
-
141
- it "does not count expired sessions" do
142
- manager.create_session("user123", expires_in: -1)
143
- # Expired sessions are not counted when retrieved, but are still in storage until cleanup
144
- # So count will include them until cleanup runs
145
- expect(manager.count).to eq(1)
146
-
147
- # After cleanup
148
- manager.instance_variable_set(:@last_cleanup, Time.now - 400)
149
- manager.create_session("user456", expires_in: 3600)
150
- # Now expired session should be cleaned up
151
- expect(manager.count).to eq(1)
152
- end
153
- end
154
-
155
- describe "thread safety" do
156
- it "handles concurrent access safely" do
157
- threads = []
158
- 10.times do
159
- threads << Thread.new do
160
- 10.times do
161
- session = manager.create_session("user#{rand(100)}")
162
- manager.get_session(session.token)
163
- manager.count
164
- end
165
- end
166
- end
167
-
168
- threads.each(&:join)
169
- expect(manager.count).to eq(100)
170
- end
171
- end
172
- end
@@ -1,112 +0,0 @@
1
- require "spec_helper"
2
-
3
- RSpec.describe DecisionAgent::Auth::Session do
4
- describe "#initialize" do
5
- it "creates a session with user_id" do
6
- session = DecisionAgent::Auth::Session.new(user_id: "user123")
7
- expect(session.user_id).to eq("user123")
8
- expect(session.token).to be_a(String)
9
- expect(session.token.length).to eq(64) # 32 bytes hex = 64 chars
10
- end
11
-
12
- it "sets expiration time" do
13
- session = DecisionAgent::Auth::Session.new(user_id: "user123", expires_in: 1800)
14
- expect(session.expires_at).to be > Time.now.utc
15
- expect(session.expires_at).to be <= Time.now.utc + 1801
16
- end
17
-
18
- it "uses default expiration of 3600 seconds" do
19
- session = DecisionAgent::Auth::Session.new(user_id: "user123")
20
- expected_expiry = session.created_at + 3600
21
- expect(session.expires_at).to be_within(1).of(expected_expiry)
22
- end
23
- end
24
-
25
- describe "#expired?" do
26
- it "returns false for valid session" do
27
- session = DecisionAgent::Auth::Session.new(user_id: "user123", expires_in: 3600)
28
- expect(session.expired?).to be false
29
- end
30
-
31
- it "returns true for expired session" do
32
- session = DecisionAgent::Auth::Session.new(user_id: "user123", expires_in: -1)
33
- expect(session.expired?).to be true
34
- end
35
- end
36
-
37
- describe "#valid?" do
38
- it "returns true for non-expired session" do
39
- session = DecisionAgent::Auth::Session.new(user_id: "user123", expires_in: 3600)
40
- expect(session.valid?).to be true
41
- end
42
-
43
- it "returns false for expired session" do
44
- session = DecisionAgent::Auth::Session.new(user_id: "user123", expires_in: -1)
45
- expect(session.valid?).to be false
46
- end
47
-
48
- it "returns false when expired? returns true" do
49
- session = DecisionAgent::Auth::Session.new(user_id: "user123", expires_in: -1)
50
- expect(session.expired?).to be true
51
- expect(session.valid?).to be false
52
- end
53
-
54
- it "returns true when expired? returns false" do
55
- session = DecisionAgent::Auth::Session.new(user_id: "user123", expires_in: 3600)
56
- expect(session.expired?).to be false
57
- expect(session.valid?).to be true
58
- end
59
- end
60
-
61
- describe "#to_h" do
62
- it "returns hash with all session attributes" do
63
- session = DecisionAgent::Auth::Session.new(user_id: "user123", expires_in: 3600)
64
- hash = session.to_h
65
-
66
- expect(hash).to be_a(Hash)
67
- expect(hash[:token]).to eq(session.token)
68
- expect(hash[:user_id]).to eq("user123")
69
- expect(hash[:created_at]).to be_a(String)
70
- expect(hash[:expires_at]).to be_a(String)
71
- end
72
-
73
- it "serializes timestamps as ISO8601 strings" do
74
- session = DecisionAgent::Auth::Session.new(user_id: "user123")
75
- hash = session.to_h
76
-
77
- expect { Time.iso8601(hash[:created_at]) }.not_to raise_error
78
- expect { Time.iso8601(hash[:expires_at]) }.not_to raise_error
79
- end
80
-
81
- it "includes correct user_id" do
82
- session = DecisionAgent::Auth::Session.new(user_id: "user456")
83
- hash = session.to_h
84
- expect(hash[:user_id]).to eq("user456")
85
- end
86
-
87
- it "includes correct token" do
88
- session = DecisionAgent::Auth::Session.new(user_id: "user123")
89
- hash = session.to_h
90
- expect(hash[:token]).to eq(session.token)
91
- end
92
- end
93
-
94
- describe "#created_at" do
95
- it "is set to current UTC time" do
96
- before = Time.now.utc
97
- session = DecisionAgent::Auth::Session.new(user_id: "user123")
98
- after = Time.now.utc
99
-
100
- expect(session.created_at).to be >= before
101
- expect(session.created_at).to be <= after
102
- end
103
- end
104
-
105
- describe "#expires_at" do
106
- it "is set based on expires_in parameter" do
107
- session = DecisionAgent::Auth::Session.new(user_id: "user123", expires_in: 7200)
108
- expected = session.created_at + 7200
109
- expect(session.expires_at).to be_within(1).of(expected)
110
- end
111
- end
112
- end
@@ -1,130 +0,0 @@
1
- require "spec_helper"
2
-
3
- RSpec.describe DecisionAgent::Auth::User do
4
- describe "#initialize" do
5
- it "creates a user with email and password" do
6
- user = DecisionAgent::Auth::User.new(
7
- email: "test@example.com",
8
- password: "password123"
9
- )
10
-
11
- expect(user.email).to eq("test@example.com")
12
- expect(user.id).to be_a(String)
13
- expect(user.active).to be true
14
- expect(user.roles).to eq([])
15
- end
16
-
17
- it "creates a user with roles" do
18
- user = DecisionAgent::Auth::User.new(
19
- email: "admin@example.com",
20
- password: "password123",
21
- roles: %i[admin editor]
22
- )
23
-
24
- expect(user.roles).to include(:admin, :editor)
25
- end
26
-
27
- it "raises error if neither password nor password_hash provided" do
28
- expect do
29
- DecisionAgent::Auth::User.new(email: "test@example.com")
30
- end.to raise_error(ArgumentError, /password/)
31
- end
32
- end
33
-
34
- describe "#authenticate" do
35
- it "returns true for correct password" do
36
- user = DecisionAgent::Auth::User.new(
37
- email: "test@example.com",
38
- password: "password123"
39
- )
40
-
41
- expect(user.authenticate("password123")).to be true
42
- end
43
-
44
- it "returns false for incorrect password" do
45
- user = DecisionAgent::Auth::User.new(
46
- email: "test@example.com",
47
- password: "password123"
48
- )
49
-
50
- expect(user.authenticate("wrongpassword")).to be false
51
- end
52
-
53
- it "returns false for inactive user" do
54
- user = DecisionAgent::Auth::User.new(
55
- email: "test@example.com",
56
- password: "password123",
57
- active: false
58
- )
59
-
60
- expect(user.authenticate("password123")).to be false
61
- end
62
- end
63
-
64
- describe "#assign_role" do
65
- it "adds a role to the user" do
66
- user = DecisionAgent::Auth::User.new(
67
- email: "test@example.com",
68
- password: "password123"
69
- )
70
-
71
- user.assign_role(:editor)
72
- expect(user.roles).to include(:editor)
73
- end
74
-
75
- it "does not add duplicate roles" do
76
- user = DecisionAgent::Auth::User.new(
77
- email: "test@example.com",
78
- password: "password123"
79
- )
80
-
81
- user.assign_role(:editor)
82
- user.assign_role(:editor)
83
-
84
- expect(user.roles.count(:editor)).to eq(1)
85
- end
86
- end
87
-
88
- describe "#remove_role" do
89
- it "removes a role from the user" do
90
- user = DecisionAgent::Auth::User.new(
91
- email: "test@example.com",
92
- password: "password123",
93
- roles: %i[editor viewer]
94
- )
95
-
96
- user.remove_role(:editor)
97
- expect(user.roles).not_to include(:editor)
98
- expect(user.roles).to include(:viewer)
99
- end
100
- end
101
-
102
- describe "#has_role?" do
103
- it "returns true if user has the role" do
104
- user = DecisionAgent::Auth::User.new(
105
- email: "test@example.com",
106
- password: "password123",
107
- roles: [:editor]
108
- )
109
-
110
- expect(user.has_role?(:editor)).to be true
111
- expect(user.has_role?(:admin)).to be false
112
- end
113
- end
114
-
115
- describe "#to_h" do
116
- it "returns a hash representation of the user" do
117
- user = DecisionAgent::Auth::User.new(
118
- email: "test@example.com",
119
- password: "password123",
120
- roles: [:editor]
121
- )
122
-
123
- hash = user.to_h
124
- expect(hash[:email]).to eq("test@example.com")
125
- expect(hash[:roles]).to eq(["editor"])
126
- expect(hash[:active]).to be true
127
- expect(hash[:id]).to be_a(String)
128
- end
129
- end
130
- end