use super::*;
#[test]
fn test20032_git_state_is_read_from_porcelain_status() {
let clean = parse_status(
"# branch.oid 1234abcd\n# branch.head card/fix\n# branch.upstream origin/card/fix\n# branch.ab +2 -3\n",
)
.expect("read");
assert_eq!(
clean,
GitState {
branch: Some("card/fix".into()),
head: Some("1234abcd".into()),
dirty: 0,
upstream: Some("origin/card/fix".into()),
ahead: Some(2),
behind: Some(3),
last_commit_at: None,
}
);
let detached = parse_status("# branch.oid 1234abcd\n# branch.head (detached)\n").expect("read");
assert_eq!(detached.branch, None, "a detached HEAD is no branch");
assert_eq!(detached.upstream, None);
assert_eq!((detached.ahead, detached.behind), (None, None), "no upstream, no distance");
let unborn = parse_status("# branch.oid (initial)\n# branch.head main\n").expect("read");
assert_eq!(unborn.head, None, "no commit yet");
for change in [
"1 .M N... 100644 100644 100644 aaaa bbbb src/lib.rs",
"? notes.md",
"u UU N... 100644 100644 100644 100644 aaaa bbbb cccc both.rs",
"1 .M S.M. 160000 160000 160000 aaaa bbbb sub",
] {
let state = parse_status(&format!("# branch.oid aaaa\n# branch.head main\n{change}\n"))
.expect("read");
assert_eq!(state.dirty, 1, "{change:?} is one path of uncommitted work");
}
let two = parse_status("# branch.oid aaaa\n# branch.head main\n? a\n? b\n").expect("read");
assert_eq!(two.dirty, 2, "each changed path is counted");
assert!(parse_status("# branch.ab +x -1\n").is_err(), "a malformed distance is refused");
}
#[test]
fn test20033_slots_are_filed_by_worktree() {
let listing = r#"[
{"group": "main", "position": 0, "name": "fixer", "key": "k1", "project": "alpha",
"worktree": "fix", "target": "alpha/fix", "status": "running", "detail": null,
"session_id": "s1", "activity_at": 1700000000},
{"group": "main", "position": 1, "name": "main", "key": "k2", "project": "alpha",
"worktree": "main", "target": "alpha", "status": "parked", "detail": "stopped",
"session_id": null, "activity_at": null},
{"group": "main", "position": 2, "name": "other", "key": "k3", "project": "alpha",
"worktree": "fix", "target": "alpha/fix", "status": "closed", "activity_at": 1}
]"#;
let slots = parse_slots(listing).expect("read");
let fix: Vec<&str> = slots[&("alpha".to_string(), "fix".to_string())]
.iter()
.map(|slot| slot.name.as_str())
.collect();
assert_eq!(fix, ["fixer", "other"]);
let main = &slots[&("alpha".to_string(), "main".to_string())];
assert_eq!(main.len(), 1);
assert_eq!(main[0].status, "parked");
assert_eq!(main[0].activity_at, None);
assert_eq!(main[0].detail.as_deref(), Some("stopped"));
assert!(parse_slots("not json").is_err());
assert!(
parse_slots(r#"[{"name": "x"}]"#).is_err(),
"a row that does not say where it runs is refused, not filed nowhere"
);
}
#[test]
fn test20034_prj_arguments_are_checked() {
for good in ["alpha", "alpha/fix", "a.b/c_d-2"] {
assert_eq!(target(good).expect(good), good);
}
for bad in ["", "--all", "alpha/", "/fix", "alpha/fix/x", "alpha fix"] {
assert!(target(bad).is_err(), "{bad:?} was taken as a target");
}
for good in ["main", "origin/main", "v1.2.3", "1234abcd"] {
assert_eq!(git_ref(good).expect(good), good);
}
for bad in ["", "-b", "--force", "a b", "a\nb"] {
assert!(git_ref(bad).is_err(), "{bad:?} was taken as a ref");
}
assert!(slot_name("--all").is_err());
assert!(slot_name("fixer").is_ok());
}
#[tokio::test]
async fn test20044_a_listing_is_announced_only_when_it_changes() {
let dir = tempfile::tempdir().expect("a scratch directory");
let machine = 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");
let listing = Listing::new(None);
let mut heard = listing.changed.subscribe();
let first = listing.read(&machine).await;
assert!(first.as_ref().as_ref().expect("read").projects.is_empty());
assert!(heard.try_recv().is_ok(), "the first reading is news");
listing.read(&machine).await;
assert!(heard.try_recv().is_err(), "the same reading again is not");
let elsewhere = tempfile::tempdir().expect("a checkout");
machine
.store
.register_worktree(&wsb::daemon::RegisteredWorktree {
id: wsb::daemon::WorktreeId::parse("~0123456789abcdef").expect("an id"),
project: None,
name: "notes".to_string(),
workspace: None,
super_root: elsewhere.path().to_path_buf(),
root_commit: None,
last_seen: 0,
available: true,
})
.expect("recorded");
let after = listing.read(&machine).await;
let standalone = &after.as_ref().as_ref().expect("read").standalone;
assert_eq!(standalone.len(), 1);
assert_eq!(standalone[0].name, "notes");
assert!(
standalone[0].error.is_some(),
"a folder that is not a repository says so rather than reading as clean"
);
assert!(heard.try_recv().is_ok(), "a worktree appearing is news");
assert_eq!(*listing.get(&machine).await, *after, "a page is served the reading held");
}