#![allow(
clippy::missing_docs_in_private_items,
reason = "test helpers and fixtures do not need doc comments"
)]
use rmcp::handler::server::wrapper::Parameters;
use super::MoadimMcp;
use crate::routes::mcp::mcp_types::{CreateFlagInput, IdInput, ResolveFlagInput};
fn make_handler() -> MoadimMcp {
MoadimMcp::new(
crate::routines::new_store(),
crate::paths::routines_dir(),
0,
std::sync::Arc::new(tokio::sync::Notify::new()),
)
}
fn make_create_routine_req() -> crate::routines::CreateRoutineRequest {
crate::routines::CreateRoutineRequest {
model: None,
schedule: "@daily".into(),
title: "Mcp Routine".into(),
agent: "claude".into(),
prompt: "p".into(),
goal: None,
repositories: vec![],
machines: vec![crate::machine::current_machine()],
enabled: true,
ttl_secs: None,
max_runtime_secs: None,
tags: vec![],
env: std::collections::HashMap::new(),
}
}
fn result_json(result: &rmcp::model::CallToolResult) -> serde_json::Value {
let text = match &result.content[0] {
rmcp::model::ContentBlock::Text(block) => block.text.clone(),
_ => panic!("expected text content"),
};
serde_json::from_str(&text).unwrap()
}
struct TempHome;
impl TempHome {
fn set() -> Self {
let dir = std::env::temp_dir().join(format!("moadim-mcptest-{}", uuid::Uuid::new_v4()));
std::fs::create_dir_all(&dir).expect("create temp home");
unsafe {
std::env::set_var("MOADIM_HOME_OVERRIDE", &dir);
}
Self
}
}
impl Drop for TempHome {
fn drop(&mut self) {
unsafe {
std::env::remove_var("MOADIM_HOME_OVERRIDE");
}
}
}
#[test]
fn resolve_flag_not_found_is_error() {
let handler = make_handler();
let result = handler
.resolve_flag(Parameters(ResolveFlagInput {
id: "no-such".into(),
filename: "bug-1.md".into(),
}))
.unwrap();
assert!(result.is_error.unwrap_or(false));
}
#[test]
fn create_list_resolve_flag_lifecycle() {
let _home = TempHome::set();
let handler = make_handler();
let created = handler
.create_routine(Parameters(make_create_routine_req()))
.unwrap();
let id = result_json(&created)["id"].as_str().unwrap().to_string();
let result = handler
.create_flag(Parameters(CreateFlagInput {
id: id.clone(),
r#type: "bug".into(),
description: "broken thing".into(),
scope: "general".into(),
}))
.unwrap();
assert!(!result.is_error.unwrap_or(false));
let flag = result_json(&result);
assert_eq!(flag["type"], "bug");
let filename = flag["filename"].as_str().unwrap().to_string();
let result = handler
.list_flags(Parameters(IdInput { id: id.clone() }))
.unwrap();
assert!(!result.is_error.unwrap_or(false));
let flags = result_json(&result);
assert_eq!(flags.as_array().unwrap().len(), 1);
let result = handler
.resolve_flag(Parameters(ResolveFlagInput {
id: id.clone(),
filename,
}))
.unwrap();
assert!(!result.is_error.unwrap_or(false));
let result = handler.list_flags(Parameters(IdInput { id })).unwrap();
assert_eq!(result_json(&result).as_array().unwrap().len(), 0);
}