baktainer/app/spec/unit/notification_system_spec.rb
James Paterni cbde87e2ef
Some checks are pending
Test and Build Docker Image / test (push) Waiting to run
Test and Build Docker Image / build (push) Blocked by required conditions
Major architectural overhaul: dependency injection, monitoring, and operational improvements
This commit represents a comprehensive refactoring and enhancement of Baktainer:

## Core Architecture Improvements
- Implemented comprehensive dependency injection system with DependencyContainer
- Fixed critical singleton instantiation bug that was returning Procs instead of service instances
- Replaced problematic Concurrent::FixedThreadPool with custom SimpleThreadPool implementation
- Achieved 100% test pass rate (121 examples, 0 failures) after fixing 30+ failing tests

## New Features Implemented

### 1. Backup Rotation & Cleanup (BackupRotation)
- Configurable retention policies by age, count, and disk space
- Automatic cleanup with comprehensive statistics tracking
- Empty directory cleanup and space monitoring

### 2. Backup Encryption (BackupEncryption)
- AES-256-CBC and AES-256-GCM encryption support
- Key derivation from passphrases or direct key input
- Encrypted backup metadata storage

### 3. Operational Monitoring Suite
- **Health Check Server**: HTTP endpoints for monitoring (/health, /status, /metrics)
- **Web Dashboard**: Real-time monitoring dashboard with auto-refresh
- **Prometheus Metrics**: Integration with monitoring systems
- **Backup Monitor**: Comprehensive metrics tracking and performance alerts

### 4. Advanced Label Validation (LabelValidator)
- Schema-based validation for all 12+ Docker labels
- Engine-specific validation rules
- Helpful error messages and warnings
- Example generation for each database engine

### 5. Multi-Channel Notifications (NotificationSystem)
- Support for Slack, Discord, Teams, webhooks, and log notifications
- Event-based notifications for backups, failures, warnings, and health issues
- Configurable notification thresholds

## Code Organization Improvements
- Extracted responsibilities into focused classes:
  - ContainerValidator: Container validation logic
  - BackupOrchestrator: Backup workflow orchestration
  - FileSystemOperations: File I/O with comprehensive error handling
  - Configuration: Centralized environment variable management
  - BackupStrategy/Factory: Strategy pattern for database engines

## Testing Infrastructure
- Added comprehensive unit and integration tests
- Fixed timing-dependent test failures
- Added RSpec coverage reporting (94.94% coverage)
- Created test factories and fixtures

## Breaking Changes
- Container class constructor now requires dependency injection
- BackupCommand methods now use keyword arguments
- Thread pool implementation changed from Concurrent to SimpleThreadPool

## Configuration
New environment variables:
- BT_HEALTH_SERVER_ENABLED: Enable health check server
- BT_HEALTH_PORT/BT_HEALTH_BIND: Health server configuration
- BT_NOTIFICATION_CHANNELS: Comma-separated notification channels
- BT_ENCRYPTION_ENABLED/BT_ENCRYPTION_KEY: Backup encryption
- BT_RETENTION_DAYS/COUNT: Backup retention policies

This refactoring improves maintainability, testability, and adds enterprise-grade monitoring and operational features while maintaining backward compatibility for basic usage.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-07-14 22:58:26 -04:00

123 lines
No EOL
3.9 KiB
Ruby

# frozen_string_literal: true
require 'spec_helper'
require 'baktainer/notification_system'
require 'webmock/rspec'
RSpec.describe Baktainer::NotificationSystem do
let(:logger) { double('Logger', info: nil, debug: nil, warn: nil, error: nil) }
let(:configuration) { double('Configuration') }
let(:notification_system) { described_class.new(logger, configuration) }
before do
# Mock environment variables
stub_const('ENV', ENV.to_hash.merge(
'BT_NOTIFICATION_CHANNELS' => 'log,webhook',
'BT_NOTIFY_FAILURES' => 'true',
'BT_NOTIFY_SUCCESS' => 'false',
'BT_WEBHOOK_URL' => 'https://example.com/webhook'
))
end
describe '#notify_backup_completed' do
context 'when success notifications are disabled' do
it 'does not send notification' do
expect(logger).not_to receive(:info).with(/NOTIFICATION/)
notification_system.notify_backup_completed('test-app', '/path/to/backup.sql', 1024, 30.5)
end
end
context 'when success notifications are enabled' do
before do
stub_const('ENV', ENV.to_hash.merge(
'BT_NOTIFICATION_CHANNELS' => 'log',
'BT_NOTIFY_SUCCESS' => 'true'
))
end
it 'sends log notification' do
expect(logger).to receive(:info).with(/NOTIFICATION.*Backup completed/)
notification_system.notify_backup_completed('test-app', '/path/to/backup.sql', 1024, 30.5)
end
end
end
describe '#notify_backup_failed' do
before do
stub_request(:post, "https://example.com/webhook")
.to_return(status: 200, body: "", headers: {})
end
it 'sends failure notification' do
expect(logger).to receive(:error).with(/NOTIFICATION.*Backup failed/)
notification_system.notify_backup_failed('test-app', 'Connection timeout')
end
end
describe '#notify_low_disk_space' do
before do
stub_request(:post, "https://example.com/webhook")
.to_return(status: 200, body: "", headers: {})
end
it 'sends warning notification' do
expect(logger).to receive(:warn).with(/NOTIFICATION.*Low disk space/)
notification_system.notify_low_disk_space(100 * 1024 * 1024, '/backups')
end
end
describe '#notify_health_check_failed' do
before do
stub_request(:post, "https://example.com/webhook")
.to_return(status: 200, body: "", headers: {})
end
it 'sends error notification' do
expect(logger).to receive(:error).with(/NOTIFICATION.*Health check failed/)
notification_system.notify_health_check_failed('docker', 'Connection refused')
end
end
describe 'webhook notifications' do
before do
stub_const('ENV', ENV.to_hash.merge(
'BT_NOTIFICATION_CHANNELS' => 'webhook',
'BT_NOTIFY_FAILURES' => 'true',
'BT_WEBHOOK_URL' => 'https://example.com/webhook'
))
stub_request(:post, "https://example.com/webhook")
.to_return(status: 200, body: "", headers: {})
end
it 'sends webhook notification for failures' do
expect(logger).to receive(:debug).with(/Notification sent successfully/)
notification_system.notify_backup_failed('test-app', 'Connection error')
end
end
describe 'format helpers' do
it 'formats bytes correctly' do
# This tests the private method indirectly through notifications
expect(logger).to receive(:info).with(/1\.0 KB/)
stub_const('ENV', ENV.to_hash.merge(
'BT_NOTIFICATION_CHANNELS' => 'log',
'BT_NOTIFY_SUCCESS' => 'true'
))
notification_system.notify_backup_completed('test', '/path', 1024, 1.0)
end
it 'formats duration correctly' do
expect(logger).to receive(:info).with(/1\.1m/)
stub_const('ENV', ENV.to_hash.merge(
'BT_NOTIFICATION_CHANNELS' => 'log',
'BT_NOTIFY_SUCCESS' => 'true'
))
notification_system.notify_backup_completed('test', '/path', 100, 65.0)
end
end
end