use crate::brain::commands::UserCommand;
use crate::brain::skills::{Skill, SkillSource};
use crate::channels::commands::{ChannelCommand, match_user_command_inner};
fn skill(name: &str, body: &str) -> Skill {
Skill {
name: name.to_string(),
slash_name: format!("/{name}"),
description: format!("test skill {name}"),
body: body.to_string(),
source: SkillSource::Builtin,
}
}
fn user_cmd(name: &str, prompt: &str) -> UserCommand {
UserCommand {
name: name.to_string(),
description: String::new(),
action: "prompt".to_string(),
prompt: prompt.to_string(),
}
}
#[test]
fn skill_slash_dispatches_skill_body_as_prompt() {
let skills = vec![skill("security-audit", "Run a comprehensive audit.")];
match match_user_command_inner("/security-audit", &[], &skills) {
ChannelCommand::UserPrompt(p) => assert_eq!(p, "Run a comprehensive audit."),
_ => panic!("expected UserPrompt for skill dispatch"),
}
}
#[test]
fn skill_args_append_after_blank_line() {
let skills = vec![skill("audit", "Body of the audit skill.")];
match match_user_command_inner("/audit focus on auth code", &[], &skills) {
ChannelCommand::UserPrompt(p) => {
assert_eq!(p, "Body of the audit skill.\n\nfocus on auth code");
}
_ => panic!("expected UserPrompt with appended args"),
}
}
#[test]
fn user_command_wins_over_same_named_skill() {
let cmds = vec![user_cmd("/audit", "User's own audit prompt.")];
let skills = vec![skill("audit", "Built-in skill body.")];
match match_user_command_inner("/audit", &cmds, &skills) {
ChannelCommand::UserPrompt(p) => assert_eq!(p, "User's own audit prompt."),
_ => panic!("expected user command to win"),
}
}
#[test]
fn skill_only_dispatches_when_no_user_command_matches() {
let cmds = vec![user_cmd("/other", "irrelevant")];
let skills = vec![skill("audit", "Skill body.")];
match match_user_command_inner("/audit", &cmds, &skills) {
ChannelCommand::UserPrompt(p) => assert_eq!(p, "Skill body."),
_ => panic!("expected skill fallback to fire"),
}
}
#[test]
fn unknown_slash_returns_not_a_command_even_with_skills_loaded() {
let skills = vec![skill("audit", "Skill body.")];
assert!(matches!(
match_user_command_inner("/does-not-exist", &[], &skills),
ChannelCommand::NotACommand
));
}
#[test]
fn skill_with_no_args_returns_body_unchanged() {
let skills = vec![skill("estimate", "Estimate the cost.")];
match match_user_command_inner("/estimate", &[], &skills) {
ChannelCommand::UserPrompt(p) => assert_eq!(p, "Estimate the cost."),
_ => panic!("expected UserPrompt with body verbatim"),
}
}
#[test]
fn skill_dispatch_ignores_leading_trailing_arg_whitespace() {
let skills = vec![skill("audit", "Body.")];
match match_user_command_inner("/audit extra args ", &[], &skills) {
ChannelCommand::UserPrompt(p) => {
assert_eq!(p, "Body.\n\nextra args");
}
_ => panic!("expected UserPrompt"),
}
}