//! Headless stand-in for the desktop app, for end-to-end tests of the browser extension and //! `kestrove-nmh`. It runs the same `Broker` as the app over the same IPC endpoint, with an //! in-memory vault holding one test login, and plays the user: it approves pairings and saves. //! Never ship this: it auto-approves. //! //! KESTROVE_IPC_DIR=/tmp/kv-e2e KESTROVE_TEST_ORIGIN=http://127.0.0.1:8765 \ //! cargo run --example test-host //! //! Prints `READY `, then `PAIRED ` and `SAVED ` as they happen. use std::io::Write; use std::sync::Arc; use std::thread; use std::time::Duration; use kestrove_desktop::broker::{Broker, PairingStore, PairingView, SaveView, Ui}; use kestrove_desktop::ipc; use kv_ffi::{ItemDto, ItemKindDto, VaultCore}; struct Quiet; impl Ui for Quiet { fn pairing_requested(&self, _: &PairingView) {} fn save_requested(&self, _: &SaveView) {} } fn main() -> Result<(), Box> { let origin = std::env::var("KESTROVE_TEST_ORIGIN").unwrap_or_else(|_| "http://127.0.0.1:8765".into()); let core = VaultCore::new_in_memory(); core.create_account("test master password".into())?; core.save_item(ItemDto { id: String::new(), kind: ItemKindDto::Login, title: "Test login".into(), username: Some("alice@example.com".into()), password: Some("correct-horse-42".into()), urls: vec![origin], totp: Some("JBSWY3DPEHPK3PXP".into()), notes: None, favorite: false, tags: vec![], modified_at: 0, })?; let broker = Broker::new(core, Arc::new(Quiet), PairingStore::in_memory()); let listener = ipc::Listener::bind()?; println!("READY {}", ipc::endpoint()?.display()); std::io::stdout().flush()?; let b = broker.clone(); thread::spawn(move || loop { thread::sleep(Duration::from_millis(200)); if let Some(p) = b.pending_pairing() { if b.resolve_pairing(&p.id, true).is_ok() { println!("PAIRED {}", p.words.join(" ")); } } for s in b.pending_saves() { if b.resolve_save(&s.id, true).is_ok() { println!("SAVED {} {}", s.origin, s.username); } } let _ = std::io::stdout().flush(); }); loop { let conn = listener.accept()?; let b = broker.clone(); thread::spawn(move || { let _ = b.serve(conn); }); } }