apeirogond 0.29.0

Daemon for Apeirogon, a cross-platform software KVM
// SPDX-License-Identifier: GPL-3.0-or-later

use super::*;

fn entry(identity: &Identity, name: &str) -> Entry {
    Entry {
        machine_id: identity.machine_id,
        pin: identity.pin,
        name: name.into(),
        spki_der: Some(identity::spki_of(&identity.cert_der).unwrap()),
        hw_addr: None,
        screen_mm: None,
    }
}

#[test]
fn merge_desk_containing_own_member_stores_only_peer_pins() {
    // Arrange
    let a_dir = tempfile::tempdir().unwrap();
    let b_dir = tempfile::tempdir().unwrap();
    let c_dir = tempfile::tempdir().unwrap();
    let a = identity::load_or_create(a_dir.path()).unwrap();
    let b = identity::load_or_create(b_dir.path()).unwrap();
    let c = identity::load_or_create(c_dir.path()).unwrap();
    let mut a_pins = PinStore::load(&a_dir.path().join("pins.toml")).unwrap();
    let mut b_pins = PinStore::load(&b_dir.path().join("pins.toml")).unwrap();
    b_pins.add(entry(&a, "a"));
    let mut a_desk =
        DeskStore::load(&a_dir.path().join("desk.pb"), a.machine_id, &mut a_pins).unwrap();
    let path = b_dir.path().join("desk.pb");
    let mut b_desk = DeskStore::load(&path, b.machine_id, &mut b_pins).unwrap();
    a_desk.admit(&a, &mut a_pins, &entry(&b, "b"), 1).unwrap();
    a_desk.admit(&a, &mut a_pins, &entry(&c, "c"), 2).unwrap();

    // Act
    let outcome = b_desk.merge(&b, &mut b_pins, &a_desk.to_desk()).unwrap();

    // Assert
    assert_eq!(outcome.admitted.len(), 2);
    assert!(b_pins.get(&b.machine_id).is_none());
    assert_eq!(b_pins.get(&a.machine_id), Some(&entry(&a, "a")));
    assert_eq!(b_pins.get(&c.machine_id).unwrap().pin, c.pin);
}

#[test]
fn merge_admission_then_revocation_survives_reload_and_stale_desk() {
    // Arrange
    let a_dir = tempfile::tempdir().unwrap();
    let b_dir = tempfile::tempdir().unwrap();
    let c_dir = tempfile::tempdir().unwrap();
    let a = identity::load_or_create(a_dir.path()).unwrap();
    let b = identity::load_or_create(b_dir.path()).unwrap();
    let c = identity::load_or_create(c_dir.path()).unwrap();
    let mut a_pins = PinStore::load(&a_dir.path().join("pins.toml")).unwrap();
    let mut c_pins = PinStore::load(&c_dir.path().join("pins.toml")).unwrap();
    c_pins.add(entry(&a, "a"));
    c_pins.save().unwrap();
    let mut a_desk =
        DeskStore::load(&a_dir.path().join("desk.pb"), a.machine_id, &mut a_pins).unwrap();
    let path = c_dir.path().join("desk.pb");
    let mut c_desk = DeskStore::load(&path, c.machine_id, &mut c_pins).unwrap();

    // Act
    a_desk.admit(&a, &mut a_pins, &entry(&b, "b"), 1).unwrap();
    let old_desk = a_desk.to_desk();
    let admitted = c_desk.merge(&c, &mut c_pins, &old_desk).unwrap();
    let accepted_b = c_pins.get(&b.machine_id).cloned();
    let stale_pins = std::fs::read(c_dir.path().join("pins.toml")).unwrap();
    a_desk.revoke(&a, &mut a_pins, b.machine_id, 2).unwrap();
    let revoked = c_desk.merge(&c, &mut c_pins, &a_desk.to_desk()).unwrap();
    // Simulate a crash between the durable desk write and the pin write.
    std::fs::write(c_dir.path().join("pins.toml"), stale_pins).unwrap();
    let mut c_pins = PinStore::load(&c_dir.path().join("pins.toml")).unwrap();
    let mut reloaded = DeskStore::load(&path, c.machine_id, &mut c_pins).unwrap();
    let stale = reloaded.merge(&c, &mut c_pins, &old_desk).unwrap();

    // Assert
    assert_eq!(admitted.admitted.len(), 1);
    assert_eq!(accepted_b.unwrap().pin, b.pin);
    assert_eq!(revoked.revoked, vec![b.machine_id]);
    assert!(a_pins.get(&b.machine_id).is_none());
    assert!(c_pins.get(&b.machine_id).is_none());
    assert_eq!(stale.admitted.len(), 0);
    assert_eq!(reloaded.to_desk(), a_desk.to_desk());
}