xp_nix/test/simulation/work_day_simulation_test.dart

268 lines
11 KiB
Dart

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<String, dynamic> 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');
}