feat: add local database persistence

This commit is contained in:
2026-06-12 01:01:50 +08:00
parent 9ba681e3c7
commit b88b947e66
10 changed files with 4322 additions and 28 deletions
+115
View File
@@ -0,0 +1,115 @@
import 'dart:convert';
import 'package:drift/native.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:focus_seed/src/data/focus_seed_store.dart';
import 'package:focus_seed/src/data/local/focus_seed_database.dart';
import 'package:focus_seed/src/models/app_state.dart';
import 'package:focus_seed/src/models/focus_session.dart';
import 'package:focus_seed/src/models/habit.dart';
import 'package:focus_seed/src/models/mood.dart';
import 'package:focus_seed/src/models/reflection_entry.dart';
import 'package:shared_preferences/shared_preferences.dart';
void main() {
setUp(() {
SharedPreferences.setMockInitialValues({});
});
test('new installs seed localized starter habits into sqlite', () async {
final database = FocusSeedDatabase(NativeDatabase.memory());
addTearDown(database.close);
final store = FocusSeedStore(database: database);
final state = await store.load(
defaultHabitTitles: const ['Plan', 'Focus', 'Reflect'],
);
expect(state.anchor, isEmpty);
expect(state.habits.map((habit) => habit.title), [
'Plan',
'Focus',
'Reflect',
]);
});
test(
'sqlite snapshot round-trips focus, habit, and reflection data',
() async {
final database = FocusSeedDatabase(NativeDatabase.memory());
addTearDown(database.close);
final store = FocusSeedStore(database: database);
final now = DateTime(2026, 6, 12, 10, 30);
final state = AppState(
anchor: 'Ship the local database',
anchorDateKey: '2026-06-12',
habits: [
Habit(
id: 'habit-1',
title: 'Review the plan',
createdAt: now.subtract(const Duration(days: 2)),
completedDates: const {'2026-06-11', '2026-06-12'},
),
],
sessions: [
FocusSession(id: 'session-1', minutes: 25, completedAt: now),
],
reflections: [
ReflectionEntry(
id: 'reflection-1',
dateKey: '2026-06-12',
note: 'Small scope kept it clean.',
mood: Mood.clear,
createdAt: now.add(const Duration(minutes: 1)),
),
],
);
await store.save(state);
final loaded = await store.load();
expect(loaded.anchor, 'Ship the local database');
expect(loaded.anchorDateKey, '2026-06-12');
expect(loaded.habits.single.completedDates, {'2026-06-11', '2026-06-12'});
expect(loaded.sessions.single.minutes, 25);
expect(loaded.reflections.single.note, 'Small scope kept it clean.');
},
);
test('legacy shared preferences json migrates once and is removed', () async {
final legacyState = AppState(
anchor: 'Migrate safely',
anchorDateKey: '2026-06-12',
habits: [
Habit(
id: 'habit-legacy',
title: 'Keep existing data',
createdAt: DateTime(2026, 6, 10),
completedDates: const {'2026-06-12'},
),
],
sessions: [
FocusSession(
id: 'session-legacy',
minutes: 15,
completedAt: DateTime(2026, 6, 12, 8),
),
],
reflections: const [],
);
SharedPreferences.setMockInitialValues({
'focus_seed_state_v1': jsonEncode(legacyState.toJson()),
});
final database = FocusSeedDatabase(NativeDatabase.memory());
addTearDown(database.close);
final store = FocusSeedStore(database: database);
final migrated = await store.load();
final preferences = await SharedPreferences.getInstance();
expect(migrated.anchor, 'Migrate safely');
expect(migrated.habits.single.title, 'Keep existing data');
expect(migrated.sessions.single.minutes, 15);
expect(preferences.getString('focus_seed_state_v1'), isNull);
});
}