first pass of merging in warp (doesn't build)
This commit is contained in:
@@ -0,0 +1,225 @@
|
||||
use repo_metadata::repositories::DetectedRepositories;
|
||||
use repo_metadata::{DirectoryWatcher, RepoMetadataModel};
|
||||
use warpui::App;
|
||||
use watcher::HomeDirectoryWatcher;
|
||||
|
||||
use super::*;
|
||||
use crate::settings::AISettings;
|
||||
use crate::warp_managed_paths_watcher::WarpManagedPathsWatcher;
|
||||
|
||||
fn bundled_skill(
|
||||
id: &str,
|
||||
path: &str,
|
||||
content: &str,
|
||||
requires_mcp: Option<&str>,
|
||||
) -> RemoteSkillProto {
|
||||
RemoteSkillProto {
|
||||
path: path.to_string(),
|
||||
content: content.to_string(),
|
||||
source: Some(remote_skill_proto::Source::Bundled(
|
||||
remote_server::proto::BundledSkillMetadata {
|
||||
id: id.to_string(),
|
||||
requires_mcp: requires_mcp.map(str::to_string),
|
||||
},
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
fn home_skill(path: &str, content: &str) -> RemoteSkillProto {
|
||||
RemoteSkillProto {
|
||||
path: path.to_string(),
|
||||
content: content.to_string(),
|
||||
source: Some(remote_skill_proto::Source::Home(
|
||||
remote_server::proto::HomeSkillMetadata {},
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
fn rule(path: &str, content: &str) -> RemoteContextFileProto {
|
||||
RemoteContextFileProto {
|
||||
path: path.to_string(),
|
||||
content: content.to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
fn snapshot() -> RemoteAgentContextSnapshot {
|
||||
RemoteAgentContextSnapshot {
|
||||
revision: 7,
|
||||
home_dir: "/home/user".to_string(),
|
||||
skills: vec![
|
||||
bundled_skill(
|
||||
"bundled",
|
||||
"/bundled/skills/bundled/SKILL.md",
|
||||
"---\nname: bundled\ndescription: Bundled skill\n---\nbody",
|
||||
None,
|
||||
),
|
||||
bundled_skill(
|
||||
"unknown-mcp",
|
||||
"/bundled/skills/unknown/SKILL.md",
|
||||
"# unknown",
|
||||
Some("unknown"),
|
||||
),
|
||||
home_skill(
|
||||
"/home/user/.agents/skills/deploy/SKILL.md",
|
||||
"---\nname: deploy\ndescription: Deploy things\n---\nbody",
|
||||
),
|
||||
home_skill("/repo/.agents/skills/project/SKILL.md", "outside home"),
|
||||
home_skill("/home/user/not-a-skill.md", "unknown provider"),
|
||||
],
|
||||
global_rules: vec![
|
||||
rule("/home/user/.agents/AGENTS.md", "global rule"),
|
||||
rule("/repo/AGENTS.md", "outside home"),
|
||||
],
|
||||
}
|
||||
}
|
||||
|
||||
fn setup_context_models(app: &mut App) {
|
||||
app.add_singleton_model(DirectoryWatcher::new);
|
||||
app.add_singleton_model(AISettings::new_with_defaults);
|
||||
app.add_singleton_model(|_| DetectedRepositories::default());
|
||||
app.add_singleton_model(RepoMetadataModel::new);
|
||||
app.add_singleton_model(HomeDirectoryWatcher::new_for_test);
|
||||
app.add_singleton_model(WarpManagedPathsWatcher::new_for_testing);
|
||||
app.add_singleton_model(SkillManager::new);
|
||||
app.add_singleton_model(|_| ProjectContextModel::default());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn snapshot_decoding_keeps_valid_context_from_each_source() {
|
||||
let _bundled_skills = FeatureFlag::BundledSkills.override_enabled(true);
|
||||
let host_id = HostId::new("remote-host".to_string());
|
||||
let state = parse_snapshot(&host_id, snapshot());
|
||||
let bundled = state.bundled_skills.unwrap();
|
||||
let home = state.home_skills.unwrap();
|
||||
|
||||
assert_eq!(bundled.skill("bundled").unwrap().name, "bundled");
|
||||
assert!(bundled.skill("unknown-mcp").is_none());
|
||||
assert_eq!(home.home_dir, remote_path(&host_id, "/home/user").unwrap());
|
||||
assert_eq!(home.skills.len(), 1);
|
||||
assert_eq!(home.skills[0].name, "deploy");
|
||||
assert_eq!(home.skills[0].provider, SkillProvider::Agents);
|
||||
assert_eq!(home.skills[0].scope, SkillScope::Home);
|
||||
assert_eq!(state.global_rules.len(), 1);
|
||||
assert_eq!(state.global_rules[0].content, "global rule");
|
||||
for path in home
|
||||
.skills
|
||||
.iter()
|
||||
.map(|skill| &skill.path)
|
||||
.chain(state.global_rules.iter().map(|rule| &rule.path))
|
||||
{
|
||||
assert_eq!(path.as_remote().unwrap().host_id, host_id);
|
||||
assert!(path.starts_with(&home.home_dir));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn invalid_home_directory_drops_only_home_context() {
|
||||
let _bundled_skills = FeatureFlag::BundledSkills.override_enabled(true);
|
||||
let host_id = HostId::new("remote-host".to_string());
|
||||
let mut snapshot = snapshot();
|
||||
snapshot.home_dir = "relative/home".to_string();
|
||||
|
||||
let state = parse_snapshot(&host_id, snapshot);
|
||||
assert!(state.bundled_skills.unwrap().skill("bundled").is_some());
|
||||
assert!(state.home_skills.is_none());
|
||||
assert!(state.global_rules.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn reconcile_snapshot_fully_replaces_all_host_context() {
|
||||
let _bundled_skills = FeatureFlag::BundledSkills.override_enabled(true);
|
||||
let host_id = HostId::new("remote-host".to_string());
|
||||
let bundled_path = remote_path(&host_id, "/bundled/skills/bundled/SKILL.md").unwrap();
|
||||
let home_path = remote_path(&host_id, "/home/user/.agents/skills/deploy/SKILL.md").unwrap();
|
||||
|
||||
App::test((), |mut app| async move {
|
||||
setup_context_models(&mut app);
|
||||
let skills = SkillManager::handle(&app);
|
||||
let rules = ProjectContextModel::handle(&app);
|
||||
let context = app.add_singleton_model(|_| RemoteAgentContext);
|
||||
|
||||
context.update(&mut app, |context, ctx| {
|
||||
context.reconcile_snapshot(host_id.clone(), snapshot(), ctx);
|
||||
});
|
||||
skills.read(&app, |manager, _| {
|
||||
assert!(manager.skill_by_path(&bundled_path).is_some());
|
||||
assert!(manager.skill_by_path(&home_path).is_some());
|
||||
});
|
||||
rules.read(&app, |model, _| {
|
||||
assert_eq!(
|
||||
model
|
||||
.find_applicable_rules(&remote_path(&host_id, "/repo").unwrap())
|
||||
.unwrap()
|
||||
.active_rules[0]
|
||||
.content,
|
||||
"global rule"
|
||||
);
|
||||
});
|
||||
|
||||
context.update(&mut app, |context, ctx| {
|
||||
context.reconcile_snapshot(
|
||||
host_id.clone(),
|
||||
RemoteAgentContextSnapshot {
|
||||
revision: 8,
|
||||
home_dir: "/home/user".to_string(),
|
||||
skills: Vec::new(),
|
||||
global_rules: Vec::new(),
|
||||
},
|
||||
ctx,
|
||||
);
|
||||
});
|
||||
skills.read(&app, |manager, _| {
|
||||
assert!(manager.skill_by_path(&bundled_path).is_none());
|
||||
assert!(manager.skill_by_path(&home_path).is_none());
|
||||
});
|
||||
rules.read(&app, |model, _| {
|
||||
assert!(model
|
||||
.find_applicable_rules(&remote_path(&host_id, "/repo").unwrap())
|
||||
.is_none());
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn remove_host_context_clears_only_the_matching_host() {
|
||||
let _bundled_skills = FeatureFlag::BundledSkills.override_enabled(true);
|
||||
let first_host = HostId::new("first-host".to_string());
|
||||
let second_host = HostId::new("second-host".to_string());
|
||||
let first_bundled_path = remote_path(&first_host, "/bundled/skills/bundled/SKILL.md").unwrap();
|
||||
let second_bundled_path =
|
||||
remote_path(&second_host, "/bundled/skills/bundled/SKILL.md").unwrap();
|
||||
|
||||
App::test((), |mut app| async move {
|
||||
setup_context_models(&mut app);
|
||||
let skills = SkillManager::handle(&app);
|
||||
let rules = ProjectContextModel::handle(&app);
|
||||
let context = app.add_singleton_model(|_| RemoteAgentContext);
|
||||
|
||||
for host_id in [&first_host, &second_host] {
|
||||
context.update(&mut app, |context, ctx| {
|
||||
context.reconcile_snapshot(host_id.clone(), snapshot(), ctx);
|
||||
});
|
||||
}
|
||||
context.update(&mut app, |context, ctx| {
|
||||
context.remove_host_context(&first_host, ctx);
|
||||
});
|
||||
|
||||
skills.read(&app, |manager, _| {
|
||||
assert!(manager.skill_by_path(&first_bundled_path).is_none());
|
||||
assert!(manager.skill_by_path(&second_bundled_path).is_some());
|
||||
});
|
||||
rules.read(&app, |model, _| {
|
||||
assert!(model
|
||||
.find_applicable_rules(&remote_path(&first_host, "/repo").unwrap())
|
||||
.is_none());
|
||||
assert_eq!(
|
||||
model
|
||||
.find_applicable_rules(&remote_path(&second_host, "/repo").unwrap())
|
||||
.unwrap()
|
||||
.active_rules[0]
|
||||
.content,
|
||||
"global rule"
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user