wsb-daemon 0.251.6311

wsb's daemon: the workbench, the GitHub mirror's schedule and peer continuity, for every workspace on the machine
//! What may be opened as a worktree, and what each one is.
//!
//! Against real git repositories and worktrees rather than a stand-in: the
//! rules being tested are a walk across a submodule boundary and the
//! difference between two checkouts of one repository, and a fixture that
//! only pretended to be either would agree with a walk that never happened.

use super::*;

/// A runtime, and a machine with nothing open in a scratch directory of its
/// own. The machine's session sync is a task, so it is made inside the
/// runtime.
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 git(dir: &Path, args: &[&str]) {
    // Through the one sanctioned constructor: a bare `git` inherits `GIT_DIR`
    // from whatever ran the tests, which overrides `-C`.
    let output = wsb::git_backend::git_command()
        .arg("-C")
        .arg(dir)
        .args(args)
        .output()
        .expect("git runs");
    assert!(
        output.status.success(),
        "git {args:?} in {}: {}",
        dir.display(),
        String::from_utf8_lossy(&output.stderr)
    );
}

/// A repository with one commit at `dir`.
fn repository(dir: &Path) {
    std::fs::create_dir_all(dir).expect("the folder is created");
    git(dir, &["init", "--quiet", "-b", "main"]);
    git(dir, &["config", "user.name", "Test"]);
    git(dir, &["config", "user.email", "test@example.com"]);
    std::fs::write(dir.join("README.md"), "one\n").expect("a file");
    git(dir, &["add", "."]);
    git(dir, &["commit", "--quiet", "-m", "one"]);
}

/// A superproject with a repository checked out inside it, as a submodule looks
/// on disk.
fn nested_repositories() -> (tempfile::TempDir, PathBuf, PathBuf) {
    let dir = tempfile::tempdir().expect("a scratch directory");
    let root = wsb::platform::canonicalize(dir.path()).expect("the scratch directory resolves");
    let inner = root.join("wsb");
    std::fs::create_dir_all(inner.join("src")).expect("the tree is created");
    for repository in [&root, &inner] {
        git(repository, &["init", "--quiet"]);
    }
    (dir, root, inner)
}

/// TEST9578: a submodule is not a worktree, however a command reaches it.
///
/// The daemon is started from whatever directory the command that wanted it
/// was standing in, and that is routinely a submodule. Opening that directory
/// as given produced two worktrees where the machine has one — each with its
/// own database, neither aware of the other.
#[test]
fn test9578_a_directory_inside_a_worktree_is_not_a_worktree() {
    let (runtime, _scratch, machine) = machine();
    let (_repos, root, inner) = nested_repositories();

    for standing_in in [inner.clone(), inner.join("src")] {
        let refusal = match runtime.block_on(machine.register(&standing_in)) {
            Err(refusal) => refusal,
            Ok(opened) => panic!(
                "{} was opened as a worktree of its own, at {}",
                standing_in.display(),
                opened.registered.super_root.display()
            ),
        };
        assert!(
            refusal.contains(&root.display().to_string()),
            "the refusal must name the worktree this directory belongs to, so the \
             caller can resolve instead of guessing; it said: {refusal}"
        );
    }
}

/// TEST9579: a record of a worktree inside another worktree is corrected.
///
/// The bad records outlive the bug that wrote them: they are rows in the
/// machine's database, and a person reading the list sees two worktrees where
/// the machine has one. The daemon drops them on the way up.
#[test]
fn test9579_a_worktree_inside_another_is_forgotten() {
    let (_runtime, _scratch, machine) = machine();
    let outer = PathBuf::from("/machines/ws/prj/machinefabric/main-worktree");
    let inner = outer.join("wsb");
    let elsewhere = PathBuf::from("/machines/ws/prj/other");

    for (index, root) in [&outer, &inner, &elsewhere].into_iter().enumerate() {
        machine
            .store
            .register_worktree(&RegisteredWorktree {
                id: WorktreeId::parse(&format!("~{:016x}", index + 1)).expect("an id"),
                project: None,
                name: root.file_name().expect("a name").to_string_lossy().into_owned(),
                workspace: None,
                super_root: root.clone(),
                root_commit: Some(format!("{:040x}", index + 1)),
                last_seen: 0,
                available: true,
            })
            .expect("the record is written");
    }

    let dropped = machine.forget_contained_worktrees().expect("the registry reconciles");
    assert_eq!(dropped, vec![inner.clone()], "only the worktree inside another one is dropped");

    let left: Vec<PathBuf> = machine
        .store
        .worktrees()
        .expect("the registry reads")
        .into_iter()
        .map(|worktree| worktree.super_root)
        .collect();
    assert!(
        left.contains(&outer) && left.contains(&elsewhere) && !left.contains(&inner),
        "the container and the unrelated worktree stay, the contained one goes; the \
         registry holds {left:?}"
    );
}

/// TEST20035: two worktrees of one repository are two worktrees.
///
/// Keyed by the repository, the first worktree opened answered for every
/// other: a command in `fix-worktree` was served `main-worktree`'s database,
/// watchers and paths, and `wsb serve` there printed the other's page. Each
/// is opened as itself — its own id, root and database — and a peer carries
/// each with its own name.
#[test]
fn test20035_two_worktrees_of_one_repository_are_two_worktrees() {
    let (runtime, _scratch, machine) = machine();
    let scratch = tempfile::tempdir().expect("a scratch directory");
    let base = wsb::platform::canonicalize(scratch.path()).expect("resolves");
    let main = base.join("main-worktree");
    repository(&main);
    let fix = base.join("fix-worktree");
    git(&main, &["worktree", "add", "--quiet", "-b", "fix", fix.to_str().expect("a path")]);

    let first = runtime.block_on(machine.register(&main)).expect("main opens");
    let second = runtime.block_on(machine.register(&fix)).expect("fix opens");

    assert_ne!(first.registered.id, second.registered.id);
    assert_eq!(first.web.workspace_root, main);
    assert_eq!(second.web.workspace_root, fix, "fix is served from its own checkout");
    assert!(main.join(".wsb/wsb.db").is_file());
    assert!(fix.join(".wsb/wsb.db").is_file(), "fix has a database of its own");

    let (one, two) = (
        first.registered.workspace.clone().expect("main has continuity"),
        second.registered.workspace.clone().expect("fix has continuity"),
    );
    assert_eq!(one.family(), two.family());
    assert_ne!(one, two);

    // Opening again finds the same one; opening by id finds it too.
    let again = runtime.block_on(machine.register(&fix)).expect("fix again");
    assert!(Arc::ptr_eq(&again, &second));
    let by_id = runtime.block_on(machine.open(&second.registered.id)).expect("by id");
    assert!(Arc::ptr_eq(&by_id, &second));
    assert_eq!(runtime.block_on(machine.all()).len(), 2);

    let advertised: Vec<String> = runtime
        .block_on(machine.summaries())
        .into_iter()
        .map(|summary| summary.workspace.to_string())
        .collect();
    assert_eq!(advertised.len(), 2, "both worktrees are offered to peers: {advertised:?}");
}

/// TEST20036: one continuity id claimed by two open worktrees is carried by
/// neither, and says which two.
///
/// Two clones of one repository whose worktrees share a name — `main` of two
/// projects that are the same repository — are one identity to a peer.
/// Carrying a peer's files into whichever was opened first would put them in
/// the wrong checkout.
#[test]
fn test20036_a_continuity_id_two_worktrees_claim_is_refused() {
    let (runtime, _scratch, machine) = machine();
    let scratch = tempfile::tempdir().expect("a scratch directory");
    let base = wsb::platform::canonicalize(scratch.path()).expect("resolves");
    let origin = base.join("origin");
    repository(&origin);
    let mut opened = Vec::new();
    for project in ["one", "two"] {
        let clone = base.join(project).join("main-worktree");
        std::fs::create_dir_all(clone.parent().expect("a parent")).expect("mkdir");
        git(&base, &["clone", "--quiet", origin.to_str().expect("a path"), clone.to_str().expect("a path")]);
        opened.push(runtime.block_on(machine.register(&clone)).expect("opens"));
    }
    let shared = opened[0].registered.workspace.clone().expect("continuity");
    assert_eq!(opened[1].registered.workspace.as_ref(), Some(&shared));

    let refusal = match runtime.block_on(machine.continuity(&shared)) {
        Err(refusal) => refusal,
        Ok(found) => panic!("two claims were not refused: {:?}", found.map(|(r, _)| r.registered.id.clone())),
    };
    for runtime in &opened {
        assert!(
            refusal.contains(&runtime.registered.super_root.display().to_string()),
            "the refusal names both worktrees: {refusal}"
        );
    }
    assert!(
        runtime.block_on(machine.summaries()).is_empty(),
        "an ambiguous id is offered to no peer"
    );
}