feat: devtools + tool dispatch tests, search_code tool definition fix

Devtools integration tests (6 new, all via live Gitea):
- gitea_list_repos, get_repo, get_file, list_branches, list_issues, list_orgs
- Tests exercise the full Gitea SDK → tool handler → JSON response path

Tool dispatch tests (8 new unit tests):
- tool_definitions: base, gitea, kratos, all-enabled variants
- agent_tool_definitions conversion
- minimal registry creation
- unknown tool error handling
- search_code without OpenSearch error

search_code: added to tool_definitions() (was only in execute dispatch)
This commit is contained in:
2026-03-24 12:06:39 +00:00
parent 42f6b38f12
commit 0efd3e32c3

View File

@@ -1284,7 +1284,7 @@ mod gitea_tests {
use super::*;
use std::sync::Arc;
fn load_env() {
pub(super) fn load_env() {
let env_path = std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join(".env");
if let Ok(contents) = std::fs::read_to_string(&env_path) {
for line in contents.lines() {
@@ -1297,7 +1297,7 @@ mod gitea_tests {
}
}
fn gitea_available() -> bool {
pub(super) fn gitea_available() -> bool {
load_env();
let url = std::env::var("GITEA_URL").unwrap_or_default();
if url.is_empty() { return false; }
@@ -1308,7 +1308,7 @@ mod gitea_tests {
.unwrap_or(false)
}
fn gitea_client() -> Option<Arc<crate::sdk::gitea::GiteaClient>> {
pub(super) fn gitea_client() -> Option<Arc<crate::sdk::gitea::GiteaClient>> {
if !gitea_available() { return None; }
let url = std::env::var("GITEA_URL").ok()?;
let user = std::env::var("GITEA_ADMIN_USERNAME").ok()?;
@@ -1422,6 +1422,160 @@ mod gitea_tests {
}
}
// ══════════════════════════════════════════════════════════════════════════
// Devtools (Gitea tool) integration tests
// ══════════════════════════════════════════════════════════════════════════
mod devtools_tests {
use super::*;
#[tokio::test]
async fn test_gitea_list_repos_tool() {
gitea_tests::load_env();
let Some(gitea) = gitea_tests::gitea_client() else {
eprintln!("Skipping: Gitea not available");
return;
};
let ctx = crate::context::ResponseContext {
matrix_user_id: "@sol:sunbeam.local".into(),
user_id: "sol".into(),
display_name: None,
is_dm: true,
is_reply: false,
room_id: "test".into(),
};
let result = crate::tools::devtools::execute(
&gitea, "gitea_list_repos", r#"{"org": "studio"}"#, &ctx,
).await;
assert!(result.is_ok(), "list_repos tool should succeed: {:?}", result.err());
let text = result.unwrap();
assert!(text.contains("sol"), "Should find sol repo in results: {text}");
}
#[tokio::test]
async fn test_gitea_get_repo_tool() {
gitea_tests::load_env();
let Some(gitea) = gitea_tests::gitea_client() else {
eprintln!("Skipping: Gitea not available");
return;
};
let ctx = crate::context::ResponseContext {
matrix_user_id: "@sol:sunbeam.local".into(),
user_id: "sol".into(),
display_name: None,
is_dm: true,
is_reply: false,
room_id: "test".into(),
};
let result = crate::tools::devtools::execute(
&gitea, "gitea_get_repo", r#"{"owner": "studio", "repo": "sol"}"#, &ctx,
).await;
assert!(result.is_ok(), "get_repo tool should succeed: {:?}", result.err());
let text = result.unwrap();
assert!(text.contains("sol"), "Should contain repo name: {text}");
}
#[tokio::test]
async fn test_gitea_get_file_tool() {
gitea_tests::load_env();
let Some(gitea) = gitea_tests::gitea_client() else {
eprintln!("Skipping: Gitea not available");
return;
};
let ctx = crate::context::ResponseContext {
matrix_user_id: "@sol:sunbeam.local".into(),
user_id: "sol".into(),
display_name: None,
is_dm: true,
is_reply: false,
room_id: "test".into(),
};
let result = crate::tools::devtools::execute(
&gitea, "gitea_get_file",
r#"{"owner": "studio", "repo": "sol", "path": "Cargo.toml"}"#,
&ctx,
).await;
assert!(result.is_ok(), "get_file tool should succeed: {:?}", result.err());
}
#[tokio::test]
async fn test_gitea_list_branches_tool() {
gitea_tests::load_env();
let Some(gitea) = gitea_tests::gitea_client() else {
eprintln!("Skipping: Gitea not available");
return;
};
let ctx = crate::context::ResponseContext {
matrix_user_id: "@sol:sunbeam.local".into(),
user_id: "sol".into(),
display_name: None,
is_dm: true,
is_reply: false,
room_id: "test".into(),
};
let result = crate::tools::devtools::execute(
&gitea, "gitea_list_branches",
r#"{"owner": "studio", "repo": "sol"}"#,
&ctx,
).await;
assert!(result.is_ok(), "list_branches tool should succeed: {:?}", result.err());
let text = result.unwrap();
assert!(text.contains("mainline") || text.contains("main"), "Should find default branch: {text}");
}
#[tokio::test]
async fn test_gitea_list_issues_tool() {
gitea_tests::load_env();
let Some(gitea) = gitea_tests::gitea_client() else {
eprintln!("Skipping: Gitea not available");
return;
};
let ctx = crate::context::ResponseContext {
matrix_user_id: "@sol:sunbeam.local".into(),
user_id: "sol".into(),
display_name: None,
is_dm: true,
is_reply: false,
room_id: "test".into(),
};
let result = crate::tools::devtools::execute(
&gitea, "gitea_list_issues",
r#"{"owner": "studio", "repo": "sol"}"#,
&ctx,
).await;
assert!(result.is_ok(), "list_issues should succeed: {:?}", result.err());
}
#[tokio::test]
async fn test_gitea_list_orgs_tool() {
gitea_tests::load_env();
let Some(gitea) = gitea_tests::gitea_client() else {
eprintln!("Skipping: Gitea not available");
return;
};
let ctx = crate::context::ResponseContext {
matrix_user_id: "@sol:sunbeam.local".into(),
user_id: "sol".into(),
display_name: None,
is_dm: true,
is_reply: false,
room_id: "test".into(),
};
let result = crate::tools::devtools::execute(
&gitea, "gitea_list_orgs", r#"{"username": "sol"}"#, &ctx,
).await;
assert!(result.is_ok(), "list_orgs should succeed: {:?}", result.err());
let text = result.unwrap();
assert!(text.contains("studio"), "Should find studio org: {text}");
}
}
// ══════════════════════════════════════════════════════════════════════════
// Web search + conversation registry tests
// ══════════════════════════════════════════════════════════════════════════