use crate::brain::tools::Tool;
use crate::brain::tools::ToolExecutionContext;
use crate::brain::tools::load_brain_file::*;
use crate::brain::tools::seen_skills;
use std::path::Path;
use uuid::Uuid;
fn ctx() -> ToolExecutionContext {
ToolExecutionContext::new(Uuid::new_v4())
}
fn tool() -> LoadBrainFileTool {
LoadBrainFileTool
}
#[tokio::test]
async fn slug_form_loads_builtin_skill_body() {
let c = ctx();
let result = tool()
.execute(serde_json::json!({"name": "cost-estimate"}), &c)
.await
.unwrap();
assert!(result.success, "slug form of a built-in skill must succeed");
let text = result.output;
assert!(
text.contains("--- skill: cost-estimate ---"),
"body must be framed as a skill, got: {}",
&text[..text.len().min(200)]
);
assert!(!text.is_empty(), "skill body must not be empty");
}
#[tokio::test]
async fn slug_form_with_query_returns_matching_sections() {
let c = ctx();
let result = tool()
.execute(
serde_json::json!({"name": "cost-estimate", "query": "usage"}),
&c,
)
.await
.unwrap();
assert!(result.success, "slug+query form must succeed");
}
#[tokio::test]
async fn path_traversal_still_refused_after_slug_form_added() {
for bad in [
"../../etc/passwd",
"sub/file.md",
"../skills/cost-estimate/SKILL.md",
] {
let result = tool()
.execute(serde_json::json!({"name": bad}), &ctx())
.await
.unwrap();
assert!(!result.success, "traversal input {bad} must fail");
}
}
#[tokio::test]
async fn unknown_slug_falls_through_to_brain_file_handling() {
let result = tool()
.execute(
serde_json::json!({"name": "no-such-skill-or-brain"}),
&ctx(),
)
.await
.unwrap();
let body = result.output;
assert!(
body.contains("not found"),
"unknown slug must fall through to the brain-file miss, got: {body}"
);
assert!(
!body.contains("--- skill:"),
"unknown slug must never be answered with a skill body, got: {body}"
);
}
#[tokio::test]
async fn slug_load_marks_skill_seen_for_session() {
let c = ctx();
let session = c.session_id;
assert!(
!seen_skills::was_seen(session, "cost-estimate"),
"fresh session must not have the skill seen"
);
tool()
.execute(serde_json::json!({"name": "cost-estimate"}), &c)
.await
.unwrap();
assert!(
seen_skills::was_seen(session, "cost-estimate"),
"slug-load must mark the skill seen"
);
assert_eq!(
seen_skills::seen_for_session(session),
vec!["cost-estimate".to_string()]
);
}
#[test]
fn read_file_whole_read_marks_skill_seen_via_hook() {
let session = Uuid::new_v4();
let path = std::path::Path::new("/root/.opencrabs/profiles/ops/skills/grafana/SKILL.md");
let slug = seen_skills::skill_slug_from_path(path).expect("skill path must yield slug");
assert_eq!(slug, "grafana");
seen_skills::mark_seen(session, &slug);
assert!(seen_skills::was_seen(session, "grafana"));
}
#[test]
fn stamp_union_dedupes_active_and_seen() {
let mut active = std::collections::BTreeSet::new();
active.insert("opencrabs-dev".to_string());
let mut seen = std::collections::BTreeSet::new();
seen.insert("opencrabs-dev".to_string());
seen.insert("grafana".to_string());
let union: Vec<String> = active.union(&seen).cloned().collect();
assert_eq!(union.len(), 2, "overlap must dedupe");
assert_eq!(
union,
vec!["grafana".to_string(), "opencrabs-dev".to_string()]
);
}
#[test]
fn slug_extraction_from_skill_paths() {
assert_eq!(
seen_skills::skill_slug_from_path(Path::new(
"/root/.opencrabs/profiles/ops/skills/opencrabs-dev/SKILL.md"
)),
Some("opencrabs-dev".to_string())
);
assert_eq!(
seen_skills::skill_slug_from_path(Path::new("skills/foo/SKILL.md")),
Some("foo".to_string())
);
}
#[test]
fn non_skill_paths_yield_none() {
assert_eq!(
seen_skills::skill_slug_from_path(Path::new("/home/user/MEMORY.md")),
None
);
assert_eq!(
seen_skills::skill_slug_from_path(Path::new("skills/foo/other.md")),
None
);
assert_eq!(
seen_skills::skill_slug_from_path(Path::new("not-skills/foo/SKILL.md")),
None
);
assert_eq!(
seen_skills::skill_slug_from_path(Path::new("skills/foo/")),
None
);
}
#[test]
fn mark_seen_is_idempotent_and_session_scoped() {
let a = Uuid::new_v4();
let b = Uuid::new_v4();
seen_skills::mark_seen(a, "opencrabs-dev");
seen_skills::mark_seen(a, "opencrabs-dev");
assert!(seen_skills::was_seen(a, "opencrabs-dev"));
assert_eq!(
seen_skills::seen_for_session(a),
vec!["opencrabs-dev".to_string()]
);
assert!(!seen_skills::was_seen(b, "opencrabs-dev"));
assert!(seen_skills::seen_for_session(b).is_empty());
}
#[test]
fn seen_list_is_sorted_and_multi() {
let a = Uuid::new_v4();
seen_skills::mark_seen(a, "zeta");
seen_skills::mark_seen(a, "alpha");
assert_eq!(
seen_skills::seen_for_session(a),
vec!["alpha".to_string(), "zeta".to_string()]
);
}