import 'package:flutter_test/flutter_test.dart'; import 'package:ikar/core/storage/message_persistence.dart'; import 'package:sqflite_common_ffi/sqflite_ffi.dart'; void main() { sqfliteFfiInit(); late Database db; final queued = { 'id': 'temp', 'account_id': 42, 'chat_id': 7, 'text': 'ciphertext', 'status': 'pending', 'deleted': 0, }; setUp(() async { db = await databaseFactoryFfi.openDatabase(inMemoryDatabasePath); await db.execute( 'CREATE TABLE messages (id TEXT NOT NULL, account_id INTEGER NOT NULL, ' 'chat_id INTEGER NOT NULL, text TEXT NOT NULL, status TEXT, deleted INTEGER, ' 'PRIMARY KEY (account_id, id))', ); await db.insert('messages', queued); }); tearDown(() => db.close()); test('receipt replaces a pending item atomically', () async { expect( await MessagePersistence.replacePending(db, { ...queued, 'id': 'ack', 'status': 'sent', }, pendingId: 'temp'), isTrue, ); expect((await db.query('messages')).single['id'], 'ack'); }); for (final mode in [ 'removed', 'deleted', 'already sent', 'other account', 'other chat', ]) { test('late result cannot overwrite $mode item', () async { if (mode == 'removed') await db.delete('messages'); if (mode == 'deleted') await db.update('messages', {'deleted': 1}); if (mode == 'already sent') { await db.update('messages', {'status': 'sent'}); } final before = await db.query('messages'); expect( await MessagePersistence.replacePending(db, { ...queued, 'status': 'error', if (mode == 'other account') 'account_id': 84, if (mode == 'other chat') 'chat_id': 8, }, pendingId: 'temp'), isFalse, ); expect(await db.query('messages'), before); }); } test( 'failed delete rolls back receipt and preserves retryable wire text', () async { await db.execute( "CREATE TRIGGER reject_delete BEFORE DELETE ON messages BEGIN SELECT RAISE(ABORT, 'disk error'); END", ); await expectLater( MessagePersistence.replacePending(db, { ...queued, 'id': 'ack', 'status': 'sent', }, pendingId: 'temp'), throwsA(isA()), ); expect((await db.query('messages')).single, queued); }, ); }