80 lines
2.4 KiB
Rust
80 lines
2.4 KiB
Rust
//! The TUI's structured actions use the shared typed daemon client; assert the
|
|
//! exact route and bare approval decision body at the HTTP boundary.
|
|
|
|
use std::sync::{Arc, Mutex};
|
|
|
|
use axum::extract::{Path, State};
|
|
use axum::routing::post;
|
|
use axum::{Json, Router};
|
|
use maestro_protocol::{DaemonClient, StructuredApprovalDecision, StructuredPromptRequest};
|
|
use uuid::Uuid;
|
|
|
|
#[derive(Debug, Default)]
|
|
struct Seen {
|
|
prompt: Option<String>,
|
|
cancelled: bool,
|
|
approval: Option<(String, StructuredApprovalDecision)>,
|
|
}
|
|
|
|
type Shared = Arc<Mutex<Seen>>;
|
|
|
|
async fn prompt(
|
|
State(seen): State<Shared>,
|
|
Path(_id): Path<Uuid>,
|
|
Json(body): Json<StructuredPromptRequest>,
|
|
) {
|
|
seen.lock().unwrap().prompt = Some(body.prompt);
|
|
}
|
|
|
|
async fn cancel(State(seen): State<Shared>, Path(_id): Path<Uuid>) {
|
|
seen.lock().unwrap().cancelled = true;
|
|
}
|
|
|
|
async fn approval(
|
|
State(seen): State<Shared>,
|
|
Path((_id, nonce)): Path<(Uuid, String)>,
|
|
Json(decision): Json<StructuredApprovalDecision>,
|
|
) {
|
|
seen.lock().unwrap().approval = Some((nonce, decision));
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn prompt_cancel_and_approval_use_structured_api_contract() {
|
|
let seen = Shared::default();
|
|
let app = Router::new()
|
|
.route("/api/sessions/:id/structured/prompt", post(prompt))
|
|
.route("/api/sessions/:id/structured/cancel", post(cancel))
|
|
.route(
|
|
"/api/sessions/:id/structured/approvals/:nonce",
|
|
post(approval),
|
|
)
|
|
.with_state(seen.clone());
|
|
let listener = tokio::net::TcpListener::bind("127.0.0.1:0").await.unwrap();
|
|
let addr = listener.local_addr().unwrap();
|
|
tokio::spawn(async move { axum::serve(listener, app).await.unwrap() });
|
|
|
|
let client = DaemonClient::new(format!("http://{addr}"));
|
|
let id = Uuid::from_u128(141);
|
|
client.send_structured_prompt(id, "ship it").await.unwrap();
|
|
client.cancel_structured_turn(id).await.unwrap();
|
|
client
|
|
.resolve_structured_approval(
|
|
id,
|
|
"one-use-nonce",
|
|
StructuredApprovalDecision::AllowForSession,
|
|
)
|
|
.await
|
|
.unwrap();
|
|
|
|
let seen = seen.lock().unwrap();
|
|
assert_eq!(seen.prompt.as_deref(), Some("ship it"));
|
|
assert!(seen.cancelled);
|
|
assert_eq!(
|
|
seen.approval,
|
|
Some((
|
|
"one-use-nonce".into(),
|
|
StructuredApprovalDecision::AllowForSession
|
|
))
|
|
);
|
|
}
|