wsb-daemon 0.258.6441

wsb's daemon: the workbench, the GitHub mirror's schedule and peer continuity, for every workspace on the machine
//! What the machine says about conflicts waiting on it.

use super::*;

use wsb::peer::identity::{Fingerprint, PairedPeer};
use wsb::peer::model::{PeerId, RepositoryId, WorkspaceId};

use crate::machine::MachineState;

/// A runtime, and a machine made inside it: its session sync is a task.
fn machine() -> (tokio::runtime::Runtime, tempfile::TempDir, Arc<MachineState>) {
    let runtime = tokio::runtime::Builder::new_current_thread()
        .enable_all()
        .build()
        .expect("a runtime");
    let dir = tempfile::tempdir().expect("a scratch directory");
    let machine = {
        let _inside = runtime.enter();
        MachineState::new(
            dir.path().to_path_buf(),
            std::net::IpAddr::V4(std::net::Ipv4Addr::LOCALHOST),
            0,
            std::net::IpAddr::V4(std::net::Ipv4Addr::LOCALHOST),
            0,
        )
        .expect("a machine with an identity and a store")
    };
    (runtime, dir, machine)
}

fn pair(machine: &Arc<MachineState>, id: &str, name: &str) -> PeerId {
    let peer = PeerId::parse(id).expect("a peer id");
    machine
        .store
        .add_peer(&PairedPeer {
            id: peer.clone(),
            name: name.to_string(),
            public_key: format!("key-{id}"),
            fingerprint: Fingerprint::from_stored(format!("{id}-{id}-{id}")),
            address: "127.0.0.1:9494".to_string(),
            paired_at: 0,
        })
        .expect("the peer is remembered");
    peer
}

fn conflict(
    machine: &Arc<MachineState>,
    peer: &PeerId,
    workspace: &str,
    repository: &str,
    path: &str,
) {
    machine
        .store
        .record_conflict(
            peer,
            &WorkspaceId::parse(workspace).expect("a workspace id"),
            &if repository.is_empty() {
                RepositoryId::root()
            } else {
                RepositoryId::parse(repository).expect("a repository id")
            },
            path,
            None,
            None,
            None,
            0,
        )
        .expect("the conflict is recorded");
}

/// TEST9583: conflicts are counted where a person can act on them.
///
/// The count drives the chrome — the machine button carries it wherever you
/// are — so it has to be the whole machine's, grouped down to the repository
/// and named by the peer it is with. A total alone says something is waiting
/// without saying where, which is a notification rather than a decision.
#[test]
fn test9583_conflicts_are_counted_by_workspace_repository_and_peer() {
    let (runtime, _scratch, machine) = machine();
    let laptop = pair(&machine, "laptop", "bahram-mac");
    let desktop = pair(&machine, "desktop", "bahram-linux");

    machine
        .store
        .register_worktree(&wsb::daemon::RegisteredWorktree {
            id: wsb::daemon::WorktreeId::parse("machinefabric:main").expect("a worktree id"),
            project: Some("machinefabric".to_string()),
            name: "main".to_string(),
            workspace: Some(WorkspaceId::parse("db3f3a6e071ee7e0@main").expect("a workspace id")),
            super_root: std::path::PathBuf::from("/ws/prj/machinefabric/main-worktree"),
            root_commit: Some(format!("{:040x}", 1)),
            last_seen: 0,
            available: true,
        })
        .expect("the worktree is recorded");

    conflict(&machine, &laptop, "db3f3a6e071ee7e0@main", "wsb", "src/main.rs");
    conflict(&machine, &laptop, "db3f3a6e071ee7e0@main", "wsb", "src/lib.rs");
    // The same repository, a different machine: two decisions, not one.
    conflict(&machine, &desktop, "db3f3a6e071ee7e0@main", "wsb", "src/main.rs");
    conflict(&machine, &laptop, "db3f3a6e071ee7e0@main", "", "README.md");

    let Json(census) = runtime.block_on(conflicts(State(machine.clone())));

    assert_eq!(census.total, 4, "every waiting conflict is counted");
    assert_eq!(census.workspaces.len(), 1);
    let workspace = &census.workspaces[0];
    assert_eq!(
        workspace.display_name, "machinefabric/main",
        "named as the projects list names the worktree, not by its id"
    );
    assert_eq!(workspace.total, 4);

    let repositories: Vec<(&str, usize, Vec<&str>)> = workspace
        .repositories
        .iter()
        .map(|repository| {
            (
                repository.repository.as_str(),
                repository.conflicts,
                repository.peers.iter().map(String::as_str).collect(),
            )
        })
        .collect();
    assert_eq!(
        repositories,
        vec![
            ("", 1, vec!["bahram-mac"]),
            ("wsb", 3, vec!["bahram-linux", "bahram-mac"]),
        ],
        "the superproject is the empty repository, exactly as the tree \
         addresses it, and each repository names the machines it disagrees \
         with, in the order a person reads them"
    );
}

/// TEST9584: a conflict against a workspace this machine no longer registers
/// is still reported.
///
/// Dropping it would hide a decision behind a registration: the rows are the
/// record of two machines disagreeing, and a workspace that is not open right
/// now has not agreed with anybody.
#[test]
fn test9584_a_conflict_in_an_unregistered_workspace_is_still_counted() {
    let (runtime, _scratch, machine) = machine();
    let laptop = pair(&machine, "laptop", "bahram-mac");
    conflict(&machine, &laptop, "2502501b3cb21cc4@main", "", "README.md");

    let Json(census) = runtime.block_on(conflicts(State(machine.clone())));

    assert_eq!(census.total, 1);
    assert_eq!(
        census.workspaces[0].display_name, "2502501b3cb21cc4@main",
        "with no name to give it, the page shows the id rather than nothing"
    );
}