onionwire/tests/store.rs

180 lines
5.4 KiB
Rust
Raw Normal View History

//! M1 store: identity on `self`, friends keyed by pubkey, onion is locator.
use std::fs;
use std::os::unix::fs::PermissionsExt;
use std::path::Path;
use std::sync::{Mutex, MutexGuard};
use onionwire::Store;
static ENV_LOCK: Mutex<()> = Mutex::new(());
struct TempHome {
dir: tempfile::TempDir,
_guard: MutexGuard<'static, ()>,
}
impl TempHome {
fn new() -> Self {
let dir = tempfile::tempdir().expect("tempdir");
let guard = ENV_LOCK.lock().expect("env lock");
unsafe {
std::env::set_var("ONIONWIRE_HOME", dir.path());
}
Self { dir, _guard: guard }
}
fn path(&self) -> &Path {
self.dir.path()
}
}
impl Drop for TempHome {
fn drop(&mut self) {
unsafe {
std::env::remove_var("ONIONWIRE_HOME");
}
}
}
fn mode(path: &Path) -> u32 {
fs::metadata(path).unwrap().permissions().mode() & 0o777
}
fn pk(tag: u8) -> [u8; 32] {
let mut k = [0u8; 32];
k[0] = tag;
k
}
#[test]
fn first_run_creates_0700_dirs_and_self_row() {
let home = TempHome::new();
let store = Store::open().expect("open");
let arti = home.path().join("arti");
let db = home.path().join("onionwire.db");
assert!(arti.is_dir(), "arti dir");
assert!(db.is_file(), "onionwire.db");
assert_eq!(mode(home.path()), 0o700);
assert_eq!(mode(&arti), 0o700);
let me = store.self_identity().expect("self");
assert_eq!(me.identity_pk.len(), 32);
assert_eq!(me.identity_sk.len(), 32);
assert_ne!(me.identity_pk, me.identity_sk);
assert_eq!(me.onion, "");
assert_eq!(store.friend_count().unwrap(), 0);
drop(store);
let store2 = Store::open().expect("reopen");
let me2 = store2.self_identity().expect("self again");
assert_eq!(me.identity_pk, me2.identity_pk);
assert_eq!(me.identity_sk, me2.identity_sk);
}
#[test]
fn insert_friend_by_pubkey() {
let _home = TempHome::new();
let store = Store::open().expect("open");
store
.upsert_friend(&pk(1), "aaa.onion", Some("alice"))
.expect("insert");
let f = store.get_friend(&pk(1)).expect("row").expect("found");
assert_eq!(f.onion, "aaa.onion");
assert_eq!(f.petname.as_deref(), Some("alice"));
assert_eq!(f.pubkey, pk(1));
assert!(!f.fingerprint.is_empty());
assert_eq!(store.friend_count().unwrap(), 1);
}
#[test]
fn same_pubkey_different_onion_updates_locator() {
let _home = TempHome::new();
let store = Store::open().expect("open");
store
.upsert_friend(&pk(1), "old.onion", Some("alice"))
.expect("insert");
store
.upsert_friend(&pk(1), "new.onion", None)
.expect("update");
assert_eq!(store.friend_count().unwrap(), 1);
let f = store.get_friend(&pk(1)).expect("row").expect("found");
assert_eq!(f.onion, "new.onion");
assert_eq!(f.petname.as_deref(), Some("alice"), "petname is local");
}
#[test]
fn unknown_pubkey_inserts_new_row() {
let _home = TempHome::new();
let store = Store::open().expect("open");
store.upsert_friend(&pk(1), "a.onion", None).expect("first");
store
.upsert_friend(&pk(2), "b.onion", None)
.expect("second");
assert_eq!(store.friend_count().unwrap(), 2);
assert_eq!(store.get_friend(&pk(1)).unwrap().unwrap().onion, "a.onion");
assert_eq!(store.get_friend(&pk(2)).unwrap().unwrap().onion, "b.onion");
}
#[test]
fn open_at_two_homes_are_independent() {
let a = tempfile::tempdir().expect("a");
let b = tempfile::tempdir().expect("b");
let sa = onionwire::Store::open_at(a.path()).expect("open a");
let sb = onionwire::Store::open_at(b.path()).expect("open b");
let ia = sa.self_identity().unwrap();
let ib = sb.self_identity().unwrap();
assert_ne!(ia.identity_pk, ib.identity_pk);
}
#[test]
fn incoming_message_persists_plaintext() {
let _home = TempHome::new();
let store = Store::open().expect("open");
store.upsert_friend(&pk(1), "b.onion", None).unwrap();
store.append_message(&pk(1), "in", b"hello wire").unwrap();
let msgs = store.list_messages(&pk(1)).unwrap();
assert_eq!(msgs.len(), 1);
assert_eq!(msgs[0].dir, "in");
assert_eq!(msgs[0].plaintext, b"hello wire");
}
#[test]
fn self_has_x25519_prekey_and_set_onion() {
let _home = TempHome::new();
let store = Store::open().expect("open");
let me = store.self_identity().unwrap();
assert_eq!(me.prekey_pk.len(), 32);
assert_eq!(me.prekey_sk.len(), 32);
assert_ne!(me.prekey_pk, me.prekey_sk);
store.set_onion("abc.onion").unwrap();
assert_eq!(store.self_identity().unwrap().onion, "abc.onion");
}
#[test]
fn friend_prekey_lookup() {
let _home = TempHome::new();
let store = Store::open().expect("open");
let spk = [9u8; 32];
store.upsert_friend(&pk(1), "b.onion", None).unwrap();
store.set_friend_prekey(&pk(1), &spk).unwrap();
let f = store.get_friend_by_prekey(&spk).unwrap().expect("found");
assert_eq!(f.pubkey, pk(1));
assert_eq!(f.prekey, spk);
}
#[test]
fn open_uses_wal_journal() {
let dir = tempfile::tempdir().expect("tempdir");
let store = Store::open_at(dir.path()).expect("open");
assert_eq!(store.journal_mode().unwrap().to_ascii_lowercase(), "wal");
}
#[test]
fn corrupt_db_fails_closed() {
let dir = tempfile::tempdir().expect("tempdir");
std::fs::write(dir.path().join("onionwire.db"), b"not a sqlite database").unwrap();
assert!(Store::open_at(dir.path()).is_err());
}