xp_nix/test/deep_idle_test.dart

204 lines
7.8 KiB
Dart

import 'package:test/test.dart';
import 'package:sqlite3/sqlite3.dart';
import 'package:xp_nix/src/monitors/productivity_monitor.dart';
import '../lib/src/testing/mock_idle_monitor.dart';
import '../lib/src/testing/mock_activity_detector.dart';
import '../lib/src/testing/mock_time_provider.dart';
import '../lib/src/testing/mock_desktop_enhancer.dart';
import '../lib/src/config/config_manager.dart';
void main() {
group('Deep Idle Event Tests', () {
late Database db;
late ProductivityMonitor monitor;
late MockIdleMonitor mockIdleMonitor;
late MockActivityDetector mockActivityDetector;
late MockTimeProvider mockTimeProvider;
late MockDesktopEnhancer mockDesktopEnhancer;
setUp(() async {
// Create in-memory database for testing
db = sqlite3.openInMemory();
// Reset and initialize ConfigManager with default config
ConfigManager.resetInstance();
await ConfigManager.instance.initialize('/tmp/test_config_${DateTime.now().millisecondsSinceEpoch}.json');
// Create mock dependencies
mockIdleMonitor = MockIdleMonitor();
mockActivityDetector = MockActivityDetector();
mockTimeProvider = MockTimeProvider();
mockDesktopEnhancer = MockDesktopEnhancer();
// Set up starting time (Monday 9 AM)
mockTimeProvider.setTime(DateTime(2024, 1, 1, 9, 0));
// Create testable monitor with mocked dependencies
monitor = ProductivityMonitor(
db: db,
idleMonitor: mockIdleMonitor,
activityDetector: mockActivityDetector,
timeProvider: mockTimeProvider,
desktopEnhancer: mockDesktopEnhancer,
);
});
tearDown(() async {
monitor.stop();
// Add a small delay to allow async operations to complete
await Future.delayed(Duration(milliseconds: 100));
try {
db.dispose();
} catch (e) {
// Database might already be closed, ignore the error
}
});
test('should end current activity and award XP when user goes deep idle', () async {
// Start the monitor
monitor.start();
await Future.delayed(Duration(milliseconds: 10));
print('\n🧪 Testing deep idle behavior...');
// Start a coding activity
mockActivityDetector.simulateActivity('vscode', 'main.dart - TestProject');
await Future.delayed(Duration(milliseconds: 10));
// Work for 30 minutes
mockTimeProvider.advanceTime(Duration(minutes: 30));
// Check that no activity has been saved yet (still in progress)
var stats = monitor.getTodayStats();
var initialXP = stats['xp'] as int;
print('📊 Initial XP before deep idle: $initialXP');
// User goes deep idle - this should trigger ending the current activity
print('😴 User goes deep idle...');
mockIdleMonitor.simulateDeepIdle();
await Future.delayed(Duration(milliseconds: 50)); // Allow event processing
// Check that the activity was saved and XP was awarded
stats = monitor.getTodayStats();
var finalXP = stats['xp'] as int;
var focusTime = stats['focus_time'] as int;
print('📊 Final XP after deep idle: $finalXP');
print('📊 Focus time: ${(focusTime / 60).toStringAsFixed(1)} minutes');
// Verify that XP was awarded for the 30-minute coding session
expect(finalXP, greaterThan(initialXP),
reason: 'Should have earned XP from the coding session when going deep idle');
// Expected: 30 minutes * 10 XP/min = 300 XP (base, before multipliers)
expect(finalXP, greaterThan(200),
reason: 'Should have earned substantial XP from 30 minutes of coding');
// Should have focus time from coding
expect(focusTime, greaterThan(25 * 60),
reason: 'Should have at least 25 minutes of focus time recorded');
print('✅ Deep idle activity ending test passed!');
});
test('should not duplicate XP when going from light idle to deep idle', () async {
// Start the monitor
monitor.start();
await Future.delayed(Duration(milliseconds: 10));
print('\n🧪 Testing light idle to deep idle transition...');
// Start a coding activity
mockActivityDetector.simulateActivity('vscode', 'feature.dart');
await Future.delayed(Duration(milliseconds: 10));
// Work for 20 minutes
mockTimeProvider.advanceTime(Duration(minutes: 20));
// User goes light idle first
print('😐 User goes light idle...');
mockIdleMonitor.simulateLightIdle();
await Future.delayed(Duration(milliseconds: 50));
var stats = monitor.getTodayStats();
var xpAfterLightIdle = stats['xp'] as int;
print('📊 XP after light idle: $xpAfterLightIdle');
// Then user goes deep idle
print('😴 User goes deep idle...');
mockIdleMonitor.simulateDeepIdle();
await Future.delayed(Duration(milliseconds: 50));
stats = monitor.getTodayStats();
var xpAfterDeepIdle = stats['xp'] as int;
print('📊 XP after deep idle: $xpAfterDeepIdle');
// XP should have been awarded when going deep idle, but not duplicated
expect(xpAfterDeepIdle, greaterThan(0),
reason: 'Should have earned XP from the coding session');
// The XP should be the same whether we went through light idle or not
// (since the activity should only be saved once when going deep idle)
expect(xpAfterDeepIdle, greaterThan(150),
reason: 'Should have earned XP for 20 minutes of coding');
print('✅ Light idle to deep idle transition test passed!');
});
test('should handle multiple activity sessions with deep idle interruptions', () async {
// Start the monitor
monitor.start();
await Future.delayed(Duration(milliseconds: 10));
print('\n🧪 Testing multiple sessions with deep idle interruptions...');
// First coding session
mockActivityDetector.simulateActivity('vscode', 'session1.dart');
await Future.delayed(Duration(milliseconds: 10));
mockTimeProvider.advanceTime(Duration(minutes: 25));
// Go deep idle (should end first session)
mockIdleMonitor.simulateDeepIdle();
await Future.delayed(Duration(milliseconds: 50));
var stats = monitor.getTodayStats();
var xpAfterFirstSession = stats['xp'] as int;
print('📊 XP after first session: $xpAfterFirstSession');
// User becomes active again
mockIdleMonitor.simulateActive();
await Future.delayed(Duration(milliseconds: 10));
// Second coding session
mockActivityDetector.simulateActivity('vscode', 'session2.dart');
await Future.delayed(Duration(milliseconds: 10));
mockTimeProvider.advanceTime(Duration(minutes: 35));
// Go deep idle again (should end second session)
mockIdleMonitor.simulateDeepIdle();
await Future.delayed(Duration(milliseconds: 50));
stats = monitor.getTodayStats();
var finalXP = stats['xp'] as int;
var totalFocusTime = stats['focus_time'] as int;
print('📊 Final XP after both sessions: $finalXP');
print('📊 Total focus time: ${(totalFocusTime / 60).toStringAsFixed(1)} minutes');
// Should have XP from both sessions
expect(finalXP, greaterThan(xpAfterFirstSession),
reason: 'Should have earned additional XP from second session');
// Should have substantial XP from 60 minutes total (25 + 35)
expect(finalXP, greaterThan(400),
reason: 'Should have earned substantial XP from both coding sessions');
// Should have focus time from both sessions (at least 55 minutes)
expect(totalFocusTime, greaterThan(55 * 60),
reason: 'Should have focus time from both sessions');
print('✅ Multiple sessions with deep idle test passed!');
});
});
}