feat(block2): persist chat history (messages survive leaving the chat)
Messages were in-memory only — gone on leaving the conversation. - MessageStore: keystore-backed (no plaintext), a capped per-peer JSON list (last 200). Simple; the encrypted Drift DB is the eventual home at scale. - MessagesCubit: loads saved history on start (subscribes FIRST, then loads, so a message arriving during the load isn't dropped) and persists every sent and received message. Wired via DI + TaneApp(messageStore). Tests (plain 'test', no hang risk): MessageStore round-trip/per-peer/cap, and a cubit test that reopens a fresh cubit and sees the saved conversation. Analyzer clean; run 'flutter test' locally to confirm.
This commit is contained in:
parent
847590ff11
commit
6d16656911
8 changed files with 186 additions and 22 deletions
|
|
@ -2,8 +2,11 @@ import 'dart:async';
|
|||
|
||||
import 'package:commons_core/commons_core.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:tane/services/message_store.dart';
|
||||
import 'package:tane/state/messages_cubit.dart';
|
||||
|
||||
import '../support/test_support.dart';
|
||||
|
||||
/// In-memory [MessageTransport]: records sends, lets a test push inbox messages.
|
||||
class FakeMessageTransport implements MessageTransport {
|
||||
final List<({String to, String text})> sent = [];
|
||||
|
|
@ -80,6 +83,33 @@ void main() {
|
|||
await cubit.close();
|
||||
});
|
||||
|
||||
test('loads saved history on start and persists across a new cubit',
|
||||
() async {
|
||||
final store = MessageStore(InMemorySecretStore());
|
||||
await store.append(
|
||||
peer, msg(peer, 'earlier')); // a message from a previous session
|
||||
|
||||
final transport = FakeMessageTransport();
|
||||
final cubit = MessagesCubit(transport,
|
||||
peerPubkey: peer, selfPubkey: me, store: store);
|
||||
await cubit.start();
|
||||
expect(cubit.state.messages.map((m) => m.text), ['earlier']);
|
||||
|
||||
await cubit.send('hi'); // persisted
|
||||
transport.receive(msg(peer, 'reply')); // persisted
|
||||
await pumpEventQueue();
|
||||
expect(cubit.state.messages.map((m) => m.text), ['earlier', 'hi', 'reply']);
|
||||
await cubit.close();
|
||||
|
||||
// A fresh cubit (even offline) sees the saved conversation.
|
||||
final reopened =
|
||||
MessagesCubit(null, peerPubkey: peer, selfPubkey: me, store: store);
|
||||
await reopened.start();
|
||||
expect(reopened.state.messages.map((m) => m.text),
|
||||
['earlier', 'hi', 'reply']);
|
||||
await reopened.close();
|
||||
});
|
||||
|
||||
test('offline (no transport) never throws', () async {
|
||||
final cubit = MessagesCubit(null, peerPubkey: peer, selfPubkey: me)..start();
|
||||
expect(cubit.isOnline, isFalse);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue