Merge pull request #1779 from dotCooCoo/fix/automation-roots-tests-on-windows

MCP: the several-roots tests expect Windows paths without the \\?\ prefix
This commit is contained in:
Brandon Thomas
2026-10-25 12:25:47 -04:00
committed by GitHub
2 changed files with 31 additions and 4 deletions
+27 -2
View File
@@ -347,6 +347,29 @@ fn make_document(path: &std::path::Path) {
assert!(path.exists());
}
/// `path` without the `\\?\` or `\\?\UNC\` prefix that `canonicalize` adds on Windows, as the
/// server shows a root (`file_access::plain`).
fn without_verbatim_prefix(path: std::path::PathBuf) -> std::path::PathBuf {
let Some(text) = path.to_str() else {
return path;
};
if let Some(rest) = text.strip_prefix(r"\\?\UNC\") {
return std::path::PathBuf::from(format!(r"\\{rest}"));
}
match text.strip_prefix(r"\\?\") {
Some(rest) => std::path::PathBuf::from(rest),
None => path,
}
}
#[test]
fn without_verbatim_prefix_keeps_a_share_a_share() {
let p = |t: &str| without_verbatim_prefix(std::path::PathBuf::from(t));
assert_eq!(p(r"\\?\UNC\server\share\dir"), std::path::PathBuf::from(r"\\server\share\dir"));
assert_eq!(p(r"\\?\C:\Users\dir"), std::path::PathBuf::from(r"C:\Users\dir"));
assert_eq!(p("/private/var/dir"), std::path::PathBuf::from("/private/var/dir"));
}
#[test]
fn mcp_automation_roots_confine_the_server() {
let base = std::env::temp_dir().join(format!("dc-cli-roots-{}", std::process::id()));
@@ -411,8 +434,10 @@ fn mcp_automation_roots_take_several_folders() {
for d in ["in", "shared", "out", "scratch", "outside", "in2", "scratch2"] {
std::fs::create_dir_all(base.join(d)).unwrap();
}
// Resolved, as the server shows the roots (`/var` is `/private/var` on macOS).
let base = std::fs::canonicalize(base).unwrap();
// Resolved, as the server shows the roots (`/var` is `/private/var` on macOS), and without the
// `\\?\` prefix that `canonicalize` adds on Windows: the server shows the roots without it, and
// Windows does not resolve `..` in a path that has it.
let base = without_verbatim_prefix(std::fs::canonicalize(base).unwrap());
for f in ["in/a.designcraft", "shared/b.designcraft", "outside/secret.designcraft", "parent.designcraft", "in2/c.designcraft"] {
make_document(&base.join(f));
}
+4 -2
View File
@@ -412,9 +412,11 @@ mod tests {
/// siblings `in2` and `scratch2` whose names start like a root's, and a file in their parent.
/// The folder is returned resolved (`/var` is `/private/var` on macOS), as the roots show it, so
/// a path that passes only because it was resolved is the case being tested and nothing else.
/// It has no `\\?\` prefix on Windows, where the roots are shown without it and `..` is not
/// resolved in a path that has it.
fn several(name: &str) -> (PathBuf, Arc<AutomationRoots>) {
let (base, _) = folders(name);
let base = std::fs::canonicalize(base).unwrap();
let base = plain(std::fs::canonicalize(base).unwrap());
for d in ["shared", "scratch", "scratch2"] {
std::fs::create_dir_all(base.join(d)).unwrap();
}
@@ -492,7 +494,7 @@ mod tests {
assert!(e.starts_with("cannot open the automation write root"), "{e}");
// A folder given twice (here once through `..`) counts once.
let twice = AutomationRoots::new(&[&base.join("in"), &base.join("in/sub/.."), &base.join("in")], &[]).unwrap().unwrap();
let real = std::fs::canonicalize(&base).unwrap();
let real = plain(std::fs::canonicalize(&base).unwrap());
assert_eq!(twice.describe(), format!("read {}; write none", s(real.join("in"))));
let e = twice.check(s(base.join("outside/x")), Access::Read).unwrap_err();
assert!(e.contains("outside the read root ") && !e.contains("roots"), "{e}");