logiguard fork v3: full patch set on verified 8c74db0 tree
Includes prior-session patches (carry forward so the app compiles): - crates/gpui/build.rs: cross-compile manifest fix - crates/gpui/src/platform.rs: PlatformWindow::activate_with_token trait method - crates/gpui/src/window.rs: Window::activate_with_token public API - crates/gpui_linux/src/linux/wayland/window.rs: WaylandWindow::activate_with_token + activate() keyboard-serial fix Plus the focus-serial fix: - serial.rs: SerialKind::KeyboardEnter - client.rs: store wl_keyboard.enter serial; latest_serial_of() Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
957
crates/project/tests/integration/trusted_worktrees.rs
Normal file
957
crates/project/tests/integration/trusted_worktrees.rs
Normal file
@@ -0,0 +1,957 @@
|
||||
use std::{cell::RefCell, path::PathBuf, rc::Rc};
|
||||
|
||||
use collections::HashSet;
|
||||
use gpui::{Entity, TestAppContext};
|
||||
use serde_json::json;
|
||||
use settings::SettingsStore;
|
||||
use util::path;
|
||||
|
||||
use crate::{FakeFs, Project};
|
||||
|
||||
use project::{trusted_worktrees::*, worktree_store::WorktreeStore};
|
||||
|
||||
fn init_test(cx: &mut TestAppContext) {
|
||||
cx.update(|cx| {
|
||||
if cx.try_global::<SettingsStore>().is_none() {
|
||||
let settings_store = SettingsStore::test(cx);
|
||||
cx.set_global(settings_store);
|
||||
}
|
||||
if cx.try_global::<TrustedWorktrees>().is_some() {
|
||||
cx.remove_global::<TrustedWorktrees>();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
fn init_trust_global(
|
||||
worktree_store: Entity<WorktreeStore>,
|
||||
cx: &mut TestAppContext,
|
||||
) -> Entity<TrustedWorktreesStore> {
|
||||
cx.update(|cx| {
|
||||
init(DbTrustedPaths::default(), cx);
|
||||
track_worktree_trust(worktree_store, None, None, None, cx);
|
||||
TrustedWorktrees::try_get_global(cx).expect("global should be set")
|
||||
})
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_single_worktree_trust(cx: &mut TestAppContext) {
|
||||
init_test(cx);
|
||||
|
||||
let fs = FakeFs::new(cx.executor());
|
||||
fs.insert_tree(path!("/root"), json!({ "main.rs": "fn main() {}" }))
|
||||
.await;
|
||||
|
||||
let project = Project::test(fs, [path!("/root").as_ref()], cx).await;
|
||||
let worktree_store = project.read_with(cx, |project, _| project.worktree_store());
|
||||
let worktree_id = worktree_store.read_with(cx, |store, cx| {
|
||||
store.worktrees().next().unwrap().read(cx).id()
|
||||
});
|
||||
|
||||
let trusted_worktrees = init_trust_global(worktree_store.clone(), cx);
|
||||
|
||||
let events: Rc<RefCell<Vec<TrustedWorktreesEvent>>> = Rc::default();
|
||||
cx.update({
|
||||
let events = events.clone();
|
||||
|cx| {
|
||||
cx.subscribe(&trusted_worktrees, move |_, event, _| {
|
||||
events.borrow_mut().push(match event {
|
||||
TrustedWorktreesEvent::Trusted(host, paths) => {
|
||||
TrustedWorktreesEvent::Trusted(host.clone(), paths.clone())
|
||||
}
|
||||
TrustedWorktreesEvent::Restricted(host, paths) => {
|
||||
TrustedWorktreesEvent::Restricted(host.clone(), paths.clone())
|
||||
}
|
||||
});
|
||||
})
|
||||
}
|
||||
})
|
||||
.detach();
|
||||
|
||||
let can_trust = trusted_worktrees.update(cx, |store, cx| {
|
||||
store.can_trust(&worktree_store, worktree_id, cx)
|
||||
});
|
||||
assert!(!can_trust, "worktree should be restricted by default");
|
||||
|
||||
{
|
||||
let events = events.borrow();
|
||||
assert_eq!(events.len(), 1);
|
||||
match &events[0] {
|
||||
TrustedWorktreesEvent::Restricted(event_worktree_store, paths) => {
|
||||
assert_eq!(event_worktree_store, &worktree_store.downgrade());
|
||||
assert!(paths.contains(&PathTrust::Worktree(worktree_id)));
|
||||
}
|
||||
_ => panic!("expected Restricted event"),
|
||||
}
|
||||
}
|
||||
|
||||
let has_restricted = trusted_worktrees.read_with(cx, |store, cx| {
|
||||
store.has_restricted_worktrees(&worktree_store, cx)
|
||||
});
|
||||
assert!(has_restricted, "should have restricted worktrees");
|
||||
|
||||
let restricted = trusted_worktrees.read_with(cx, |trusted_worktrees, cx| {
|
||||
trusted_worktrees.restricted_worktrees(&worktree_store, cx)
|
||||
});
|
||||
assert!(restricted.iter().any(|(id, _)| *id == worktree_id));
|
||||
|
||||
events.borrow_mut().clear();
|
||||
|
||||
let can_trust_again = trusted_worktrees.update(cx, |store, cx| {
|
||||
store.can_trust(&worktree_store, worktree_id, cx)
|
||||
});
|
||||
assert!(!can_trust_again, "worktree should still be restricted");
|
||||
assert!(
|
||||
events.borrow().is_empty(),
|
||||
"no duplicate Restricted event on repeated can_trust"
|
||||
);
|
||||
|
||||
trusted_worktrees.update(cx, |store, cx| {
|
||||
store.trust(
|
||||
&worktree_store,
|
||||
HashSet::from_iter([PathTrust::Worktree(worktree_id)]),
|
||||
cx,
|
||||
);
|
||||
});
|
||||
|
||||
{
|
||||
let events = events.borrow();
|
||||
assert_eq!(events.len(), 1);
|
||||
match &events[0] {
|
||||
TrustedWorktreesEvent::Trusted(event_worktree_store, paths) => {
|
||||
assert_eq!(event_worktree_store, &worktree_store.downgrade());
|
||||
assert!(paths.contains(&PathTrust::Worktree(worktree_id)));
|
||||
}
|
||||
_ => panic!("expected Trusted event"),
|
||||
}
|
||||
}
|
||||
|
||||
let can_trust_after = trusted_worktrees.update(cx, |store, cx| {
|
||||
store.can_trust(&worktree_store, worktree_id, cx)
|
||||
});
|
||||
assert!(can_trust_after, "worktree should be trusted after trust()");
|
||||
|
||||
let has_restricted_after = trusted_worktrees.read_with(cx, |store, cx| {
|
||||
store.has_restricted_worktrees(&worktree_store, cx)
|
||||
});
|
||||
assert!(
|
||||
!has_restricted_after,
|
||||
"should have no restricted worktrees after trust"
|
||||
);
|
||||
|
||||
let restricted_after = trusted_worktrees.read_with(cx, |trusted_worktrees, cx| {
|
||||
trusted_worktrees.restricted_worktrees(&worktree_store, cx)
|
||||
});
|
||||
assert!(
|
||||
restricted_after.is_empty(),
|
||||
"restricted set should be empty"
|
||||
);
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_single_file_worktree_trust(cx: &mut TestAppContext) {
|
||||
init_test(cx);
|
||||
|
||||
let fs = FakeFs::new(cx.executor());
|
||||
fs.insert_tree(path!("/root"), json!({ "foo.rs": "fn foo() {}" }))
|
||||
.await;
|
||||
|
||||
let project = Project::test(fs, [path!("/root/foo.rs").as_ref()], cx).await;
|
||||
let worktree_store = project.read_with(cx, |project, _| project.worktree_store());
|
||||
let worktree_id = worktree_store.read_with(cx, |store, cx| {
|
||||
let worktree = store.worktrees().next().unwrap();
|
||||
let worktree = worktree.read(cx);
|
||||
assert!(worktree.is_single_file(), "expected single-file worktree");
|
||||
worktree.id()
|
||||
});
|
||||
|
||||
let trusted_worktrees = init_trust_global(worktree_store.clone(), cx);
|
||||
|
||||
let events: Rc<RefCell<Vec<TrustedWorktreesEvent>>> = Rc::default();
|
||||
cx.update({
|
||||
let events = events.clone();
|
||||
|cx| {
|
||||
cx.subscribe(&trusted_worktrees, move |_, event, _| {
|
||||
events.borrow_mut().push(match event {
|
||||
TrustedWorktreesEvent::Trusted(host, paths) => {
|
||||
TrustedWorktreesEvent::Trusted(host.clone(), paths.clone())
|
||||
}
|
||||
TrustedWorktreesEvent::Restricted(host, paths) => {
|
||||
TrustedWorktreesEvent::Restricted(host.clone(), paths.clone())
|
||||
}
|
||||
});
|
||||
})
|
||||
}
|
||||
})
|
||||
.detach();
|
||||
|
||||
let can_trust = trusted_worktrees.update(cx, |store, cx| {
|
||||
store.can_trust(&worktree_store, worktree_id, cx)
|
||||
});
|
||||
assert!(
|
||||
!can_trust,
|
||||
"single-file worktree should be restricted by default"
|
||||
);
|
||||
|
||||
{
|
||||
let events = events.borrow();
|
||||
assert_eq!(events.len(), 1);
|
||||
match &events[0] {
|
||||
TrustedWorktreesEvent::Restricted(event_worktree_store, paths) => {
|
||||
assert_eq!(event_worktree_store, &worktree_store.downgrade());
|
||||
assert!(paths.contains(&PathTrust::Worktree(worktree_id)));
|
||||
}
|
||||
_ => panic!("expected Restricted event"),
|
||||
}
|
||||
}
|
||||
|
||||
events.borrow_mut().clear();
|
||||
|
||||
trusted_worktrees.update(cx, |store, cx| {
|
||||
store.trust(
|
||||
&worktree_store,
|
||||
HashSet::from_iter([PathTrust::Worktree(worktree_id)]),
|
||||
cx,
|
||||
);
|
||||
});
|
||||
|
||||
{
|
||||
let events = events.borrow();
|
||||
assert_eq!(events.len(), 1);
|
||||
match &events[0] {
|
||||
TrustedWorktreesEvent::Trusted(event_worktree_store, paths) => {
|
||||
assert_eq!(event_worktree_store, &worktree_store.downgrade());
|
||||
assert!(paths.contains(&PathTrust::Worktree(worktree_id)));
|
||||
}
|
||||
_ => panic!("expected Trusted event"),
|
||||
}
|
||||
}
|
||||
|
||||
let can_trust_after = trusted_worktrees.update(cx, |store, cx| {
|
||||
store.can_trust(&worktree_store, worktree_id, cx)
|
||||
});
|
||||
assert!(
|
||||
can_trust_after,
|
||||
"single-file worktree should be trusted after trust()"
|
||||
);
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_multiple_single_file_worktrees_trust_one(cx: &mut TestAppContext) {
|
||||
init_test(cx);
|
||||
|
||||
let fs = FakeFs::new(cx.executor());
|
||||
fs.insert_tree(
|
||||
path!("/root"),
|
||||
json!({
|
||||
"a.rs": "fn a() {}",
|
||||
"b.rs": "fn b() {}",
|
||||
"c.rs": "fn c() {}"
|
||||
}),
|
||||
)
|
||||
.await;
|
||||
|
||||
let project = Project::test(
|
||||
fs,
|
||||
[
|
||||
path!("/root/a.rs").as_ref(),
|
||||
path!("/root/b.rs").as_ref(),
|
||||
path!("/root/c.rs").as_ref(),
|
||||
],
|
||||
cx,
|
||||
)
|
||||
.await;
|
||||
let worktree_store = project.read_with(cx, |project, _| project.worktree_store());
|
||||
let worktree_ids: Vec<_> = worktree_store.read_with(cx, |store, cx| {
|
||||
store
|
||||
.worktrees()
|
||||
.map(|worktree| {
|
||||
let worktree = worktree.read(cx);
|
||||
assert!(worktree.is_single_file());
|
||||
worktree.id()
|
||||
})
|
||||
.collect()
|
||||
});
|
||||
assert_eq!(worktree_ids.len(), 3);
|
||||
|
||||
let trusted_worktrees = init_trust_global(worktree_store.clone(), cx);
|
||||
|
||||
for &worktree_id in &worktree_ids {
|
||||
let can_trust = trusted_worktrees.update(cx, |store, cx| {
|
||||
store.can_trust(&worktree_store, worktree_id, cx)
|
||||
});
|
||||
assert!(
|
||||
!can_trust,
|
||||
"worktree {worktree_id:?} should be restricted initially"
|
||||
);
|
||||
}
|
||||
|
||||
trusted_worktrees.update(cx, |store, cx| {
|
||||
store.trust(
|
||||
&worktree_store,
|
||||
HashSet::from_iter([PathTrust::Worktree(worktree_ids[1])]),
|
||||
cx,
|
||||
);
|
||||
});
|
||||
|
||||
let can_trust_0 = trusted_worktrees.update(cx, |store, cx| {
|
||||
store.can_trust(&worktree_store, worktree_ids[0], cx)
|
||||
});
|
||||
let can_trust_1 = trusted_worktrees.update(cx, |store, cx| {
|
||||
store.can_trust(&worktree_store, worktree_ids[1], cx)
|
||||
});
|
||||
let can_trust_2 = trusted_worktrees.update(cx, |store, cx| {
|
||||
store.can_trust(&worktree_store, worktree_ids[2], cx)
|
||||
});
|
||||
|
||||
assert!(!can_trust_0, "worktree 0 should still be restricted");
|
||||
assert!(can_trust_1, "worktree 1 should be trusted");
|
||||
assert!(!can_trust_2, "worktree 2 should still be restricted");
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_two_directory_worktrees_separate_trust(cx: &mut TestAppContext) {
|
||||
init_test(cx);
|
||||
|
||||
let fs = FakeFs::new(cx.executor());
|
||||
fs.insert_tree(
|
||||
path!("/projects"),
|
||||
json!({
|
||||
"project_a": { "main.rs": "fn main() {}" },
|
||||
"project_b": { "lib.rs": "pub fn lib() {}" }
|
||||
}),
|
||||
)
|
||||
.await;
|
||||
|
||||
let project = Project::test(
|
||||
fs,
|
||||
[
|
||||
path!("/projects/project_a").as_ref(),
|
||||
path!("/projects/project_b").as_ref(),
|
||||
],
|
||||
cx,
|
||||
)
|
||||
.await;
|
||||
let worktree_store = project.read_with(cx, |project, _| project.worktree_store());
|
||||
let worktree_ids: Vec<_> = worktree_store.read_with(cx, |store, cx| {
|
||||
store
|
||||
.worktrees()
|
||||
.map(|worktree| {
|
||||
let worktree = worktree.read(cx);
|
||||
assert!(!worktree.is_single_file());
|
||||
worktree.id()
|
||||
})
|
||||
.collect()
|
||||
});
|
||||
assert_eq!(worktree_ids.len(), 2);
|
||||
|
||||
let trusted_worktrees = init_trust_global(worktree_store.clone(), cx);
|
||||
|
||||
let can_trust_a = trusted_worktrees.update(cx, |store, cx| {
|
||||
store.can_trust(&worktree_store, worktree_ids[0], cx)
|
||||
});
|
||||
let can_trust_b = trusted_worktrees.update(cx, |store, cx| {
|
||||
store.can_trust(&worktree_store, worktree_ids[1], cx)
|
||||
});
|
||||
assert!(!can_trust_a, "project_a should be restricted initially");
|
||||
assert!(!can_trust_b, "project_b should be restricted initially");
|
||||
|
||||
trusted_worktrees.update(cx, |store, cx| {
|
||||
store.trust(
|
||||
&worktree_store,
|
||||
HashSet::from_iter([PathTrust::Worktree(worktree_ids[0])]),
|
||||
cx,
|
||||
);
|
||||
});
|
||||
|
||||
let can_trust_a = trusted_worktrees.update(cx, |store, cx| {
|
||||
store.can_trust(&worktree_store, worktree_ids[0], cx)
|
||||
});
|
||||
let can_trust_b = trusted_worktrees.update(cx, |store, cx| {
|
||||
store.can_trust(&worktree_store, worktree_ids[1], cx)
|
||||
});
|
||||
assert!(can_trust_a, "project_a should be trusted after trust()");
|
||||
assert!(!can_trust_b, "project_b should still be restricted");
|
||||
|
||||
trusted_worktrees.update(cx, |store, cx| {
|
||||
store.trust(
|
||||
&worktree_store,
|
||||
HashSet::from_iter([PathTrust::Worktree(worktree_ids[1])]),
|
||||
cx,
|
||||
);
|
||||
});
|
||||
|
||||
let can_trust_a = trusted_worktrees.update(cx, |store, cx| {
|
||||
store.can_trust(&worktree_store, worktree_ids[0], cx)
|
||||
});
|
||||
let can_trust_b = trusted_worktrees.update(cx, |store, cx| {
|
||||
store.can_trust(&worktree_store, worktree_ids[1], cx)
|
||||
});
|
||||
assert!(can_trust_a, "project_a should remain trusted");
|
||||
assert!(can_trust_b, "project_b should now be trusted");
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_directory_worktree_trust_enables_single_file(cx: &mut TestAppContext) {
|
||||
init_test(cx);
|
||||
|
||||
let fs = FakeFs::new(cx.executor());
|
||||
fs.insert_tree(
|
||||
path!("/"),
|
||||
json!({
|
||||
"project": { "main.rs": "fn main() {}" },
|
||||
"standalone.rs": "fn standalone() {}"
|
||||
}),
|
||||
)
|
||||
.await;
|
||||
|
||||
let project = Project::test(
|
||||
fs,
|
||||
[path!("/project").as_ref(), path!("/standalone.rs").as_ref()],
|
||||
cx,
|
||||
)
|
||||
.await;
|
||||
let worktree_store = project.read_with(cx, |project, _| project.worktree_store());
|
||||
let (dir_worktree_id, file_worktree_id) = worktree_store.read_with(cx, |store, cx| {
|
||||
let worktrees: Vec<_> = store.worktrees().collect();
|
||||
assert_eq!(worktrees.len(), 2);
|
||||
let (dir_worktree, file_worktree) = if worktrees[0].read(cx).is_single_file() {
|
||||
(&worktrees[1], &worktrees[0])
|
||||
} else {
|
||||
(&worktrees[0], &worktrees[1])
|
||||
};
|
||||
assert!(!dir_worktree.read(cx).is_single_file());
|
||||
assert!(file_worktree.read(cx).is_single_file());
|
||||
(dir_worktree.read(cx).id(), file_worktree.read(cx).id())
|
||||
});
|
||||
|
||||
let trusted_worktrees = init_trust_global(worktree_store.clone(), cx);
|
||||
|
||||
let can_trust_file = trusted_worktrees.update(cx, |store, cx| {
|
||||
store.can_trust(&worktree_store, file_worktree_id, cx)
|
||||
});
|
||||
assert!(
|
||||
!can_trust_file,
|
||||
"single-file worktree should be restricted initially"
|
||||
);
|
||||
|
||||
let can_trust_directory = trusted_worktrees.update(cx, |store, cx| {
|
||||
store.can_trust(&worktree_store, dir_worktree_id, cx)
|
||||
});
|
||||
assert!(
|
||||
!can_trust_directory,
|
||||
"directory worktree should be restricted initially"
|
||||
);
|
||||
|
||||
trusted_worktrees.update(cx, |store, cx| {
|
||||
store.trust(
|
||||
&worktree_store,
|
||||
HashSet::from_iter([PathTrust::Worktree(dir_worktree_id)]),
|
||||
cx,
|
||||
);
|
||||
});
|
||||
|
||||
let can_trust_dir = trusted_worktrees.update(cx, |store, cx| {
|
||||
store.can_trust(&worktree_store, dir_worktree_id, cx)
|
||||
});
|
||||
let can_trust_file_after = trusted_worktrees.update(cx, |store, cx| {
|
||||
store.can_trust(&worktree_store, file_worktree_id, cx)
|
||||
});
|
||||
assert!(can_trust_dir, "directory worktree should be trusted");
|
||||
assert!(
|
||||
can_trust_file_after,
|
||||
"single-file worktree should be trusted after directory worktree trust"
|
||||
);
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_parent_path_trust_enables_single_file(cx: &mut TestAppContext) {
|
||||
init_test(cx);
|
||||
|
||||
let fs = FakeFs::new(cx.executor());
|
||||
fs.insert_tree(
|
||||
path!("/"),
|
||||
json!({
|
||||
"project": { "main.rs": "fn main() {}" },
|
||||
"standalone.rs": "fn standalone() {}"
|
||||
}),
|
||||
)
|
||||
.await;
|
||||
|
||||
let project = Project::test(
|
||||
fs,
|
||||
[path!("/project").as_ref(), path!("/standalone.rs").as_ref()],
|
||||
cx,
|
||||
)
|
||||
.await;
|
||||
let worktree_store = project.read_with(cx, |project, _| project.worktree_store());
|
||||
let (dir_worktree_id, file_worktree_id) = worktree_store.read_with(cx, |store, cx| {
|
||||
let worktrees: Vec<_> = store.worktrees().collect();
|
||||
assert_eq!(worktrees.len(), 2);
|
||||
let (dir_worktree, file_worktree) = if worktrees[0].read(cx).is_single_file() {
|
||||
(&worktrees[1], &worktrees[0])
|
||||
} else {
|
||||
(&worktrees[0], &worktrees[1])
|
||||
};
|
||||
assert!(!dir_worktree.read(cx).is_single_file());
|
||||
assert!(file_worktree.read(cx).is_single_file());
|
||||
(dir_worktree.read(cx).id(), file_worktree.read(cx).id())
|
||||
});
|
||||
|
||||
let trusted_worktrees = init_trust_global(worktree_store.clone(), cx);
|
||||
|
||||
let can_trust_file = trusted_worktrees.update(cx, |store, cx| {
|
||||
store.can_trust(&worktree_store, file_worktree_id, cx)
|
||||
});
|
||||
assert!(
|
||||
!can_trust_file,
|
||||
"single-file worktree should be restricted initially"
|
||||
);
|
||||
|
||||
let can_trust_directory = trusted_worktrees.update(cx, |store, cx| {
|
||||
store.can_trust(&worktree_store, dir_worktree_id, cx)
|
||||
});
|
||||
assert!(
|
||||
!can_trust_directory,
|
||||
"directory worktree should be restricted initially"
|
||||
);
|
||||
|
||||
trusted_worktrees.update(cx, |store, cx| {
|
||||
store.trust(
|
||||
&worktree_store,
|
||||
HashSet::from_iter([PathTrust::AbsPath(PathBuf::from(path!("/project")))]),
|
||||
cx,
|
||||
);
|
||||
});
|
||||
|
||||
let can_trust_dir = trusted_worktrees.update(cx, |store, cx| {
|
||||
store.can_trust(&worktree_store, dir_worktree_id, cx)
|
||||
});
|
||||
let can_trust_file_after = trusted_worktrees.update(cx, |store, cx| {
|
||||
store.can_trust(&worktree_store, file_worktree_id, cx)
|
||||
});
|
||||
assert!(
|
||||
can_trust_dir,
|
||||
"directory worktree should be trusted after its parent is trusted"
|
||||
);
|
||||
assert!(
|
||||
can_trust_file_after,
|
||||
"single-file worktree should be trusted after directory worktree trust via its parent directory trust"
|
||||
);
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_abs_path_trust_covers_multiple_worktrees(cx: &mut TestAppContext) {
|
||||
init_test(cx);
|
||||
|
||||
let fs = FakeFs::new(cx.executor());
|
||||
fs.insert_tree(
|
||||
path!("/root"),
|
||||
json!({
|
||||
"project_a": { "main.rs": "fn main() {}" },
|
||||
"project_b": { "lib.rs": "pub fn lib() {}" }
|
||||
}),
|
||||
)
|
||||
.await;
|
||||
|
||||
let project = Project::test(
|
||||
fs,
|
||||
[
|
||||
path!("/root/project_a").as_ref(),
|
||||
path!("/root/project_b").as_ref(),
|
||||
],
|
||||
cx,
|
||||
)
|
||||
.await;
|
||||
let worktree_store = project.read_with(cx, |project, _| project.worktree_store());
|
||||
let worktree_ids: Vec<_> = worktree_store.read_with(cx, |store, cx| {
|
||||
store
|
||||
.worktrees()
|
||||
.map(|worktree| worktree.read(cx).id())
|
||||
.collect()
|
||||
});
|
||||
assert_eq!(worktree_ids.len(), 2);
|
||||
|
||||
let trusted_worktrees = init_trust_global(worktree_store.clone(), cx);
|
||||
|
||||
for &worktree_id in &worktree_ids {
|
||||
let can_trust = trusted_worktrees.update(cx, |store, cx| {
|
||||
store.can_trust(&worktree_store, worktree_id, cx)
|
||||
});
|
||||
assert!(!can_trust, "worktree should be restricted initially");
|
||||
}
|
||||
|
||||
trusted_worktrees.update(cx, |store, cx| {
|
||||
store.trust(
|
||||
&worktree_store,
|
||||
HashSet::from_iter([PathTrust::AbsPath(PathBuf::from(path!("/root")))]),
|
||||
cx,
|
||||
);
|
||||
});
|
||||
|
||||
for &worktree_id in &worktree_ids {
|
||||
let can_trust = trusted_worktrees.update(cx, |store, cx| {
|
||||
store.can_trust(&worktree_store, worktree_id, cx)
|
||||
});
|
||||
assert!(
|
||||
can_trust,
|
||||
"worktree should be trusted after parent path trust"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_auto_trust_all(cx: &mut TestAppContext) {
|
||||
init_test(cx);
|
||||
|
||||
let fs = FakeFs::new(cx.executor());
|
||||
fs.insert_tree(
|
||||
path!("/"),
|
||||
json!({
|
||||
"project_a": { "main.rs": "fn main() {}" },
|
||||
"project_b": { "lib.rs": "pub fn lib() {}" },
|
||||
"single.rs": "fn single() {}"
|
||||
}),
|
||||
)
|
||||
.await;
|
||||
|
||||
let project = Project::test(
|
||||
fs,
|
||||
[
|
||||
path!("/project_a").as_ref(),
|
||||
path!("/project_b").as_ref(),
|
||||
path!("/single.rs").as_ref(),
|
||||
],
|
||||
cx,
|
||||
)
|
||||
.await;
|
||||
let worktree_store = project.read_with(cx, |project, _| project.worktree_store());
|
||||
let worktree_ids: Vec<_> = worktree_store.read_with(cx, |store, cx| {
|
||||
store
|
||||
.worktrees()
|
||||
.map(|worktree| worktree.read(cx).id())
|
||||
.collect()
|
||||
});
|
||||
assert_eq!(worktree_ids.len(), 3);
|
||||
|
||||
let trusted_worktrees = init_trust_global(worktree_store.clone(), cx);
|
||||
|
||||
let events: Rc<RefCell<Vec<TrustedWorktreesEvent>>> = Rc::default();
|
||||
cx.update({
|
||||
let events = events.clone();
|
||||
|cx| {
|
||||
cx.subscribe(&trusted_worktrees, move |_, event, _| {
|
||||
events.borrow_mut().push(match event {
|
||||
TrustedWorktreesEvent::Trusted(host, paths) => {
|
||||
TrustedWorktreesEvent::Trusted(host.clone(), paths.clone())
|
||||
}
|
||||
TrustedWorktreesEvent::Restricted(host, paths) => {
|
||||
TrustedWorktreesEvent::Restricted(host.clone(), paths.clone())
|
||||
}
|
||||
});
|
||||
})
|
||||
}
|
||||
})
|
||||
.detach();
|
||||
|
||||
for &worktree_id in &worktree_ids {
|
||||
let can_trust = trusted_worktrees.update(cx, |store, cx| {
|
||||
store.can_trust(&worktree_store, worktree_id, cx)
|
||||
});
|
||||
assert!(!can_trust, "worktree should be restricted initially");
|
||||
}
|
||||
|
||||
let has_restricted = trusted_worktrees.read_with(cx, |store, cx| {
|
||||
store.has_restricted_worktrees(&worktree_store, cx)
|
||||
});
|
||||
assert!(has_restricted, "should have restricted worktrees");
|
||||
|
||||
events.borrow_mut().clear();
|
||||
|
||||
trusted_worktrees.update(cx, |store, cx| {
|
||||
store.auto_trust_all(cx);
|
||||
});
|
||||
|
||||
for &worktree_id in &worktree_ids {
|
||||
let can_trust = trusted_worktrees.update(cx, |store, cx| {
|
||||
store.can_trust(&worktree_store, worktree_id, cx)
|
||||
});
|
||||
assert!(
|
||||
can_trust,
|
||||
"worktree {worktree_id:?} should be trusted after auto_trust_all"
|
||||
);
|
||||
}
|
||||
|
||||
let has_restricted_after = trusted_worktrees.read_with(cx, |store, cx| {
|
||||
store.has_restricted_worktrees(&worktree_store, cx)
|
||||
});
|
||||
assert!(
|
||||
!has_restricted_after,
|
||||
"should have no restricted worktrees after auto_trust_all"
|
||||
);
|
||||
|
||||
let trusted_event_count = events
|
||||
.borrow()
|
||||
.iter()
|
||||
.filter(|e| matches!(e, TrustedWorktreesEvent::Trusted(..)))
|
||||
.count();
|
||||
assert!(
|
||||
trusted_event_count > 0,
|
||||
"should have emitted Trusted events"
|
||||
);
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_trust_restrict_trust_cycle(cx: &mut TestAppContext) {
|
||||
init_test(cx);
|
||||
|
||||
let fs = FakeFs::new(cx.executor());
|
||||
fs.insert_tree(path!("/root"), json!({ "main.rs": "fn main() {}" }))
|
||||
.await;
|
||||
|
||||
let project = Project::test(fs, [path!("/root").as_ref()], cx).await;
|
||||
let worktree_store = project.read_with(cx, |project, _| project.worktree_store());
|
||||
let worktree_id = worktree_store.read_with(cx, |store, cx| {
|
||||
store.worktrees().next().unwrap().read(cx).id()
|
||||
});
|
||||
|
||||
let trusted_worktrees = init_trust_global(worktree_store.clone(), cx);
|
||||
|
||||
let events: Rc<RefCell<Vec<TrustedWorktreesEvent>>> = Rc::default();
|
||||
cx.update({
|
||||
let events = events.clone();
|
||||
|cx| {
|
||||
cx.subscribe(&trusted_worktrees, move |_, event, _| {
|
||||
events.borrow_mut().push(match event {
|
||||
TrustedWorktreesEvent::Trusted(host, paths) => {
|
||||
TrustedWorktreesEvent::Trusted(host.clone(), paths.clone())
|
||||
}
|
||||
TrustedWorktreesEvent::Restricted(host, paths) => {
|
||||
TrustedWorktreesEvent::Restricted(host.clone(), paths.clone())
|
||||
}
|
||||
});
|
||||
})
|
||||
}
|
||||
})
|
||||
.detach();
|
||||
|
||||
let can_trust = trusted_worktrees.update(cx, |store, cx| {
|
||||
store.can_trust(&worktree_store, worktree_id, cx)
|
||||
});
|
||||
assert!(!can_trust, "should be restricted initially");
|
||||
assert_eq!(events.borrow().len(), 1);
|
||||
events.borrow_mut().clear();
|
||||
|
||||
trusted_worktrees.update(cx, |store, cx| {
|
||||
store.trust(
|
||||
&worktree_store,
|
||||
HashSet::from_iter([PathTrust::Worktree(worktree_id)]),
|
||||
cx,
|
||||
);
|
||||
});
|
||||
let can_trust = trusted_worktrees.update(cx, |store, cx| {
|
||||
store.can_trust(&worktree_store, worktree_id, cx)
|
||||
});
|
||||
assert!(can_trust, "should be trusted after trust()");
|
||||
assert_eq!(events.borrow().len(), 1);
|
||||
assert!(matches!(
|
||||
&events.borrow()[0],
|
||||
TrustedWorktreesEvent::Trusted(..)
|
||||
));
|
||||
events.borrow_mut().clear();
|
||||
|
||||
trusted_worktrees.update(cx, |store, cx| {
|
||||
store.restrict(
|
||||
worktree_store.downgrade(),
|
||||
HashSet::from_iter([PathTrust::Worktree(worktree_id)]),
|
||||
cx,
|
||||
);
|
||||
});
|
||||
let can_trust = trusted_worktrees.update(cx, |store, cx| {
|
||||
store.can_trust(&worktree_store, worktree_id, cx)
|
||||
});
|
||||
assert!(!can_trust, "should be restricted after restrict()");
|
||||
assert_eq!(events.borrow().len(), 1);
|
||||
assert!(matches!(
|
||||
&events.borrow()[0],
|
||||
TrustedWorktreesEvent::Restricted(..)
|
||||
));
|
||||
|
||||
let has_restricted = trusted_worktrees.read_with(cx, |store, cx| {
|
||||
store.has_restricted_worktrees(&worktree_store, cx)
|
||||
});
|
||||
assert!(has_restricted);
|
||||
events.borrow_mut().clear();
|
||||
|
||||
trusted_worktrees.update(cx, |store, cx| {
|
||||
store.trust(
|
||||
&worktree_store,
|
||||
HashSet::from_iter([PathTrust::Worktree(worktree_id)]),
|
||||
cx,
|
||||
);
|
||||
});
|
||||
let can_trust = trusted_worktrees.update(cx, |store, cx| {
|
||||
store.can_trust(&worktree_store, worktree_id, cx)
|
||||
});
|
||||
assert!(can_trust, "should be trusted again after second trust()");
|
||||
assert_eq!(events.borrow().len(), 1);
|
||||
assert!(matches!(
|
||||
&events.borrow()[0],
|
||||
TrustedWorktreesEvent::Trusted(..)
|
||||
));
|
||||
|
||||
let has_restricted = trusted_worktrees.read_with(cx, |store, cx| {
|
||||
store.has_restricted_worktrees(&worktree_store, cx)
|
||||
});
|
||||
assert!(!has_restricted);
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_multi_host_trust_isolation(cx: &mut TestAppContext) {
|
||||
init_test(cx);
|
||||
|
||||
let fs = FakeFs::new(cx.executor());
|
||||
fs.insert_tree(
|
||||
path!("/"),
|
||||
json!({
|
||||
"local_project": { "main.rs": "fn main() {}" },
|
||||
"remote_project": { "lib.rs": "pub fn lib() {}" }
|
||||
}),
|
||||
)
|
||||
.await;
|
||||
|
||||
let project = Project::test(
|
||||
fs,
|
||||
[
|
||||
path!("/local_project").as_ref(),
|
||||
path!("/remote_project").as_ref(),
|
||||
],
|
||||
cx,
|
||||
)
|
||||
.await;
|
||||
let worktree_store = project.read_with(cx, |project, _| project.worktree_store());
|
||||
let worktree_ids: Vec<_> = worktree_store.read_with(cx, |store, cx| {
|
||||
store
|
||||
.worktrees()
|
||||
.map(|worktree| worktree.read(cx).id())
|
||||
.collect()
|
||||
});
|
||||
assert_eq!(worktree_ids.len(), 2);
|
||||
let local_worktree = worktree_ids[0];
|
||||
let _remote_worktree = worktree_ids[1];
|
||||
|
||||
let trusted_worktrees = init_trust_global(worktree_store.clone(), cx);
|
||||
|
||||
let can_trust_local = trusted_worktrees.update(cx, |store, cx| {
|
||||
store.can_trust(&worktree_store, local_worktree, cx)
|
||||
});
|
||||
assert!(!can_trust_local, "local worktree restricted on host_a");
|
||||
|
||||
trusted_worktrees.update(cx, |store, cx| {
|
||||
store.trust(
|
||||
&worktree_store,
|
||||
HashSet::from_iter([PathTrust::Worktree(local_worktree)]),
|
||||
cx,
|
||||
);
|
||||
});
|
||||
|
||||
let can_trust_local_after = trusted_worktrees.update(cx, |store, cx| {
|
||||
store.can_trust(&worktree_store, local_worktree, cx)
|
||||
});
|
||||
assert!(
|
||||
can_trust_local_after,
|
||||
"local worktree should be trusted on local host"
|
||||
);
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_invisible_worktree_stores_do_not_affect_trust(cx: &mut TestAppContext) {
|
||||
init_test(cx);
|
||||
|
||||
let fs = FakeFs::new(cx.executor());
|
||||
fs.insert_tree(
|
||||
path!("/"),
|
||||
json!({
|
||||
"visible": { "main.rs": "fn main() {}" },
|
||||
"other": { "a.rs": "fn other() {}" },
|
||||
"invisible": { "b.rs": "fn invisible() {}" }
|
||||
}),
|
||||
)
|
||||
.await;
|
||||
|
||||
let project = Project::test(fs, [path!("/visible").as_ref()], cx).await;
|
||||
let worktree_store = project.read_with(cx, |project, _| project.worktree_store());
|
||||
let visible_worktree_id = worktree_store.read_with(cx, |store, cx| {
|
||||
store
|
||||
.worktrees()
|
||||
.find(|worktree| worktree.read(cx).root_dir().unwrap().ends_with("visible"))
|
||||
.expect("visible worktree")
|
||||
.read(cx)
|
||||
.id()
|
||||
});
|
||||
let trusted_worktrees = init_trust_global(worktree_store.clone(), cx);
|
||||
|
||||
let events: Rc<RefCell<Vec<TrustedWorktreesEvent>>> = Rc::default();
|
||||
cx.update({
|
||||
let events = events.clone();
|
||||
|cx| {
|
||||
cx.subscribe(&trusted_worktrees, move |_, event, _| {
|
||||
events.borrow_mut().push(match event {
|
||||
TrustedWorktreesEvent::Trusted(host, paths) => {
|
||||
TrustedWorktreesEvent::Trusted(host.clone(), paths.clone())
|
||||
}
|
||||
TrustedWorktreesEvent::Restricted(host, paths) => {
|
||||
TrustedWorktreesEvent::Restricted(host.clone(), paths.clone())
|
||||
}
|
||||
});
|
||||
})
|
||||
}
|
||||
})
|
||||
.detach();
|
||||
|
||||
assert!(
|
||||
!trusted_worktrees.update(cx, |store, cx| {
|
||||
store.can_trust(&worktree_store, visible_worktree_id, cx)
|
||||
}),
|
||||
"visible worktree should be restricted initially"
|
||||
);
|
||||
assert_eq!(
|
||||
HashSet::from_iter([(visible_worktree_id)]),
|
||||
trusted_worktrees.read_with(cx, |store, _| {
|
||||
store.restricted_worktrees_for_store(&worktree_store)
|
||||
}),
|
||||
"only visible worktree should be restricted",
|
||||
);
|
||||
|
||||
let (new_visible_worktree, new_invisible_worktree) =
|
||||
worktree_store.update(cx, |worktree_store, cx| {
|
||||
let new_visible_worktree = worktree_store.create_worktree("/other", true, cx);
|
||||
let new_invisible_worktree = worktree_store.create_worktree("/invisible", false, cx);
|
||||
(new_visible_worktree, new_invisible_worktree)
|
||||
});
|
||||
let (new_visible_worktree, new_invisible_worktree) = (
|
||||
new_visible_worktree.await.unwrap(),
|
||||
new_invisible_worktree.await.unwrap(),
|
||||
);
|
||||
|
||||
let new_visible_worktree_id =
|
||||
new_visible_worktree.read_with(cx, |new_visible_worktree, _| new_visible_worktree.id());
|
||||
assert!(
|
||||
!trusted_worktrees.update(cx, |store, cx| {
|
||||
store.can_trust(&worktree_store, new_visible_worktree_id, cx)
|
||||
}),
|
||||
"new visible worktree should be restricted initially",
|
||||
);
|
||||
assert!(
|
||||
trusted_worktrees.update(cx, |store, cx| {
|
||||
store.can_trust(&worktree_store, new_invisible_worktree.read(cx).id(), cx)
|
||||
}),
|
||||
"invisible worktree should be skipped",
|
||||
);
|
||||
assert_eq!(
|
||||
HashSet::from_iter([visible_worktree_id, new_visible_worktree_id]),
|
||||
trusted_worktrees.read_with(cx, |store, _| {
|
||||
store.restricted_worktrees_for_store(&worktree_store)
|
||||
}),
|
||||
"only visible worktrees should be restricted"
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user