activerabbit-ai 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/TESTING_GUIDE.md ADDED
@@ -0,0 +1,585 @@
1
+ # Testing ActiveRabbit Client with Rails Applications
2
+
3
+ This guide covers comprehensive testing strategies for the ActiveRabbit client gem in Rails applications before production deployment.
4
+
5
+ ## ๐Ÿงช Testing Strategy Overview
6
+
7
+ ### 1. **Unit Tests** - Test gem components in isolation
8
+ ### 2. **Integration Tests** - Test gem integration with Rails
9
+ ### 3. **End-to-End Tests** - Test complete error tracking flow
10
+ ### 4. **Performance Tests** - Ensure minimal performance impact
11
+ ### 5. **Production Simulation** - Test with realistic data volumes
12
+
13
+ ## ๐Ÿ”ง Test Environment Setup
14
+
15
+ ### Gemfile for Testing
16
+ ```ruby
17
+ group :test do
18
+ gem 'rspec-rails'
19
+ gem 'webmock'
20
+ gem 'vcr'
21
+ gem 'factory_bot_rails'
22
+ gem 'database_cleaner-active_record'
23
+ gem 'timecop'
24
+ gem 'rails-controller-testing'
25
+ end
26
+ ```
27
+
28
+ ### Test Configuration
29
+ ```ruby
30
+ # spec/rails_helper.rb
31
+ require 'spec_helper'
32
+ require 'rspec/rails'
33
+ require 'webmock/rspec'
34
+ require 'vcr'
35
+
36
+ # Configure WebMock to stub HTTP requests
37
+ WebMock.disable_net_connect!(allow_localhost: true)
38
+
39
+ # Configure VCR for recording real API interactions
40
+ VCR.configure do |config|
41
+ config.cassette_library_dir = "spec/vcr_cassettes"
42
+ config.hook_into :webmock
43
+ config.configure_rspec_metadata!
44
+ end
45
+
46
+ RSpec.configure do |config|
47
+ config.use_transactional_fixtures = true
48
+ config.infer_spec_type_from_file_location!
49
+ config.filter_rails_from_backtrace!
50
+
51
+ # Reset ActiveRabbit configuration before each test
52
+ config.before(:each) do
53
+ ActiveRabbit::Client.configuration = nil
54
+ Thread.current[:active_rabbit_request_context] = nil
55
+ end
56
+
57
+ config.after(:each) do
58
+ ActiveRabbit::Client.configuration = nil
59
+ Thread.current[:active_rabbit_request_context] = nil
60
+ end
61
+ end
62
+ ```
63
+
64
+ ## ๐Ÿ“ Unit Tests for Gem Components
65
+
66
+ ### Test Configuration
67
+ ```ruby
68
+ # spec/active_rabbit/configuration_spec.rb
69
+ RSpec.describe ActiveRabbit::Client::Configuration do
70
+ describe "environment variable loading" do
71
+ it "loads API key from environment" do
72
+ allow(ENV).to receive(:[]).with("active_rabbit_API_KEY").and_return("test-key")
73
+ config = described_class.new
74
+ expect(config.api_key).to eq("test-key")
75
+ end
76
+ end
77
+
78
+ describe "validation" do
79
+ it "validates required configuration" do
80
+ config = described_class.new
81
+ config.api_key = "test-key"
82
+ config.api_url = "https://api.test.com"
83
+ expect(config.valid?).to be true
84
+ end
85
+ end
86
+ end
87
+ ```
88
+
89
+ ### Test Exception Tracking
90
+ ```ruby
91
+ # spec/active_rabbit/exception_tracker_spec.rb
92
+ RSpec.describe ActiveRabbit::Client::ExceptionTracker do
93
+ let(:configuration) { ActiveRabbit::Client::Configuration.new }
94
+ let(:http_client) { instance_double(ActiveRabbit::Client::HttpClient) }
95
+ let(:tracker) { described_class.new(configuration, http_client) }
96
+ let(:exception) { StandardError.new("Test error") }
97
+
98
+ before do
99
+ configuration.api_key = "test-key"
100
+ configuration.project_id = "test-project"
101
+ end
102
+
103
+ describe "#track_exception" do
104
+ it "sends exception data to API" do
105
+ expect(http_client).to receive(:post_exception).with(hash_including(
106
+ type: "StandardError",
107
+ message: "Test error"
108
+ ))
109
+
110
+ tracker.track_exception(
111
+ exception: exception,
112
+ context: { user_id: "123" }
113
+ )
114
+ end
115
+
116
+ it "ignores filtered exceptions" do
117
+ configuration.ignored_exceptions = ["StandardError"]
118
+
119
+ expect(http_client).not_to receive(:post_exception)
120
+
121
+ tracker.track_exception(exception: exception)
122
+ end
123
+
124
+ it "applies before_send callback" do
125
+ configuration.before_send_exception = proc do |data|
126
+ data[:custom_field] = "test_value"
127
+ data
128
+ end
129
+
130
+ expect(http_client).to receive(:post_exception).with(
131
+ hash_including(custom_field: "test_value")
132
+ )
133
+
134
+ tracker.track_exception(exception: exception)
135
+ end
136
+ end
137
+ end
138
+ ```
139
+
140
+ ### Test PII Scrubbing
141
+ ```ruby
142
+ # spec/active_rabbit/pii_scrubber_spec.rb
143
+ RSpec.describe ActiveRabbit::Client::PiiScrubber do
144
+ let(:configuration) { ActiveRabbit::Client::Configuration.new }
145
+ let(:scrubber) { described_class.new(configuration) }
146
+
147
+ describe "#scrub" do
148
+ it "scrubs sensitive data" do
149
+ data = {
150
+ email: "user@example.com",
151
+ password: "secret123",
152
+ safe_field: "safe_value"
153
+ }
154
+
155
+ result = scrubber.scrub(data)
156
+
157
+ expect(result[:email]).to eq("[FILTERED]")
158
+ expect(result[:password]).to eq("[FILTERED]")
159
+ expect(result[:safe_field]).to eq("safe_value")
160
+ end
161
+ end
162
+ end
163
+ ```
164
+
165
+ ## ๐Ÿ”— Integration Tests with Rails
166
+
167
+ ### Test Rails Middleware Integration
168
+ ```ruby
169
+ # spec/integration/rails_integration_spec.rb
170
+ RSpec.describe "Rails Integration", type: :request do
171
+ before do
172
+ ActiveRabbit::Client.configure do |config|
173
+ config.api_key = "test-api-key"
174
+ config.project_id = "test-project"
175
+ end
176
+ end
177
+
178
+ describe "exception middleware" do
179
+ it "captures unhandled exceptions" do
180
+ # Stub the API call
181
+ stub_request(:post, "https://api.activerabbit.com/api/v1/exceptions")
182
+ .to_return(status: 200, body: '{"status":"ok"}')
183
+
184
+ # Create a route that raises an exception
185
+ Rails.application.routes.draw do
186
+ get '/test_error', to: proc { raise StandardError, "Test error" }
187
+ end
188
+
189
+ expect {
190
+ get '/test_error'
191
+ }.to raise_error(StandardError, "Test error")
192
+
193
+ # Verify the API was called
194
+ expect(WebMock).to have_requested(:post, "https://api.activerabbit.com/api/v1/exceptions")
195
+ .with(body: hash_including(
196
+ type: "StandardError",
197
+ message: "Test error"
198
+ ))
199
+ end
200
+ end
201
+
202
+ describe "performance monitoring" do
203
+ it "tracks controller performance" do
204
+ stub_request(:post, "https://api.activerabbit.com/api/v1/performance")
205
+ .to_return(status: 200, body: '{"status":"ok"}')
206
+
207
+ get '/some_endpoint'
208
+
209
+ expect(WebMock).to have_requested(:post, "https://api.activerabbit.com/api/v1/performance")
210
+ end
211
+ end
212
+ end
213
+ ```
214
+
215
+ ### Test Controller Integration
216
+ ```ruby
217
+ # spec/controllers/application_controller_spec.rb
218
+ RSpec.describe ApplicationController, type: :controller do
219
+ controller do
220
+ def index
221
+ raise StandardError, "Controller error"
222
+ end
223
+ end
224
+
225
+ before do
226
+ ActiveRabbit::Client.configure do |config|
227
+ config.api_key = "test-key"
228
+ config.project_id = "test-project"
229
+ end
230
+
231
+ stub_request(:post, /api\.activerabbit\.com/)
232
+ .to_return(status: 200, body: '{"status":"ok"}')
233
+ end
234
+
235
+ it "tracks exceptions with request context" do
236
+ expect {
237
+ get :index
238
+ }.to raise_error(StandardError)
239
+
240
+ expect(WebMock).to have_requested(:post, "https://api.activerabbit.com/api/v1/exceptions")
241
+ .with { |req|
242
+ body = JSON.parse(req.body)
243
+ body["context"]["request"]["method"] == "GET"
244
+ }
245
+ end
246
+ end
247
+ ```
248
+
249
+ ## ๐ŸŽญ End-to-End Testing
250
+
251
+ ### Test Complete Error Flow
252
+ ```ruby
253
+ # spec/features/error_tracking_spec.rb
254
+ RSpec.describe "Error Tracking Flow", type: :feature do
255
+ before do
256
+ ActiveRabbit::Client.configure do |config|
257
+ config.api_key = "test-key"
258
+ config.project_id = "test-project"
259
+ end
260
+ end
261
+
262
+ scenario "user triggers an error and it gets tracked" do
263
+ # Stub API calls
264
+ exception_stub = stub_request(:post, "https://api.activerabbit.com/api/v1/exceptions")
265
+ .to_return(status: 200, body: '{"status":"ok"}')
266
+
267
+ # Create a user and simulate an error condition
268
+ user = create(:user)
269
+
270
+ # Visit a page that will cause an error
271
+ visit "/users/999999" # Non-existent user
272
+
273
+ # Verify the exception was tracked
274
+ expect(exception_stub).to have_been_requested.with { |req|
275
+ body = JSON.parse(req.body)
276
+ body["type"] == "ActiveRecord::RecordNotFound" &&
277
+ body["context"]["request"]["path"] == "/users/999999"
278
+ }
279
+ end
280
+ end
281
+ ```
282
+
283
+ ### Test N+1 Query Detection
284
+ ```ruby
285
+ # spec/features/n_plus_one_detection_spec.rb
286
+ RSpec.describe "N+1 Query Detection", type: :feature do
287
+ before do
288
+ ActiveRabbit::Client.configure do |config|
289
+ config.api_key = "test-key"
290
+ config.enable_n_plus_one_detection = true
291
+ end
292
+ end
293
+
294
+ scenario "detects N+1 queries" do
295
+ event_stub = stub_request(:post, "https://api.activerabbit.com/api/v1/events")
296
+ .to_return(status: 200, body: '{"status":"ok"}')
297
+
298
+ # Create test data that will cause N+1
299
+ users = create_list(:user, 5)
300
+ users.each { |user| create(:post, user: user) }
301
+
302
+ # Visit page that triggers N+1 (without includes)
303
+ visit "/users" # Assuming this lists users and their post counts
304
+
305
+ # Verify N+1 detection was triggered
306
+ expect(event_stub).to have_been_requested.with { |req|
307
+ body = JSON.parse(req.body)
308
+ body["name"] == "n_plus_one_detected"
309
+ }
310
+ end
311
+ end
312
+ ```
313
+
314
+ ## โšก Performance Testing
315
+
316
+ ### Test Performance Impact
317
+ ```ruby
318
+ # spec/performance/activerabbit_performance_spec.rb
319
+ RSpec.describe "ActiveRabbit Performance Impact" do
320
+ let(:iterations) { 100 }
321
+
322
+ it "has minimal impact on request processing time" do
323
+ # Baseline without ActiveRabbit
324
+ baseline_time = Benchmark.measure do
325
+ iterations.times { make_test_request }
326
+ end
327
+
328
+ # Configure ActiveRabbit
329
+ ActiveRabbit::Client.configure do |config|
330
+ config.api_key = "test-key"
331
+ config.project_id = "test-project"
332
+ end
333
+
334
+ stub_request(:post, /api\.activerabbit\.com/)
335
+ .to_return(status: 200, body: '{"status":"ok"}')
336
+
337
+ # Time with ActiveRabbit
338
+ activerabbit_time = Benchmark.measure do
339
+ iterations.times { make_test_request }
340
+ end
341
+
342
+ # Performance impact should be minimal (< 5%)
343
+ impact_percentage = ((activerabbit_time.real - baseline_time.real) / baseline_time.real) * 100
344
+ expect(impact_percentage).to be < 5
345
+ end
346
+
347
+ private
348
+
349
+ def make_test_request
350
+ get "/test_endpoint"
351
+ end
352
+ end
353
+ ```
354
+
355
+ ### Test Memory Usage
356
+ ```ruby
357
+ # spec/performance/memory_usage_spec.rb
358
+ RSpec.describe "Memory Usage" do
359
+ it "doesn't cause memory leaks" do
360
+ ActiveRabbit::Client.configure do |config|
361
+ config.api_key = "test-key"
362
+ end
363
+
364
+ stub_request(:post, /api\.activerabbit\.com/)
365
+ .to_return(status: 200, body: '{"status":"ok"}')
366
+
367
+ # Measure initial memory
368
+ GC.start
369
+ initial_memory = `ps -o rss= -p #{Process.pid}`.to_i
370
+
371
+ # Generate many errors
372
+ 1000.times do |i|
373
+ begin
374
+ raise StandardError, "Test error #{i}"
375
+ rescue => e
376
+ ActiveRabbit::Client.track_exception(e)
377
+ end
378
+ end
379
+
380
+ # Force cleanup
381
+ ActiveRabbit::Client.flush
382
+ GC.start
383
+
384
+ # Measure final memory
385
+ final_memory = `ps -o rss= -p #{Process.pid}`.to_i
386
+ memory_increase = final_memory - initial_memory
387
+
388
+ # Memory increase should be reasonable (< 50MB)
389
+ expect(memory_increase).to be < 50_000 # KB
390
+ end
391
+ end
392
+ ```
393
+
394
+ ## ๐ŸŽฏ Production Simulation Tests
395
+
396
+ ### Test with High Volume
397
+ ```ruby
398
+ # spec/load/high_volume_spec.rb
399
+ RSpec.describe "High Volume Testing" do
400
+ before do
401
+ ActiveRabbit::Client.configure do |config|
402
+ config.api_key = "test-key"
403
+ config.batch_size = 10
404
+ config.flush_interval = 1
405
+ end
406
+
407
+ stub_request(:post, /api\.activerabbit\.com/)
408
+ .to_return(status: 200, body: '{"status":"ok"}')
409
+ end
410
+
411
+ it "handles high volume of exceptions" do
412
+ # Simulate high error volume
413
+ threads = []
414
+
415
+ 10.times do
416
+ threads << Thread.new do
417
+ 100.times do |i|
418
+ begin
419
+ raise StandardError, "High volume error #{i}"
420
+ rescue => e
421
+ ActiveRabbit::Client.track_exception(e)
422
+ end
423
+ sleep(0.01) # Small delay to simulate real usage
424
+ end
425
+ end
426
+ end
427
+
428
+ threads.each(&:join)
429
+ ActiveRabbit::Client.flush
430
+
431
+ # Verify all requests were batched and sent
432
+ expect(WebMock).to have_requested(:post, /api\.activerabbit\.com/).at_least_times(100)
433
+ end
434
+ end
435
+ ```
436
+
437
+ ### Test API Failures
438
+ ```ruby
439
+ # spec/resilience/api_failure_spec.rb
440
+ RSpec.describe "API Failure Resilience" do
441
+ before do
442
+ ActiveRabbit::Client.configure do |config|
443
+ config.api_key = "test-key"
444
+ config.retry_count = 2
445
+ end
446
+ end
447
+
448
+ it "handles API failures gracefully" do
449
+ # Stub API to fail initially, then succeed
450
+ stub_request(:post, /api\.activerabbit\.com/)
451
+ .to_return(status: 500).times(2)
452
+ .then.to_return(status: 200, body: '{"status":"ok"}')
453
+
454
+ # This should not raise an error
455
+ expect {
456
+ ActiveRabbit::Client.track_exception(StandardError.new("Test"))
457
+ }.not_to raise_error
458
+
459
+ # Verify retries were attempted
460
+ expect(WebMock).to have_requested(:post, /api\.activerabbit\.com/).times(3)
461
+ end
462
+
463
+ it "doesn't crash app when API is completely down" do
464
+ stub_request(:post, /api\.activerabbit\.com/)
465
+ .to_return(status: 500)
466
+
467
+ # App should continue working even if ActiveRabbit API is down
468
+ expect {
469
+ 100.times do
470
+ ActiveRabbit::Client.track_exception(StandardError.new("Test"))
471
+ end
472
+ }.not_to raise_error
473
+ end
474
+ end
475
+ ```
476
+
477
+ ## ๐Ÿš€ Pre-Production Checklist
478
+
479
+ ### Test Script for Manual Verification
480
+ ```ruby
481
+ # script/test_activerabbit.rb
482
+ #!/usr/bin/env ruby
483
+
484
+ puts "๐Ÿงช Testing ActiveRabbit Integration..."
485
+
486
+ # Test 1: Configuration
487
+ puts "\n1. Testing Configuration..."
488
+ ActiveRabbit::Client.configure do |config|
489
+ config.api_key = ENV['active_rabbit_API_KEY'] || 'test-key'
490
+ config.project_id = ENV['active_rabbit_PROJECT_ID'] || 'test-project'
491
+ config.environment = 'test'
492
+ end
493
+
494
+ if ActiveRabbit::Client.configured?
495
+ puts "โœ… Configuration successful"
496
+ else
497
+ puts "โŒ Configuration failed"
498
+ exit 1
499
+ end
500
+
501
+ # Test 2: Exception Tracking
502
+ puts "\n2. Testing Exception Tracking..."
503
+ begin
504
+ raise StandardError, "Test exception for ActiveRabbit"
505
+ rescue => e
506
+ ActiveRabbit::Client.track_exception(e, context: { test: true })
507
+ puts "โœ… Exception tracked"
508
+ end
509
+
510
+ # Test 3: Event Tracking
511
+ puts "\n3. Testing Event Tracking..."
512
+ ActiveRabbit::Client.track_event(
513
+ 'test_event',
514
+ { component: 'test_script', timestamp: Time.current },
515
+ user_id: 'test-user'
516
+ )
517
+ puts "โœ… Event tracked"
518
+
519
+ # Test 4: Performance Tracking
520
+ puts "\n4. Testing Performance Tracking..."
521
+ ActiveRabbit::Client.track_performance(
522
+ 'test_operation',
523
+ 150.5,
524
+ metadata: { test: true }
525
+ )
526
+ puts "โœ… Performance tracked"
527
+
528
+ # Test 5: Flush and Shutdown
529
+ puts "\n5. Testing Flush and Shutdown..."
530
+ ActiveRabbit::Client.flush
531
+ ActiveRabbit::Client.shutdown
532
+ puts "โœ… Flush and shutdown successful"
533
+
534
+ puts "\n๐ŸŽ‰ All tests passed! ActiveRabbit is ready for production."
535
+ ```
536
+
537
+ ## ๐Ÿ“‹ Running the Tests
538
+
539
+ ### Complete Test Suite
540
+ ```bash
541
+ # Run all tests
542
+ bundle exec rspec
543
+
544
+ # Run specific test types
545
+ bundle exec rspec spec/active_rabbit/ # Unit tests
546
+ bundle exec rspec spec/integration/ # Integration tests
547
+ bundle exec rspec spec/features/ # End-to-end tests
548
+ bundle exec rspec spec/performance/ # Performance tests
549
+
550
+ # Run with coverage
551
+ COVERAGE=true bundle exec rspec
552
+
553
+ # Run manual test script
554
+ ruby script/test_activerabbit.rb
555
+ ```
556
+
557
+ ### CI/CD Integration
558
+ ```yaml
559
+ # .github/workflows/test.yml
560
+ name: Test ActiveRabbit Integration
561
+
562
+ on: [push, pull_request]
563
+
564
+ jobs:
565
+ test:
566
+ runs-on: ubuntu-latest
567
+
568
+ steps:
569
+ - uses: actions/checkout@v2
570
+ - name: Setup Ruby
571
+ uses: ruby/setup-ruby@v1
572
+ with:
573
+ ruby-version: 3.2.0
574
+ bundler-cache: true
575
+
576
+ - name: Run tests
577
+ run: |
578
+ bundle exec rspec
579
+ ruby script/test_activerabbit.rb
580
+ env:
581
+ active_rabbit_API_KEY: ${{ secrets.active_rabbit_API_KEY }}
582
+ active_rabbit_PROJECT_ID: ${{ secrets.active_rabbit_PROJECT_ID }}
583
+ ```
584
+
585
+ This comprehensive testing approach ensures your Rails application and ActiveRabbit gem integration is thoroughly tested before production deployment, covering functionality, performance, and resilience scenarios.