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,498 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "spec_helper"
4
- require "active_record"
5
- require "decision_agent/monitoring/storage/activerecord_adapter"
6
-
7
- RSpec.describe DecisionAgent::Monitoring::Storage::ActiveRecordAdapter do
8
- # Setup in-memory SQLite database for testing
9
- before(:all) do
10
- ActiveRecord::Base.establish_connection(
11
- adapter: "sqlite3",
12
- database: ":memory:"
13
- )
14
-
15
- # Create tables
16
- ActiveRecord::Schema.define do
17
- create_table :decision_logs, force: true do |t|
18
- t.string :decision, null: false
19
- t.float :confidence
20
- t.integer :evaluations_count, default: 0
21
- t.float :duration_ms
22
- t.string :status
23
- t.text :context
24
- t.text :metadata
25
- t.timestamps
26
- end
27
-
28
- create_table :evaluation_metrics, force: true do |t|
29
- t.references :decision_log, foreign_key: true
30
- t.string :evaluator_name, null: false
31
- t.float :score
32
- t.boolean :success
33
- t.float :duration_ms
34
- t.text :details
35
- t.timestamps
36
- end
37
-
38
- create_table :performance_metrics, force: true do |t|
39
- t.string :operation, null: false
40
- t.float :duration_ms
41
- t.string :status
42
- t.text :metadata
43
- t.timestamps
44
- end
45
-
46
- create_table :error_metrics, force: true do |t|
47
- t.string :error_type, null: false
48
- t.text :message
49
- t.text :stack_trace
50
- t.string :severity
51
- t.text :context
52
- t.timestamps
53
- end
54
- end
55
-
56
- # Define models
57
- # rubocop:disable Lint/ConstantDefinitionInBlock
58
- class DecisionLog < ActiveRecord::Base
59
- has_many :evaluation_metrics, dependent: :destroy
60
-
61
- scope :recent, ->(time_range) { where("created_at >= ?", Time.now - time_range) }
62
-
63
- def self.success_rate(time_range: 3600)
64
- total = recent(time_range).where.not(status: nil).count
65
- return 0.0 if total.zero?
66
-
67
- recent(time_range).where(status: "success").count.to_f / total
68
- end
69
-
70
- def parsed_context
71
- JSON.parse(context, symbolize_names: true)
72
- rescue StandardError
73
- {}
74
- end
75
- end
76
-
77
- class EvaluationMetric < ActiveRecord::Base
78
- belongs_to :decision_log, optional: true
79
-
80
- scope :recent, ->(time_range) { where("created_at >= ?", Time.now - time_range) }
81
- scope :successful, -> { where(success: true) }
82
-
83
- def parsed_details
84
- JSON.parse(details, symbolize_names: true)
85
- rescue StandardError
86
- {}
87
- end
88
- end
89
-
90
- class PerformanceMetric < ActiveRecord::Base
91
- scope :recent, ->(time_range) { where("created_at >= ?", Time.now - time_range) }
92
-
93
- def self.average_duration(time_range: 3600)
94
- recent(time_range).average(:duration_ms).to_f
95
- end
96
-
97
- def self.p50(time_range: 3600)
98
- percentile(0.50, time_range: time_range)
99
- end
100
-
101
- def self.p95(time_range: 3600)
102
- percentile(0.95, time_range: time_range)
103
- end
104
-
105
- def self.p99(time_range: 3600)
106
- percentile(0.99, time_range: time_range)
107
- end
108
-
109
- def self.percentile(pct, time_range: 3600)
110
- durations = recent(time_range).where.not(duration_ms: nil).order(:duration_ms).pluck(:duration_ms)
111
- return 0.0 if durations.empty?
112
-
113
- durations[(durations.length * pct).ceil - 1].to_f
114
- end
115
-
116
- def self.success_rate(time_range: 3600)
117
- total = recent(time_range).where.not(status: nil).count
118
- return 0.0 if total.zero?
119
-
120
- recent(time_range).where(status: "success").count.to_f / total
121
- end
122
- end
123
-
124
- class ErrorMetric < ActiveRecord::Base
125
- scope :recent, ->(time_range) { where("created_at >= ?", Time.now - time_range) }
126
- scope :critical, -> { where(severity: "critical") }
127
-
128
- def parsed_context
129
- JSON.parse(context, symbolize_names: true)
130
- rescue StandardError
131
- {}
132
- end
133
- end
134
- # rubocop:enable Lint/ConstantDefinitionInBlock
135
- end
136
-
137
- before do
138
- DecisionLog.delete_all
139
- EvaluationMetric.delete_all
140
- PerformanceMetric.delete_all
141
- ErrorMetric.delete_all
142
- end
143
-
144
- let(:adapter) { described_class.new }
145
-
146
- describe ".available?" do
147
- it "returns true when ActiveRecord and models are defined" do
148
- expect(described_class.available?).to be_truthy
149
- end
150
- end
151
-
152
- describe "#record_decision" do
153
- it "creates a decision log record" do
154
- expect do
155
- adapter.record_decision(
156
- "approve_payment",
157
- { user_id: 123, amount: 500 },
158
- confidence: 0.85,
159
- evaluations_count: 3,
160
- duration_ms: 45.5,
161
- status: "success"
162
- )
163
- end.to change(DecisionLog, :count).by(1)
164
-
165
- log = DecisionLog.last
166
- expect(log.decision).to eq("approve_payment")
167
- expect(log.confidence).to eq(0.85)
168
- expect(log.evaluations_count).to eq(3)
169
- expect(log.duration_ms).to eq(45.5)
170
- expect(log.status).to eq("success")
171
- expect(log.parsed_context).to eq(user_id: 123, amount: 500)
172
- end
173
-
174
- it "handles database errors gracefully" do
175
- allow(DecisionLog).to receive(:create!).and_raise(StandardError.new("DB error"))
176
- expect do
177
- adapter.record_decision("test", {})
178
- end.not_to raise_error
179
- end
180
- end
181
-
182
- describe "#record_evaluation" do
183
- it "creates an evaluation metric record" do
184
- expect do
185
- adapter.record_evaluation(
186
- "FraudDetector",
187
- score: 0.92,
188
- success: true,
189
- duration_ms: 12.3,
190
- details: { risk_level: "low" }
191
- )
192
- end.to change(EvaluationMetric, :count).by(1)
193
-
194
- metric = EvaluationMetric.last
195
- expect(metric.evaluator_name).to eq("FraudDetector")
196
- expect(metric.score).to eq(0.92)
197
- expect(metric.success).to be true
198
- expect(metric.duration_ms).to eq(12.3)
199
- expect(metric.parsed_details).to eq(risk_level: "low")
200
- end
201
-
202
- it "handles database errors gracefully" do
203
- allow(EvaluationMetric).to receive(:create!).and_raise(StandardError.new("DB error"))
204
- expect do
205
- adapter.record_evaluation("test")
206
- end.not_to raise_error
207
- end
208
- end
209
-
210
- describe "#record_performance" do
211
- it "creates a performance metric record" do
212
- expect do
213
- adapter.record_performance(
214
- "api_call",
215
- duration_ms: 250.5,
216
- status: "success",
217
- metadata: { endpoint: "/api/v1/users" }
218
- )
219
- end.to change(PerformanceMetric, :count).by(1)
220
-
221
- metric = PerformanceMetric.last
222
- expect(metric.operation).to eq("api_call")
223
- expect(metric.duration_ms).to eq(250.5)
224
- expect(metric.status).to eq("success")
225
- end
226
-
227
- it "handles database errors gracefully" do
228
- allow(PerformanceMetric).to receive(:create!).and_raise(StandardError.new("DB error"))
229
- expect do
230
- adapter.record_performance("test")
231
- end.not_to raise_error
232
- end
233
- end
234
-
235
- describe "#record_error" do
236
- it "creates an error metric record" do
237
- expect do
238
- adapter.record_error(
239
- "RuntimeError",
240
- message: "Something went wrong",
241
- stack_trace: ["line 1", "line 2"],
242
- severity: "critical",
243
- context: { user_id: 456 }
244
- )
245
- end.to change(ErrorMetric, :count).by(1)
246
-
247
- error = ErrorMetric.last
248
- expect(error.error_type).to eq("RuntimeError")
249
- expect(error.message).to eq("Something went wrong")
250
- expect(error.severity).to eq("critical")
251
- expect(error.parsed_context).to eq(user_id: 456)
252
- end
253
-
254
- it "handles nil stack_trace" do
255
- adapter.record_error("TestError", stack_trace: nil)
256
- error = ErrorMetric.last
257
- expect(error.stack_trace).to be_nil
258
- end
259
-
260
- it "handles database errors gracefully" do
261
- allow(ErrorMetric).to receive(:create!).and_raise(StandardError.new("DB error"))
262
- expect do
263
- adapter.record_error("test")
264
- end.not_to raise_error
265
- end
266
- end
267
-
268
- describe "#statistics" do
269
- before do
270
- # Create test data
271
- 3.times do |i|
272
- adapter.record_decision(
273
- "decision_#{i}",
274
- { index: i },
275
- confidence: 0.5 + (i * 0.1),
276
- evaluations_count: 2,
277
- duration_ms: 100 + (i * 10),
278
- status: "success"
279
- )
280
- end
281
-
282
- 2.times do |i|
283
- adapter.record_evaluation(
284
- "Evaluator#{i}",
285
- score: 0.8,
286
- success: true,
287
- duration_ms: 50
288
- )
289
- end
290
-
291
- 4.times do |i|
292
- adapter.record_performance(
293
- "operation_#{i}",
294
- duration_ms: 100 + (i * 50),
295
- status: i.even? ? "success" : "failure"
296
- )
297
- end
298
-
299
- adapter.record_error("TestError", severity: "critical")
300
- end
301
-
302
- it "returns comprehensive statistics" do
303
- stats = adapter.statistics(time_range: 3600)
304
-
305
- expect(stats[:decisions][:total]).to eq(3)
306
- expect(stats[:decisions][:average_confidence]).to be_within(0.01).of(0.6)
307
- expect(stats[:evaluations][:total]).to eq(2)
308
- expect(stats[:performance][:total]).to eq(4)
309
- expect(stats[:errors][:total]).to eq(1)
310
- expect(stats[:errors][:critical_count]).to eq(1)
311
- end
312
-
313
- it "handles empty statistics" do
314
- DecisionLog.delete_all
315
- EvaluationMetric.delete_all
316
- PerformanceMetric.delete_all
317
- ErrorMetric.delete_all
318
-
319
- stats = adapter.statistics(time_range: 3600)
320
-
321
- expect(stats[:decisions][:total]).to eq(0)
322
- expect(stats[:decisions][:average_confidence]).to eq(0.0)
323
- expect(stats[:evaluations][:total]).to eq(0)
324
- expect(stats[:performance][:total]).to eq(0)
325
- expect(stats[:errors][:total]).to eq(0)
326
- end
327
-
328
- it "handles decisions without confidence" do
329
- DecisionLog.delete_all
330
- adapter.record_decision("test", {}, confidence: nil)
331
-
332
- stats = adapter.statistics(time_range: 3600)
333
- expect(stats[:decisions][:average_confidence]).to eq(0.0)
334
- end
335
-
336
- it "handles database errors gracefully" do
337
- allow(DecisionLog).to receive(:recent).and_raise(StandardError.new("DB error"))
338
- stats = adapter.statistics(time_range: 3600)
339
-
340
- expect(stats[:decisions][:total]).to eq(0)
341
- expect(stats[:evaluations][:total]).to eq(0)
342
- end
343
- end
344
-
345
- describe "#time_series" do
346
- before do
347
- # Create metrics at different times
348
- [10, 70, 130].each do |seconds_ago|
349
- travel_back = Time.now - seconds_ago
350
- DecisionLog.create!(
351
- decision: "test",
352
- confidence: 0.8,
353
- created_at: travel_back
354
- )
355
- end
356
- end
357
-
358
- it "returns time series data grouped by buckets for decisions" do
359
- series = adapter.time_series(:decisions, bucket_size: 60, time_range: 200)
360
-
361
- expect(series[:timestamps]).to be_an(Array)
362
- expect(series[:data]).to be_an(Array)
363
- expect(series[:data].sum).to eq(3)
364
- end
365
-
366
- it "returns time series data for evaluations" do
367
- [10, 70].each do |seconds_ago|
368
- travel_back = Time.now - seconds_ago
369
- EvaluationMetric.create!(
370
- evaluator_name: "test",
371
- score: 0.8,
372
- created_at: travel_back
373
- )
374
- end
375
-
376
- series = adapter.time_series(:evaluations, bucket_size: 60, time_range: 200)
377
-
378
- expect(series[:timestamps]).to be_an(Array)
379
- expect(series[:data]).to be_an(Array)
380
- expect(series[:data].sum).to eq(2)
381
- end
382
-
383
- it "returns time series data for performance" do
384
- [10, 70].each do |seconds_ago|
385
- travel_back = Time.now - seconds_ago
386
- PerformanceMetric.create!(
387
- operation: "test",
388
- duration_ms: 100,
389
- created_at: travel_back
390
- )
391
- end
392
-
393
- series = adapter.time_series(:performance, bucket_size: 60, time_range: 200)
394
-
395
- expect(series[:timestamps]).to be_an(Array)
396
- expect(series[:data]).to be_an(Array)
397
- end
398
-
399
- it "returns time series data for errors" do
400
- [10, 70].each do |seconds_ago|
401
- travel_back = Time.now - seconds_ago
402
- ErrorMetric.create!(
403
- error_type: "TestError",
404
- created_at: travel_back
405
- )
406
- end
407
-
408
- series = adapter.time_series(:errors, bucket_size: 60, time_range: 200)
409
-
410
- expect(series[:timestamps]).to be_an(Array)
411
- expect(series[:data]).to be_an(Array)
412
- expect(series[:data].sum).to eq(2)
413
- end
414
-
415
- it "returns empty data for unknown metric type" do
416
- series = adapter.time_series(:unknown, bucket_size: 60, time_range: 200)
417
-
418
- expect(series[:timestamps]).to eq([])
419
- expect(series[:data]).to eq([])
420
- end
421
-
422
- it "handles database errors gracefully" do
423
- allow(DecisionLog).to receive(:recent).and_raise(StandardError.new("DB error"))
424
- series = adapter.time_series(:decisions, bucket_size: 60, time_range: 200)
425
-
426
- expect(series[:timestamps]).to eq([])
427
- expect(series[:data]).to eq([])
428
- end
429
- end
430
-
431
- describe "#metrics_count" do
432
- before do
433
- adapter.record_decision("test", {}, confidence: 0.8)
434
- adapter.record_evaluation("TestEval", score: 0.9)
435
- adapter.record_performance("test_op", duration_ms: 100)
436
- adapter.record_error("TestError")
437
- end
438
-
439
- it "returns count of all metric types" do
440
- counts = adapter.metrics_count
441
-
442
- expect(counts[:decisions]).to eq(1)
443
- expect(counts[:evaluations]).to eq(1)
444
- expect(counts[:performance]).to eq(1)
445
- expect(counts[:errors]).to eq(1)
446
- end
447
-
448
- it "handles database errors gracefully" do
449
- allow(DecisionLog).to receive(:count).and_raise(StandardError.new("DB error"))
450
- counts = adapter.metrics_count
451
-
452
- expect(counts[:decisions]).to eq(0)
453
- expect(counts[:evaluations]).to eq(0)
454
- expect(counts[:performance]).to eq(0)
455
- expect(counts[:errors]).to eq(0)
456
- end
457
- end
458
-
459
- describe "#cleanup" do
460
- before do
461
- # Create old metrics
462
- old_time = Time.now - 8.days
463
- DecisionLog.create!(decision: "old", confidence: 0.8, created_at: old_time)
464
- EvaluationMetric.create!(evaluator_name: "old", created_at: old_time)
465
- PerformanceMetric.create!(operation: "old", created_at: old_time)
466
- ErrorMetric.create!(error_type: "old", created_at: old_time)
467
-
468
- # Create recent metrics
469
- adapter.record_decision("recent", {}, confidence: 0.8)
470
- adapter.record_evaluation("recent", score: 0.9)
471
- adapter.record_performance("recent", duration_ms: 100)
472
- adapter.record_error("recent")
473
- end
474
-
475
- it "removes old metrics and keeps recent ones" do
476
- count = adapter.cleanup(older_than: 7.days.to_i)
477
-
478
- expect(count).to eq(4) # 4 old metrics removed
479
- expect(DecisionLog.count).to eq(1)
480
- expect(EvaluationMetric.count).to eq(1)
481
- expect(PerformanceMetric.count).to eq(1)
482
- expect(ErrorMetric.count).to eq(1)
483
- end
484
-
485
- it "handles database errors gracefully" do
486
- allow(DecisionLog).to receive(:where).and_raise(StandardError.new("DB error"))
487
- count = adapter.cleanup(older_than: 7.days.to_i)
488
-
489
- expect(count).to eq(0)
490
- end
491
- end
492
-
493
- describe "#initialize" do
494
- it "validates required models exist" do
495
- expect { described_class.new }.not_to raise_error
496
- end
497
- end
498
- end
@@ -1,61 +0,0 @@
1
- require "spec_helper"
2
-
3
- RSpec.describe DecisionAgent::Monitoring::Storage::BaseAdapter do
4
- let(:adapter) { described_class.new }
5
-
6
- describe "abstract methods" do
7
- it "raises NotImplementedError for record_decision" do
8
- expect do
9
- adapter.record_decision("approve", {})
10
- end.to raise_error(NotImplementedError)
11
- end
12
-
13
- it "raises NotImplementedError for record_evaluation" do
14
- expect do
15
- adapter.record_evaluation("evaluator1")
16
- end.to raise_error(NotImplementedError)
17
- end
18
-
19
- it "raises NotImplementedError for record_performance" do
20
- expect do
21
- adapter.record_performance("operation")
22
- end.to raise_error(NotImplementedError)
23
- end
24
-
25
- it "raises NotImplementedError for record_error" do
26
- expect do
27
- adapter.record_error("ErrorType")
28
- end.to raise_error(NotImplementedError)
29
- end
30
-
31
- it "raises NotImplementedError for statistics" do
32
- expect do
33
- adapter.statistics
34
- end.to raise_error(NotImplementedError)
35
- end
36
-
37
- it "raises NotImplementedError for time_series" do
38
- expect do
39
- adapter.time_series(:decisions)
40
- end.to raise_error(NotImplementedError)
41
- end
42
-
43
- it "raises NotImplementedError for metrics_count" do
44
- expect do
45
- adapter.metrics_count
46
- end.to raise_error(NotImplementedError)
47
- end
48
-
49
- it "raises NotImplementedError for cleanup" do
50
- expect do
51
- adapter.cleanup(older_than: 3600)
52
- end.to raise_error(NotImplementedError)
53
- end
54
-
55
- it "raises NotImplementedError for available?" do
56
- expect do
57
- described_class.available?
58
- end.to raise_error(NotImplementedError)
59
- end
60
- end
61
- end