feat(maestro): serve POST /api/sessions/:id/activity
This commit is contained in:
@@ -346,6 +346,109 @@ async fn delete_owned_session_kills_it() {
|
||||
assert!(!h.manager.has(session.id).await);
|
||||
}
|
||||
|
||||
/// Spawn a session through the manager and return its id.
|
||||
async fn spawn_via_manager(h: &Harness) -> Uuid {
|
||||
h.manager
|
||||
.create(CreateSessionRequest {
|
||||
cwd: "/tmp".into(),
|
||||
command: "sleep 60".into(),
|
||||
env: HashMap::new(),
|
||||
metadata: HashMap::new(),
|
||||
worktree: None,
|
||||
})
|
||||
.await
|
||||
.unwrap()
|
||||
.id
|
||||
}
|
||||
|
||||
/// The agent activity endpoint on the wire (#105): a report lands in the
|
||||
/// session snapshot with its detail.
|
||||
#[tokio::test]
|
||||
async fn post_activity_reports_error_with_detail() {
|
||||
let h = spawn("naps-laptop").await;
|
||||
let id = spawn_via_manager(&h).await;
|
||||
|
||||
let resp = reqwest::Client::new()
|
||||
.post(url(h.addr, &format!("/api/sessions/{id}/activity")))
|
||||
.json(&serde_json::json!({
|
||||
"state": "Error",
|
||||
"detail": "cargo build failed"
|
||||
}))
|
||||
.send()
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(resp.status(), reqwest::StatusCode::NO_CONTENT);
|
||||
|
||||
let sessions: Vec<Session> = reqwest::get(url(h.addr, "/api/sessions"))
|
||||
.await
|
||||
.unwrap()
|
||||
.json()
|
||||
.await
|
||||
.unwrap();
|
||||
let s = sessions.iter().find(|s| s.id == id).unwrap();
|
||||
assert_eq!(s.activity, maestro_protocol::ActivityState::Error);
|
||||
assert_eq!(s.activity_detail.as_deref(), Some("cargo build failed"));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn post_activity_to_unknown_session_returns_404() {
|
||||
let h = spawn("naps-laptop").await;
|
||||
let resp = reqwest::Client::new()
|
||||
.post(url(
|
||||
h.addr,
|
||||
&format!("/api/sessions/{}/activity", Uuid::new_v4()),
|
||||
))
|
||||
.json(&serde_json::json!({ "state": "Idle" }))
|
||||
.send()
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(resp.status(), reqwest::StatusCode::NOT_FOUND);
|
||||
}
|
||||
|
||||
/// Unknown state values are rejected rather than coerced (#105).
|
||||
#[tokio::test]
|
||||
async fn post_activity_rejects_an_unknown_state_value() {
|
||||
let h = spawn("naps-laptop").await;
|
||||
let id = spawn_via_manager(&h).await;
|
||||
|
||||
let resp = reqwest::Client::new()
|
||||
.post(url(h.addr, &format!("/api/sessions/{id}/activity")))
|
||||
.json(&serde_json::json!({ "state": "Teleported" }))
|
||||
.send()
|
||||
.await
|
||||
.unwrap();
|
||||
assert!(resp.status().is_client_error(), "got {}", resp.status());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn post_activity_caps_an_overlong_detail_server_side() {
|
||||
let h = spawn("naps-laptop").await;
|
||||
let id = spawn_via_manager(&h).await;
|
||||
|
||||
let resp = reqwest::Client::new()
|
||||
.post(url(h.addr, &format!("/api/sessions/{id}/activity")))
|
||||
.json(&serde_json::json!({
|
||||
"state": "Error",
|
||||
"detail": "x".repeat(10_000)
|
||||
}))
|
||||
.send()
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(resp.status(), reqwest::StatusCode::NO_CONTENT);
|
||||
|
||||
let sessions: Vec<Session> = reqwest::get(url(h.addr, "/api/sessions"))
|
||||
.await
|
||||
.unwrap()
|
||||
.json()
|
||||
.await
|
||||
.unwrap();
|
||||
let s = sessions.iter().find(|s| s.id == id).unwrap();
|
||||
assert_eq!(
|
||||
s.activity_detail.as_ref().map(|d| d.chars().count()),
|
||||
Some(maestro_daemon::MAX_REPORT_DETAIL_CHARS)
|
||||
);
|
||||
}
|
||||
|
||||
/// The kill guard on the wire (#79): `DELETE /api/sessions/:id` answers 409
|
||||
/// with the structured breakdown, and the session keeps running.
|
||||
#[tokio::test]
|
||||
|
||||
Reference in New Issue
Block a user