#![allow(clippy::unwrap_used, clippy::expect_used)]
use std::sync::Arc;
use async_trait::async_trait;
use kaish_kernel::tools::{ToolArgs, ToolCtx, ToolSchema};
use kaish_kernel::vfs::{MemoryFs, VfsRouter};
use kaish_kernel::{Kernel, KernelBackend, KernelConfig, LocalBackend, Tool};
use kaish_types::ExecResult;
struct OwnedTool;
#[async_trait]
impl Tool for OwnedTool {
fn name(&self) -> &str {
"owned"
}
fn schema(&self) -> ToolSchema {
ToolSchema::new("owned", "owned-output test tool").with_owned_output()
}
async fn execute(&self, args: ToolArgs, _ctx: &mut dyn ToolCtx) -> ExecResult {
if args.flags.contains("help") || args.flags.contains("h") {
return ExecResult::success("OWNED-HELP-RENDERED");
}
ExecResult::success("OWNED-RAN")
}
}
struct OwnedTree;
#[async_trait]
impl Tool for OwnedTree {
fn name(&self) -> &str {
"ownedtree"
}
fn schema(&self) -> ToolSchema {
ToolSchema::new("ownedtree", "owned-output subcommand tree")
.subcommand(
ToolSchema::new("context", "context commands")
.subcommand(ToolSchema::new("create", "create a context")),
)
.with_owned_output()
}
async fn execute(&self, args: ToolArgs, _ctx: &mut dyn ToolCtx) -> ExecResult {
if args.flags.contains("help") || args.flags.contains("h") {
return ExecResult::success("OWNEDTREE-HELP-RENDERED");
}
ExecResult::success("OWNEDTREE-RAN")
}
}
struct PlainTool;
#[async_trait]
impl Tool for PlainTool {
fn name(&self) -> &str {
"plain"
}
fn schema(&self) -> ToolSchema {
ToolSchema::new("plain", "plain test tool")
}
async fn execute(&self, _args: ToolArgs, _ctx: &mut dyn ToolCtx) -> ExecResult {
ExecResult::success("PLAIN-RAN")
}
}
fn kernel_with_tools() -> Arc<Kernel> {
let mut vfs = VfsRouter::new();
vfs.mount("/", MemoryFs::new());
let backend: Arc<dyn KernelBackend> = Arc::new(LocalBackend::new(Arc::new(vfs)));
Kernel::with_backend(backend, KernelConfig::isolated(), |_| {}, |tools| {
tools.register(OwnedTool);
tools.register(OwnedTree);
tools.register(PlainTool);
})
.expect("with_backend kernel")
.into_arc()
}
#[tokio::test]
async fn owned_output_tool_receives_help_flag() {
let kernel = kernel_with_tools();
let result = kernel.execute("owned sub --help").await.expect("execute");
assert_eq!(
result.text_out().trim(),
"OWNED-HELP-RENDERED",
"owned-output tool didn't receive --help; the kernel intercepted it",
);
}
#[tokio::test]
async fn owned_output_tool_receives_h_flag() {
let kernel = kernel_with_tools();
let result = kernel.execute("owned -h").await.expect("execute");
assert_eq!(result.text_out().trim(), "OWNED-HELP-RENDERED");
}
#[tokio::test]
async fn owned_output_tool_runs_without_help() {
let kernel = kernel_with_tools();
let result = kernel.execute("owned").await.expect("execute");
assert_eq!(result.text_out().trim(), "OWNED-RAN");
}
#[tokio::test]
async fn owned_output_subcommand_tree_receives_leaf_help() {
let kernel = kernel_with_tools();
let result = kernel
.execute("ownedtree context create --help")
.await
.expect("execute");
assert_eq!(
result.text_out().trim(),
"OWNEDTREE-HELP-RENDERED",
"leaf --help didn't reach the owned-output subcommand tree",
);
}
#[tokio::test]
async fn plain_tool_help_is_still_intercepted() {
let kernel = kernel_with_tools();
let result = kernel.execute("plain --help").await.expect("execute");
let text = result.text_out();
assert!(
!text.contains("PLAIN-RAN"),
"plain tool's --help should be intercepted by the kernel, not run execute()",
);
assert!(
text.contains("plain — plain test tool"),
"expected the generic whole-tool help to render; got {text:?}",
);
}
#[tokio::test]
async fn plain_tool_runs_without_help() {
let kernel = kernel_with_tools();
let result = kernel.execute("plain").await.expect("execute");
assert_eq!(result.text_out().trim(), "PLAIN-RAN");
}