import 'package:drift/native.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:kouyu_english/core/local_store.dart'; import 'dart:convert'; void main() { test( 'a legacy snapshot migrates into canonical entities without replacement', () async { final executor = NativeDatabase.memory(); final store = LocalSnapshotStore.forTesting(executor); const legacy = '''{ "onboardingComplete": true, "lessonStep": "reading", "activeLessonId": "a0-02", "attemptEvidence": [], "mastery": [], "reviews": [] }'''; // Open the schema first, then model the row left by a pre-entity build. expect(await store.read(), isNull); await executor.runCustom( '''INSERT INTO learning_snapshots(snapshot_id, schema_version, payload, updated_at) VALUES (?, ?, ?, ?)''', const ['current', 1, legacy, '2026-09-14T00:00:00.000Z'], ); expect(await store.read(), legacy); await store.write(legacy); final restored = jsonDecode((await store.read())!) as Map; expect(restored['activeLessonId'], 'a0-02'); expect(restored['onboardingComplete'], isTrue); expect( await executor.runSelect( 'SELECT payload FROM learning_snapshots WHERE snapshot_id = ?', const ['current'], ), [ {'payload': legacy}, ], ); expect( await executor.runSelect( 'SELECT profile_id FROM learner_profiles', const [], ), [ {'profile_id': 'current'}, ], ); await executor.close(); }, ); test( 'SQLite entity store restores canonical state without writing a snapshot', () async { final executor = NativeDatabase.memory(); final store = LocalSnapshotStore.forTesting(executor); expect(await store.read(), isNull); const first = '''{ "revision": 1, "attemptEvidence": [{"id":"a1","itemId":"A0-P01","taskId":"t1","skill":"speaking","inputMode":"speechToText","outcome":"independentSuccess","createdAt":"2026-09-14T00:00:00.000Z","assisted":false,"variantIndex":0,"originalTranscript":"Hello","transcriptConfirmed":true,"transcriptEdited":false}], "mastery": [{"id":"A0-P01","label":"Hello","status":"recognize","checkpoint":1,"needsReview":false}], "reviews": [{"id":"A0-P01","dueAt":"2026-09-15T00:00:00.000Z","attempts":1,"successfulReviews":1,"variantIndex":0}], "temporaryLexicon": [{"query":"library","definition":"图书馆","provider":"compatible","model":"demo","createdAt":"2026-09-14T00:00:00.000Z"}], "lessonStep":"speaking", "activeLessonId":"a0-01" }'''; await store.write(first); final restored = jsonDecode((await store.read())!) as Map; expect(restored['lessonStep'], 'speaking'); expect(restored['attemptEvidence'], hasLength(1)); expect(restored['mastery'], hasLength(1)); expect( await executor.runSelect( 'SELECT snapshot_id FROM learning_snapshots', const [], ), isEmpty, ); expect( await executor.runSelect( 'SELECT item_id FROM attempt_evidence', const [], ), [ {'item_id': 'A0-P01'}, ], ); await executor.runCustom( 'UPDATE learning_snapshots SET payload = ? WHERE snapshot_id = ?', const ['{"corrupt":"backup only"}', 'current'], ); final canonicalAfterSnapshotChange = jsonDecode((await store.read())!) as Map; expect(canonicalAfterSnapshotChange['mastery'], hasLength(1)); expect( await executor.runSelect( 'SELECT original_transcript, transcript_confirmed, transcript_edited FROM attempt_evidence', const [], ), [ { 'original_transcript': 'Hello', 'transcript_confirmed': 1, 'transcript_edited': 0, }, ], ); expect( await executor.runSelect( 'SELECT definition FROM temporary_lexicon_entries', const [], ), [ {'definition': '图书馆'}, ], ); expect( await executor.runSelect('SELECT status FROM mastery_items', const []), [ {'status': 'recognize'}, ], ); expect( await executor.runSelect('SELECT item_id FROM review_items', const []), [ {'item_id': 'A0-P01'}, ], ); expect( await executor.runSelect( 'SELECT session_type FROM study_sessions', const [], ), [ {'session_type': 'lesson'}, ], ); await store.write('{"revision":2}'); expect( (jsonDecode((await store.read())!) as Map)['attemptEvidence'], isEmpty, ); await store.clear(); expect(await store.read(), isNull); expect( await executor.runSelect( 'SELECT query_key FROM temporary_lexicon_entries', const [], ), isEmpty, ); await executor.close(); }, ); }