188 lines
7.4 KiB
Dart
188 lines
7.4 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('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!');
|
||
});
|
||
});
|
||
}
|