import 'package:commons_core/commons_core.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:tane/db/chat_database.dart'; import 'package:tane/services/message_store.dart'; import '../support/test_support.dart'; void main() { late ChatDatabase db; late MessageStore store; setUp(() { db = newTestChatDatabase(); store = MessageStore(db); }); tearDown(() => db.close()); PrivateMessage msg(String from, String text, int atMs) => PrivateMessage( fromPubkey: from, text: text, at: DateTime.fromMillisecondsSinceEpoch(atMs), ); test('history is empty for an unknown peer', () async { expect(await store.history('peer'), isEmpty); }); test('append then history round-trips, oldest first', () async { await store.append('peer', msg('peer', 'hi', 1000)); await store.append('peer', msg('me', 'hello', 2000)); final history = await store.history('peer'); expect(history.map((m) => m.text), ['hi', 'hello']); expect(history.first.fromPubkey, 'peer'); expect(history.last.at.millisecondsSinceEpoch, 2000); }); test('conversations are kept separate per peer', () async { await store.append('a', msg('a', 'toA', 1)); await store.append('b', msg('b', 'toB', 1)); expect((await store.history('a')).single.text, 'toA'); expect((await store.history('b')).single.text, 'toB'); }); test( 'conversations lists peers newest-first with their last message', () async { await store.append('peerA', msg('me', 'hi A', 1000)); await store.append('peerB', msg('peerB', 'yo B', 3000)); await store.append('peerA', msg('peerA', 'back A', 2000)); final convos = await store.conversations(); expect(convos.map((c) => c.peerPubkey), [ 'peerB', 'peerA', ]); // 3000 > 2000 expect(convos.first.lastText, 'yo B'); expect(convos.last.lastText, 'back A'); }, ); test( 'append is idempotent: a re-delivered message is not duplicated', () async { // Same sender + timestamp + text = the same gift wrap redelivered by a // relay on resubscribe. It must not pile up. expect(await store.append('peer', msg('peer', 'hola', 1000)), isTrue); expect(await store.append('peer', msg('peer', 'hola', 1000)), isFalse); expect(await store.history('peer'), hasLength(1)); // A genuinely different message (later timestamp) still lands. expect(await store.append('peer', msg('peer', 'hola', 2000)), isTrue); expect(await store.history('peer'), hasLength(2)); }, ); test( 'per-identity scope isolates conversations (0 = legacy scope)', () async { final acct0 = MessageStore(db); // legacy / account 0 final acct1 = MessageStore(db, accountScope: 'acct1'); await acct0.append('peer', msg('peer', 'for identity 0', 1000)); await acct1.append('peer', msg('peer', 'for identity 1', 2000)); // Same peer, but each identity sees only its own conversation. expect((await acct0.history('peer')).single.text, 'for identity 0'); expect((await acct1.history('peer')).single.text, 'for identity 1'); expect((await acct0.conversations()).single.lastText, 'for identity 0'); expect((await acct1.conversations()).single.lastText, 'for identity 1'); }, ); test('history is uncapped: nothing is silently dropped', () async { for (var i = 0; i < 210; i++) { await store.append('peer', msg('me', 'm$i', i)); } final history = await store.history('peer'); expect(history, hasLength(210)); expect(history.first.text, 'm0'); // the oldest survives expect(history.last.text, 'm209'); }); }