- Add open-skills auto-clone/pull/sync support in skills loader - Clone https://github.com/besoeasy/open-skills to ~/open-skills - Weekly sync via .zeroclaw-open-skills-sync marker - Env controls: ZEROCLAW_OPEN_SKILLS_ENABLED, ZEROCLAW_OPEN_SKILLS_DIR - Load open-skills markdown files before workspace skills - Track Skill.location for accurate prompt rendering - Update system prompt to render skill.location with fallback - Use actual file path when available - Maintain backward compatibility with workspace SKILL.md path - Fix clippy warnings across tests and supporting files - Readable timestamp literals - Remove underscore bindings in tests - Use struct update syntax for Config::default() patterns - Fix module inception, duplicate attributes, manual strip - Clean raw string hashes and empty string construction Resolves: #77
109 lines
4.2 KiB
Rust
109 lines
4.2 KiB
Rust
#[cfg(test)]
|
|
mod tests {
|
|
use crate::skills::skills_dir;
|
|
use std::path::Path;
|
|
use tempfile::TempDir;
|
|
|
|
#[test]
|
|
fn test_skills_symlink_unix_edge_cases() {
|
|
let tmp = TempDir::new().unwrap();
|
|
let workspace_dir = tmp.path().join("workspace");
|
|
std::fs::create_dir_all(&workspace_dir).unwrap();
|
|
|
|
let skills_path = skills_dir(&workspace_dir);
|
|
std::fs::create_dir_all(&skills_path).unwrap();
|
|
|
|
// Test case 1: Valid symlink creation on Unix
|
|
#[cfg(unix)]
|
|
{
|
|
let source_dir = tmp.path().join("source_skill");
|
|
std::fs::create_dir_all(&source_dir).unwrap();
|
|
std::fs::write(source_dir.join("SKILL.md"), "# Test Skill\nContent").unwrap();
|
|
|
|
let dest_link = skills_path.join("linked_skill");
|
|
|
|
// Create symlink
|
|
let result = std::os::unix::fs::symlink(&source_dir, &dest_link);
|
|
assert!(result.is_ok(), "Symlink creation should succeed");
|
|
|
|
// Verify symlink works
|
|
assert!(dest_link.exists());
|
|
assert!(dest_link.is_symlink());
|
|
|
|
// Verify we can read through symlink
|
|
let content = std::fs::read_to_string(dest_link.join("SKILL.md"));
|
|
assert!(content.is_ok());
|
|
assert!(content.unwrap().contains("Test Skill"));
|
|
|
|
// Test case 2: Symlink to non-existent target should fail gracefully
|
|
let broken_link = skills_path.join("broken_skill");
|
|
let non_existent = tmp.path().join("non_existent");
|
|
let result = std::os::unix::fs::symlink(&non_existent, &broken_link);
|
|
assert!(
|
|
result.is_ok(),
|
|
"Symlink creation should succeed even if target doesn't exist"
|
|
);
|
|
|
|
// But reading through it should fail
|
|
let content = std::fs::read_to_string(broken_link.join("SKILL.md"));
|
|
assert!(content.is_err());
|
|
}
|
|
|
|
// Test case 3: Non-Unix platforms should handle symlink errors gracefully
|
|
#[cfg(not(unix))]
|
|
{
|
|
let source_dir = tmp.path().join("source_skill");
|
|
std::fs::create_dir_all(&source_dir).unwrap();
|
|
|
|
let dest_link = skills_path.join("linked_skill");
|
|
|
|
// Symlink should fail on non-Unix
|
|
let result = std::os::unix::fs::symlink(&source_dir, &dest_link);
|
|
assert!(result.is_err());
|
|
|
|
// Directory should not exist
|
|
assert!(!dest_link.exists());
|
|
}
|
|
|
|
// Test case 4: skills_dir function edge cases
|
|
let workspace_with_trailing_slash = format!("{}/", workspace_dir.display());
|
|
let path_from_str = skills_dir(Path::new(&workspace_with_trailing_slash));
|
|
assert_eq!(path_from_str, skills_path);
|
|
|
|
// Test case 5: Empty workspace directory
|
|
let empty_workspace = tmp.path().join("empty");
|
|
let empty_skills_path = skills_dir(&empty_workspace);
|
|
assert_eq!(empty_skills_path, empty_workspace.join("skills"));
|
|
assert!(!empty_skills_path.exists());
|
|
}
|
|
|
|
#[test]
|
|
fn test_skills_symlink_permissions_and_safety() {
|
|
let tmp = TempDir::new().unwrap();
|
|
let workspace_dir = tmp.path().join("workspace");
|
|
std::fs::create_dir_all(&workspace_dir).unwrap();
|
|
|
|
let skills_path = skills_dir(&workspace_dir);
|
|
std::fs::create_dir_all(&skills_path).unwrap();
|
|
|
|
#[cfg(unix)]
|
|
{
|
|
// Test case: Symlink outside workspace should be allowed (user responsibility)
|
|
let outside_dir = tmp.path().join("outside_skill");
|
|
std::fs::create_dir_all(&outside_dir).unwrap();
|
|
std::fs::write(outside_dir.join("SKILL.md"), "# Outside Skill\nContent").unwrap();
|
|
|
|
let dest_link = skills_path.join("outside_skill");
|
|
let result = std::os::unix::fs::symlink(&outside_dir, &dest_link);
|
|
assert!(
|
|
result.is_ok(),
|
|
"Should allow symlinking to directories outside workspace"
|
|
);
|
|
|
|
// Should still be readable
|
|
let content = std::fs::read_to_string(dest_link.join("SKILL.md"));
|
|
assert!(content.is_ok());
|
|
assert!(content.unwrap().contains("Outside Skill"));
|
|
}
|
|
}
|
|
}
|