mod common;
use common::*;
use serde_json::json;
const NS: &str = "e2e-full";
fn ns_h() -> Vec<(&'static str, &'static str)> {
vec![("X-Namespace-ID", NS)]
}
#[test]
fn manual_creation() {
let server = TestServer::spawn();
let c = server.client();
let b = format!("{}/api", server.url);
let (s, body) = post_json(
&c,
&format!("{}/namespaces", b),
&[],
&json!({"namespace_id": NS, "description": "e2e test"}),
);
assert!(body.contains("created"), "{}", body);
assert_eq!(s, 200);
let s = post_json(&c, &format!("{}/intents", b), &ns_h(),
&json!({
"id": "billing:cancel_subscription",
"phrases_by_lang": {
"en": ["cancel my subscription", "stop my subscription", "end my plan", "I want to cancel"],
"fr": ["annuler mon abonnement", "arrêter mon abonnement"]
},
"intent_type": "action",
"description": "Cancel a recurring subscription"
})).0;
assert_eq!(s, 201);
let s = patch_json(
&c,
&format!("{}/intents/billing:cancel_subscription", b),
&ns_h(),
&json!({
"persona": "warm and apologetic",
"guardrails": ["confirm with user before canceling", "offer pause as alternative"]
}),
);
assert_eq!(s, 204);
let (_, body) = get(&c, &format!("{}/intents", b), &ns_h());
assert!(body.contains("warm and apologetic"), "persona persisted");
assert!(
body.contains("confirm with user before canceling"),
"guardrail persisted"
);
assert!(
body.contains("annuler mon abonnement"),
"French phrase persisted"
);
post_json(
&c,
&format!("{}/intents", b),
&ns_h(),
&json!({"id":"weather:check","phrases":["what is the weather","is it raining"]}),
);
let (_, body) = post_json(
&c,
&format!("{}/route_multi", b),
&ns_h(),
&json!({"query":"please cancel my plan"}),
);
assert!(
body.contains("billing:cancel_subscription"),
"EN routes correctly: {}",
body
);
let (_, body) = post_json(
&c,
&format!("{}/route_multi", b),
&ns_h(),
&json!({"query":"je veux annuler"}),
);
assert!(
body.contains("billing:cancel_subscription"),
"FR routes correctly: {}",
body
);
delete_json(
&c,
&format!("{}/namespaces", b),
&[],
&json!({"namespace_id": NS}),
);
}
#[test]
fn mcp_import_three_tools() {
let server = TestServer::spawn();
let c = server.client();
let b = format!("{}/api", server.url);
const NS_MCP: &str = "e2e-mcp";
post_json(
&c,
&format!("{}/namespaces", b),
&[],
&json!({"namespace_id": NS_MCP}),
);
let h: Vec<(&str, &str)> = vec![("X-Namespace-ID", NS_MCP)];
let tools_json = serde_json::to_string(&json!({
"tools": [
{
"name": "search_orders",
"description": "Search through customer orders by date range, status, or customer ID",
"inputSchema": {"type":"object","properties":{"customer_id":{"type":"string"}},"required":["customer_id"]},
"annotations": {"readOnlyHint": true}
},
{
"name": "create_refund",
"description": "Issue a refund for a completed order. Requires approval for amounts over $100.",
"inputSchema": {"type":"object","properties":{"order_id":{"type":"string"}},"required":["order_id"]}
},
{
"name": "send_notification",
"description": "Send an email or SMS notification to a customer about their order status",
"inputSchema": {"type":"object","properties":{"customer_id":{"type":"string"}},"required":["customer_id"]}
}
]
})).unwrap();
let (_, body) = post_json(
&c,
&format!("{}/import/mcp/parse", b),
&h,
&json!({"tools_json": tools_json}),
);
assert!(
body.contains("\"total_tools\":3"),
"parse 3 tools: {}",
body
);
assert!(body.contains("search_orders"));
assert!(body.contains("create_refund"));
assert!(body.contains("send_notification"));
let (s, _) = post_json(
&c,
&format!("{}/import/mcp/apply", b),
&h,
&json!({
"tools_json": tools_json,
"selected": ["search_orders","create_refund","send_notification"],
"domain": "shop"
}),
);
assert!((200..300).contains(&s), "MCP apply 2xx: {}", s);
let (_, body) = get(&c, &format!("{}/intents", b), &h);
assert!(body.contains("shop:search_orders"));
assert!(body.contains("shop:create_refund"));
assert!(body.contains("shop:send_notification"));
assert!(
body.contains("\"intent_type\":\"context\""),
"readOnly → Context"
);
assert!(
body.contains("\"intent_type\":\"action\""),
"non-readOnly → Action"
);
assert!(body.contains("customer_id"));
assert!(body.contains("mcp_server"));
let (_, body) = post_json(
&c,
&format!("{}/route_multi", b),
&h,
&json!({"query":"refund this order"}),
);
assert!(
body.contains("shop:create_refund"),
"refund routes: {}",
body
);
let (_, body) = post_json(
&c,
&format!("{}/route_multi", b),
&h,
&json!({"query":"send email to customer"}),
);
assert!(
body.contains("shop:send_notification"),
"notification routes: {}",
body
);
delete_json(
&c,
&format!("{}/namespaces", b),
&[],
&json!({"namespace_id": NS_MCP}),
);
}
#[test]
fn auto_learn_deterministic_path() {
let server = TestServer::spawn();
let c = server.client();
let b = format!("{}/api", server.url);
const NS_AL: &str = "e2e-autolearn";
post_json(
&c,
&format!("{}/namespaces", b),
&[],
&json!({"namespace_id": NS_AL}),
);
let h: Vec<(&str, &str)> = vec![("X-Namespace-ID", NS_AL)];
post_json(
&c,
&format!("{}/intents", b),
&h,
&json!({"id":"refund","phrases":["refund please","I want a refund"]}),
);
post_json(
&c,
&format!("{}/intents", b),
&h,
&json!({"id":"cancel","phrases":["cancel order","stop order"]}),
);
let (s, _) = post_json(
&c,
&format!("{}/namespaces/train_negative", b),
&[],
&json!({"namespace_id": NS_AL, "queries":["weather is nice today"], "alpha": 0.1}),
);
assert_eq!(s, 200);
let (s, _) = post_json(
&c,
&format!("{}/namespaces/rebuild", b),
&[],
&json!({"namespace_id": NS_AL}),
);
assert_eq!(s, 200);
let (_, body) = post_json(
&c,
&format!("{}/route_multi", b),
&h,
&json!({"query":"refund please"}),
);
assert!(
body.contains("refund"),
"routing works post-rebuild: {}",
body
);
delete_json(
&c,
&format!("{}/namespaces", b),
&[],
&json!({"namespace_id": NS_AL}),
);
}
#[test]
fn auth_keys_endpoint() {
let server = TestServer::spawn();
let c = server.client();
let b = format!("{}/api", server.url);
let (_, body) = get(&c, &format!("{}/auth/keys", b), &[]);
assert!(
body.contains("\"enabled\":false"),
"initially open: {}",
body
);
let (s, body) = post_json(
&c,
&format!("{}/auth/keys", b),
&[],
&json!({"name": "test-key"}),
);
assert_eq!(s, 200);
assert!(body.contains("mr_"), "key has mr_ prefix");
assert!(body.contains("This key is shown once"), "warning included");
let key: serde_json::Value = serde_json::from_str(&body).unwrap();
let full_key = key["key"].as_str().unwrap();
let (s, _) = get(
&c,
&format!("{}/sync?version=0", b),
&[("X-Namespace-ID", "default")],
);
assert_eq!(s, 401, "without key returns 401");
let (s, _) = get(
&c,
&format!("{}/sync?version=0", b),
&[("X-Namespace-ID", "default"), ("X-Api-Key", "wrong")],
);
assert_eq!(s, 401, "wrong key returns 401");
let (s, _) = get(
&c,
&format!("{}/sync?version=0", b),
&[("X-Namespace-ID", "default"), ("X-Api-Key", full_key)],
);
assert_eq!(s, 200, "right key returns 200");
let (_, body) = get(&c, &format!("{}/auth/keys", b), &[]);
assert!(body.contains("test-key"), "key listed");
assert!(!body.contains(full_key), "full key NOT in list (redacted)");
let s = delete_json(&c, &format!("{}/auth/keys/test-key", b), &[], &json!({}));
assert_eq!(s, 204, "revoke");
}