mirror of
https://github.com/logos-messaging/libchat.git
synced 2026-06-28 03:59:27 +00:00
* chore(flake): accept extra system attr; add perl for openssl-sys build
forAllSystems calls the lambda with {system, pkgs}; strict
destructuring requires `..` to ignore the system attribute.
`pkgs.perl` is needed because openssl-sys is pulled vendored via
libsqlite3-sys / rusqlite / chat-sqlite, and its `perl Configure`
step needs FindBin.pm, which Fedora's system perl doesn't ship.
* feat: introduce client event system
- Core processing yields a `PayloadOutcome` enum — `Empty`, `Convo`, or
`Inbox`. `ConvoOutcome` carries a conversation id and an optional
decrypted `Content`; `InboxOutcome` adds a `NewConversation`
(id + `ConversationClass`) for a peer-initiated conversation.
- Client translates `PayloadOutcome` into app-facing `Vec<Event>`
(`ConversationStarted`, `MessageReceived`) at the boundary, so the
application loop sees discrete events rather than core types.
- MLS group welcomes produce a `ConversationStarted` event with no
initial content, fixing the silent-group-join case where the inbox
layer dropped the observation.
- C FFI exposes an `EventList` opaque type with indexed accessors and
an `Invalid` sentinel for out-of-bounds / non-applicable reads.
- Symmetric `Inbox` / `InboxV2` handlers: both return
`Result<InboxOutcome, _>` and own the persistence + ephemeral-key
cleanup for the conversations they create.
- Updated and simplified `docs/adr/0001-client-event-system.md`.
* chore(flake): bump nixpkgs to nixos-unstable-small
Temporary. The two crates.io UA fixes (NixOS/nixpkgs#512735 for
fetchCargoVendor's python-requests UA, NixOS/nixpkgs#524985 for
importCargoLock's curl UA) haven't propagated to nixos-unstable yet.
Switch to nixos-unstable-small and force logos-delivery to follow so
the smoketest gets the same fix. Revert once nixos-unstable catches up.
Refs:
- https://github.com/rust-lang/crates.io/issues/13482
- https://github.com/rust-lang/crates.io/issues/13783
- https://crates.io/data-access
99 lines
3.4 KiB
Rust
99 lines
3.4 KiB
Rust
use logos_chat::{
|
|
ChatClient, ConversationClass, ConversationId, Cursor, Event, InProcessDelivery, StorageConfig,
|
|
};
|
|
|
|
/// Pulls one envelope, decrypts, and returns the events emitted.
|
|
fn receive(receiver: &mut ChatClient<InProcessDelivery>, cursor: &mut Cursor) -> Vec<Event> {
|
|
let raw = cursor.next().expect("expected envelope");
|
|
receiver.receive(&raw).expect("receive failed")
|
|
}
|
|
|
|
fn expect_message(event: &Event) -> (&str, &[u8]) {
|
|
match event {
|
|
Event::MessageReceived {
|
|
convo_id, content, ..
|
|
} => (convo_id.as_ref(), content.as_slice()),
|
|
other => panic!("expected MessageReceived, got {other:?}"),
|
|
}
|
|
}
|
|
|
|
fn expect_conversation_started(event: &Event) -> (&str, ConversationClass) {
|
|
match event {
|
|
Event::ConversationStarted {
|
|
convo_id, class, ..
|
|
} => (convo_id.as_ref(), *class),
|
|
other => panic!("expected ConversationStarted, got {other:?}"),
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn saro_raya_message_exchange() {
|
|
let delivery = InProcessDelivery::new(Default::default());
|
|
let mut cursor = delivery.cursor_at_tail("delivery_address");
|
|
|
|
let mut saro = ChatClient::new("saro", delivery.clone());
|
|
let mut raya = ChatClient::new("raya", delivery);
|
|
|
|
let raya_bundle = raya.create_intro_bundle().unwrap();
|
|
let saro_convo_id = saro
|
|
.create_conversation(&raya_bundle, b"hello raya")
|
|
.unwrap();
|
|
|
|
let events = receive(&mut raya, &mut cursor);
|
|
assert_eq!(
|
|
events.len(),
|
|
2,
|
|
"expected ConversationStarted + MessageReceived"
|
|
);
|
|
let (started_id, class) = expect_conversation_started(&events[0]);
|
|
assert_eq!(class, ConversationClass::Private);
|
|
let (msg_id, content) = expect_message(&events[1]);
|
|
assert_eq!(content, b"hello raya");
|
|
assert_eq!(started_id, msg_id);
|
|
let raya_convo_id: ConversationId = started_id.to_owned();
|
|
|
|
raya.send_message(&raya_convo_id, b"hi saro").unwrap();
|
|
let events = receive(&mut saro, &mut cursor);
|
|
assert_eq!(events.len(), 1);
|
|
let (_, content) = expect_message(&events[0]);
|
|
assert_eq!(content, b"hi saro");
|
|
|
|
for i in 0u8..5 {
|
|
let msg = format!("msg {i}");
|
|
saro.send_message(&saro_convo_id, msg.as_bytes()).unwrap();
|
|
let events = receive(&mut raya, &mut cursor);
|
|
assert_eq!(events.len(), 1);
|
|
let (_, content) = expect_message(&events[0]);
|
|
assert_eq!(content, msg.as_bytes());
|
|
|
|
let reply = format!("reply {i}");
|
|
raya.send_message(&raya_convo_id, reply.as_bytes()).unwrap();
|
|
let events = receive(&mut saro, &mut cursor);
|
|
assert_eq!(events.len(), 1);
|
|
let (_, content) = expect_message(&events[0]);
|
|
assert_eq!(content, reply.as_bytes());
|
|
}
|
|
|
|
assert_eq!(saro.list_conversations().unwrap().len(), 1);
|
|
assert_eq!(raya.list_conversations().unwrap().len(), 1);
|
|
}
|
|
|
|
#[test]
|
|
fn open_persistent_client() {
|
|
let dir = tempfile::tempdir().unwrap();
|
|
let db_path = dir.path().join("test.db").to_string_lossy().to_string();
|
|
let config = StorageConfig::File(db_path);
|
|
|
|
let client1 = ChatClient::open("saro", config.clone(), InProcessDelivery::default()).unwrap();
|
|
let name1 = client1.installation_name().to_string();
|
|
drop(client1);
|
|
|
|
let client2 = ChatClient::open("saro", config, InProcessDelivery::default()).unwrap();
|
|
let name2 = client2.installation_name().to_string();
|
|
|
|
assert_eq!(
|
|
name1, name2,
|
|
"installation name should persist across restarts"
|
|
);
|
|
}
|