use super::*;
#[test]
fn test9926_identities_refuse_path_shapes() {
for hostile in ["../escape", "a/b", "", "UPPER", "with space", "peer.id"] {
assert!(
PeerId::parse(hostile).is_err(),
"{hostile:?} was accepted as a peer id"
);
assert!(
WorkspaceId::parse(hostile).is_err(),
"{hostile:?} was accepted as a workspace id"
);
}
for hostile in [
"9ab3cd7e",
"9ab3cd7e@",
"@main",
"9AB3@main",
"xyz@main",
"9ab3cd7e@../up",
"9ab3cd7e@a/b",
"9ab3cd7e@my notes",
"9ab3cd7e@-main",
] {
assert!(WorkspaceId::parse(hostile).is_err(), "{hostile:?} was accepted as a workspace id");
}
assert!(PeerId::parse("k7f3q9zx2m").is_ok());
let id = WorkspaceId::parse("9ab3cd7e@fix-2").expect("a continuity id");
assert_eq!((id.family(), id.worktree()), ("9ab3cd7e", "fix-2"));
assert_eq!(WorkspaceId::of("9ab3cd7e", "main"), WorkspaceId::parse("9ab3cd7e@main"));
assert!(serde_json::from_str::<WorkspaceId>("\"9ab3cd7e@../up\"").is_err());
assert_eq!(
serde_json::to_string(&id).expect("encodes"),
"\"9ab3cd7e@fix-2\""
);
assert!(PeerId::parse(&"a".repeat(65)).is_err());
}
#[test]
fn test9927_repository_ids_are_workspace_relative() {
let root = RepositoryId::root();
assert!(root.is_root());
assert_eq!(root.label(), "superproject");
assert_eq!(root.as_str(), "");
let nested = RepositoryId::parse("cartridges/mlxcartridge").expect("a nested repository");
assert_eq!(nested.label(), "cartridges/mlxcartridge");
for hostile in ["/absolute", "../up", "a/../../b", "back\\slash"] {
assert!(
RepositoryId::parse(hostile).is_err(),
"{hostile:?} was accepted as a repository path"
);
}
}
#[test]
fn test9928_blocked_always_carries_its_reason() {
let mut status = RepositoryStatus {
repository: RepositoryId::root(),
desired: Desired::Enabled,
continuity: Continuity::Auto,
observed: Observed::Blocked,
blocked: None,
pending: 0,
incoming: 0,
outgoing: 0,
conflicts: 0,
undecided: 0,
branch: None,
head: None,
};
let error = status.check().expect_err("blocked with no reason must be refused");
assert!(error.contains("blocked with no reason"), "{error}");
status.blocked = Some(Blocked::BranchMismatch {
here: "feature/peer".into(),
there: "main".into(),
});
status.check().expect("blocked with a reason is well formed");
status.observed = Observed::Syncing;
let error = status.check().expect_err("a syncing repository has no blocked reason");
assert!(error.contains("blocked reason"), "{error}");
}
#[test]
fn test9929_every_blocked_reason_reads_in_both_directions() {
let cases = [
(
Blocked::DetachedHead { here: true, there: false },
"detached_head",
"this side",
),
(
Blocked::DetachedHead { here: false, there: true },
"detached_head",
"the peer",
),
(
Blocked::GitOperation { here: false, there: true },
"git_operation",
"the peer",
),
(
Blocked::SubmoduleMissing { here: true, there: false },
"submodule_missing",
"the peer",
),
(Blocked::PeerOffline, "peer_offline", "not connected"),
];
for (reason, code, expected) in cases {
assert_eq!(reason.code(), code);
let described = reason.describe();
assert!(
described.contains(expected),
"{code} reads {described:?}, which does not say {expected:?}"
);
}
let mismatch = Blocked::HeadMismatch {
here: "a81f93d2c4e5f6".into(),
there: "def4567890abcd".into(),
};
let described = mismatch.describe();
assert!(described.contains("a81f93d"), "{described}");
assert!(described.contains("def4567"), "{described}");
assert!(!described.contains("a81f93d2c4e5f6"), "the full id is not shown: {described}");
}
#[test]
fn test9930_desired_and_observed_do_not_collapse() {
let enabled_but_blocked = RepositoryStatus {
repository: RepositoryId::root(),
desired: Desired::Enabled,
continuity: Continuity::Auto,
observed: Observed::Blocked,
blocked: Some(Blocked::HeadMismatch {
here: "abc123".into(),
there: "def456".into(),
}),
pending: 0,
incoming: 0,
outgoing: 0,
conflicts: 0,
undecided: 0,
branch: Some("feature/peer".into()),
head: Some("abc123".into()),
};
let paused = RepositoryStatus {
desired: Desired::Paused,
continuity: Continuity::Paused,
observed: Observed::Paused,
blocked: None,
..enabled_but_blocked.clone()
};
enabled_but_blocked.check().expect("well formed");
paused.check().expect("well formed");
assert_ne!(enabled_but_blocked.desired, paused.desired);
assert_ne!(enabled_but_blocked.observed, paused.observed);
}
#[test]
fn test10134_continuity_is_the_three_switches_combined() {
use Continuity as C;
for desired in [Desired::Enabled, Desired::Paused, Desired::Disabled] {
for mode in [SyncMode::Manual, SyncMode::Auto] {
assert_eq!(
C::of(Switch::Off, desired, mode),
C::Off,
"the files switch must win over desired={desired:?} mode={mode:?}"
);
}
}
assert_eq!(C::of(Switch::On, Desired::Disabled, SyncMode::Auto), C::Off);
assert_eq!(C::of(Switch::On, Desired::Paused, SyncMode::Auto), C::Paused);
assert_eq!(C::of(Switch::On, Desired::Enabled, SyncMode::Manual), C::Manual);
assert_eq!(C::of(Switch::On, Desired::Enabled, SyncMode::Auto), C::Auto);
}
#[test]
fn test10135_each_continuity_permits_exactly_what_it_says() {
use Continuity as C;
assert!(!C::Off.compares(), "off must not exchange reports");
assert!(C::Paused.compares(), "a pause is something you watch");
assert!(C::Manual.compares());
assert!(C::Auto.compares());
assert!(!C::Off.carries_decided());
assert!(
!C::Paused.carries_decided(),
"a decision made before a pause is not consent to act during it"
);
assert!(C::Manual.carries_decided());
assert!(C::Auto.carries_decided());
for state in [C::Off, C::Paused, C::Manual] {
assert!(
!state.takes_undecided(),
"{} must never take a file nobody decided about",
state.as_str()
);
}
assert!(
C::Auto.takes_undecided(),
"auto is exactly the state in which what arrives is applied unasked"
);
}
#[test]
fn test10136_pairing_starts_listed_not_carried() {
assert_eq!(SyncMode::default(), SyncMode::Manual);
for service in Service::BUILT_IN {
assert_eq!(
Switch::default_for(&service),
Switch::On,
"{service} must be available to a machine somebody paired"
);
}
let plugin = Service::parse("sdx.secrets").expect("a plugin service");
assert_eq!(
Switch::default_for(&plugin),
Switch::Off,
"a plugin's service must not be answered for a machine nobody switched it on for"
);
assert_eq!(
Continuity::of(
Switch::default_for(&Service::Files),
Desired::Enabled,
SyncMode::default()
),
Continuity::Manual,
"a freshly enabled workspace lists differences and moves nothing"
);
}
#[test]
fn test10137_service_vocabulary_round_trips() {
let plugin = Service::parse("sdx.secrets").expect("a plugin service");
for service in Service::BUILT_IN.into_iter().chain([plugin]) {
assert_eq!(Service::parse(service.as_str()), Ok(service.clone()));
assert!(!service.describe().is_empty());
}
assert!(Service::parse("secrets").is_err(), "a bare word that is not wsb's is no service");
for switch in [Switch::On, Switch::Off] {
assert_eq!(Switch::parse(switch.as_str()), Ok(switch));
}
for mode in [SyncMode::Manual, SyncMode::Auto] {
assert_eq!(SyncMode::parse(mode.as_str()), Ok(mode));
}
assert!(Service::parse("Files").is_err(), "case is not a service name");
assert!(Service::parse("sessions").is_err());
assert!(Switch::parse("true").is_err(), "a switch is on or off, not a bool");
assert!(SyncMode::parse("automatic").is_err());
}