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('Simple Productivity Simulation 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('simulates basic coding session with XP calculation', () async { // Start the monitor monitor.start(); await Future.delayed(Duration(milliseconds: 10)); print('\nšŸ’» Starting coding session...'); // Simulate 1 hour of coding mockActivityDetector.simulateActivity('vscode', 'main.dart - TestProject'); await Future.delayed(Duration(milliseconds: 1)); // Allow event to be processed mockTimeProvider.advanceTime(Duration(minutes: 60)); monitor.flushCurrentActivity(); // Check stats final stats = monitor.getTodayStats(); print( 'šŸ“Š Stats: ${stats['xp']} XP, Level ${stats['level']}, ${(stats['focus_time'] / 3600).toStringAsFixed(1)}h focus', ); // Verify XP was earned expect(stats['xp'], greaterThan(0), reason: 'Should have earned XP from coding'); expect(stats['focus_time'], greaterThan(0), reason: 'Should have focus time from coding'); expect(stats['level'], greaterThanOrEqualTo(1), reason: 'Should have at least level 1'); // Expected: 60 minutes * 10 XP/min = 600 XP (plus any time multipliers) expect(stats['xp'], greaterThan(500), reason: 'Should have substantial XP from 1 hour coding'); print('āœ… Basic coding session test passed!'); }); test('simulates mixed activity day', () async { monitor.start(); await Future.delayed(Duration(milliseconds: 10)); print('\nšŸ”„ Simulating mixed activity day...'); // Morning coding (2 hours) mockActivityDetector.simulateActivity('vscode', 'feature.dart'); await Future.delayed(Duration(milliseconds: 1)); mockTimeProvider.advanceTime(Duration(minutes: 120)); monitor.flushCurrentActivity(); // Meeting (1 hour) mockActivityDetector.simulateActivity('zoom', 'Team Meeting'); await Future.delayed(Duration(milliseconds: 1)); mockTimeProvider.advanceTime(Duration(minutes: 60)); monitor.flushCurrentActivity(); // Research (30 minutes) mockActivityDetector.simulateActivity('firefox', 'Documentation - dart.dev'); await Future.delayed(Duration(milliseconds: 1)); mockTimeProvider.advanceTime(Duration(minutes: 30)); monitor.flushCurrentActivity(); final stats = monitor.getTodayStats(); print('šŸ“Š Mixed day stats: ${stats['xp']} XP, Level ${stats['level']}'); print( ' Focus: ${(stats['focus_time'] / 3600).toStringAsFixed(1)}h, Meetings: ${(stats['meeting_time'] / 3600).toStringAsFixed(1)}h', ); // Verify different activity types expect(stats['xp'], greaterThan(1000), reason: 'Should have substantial XP from mixed activities'); expect(stats['focus_time'], greaterThan(0), reason: 'Should have focus time from coding and research'); expect(stats['meeting_time'], greaterThan(0), reason: 'Should have meeting time from zoom'); print('āœ… Mixed activity day test passed!'); }); test('demonstrates XP calculation accuracy', () async { monitor.start(); await Future.delayed(Duration(milliseconds: 10)); print('\n🧮 Testing XP calculation accuracy...'); // Test different activity types with known durations final activities = [ ('vscode', 'coding', 30, 10), // 30 min coding at 10 XP/min = 300 XP ('firefox', 'research', 20, 6), // 20 min research at 6 XP/min = 120 XP ('slack', 'collaboration', 15, 7), // 15 min collaboration at 7 XP/min = 105 XP ('zoom', 'meeting', 60, 3), // 60 min meeting at 3 XP/min = 180 XP ]; int expectedTotalXP = 0; for (final (app, description, minutes, xpPerMin) in activities) { mockActivityDetector.simulateActivity(app, description); await Future.delayed(Duration(milliseconds: 1)); mockTimeProvider.advanceTime(Duration(minutes: minutes)); monitor.flushCurrentActivity(); expectedTotalXP += minutes * xpPerMin; print(' $description: $minutes min Ɨ $xpPerMin XP/min = ${minutes * xpPerMin} XP'); } final stats = monitor.getTodayStats(); final actualXP = stats['xp'] as int; print('šŸ“Š Expected: ~$expectedTotalXP XP, Actual: $actualXP XP'); // Allow for time multipliers (should be close to expected) expect(actualXP, greaterThan(expectedTotalXP * 0.8), reason: 'XP should be reasonably close to expected'); expect(actualXP, lessThan(expectedTotalXP * 2.0), reason: 'XP should not be wildly inflated'); print('āœ… XP calculation accuracy test passed!'); }); test('verifies level progression', () async { monitor.start(); await Future.delayed(Duration(milliseconds: 10)); print('\nšŸ“ˆ Testing level progression...'); // Simulate enough activity to level up (need 100+ XP per level) for (int i = 0; i < 5; i++) { mockActivityDetector.simulateActivity('vscode', 'level_up_test_$i.dart'); await Future.delayed(Duration(milliseconds: 1)); mockTimeProvider.advanceTime(Duration(minutes: 30)); // 30 min * 10 XP/min = 300 XP monitor.flushCurrentActivity(); final stats = monitor.getTodayStats(); print(' Session ${i + 1}: ${stats['xp']} XP, Level ${stats['level']}'); } final finalStats = monitor.getTodayStats(); expect(finalStats['level'], greaterThan(1), reason: 'Should have leveled up'); expect(finalStats['xp'], greaterThan(1000), reason: 'Should have substantial XP'); print('āœ… Level progression test passed!'); }); }); }