use axum::body::Body;
use axum::http::{Request, StatusCode};
use jamjet_agents::InMemoryAgentRegistry;
use jamjet_api::{routes::build_router_with_opts, state::AppState};
use jamjet_audit::{AuditEnricher, NoopAuditBackend};
use jamjet_ir::WorkflowIr;
use jamjet_state::backend::StateBackend;
use jamjet_state::InMemoryBackend;
use serde_json::{json, Value};
use std::sync::Arc;
use tower::ServiceExt;
#[test]
fn canonical_compiled_ir_deserializes() {
let json = include_str!("fixtures/hello_agent_ir.json");
let value: serde_json::Value = serde_json::from_str(json).expect("fixture is valid JSON");
let ir = serde_json::from_value::<WorkflowIr>(value);
assert!(
ir.is_ok(),
"compiled hello-agent IR must deserialize into WorkflowIr, else create_workflow \
would 400 a valid `jamjet run` workflow: {:?}",
ir.err()
);
}
#[test]
fn structurally_broken_ir_is_rejected() {
let value = serde_json::json!({ "workflow_id": "x", "version": "0.1.0" });
assert!(
serde_json::from_value::<WorkflowIr>(value).is_err(),
"incomplete IR must not deserialize into WorkflowIr"
);
}
#[test]
fn fleet_agent_ir_registers() {
let json = include_str!("fixtures/fleet_researcher_ir.json");
let value: serde_json::Value = serde_json::from_str(json).expect("parse fixture json");
let parsed = serde_json::from_value::<jamjet_ir::WorkflowIr>(value);
assert!(
parsed.is_ok(),
"fleet researcher IR must deserialize into WorkflowIr, got: {:?}",
parsed.err()
);
}
const WF: &str = "adk-wf";
const VERSION: &str = "1.0.0";
fn make_state(backend: Arc<dyn StateBackend>) -> AppState {
let backend_for_fn = backend.clone();
let audit: Arc<dyn jamjet_audit::AuditBackend> = Arc::new(NoopAuditBackend);
let enricher = Arc::new(AuditEnricher::new(Arc::clone(&audit)));
AppState {
backend: backend.clone(),
backend_for_fn: Arc::new(move |_tenant_id: &jamjet_state::TenantId| backend_for_fn.clone()),
agents: Arc::new(InMemoryAgentRegistry::new()),
audit,
enricher,
protocols: jamjet_api::state::default_protocol_registry(),
cron_store: None,
}
}
fn dispatch_ir(marker: Option<bool>) -> Value {
let mut kind = json!({
"type": "python_fn",
"module": "jamjet.agents.tool_runtime",
"function": "dispatch_tool_calls",
"output_schema": ""
});
if let Some(m) = marker {
kind["agent_tool_dispatch"] = json!(m);
}
json!({
"workflow_id": WF,
"version": VERSION,
"state_schema": "{}",
"start_node": "n1",
"nodes": { "n1": { "id": "n1", "kind": kind } },
"edges": [],
"retry_policies": {},
"models": {},
"tools": {},
"mcp_servers": {},
"remote_agents": {}
})
}
async fn post_workflow(state: &AppState, ir: Value) -> StatusCode {
let body = json!({ "ir": ir });
build_router_with_opts(state.clone(), true)
.oneshot(
Request::post("/workflows")
.header("content-type", "application/json")
.body(Body::from(serde_json::to_vec(&body).unwrap()))
.unwrap(),
)
.await
.unwrap()
.status()
}
#[tokio::test]
async fn unmarked_adk_dispatch_ir_is_rejected_by_the_route() {
let backend = Arc::new(InMemoryBackend::new());
let state = make_state(backend.clone());
let status = post_workflow(&state, dispatch_ir(None)).await;
assert_eq!(
status,
StatusCode::BAD_REQUEST,
"an unmarked ADK dispatch node must be refused at registration"
);
assert!(
backend
.get_workflow(WF, VERSION)
.await
.expect("backend lookup must succeed")
.is_none(),
"a refused IR must not be stored — a persisted definition runs its tool \
calls unpoliced on every later execution"
);
}
#[tokio::test]
async fn explicitly_unmarked_adk_dispatch_ir_is_rejected_by_the_route() {
let backend = Arc::new(InMemoryBackend::new());
let state = make_state(backend.clone());
let status = post_workflow(&state, dispatch_ir(Some(false))).await;
assert_eq!(status, StatusCode::BAD_REQUEST);
assert!(backend
.get_workflow(WF, VERSION)
.await
.expect("backend lookup must succeed")
.is_none());
}
#[tokio::test]
async fn marked_adk_dispatch_ir_registers() {
let backend = Arc::new(InMemoryBackend::new());
let state = make_state(backend.clone());
let status = post_workflow(&state, dispatch_ir(Some(true))).await;
assert_eq!(
status,
StatusCode::CREATED,
"a correctly marked ADK dispatch IR must still register"
);
assert!(
backend
.get_workflow(WF, VERSION)
.await
.expect("backend lookup must succeed")
.is_some(),
"the accepted definition must be stored"
);
}