import 'package:test/test.dart'; import 'package:sqlite3/sqlite3.dart'; import 'package:xp_nix/src/monitors/productivity_monitor.dart'; import 'package:xp_nix/src/testing/mock_idle_monitor.dart'; import 'package:xp_nix/src/testing/mock_activity_detector.dart'; import 'package:xp_nix/src/testing/mock_time_provider.dart'; import 'package:xp_nix/src/testing/mock_desktop_enhancer.dart'; import 'package:xp_nix/src/config/config_manager.dart'; void main() { group('Work Day 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 monitor with mocked dependencies monitor = ProductivityMonitor( db: db, idleMonitor: mockIdleMonitor, timeProvider: mockTimeProvider, desktopEnhancer: mockDesktopEnhancer, activityDetector: mockActivityDetector, ); }); 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 a full productive work day', () async { // Start the monitor monitor.start(); // Wait a moment for initialization await Future.delayed(Duration(milliseconds: 10)); // === MORNING CODING SESSION (9:00 - 10:30) === print('\nšŸŒ… Starting morning coding session...'); mockActivityDetector.simulateActivity('vscode', 'main.dart - ProductivityApp'); mockTimeProvider.advanceTime(Duration(minutes: 90)); monitor.flushCurrentActivity(); // Save the activity // Simulate switching to a different file mockActivityDetector.simulateActivity('vscode', 'database_manager.dart - ProductivityApp'); mockTimeProvider.advanceTime(Duration(minutes: 30)); monitor.flushCurrentActivity(); // Save the activity // Check stats after morning coding var stats = monitor.getTodayStats(); 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'); print('šŸ“Š After morning coding: ${stats['xp']} XP, ${(stats['focus_time'] / 3600).toStringAsFixed(1)}h focus'); // === BRIEF BREAK - CHECK SLACK (10:30 - 10:45) === print('\nšŸ’¬ Quick Slack check...'); mockActivityDetector.simulateActivity('slack', 'General Channel'); mockTimeProvider.advanceTime(Duration(minutes: 15)); monitor.flushCurrentActivity(); // === RESEARCH SESSION (10:45 - 11:30) === print('\nšŸ” Research session...'); mockActivityDetector.simulateActivity('firefox', 'Dart Documentation - dart.dev'); mockTimeProvider.advanceTime(Duration(minutes: 45)); monitor.flushCurrentActivity(); // === TEAM MEETING (11:30 - 12:30) === print('\nšŸ“… Team meeting...'); mockActivityDetector.simulateActivity('zoom', 'Weekly Team Standup'); mockTimeProvider.advanceTime(Duration(minutes: 60)); monitor.flushCurrentActivity(); // Check stats after meeting stats = monitor.getTodayStats(); expect(stats['meeting_time'], greaterThan(0), reason: 'Should have meeting time'); print('šŸ“Š After meeting: ${stats['xp']} XP, ${(stats['meeting_time'] / 3600).toStringAsFixed(1)}h meetings'); // === LUNCH BREAK - GO IDLE (12:30 - 13:30) === print('\nšŸ½ļø Lunch break - going idle...'); mockIdleMonitor.simulateIdle(); mockTimeProvider.advanceTime(Duration(minutes: 60)); // Come back from lunch print('\nšŸ”„ Back from lunch...'); mockIdleMonitor.simulateActive(); // Should get focus session bonus for the morning work stats = monitor.getTodayStats(); expect(stats['focus_sessions'], greaterThan(0), reason: 'Should have focus sessions from idle recovery'); print('šŸ“Š After lunch: ${stats['focus_sessions']} focus sessions completed'); // === AFTERNOON CODING SPRINT (13:30 - 15:30) === print('\n⚔ Afternoon coding sprint...'); mockActivityDetector.simulateActivity('vscode', 'test_suite.dart - ProductivityApp'); mockTimeProvider.advanceTime(Duration(minutes: 120)); monitor.flushCurrentActivity(); // === DOCUMENTATION WORK (15:30 - 16:30) === print('\nšŸ“ Documentation work...'); mockActivityDetector.simulateActivity('vscode', 'README.md - ProductivityApp'); mockTimeProvider.advanceTime(Duration(minutes: 60)); monitor.flushCurrentActivity(); // === END OF DAY WRAP-UP (16:30 - 17:00) === print('\nšŸ“‹ End of day wrap-up...'); mockActivityDetector.simulateActivity('slack', 'Daily Summary'); mockTimeProvider.advanceTime(Duration(minutes: 30)); monitor.flushCurrentActivity(); // === FINAL STATS VERIFICATION === print('\nšŸ“ˆ Final day statistics:'); stats = monitor.getTodayStats(); monitor.printDetailedStats(); // Verify XP calculations expect(stats['xp'], greaterThan(1000), reason: 'Should have substantial XP from full work day'); expect(stats['level'], greaterThanOrEqualTo(2), reason: 'Should have leveled up'); // Verify focus time (coding + research) final focusHours = stats['focus_time'] / 3600; expect(focusHours, greaterThan(4), reason: 'Should have 4+ hours of focus time'); expect(focusHours, lessThan(6), reason: 'Focus time should be reasonable'); // Verify meeting time final meetingHours = stats['meeting_time'] / 3600; expect(meetingHours, greaterThan(0.9), reason: 'Should have ~1 hour of meeting time'); expect(meetingHours, lessThan(1.1), reason: 'Meeting time should be accurate'); // Verify focus sessions expect(stats['focus_sessions'], greaterThanOrEqualTo(1), reason: 'Should have at least 1 focus session'); // Test specific XP calculations _verifyXPCalculations(stats); print('\nāœ… Work day simulation completed successfully!'); print( 'šŸ“Š Final Stats: Level ${stats['level']}, ${stats['xp']} XP, ${focusHours.toStringAsFixed(1)}h focus, ${stats['focus_sessions']} sessions', ); }); test('simulates rapid context switching day', () async { monitor.start(); await Future.delayed(Duration(milliseconds: 10)); print('\nšŸ”„ Simulating rapid context switching...'); // Simulate a day with lots of short activities final activities = [ ('vscode', 'main.dart', 15), // 15 min coding ('slack', 'General', 5), // 5 min slack ('firefox', 'Stack Overflow', 10), // 10 min research ('vscode', 'test.dart', 20), // 20 min coding ('zoom', 'Quick sync', 15), // 15 min meeting ('vscode', 'bug_fix.dart', 25), // 25 min coding ('slack', 'Code review', 10), // 10 min collaboration ]; for (final (app, title, minutes) in activities) { mockActivityDetector.simulateActivity(app, title); mockTimeProvider.advanceTime(Duration(minutes: minutes)); monitor.flushCurrentActivity(); // Flush each activity } final stats = monitor.getTodayStats(); // Should still accumulate reasonable XP despite context switching expect(stats['xp'], greaterThan(200), reason: 'Should earn XP from varied activities'); expect(stats['focus_time'], greaterThan(0), reason: 'Should have some focus time'); print('šŸ“Š Context switching day: ${stats['xp']} XP, ${(stats['focus_time'] / 3600).toStringAsFixed(1)}h focus'); }); test('simulates achievement unlocking', () async { monitor.start(); await Future.delayed(Duration(milliseconds: 10)); print('\nšŸ† Testing achievement unlocking...'); // Simulate extended coding to trigger achievements mockActivityDetector.simulateActivity('vscode', 'epic_feature.dart'); // Advance time to accumulate enough XP for level 5 (need ~500 XP) // 30 min * 10 XP/min = 300 XP per flush, need at least 2 flushes for (int i = 0; i < 3; i++) { mockTimeProvider.advanceTime(Duration(minutes: 60)); // 1 hour each monitor.flushCurrentActivity(); await Future.delayed(Duration(milliseconds: 1)); // Allow level check } final stats = monitor.getTodayStats(); expect(stats['level'], greaterThanOrEqualTo(2), reason: 'Should reach at least level 2'); expect(stats['xp'], greaterThan(500), reason: 'Should have substantial XP'); print('šŸ“Š Achievement test: Level ${stats['level']}, ${stats['xp']} XP'); }); test('verifies idle time handling', () async { monitor.start(); await Future.delayed(Duration(milliseconds: 10)); print('\n😓 Testing idle time handling...'); // Start with some activity and make sure user is active mockIdleMonitor.simulateActive(); // Ensure we start active mockActivityDetector.simulateActivity('vscode', 'work.dart'); mockTimeProvider.advanceTime(Duration(minutes: 30)); monitor.flushCurrentActivity(); // Go idle for extended period mockIdleMonitor.simulateIdle(); mockTimeProvider.advanceTime(Duration(minutes: 90)); // 1.5 hours idle // Come back active - this should trigger focus session calculation mockIdleMonitor.simulateActive(); // Should get focus session bonus final stats = monitor.getTodayStats(); expect(stats['focus_sessions'], greaterThan(0), reason: 'Should award focus session after idle period'); print('šŸ“Š Idle handling: ${stats['focus_sessions']} focus sessions awarded'); }); }); } /// Verify that XP calculations are working correctly void _verifyXPCalculations(Map stats) { print('\n🧮 Verifying XP calculations...'); // Expected XP breakdown (approximate): // - Coding: ~4 hours * 60 min * 10 XP/min = ~2400 XP // - Research: ~45 min * 6 XP/min = ~270 XP // - Meetings: ~60 min * 3 XP/min = ~180 XP // - Collaboration: ~45 min * 7 XP/min = ~315 XP // - Focus session bonuses: varies final totalXP = stats['xp'] as int; expect(totalXP, greaterThan(2000), reason: 'Should have substantial XP from full day'); expect(totalXP, lessThan(5000), reason: 'XP should be reasonable, not inflated'); print('āœ… XP calculations appear correct: $totalXP total XP'); }