huskies: merge 766
This commit is contained in:
+1
-196
@@ -12,7 +12,7 @@ use std::sync::Arc;
|
||||
|
||||
// Re-export public types that callers reference as `crate::gateway::*`.
|
||||
pub use crate::service::gateway::{
|
||||
GatewayConfig, GatewayState as GatewayStateType, GatewayStatusEvent, JoinedAgent, ProjectEntry,
|
||||
GatewayConfig, GatewayState as GatewayStateType, GatewayStatusEvent, ProjectEntry,
|
||||
broadcast_status_event, fetch_all_project_pipeline_statuses, format_aggregate_status_compact,
|
||||
spawn_gateway_broadcaster_forwarder, spawn_gateway_notification_poller,
|
||||
subscribe_status_events,
|
||||
@@ -54,23 +54,6 @@ pub fn build_gateway_route(state_arc: Arc<GatewayState>) -> impl poem::Endpoint
|
||||
"/gateway/tokens",
|
||||
poem::post(gateway_generate_token_handler),
|
||||
)
|
||||
.at(
|
||||
"/gateway/register",
|
||||
poem::post(gateway_register_agent_handler),
|
||||
)
|
||||
.at("/gateway/agents", poem::get(gateway_list_agents_handler))
|
||||
.at(
|
||||
"/gateway/agents/:id",
|
||||
poem::delete(gateway_remove_agent_handler),
|
||||
)
|
||||
.at(
|
||||
"/gateway/agents/:id/assign",
|
||||
poem::post(gateway_assign_agent_handler),
|
||||
)
|
||||
.at(
|
||||
"/gateway/agents/:id/heartbeat",
|
||||
poem::post(gateway_heartbeat_handler),
|
||||
)
|
||||
.at(
|
||||
"/gateway/events/push",
|
||||
poem::get(gateway_event_push_handler),
|
||||
@@ -197,184 +180,6 @@ mod tests {
|
||||
assert!(tokens.contains_key(token));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn register_agent_consumes_token() {
|
||||
let state = make_test_state();
|
||||
|
||||
let token = "test-token-123".to_string();
|
||||
state.pending_tokens.write().await.insert(
|
||||
token.clone(),
|
||||
gateway::PendingToken {
|
||||
created_at: chrono::Utc::now().timestamp() as f64,
|
||||
},
|
||||
);
|
||||
|
||||
let app = poem::Route::new()
|
||||
.at(
|
||||
"/gateway/register",
|
||||
poem::post(gateway_register_agent_handler),
|
||||
)
|
||||
.data(state.clone());
|
||||
let cli = poem::test::TestClient::new(app);
|
||||
let resp = cli
|
||||
.post("/gateway/register")
|
||||
.header("Content-Type", "application/json")
|
||||
.body(
|
||||
serde_json::json!({
|
||||
"token": token,
|
||||
"label": "test-agent",
|
||||
"address": "ws://localhost:3001/crdt-sync"
|
||||
})
|
||||
.to_string(),
|
||||
)
|
||||
.send()
|
||||
.await;
|
||||
assert_eq!(resp.0.status(), poem::http::StatusCode::OK);
|
||||
assert!(state.pending_tokens.read().await.is_empty());
|
||||
let agents = state.joined_agents.read().await;
|
||||
assert_eq!(agents.len(), 1);
|
||||
assert_eq!(agents[0].label, "test-agent");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn register_agent_rejects_invalid_token() {
|
||||
let state = make_test_state();
|
||||
let app = poem::Route::new()
|
||||
.at(
|
||||
"/gateway/register",
|
||||
poem::post(gateway_register_agent_handler),
|
||||
)
|
||||
.data(state.clone());
|
||||
let cli = poem::test::TestClient::new(app);
|
||||
let resp = cli
|
||||
.post("/gateway/register")
|
||||
.header("Content-Type", "application/json")
|
||||
.body(
|
||||
serde_json::json!({
|
||||
"token": "bad-token",
|
||||
"label": "agent",
|
||||
"address": "ws://localhost:3001/crdt-sync"
|
||||
})
|
||||
.to_string(),
|
||||
)
|
||||
.send()
|
||||
.await;
|
||||
assert_eq!(resp.0.status(), poem::http::StatusCode::UNAUTHORIZED);
|
||||
assert!(state.joined_agents.read().await.is_empty());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn list_agents_returns_registered_agents() {
|
||||
let state = make_test_state();
|
||||
state
|
||||
.joined_agents
|
||||
.write()
|
||||
.await
|
||||
.push(gateway::JoinedAgent {
|
||||
id: "id-1".into(),
|
||||
label: "agent-1".into(),
|
||||
address: "ws://a:3001/crdt-sync".into(),
|
||||
registered_at: 0.0,
|
||||
last_seen: 0.0,
|
||||
assigned_project: None,
|
||||
});
|
||||
let app = poem::Route::new()
|
||||
.at("/gateway/agents", poem::get(gateway_list_agents_handler))
|
||||
.data(state.clone());
|
||||
let cli = poem::test::TestClient::new(app);
|
||||
let resp = cli.get("/gateway/agents").send().await;
|
||||
assert_eq!(resp.0.status(), poem::http::StatusCode::OK);
|
||||
let agents: Vec<serde_json::Value> = resp.0.into_body().into_json().await.unwrap();
|
||||
assert_eq!(agents.len(), 1);
|
||||
assert_eq!(agents[0]["label"], "agent-1");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn remove_agent_deletes_by_id() {
|
||||
let state = make_test_state();
|
||||
state
|
||||
.joined_agents
|
||||
.write()
|
||||
.await
|
||||
.push(gateway::JoinedAgent {
|
||||
id: "del-id".into(),
|
||||
label: "to-delete".into(),
|
||||
address: "ws://x:3001/crdt-sync".into(),
|
||||
registered_at: 0.0,
|
||||
last_seen: 0.0,
|
||||
assigned_project: None,
|
||||
});
|
||||
let app = poem::Route::new()
|
||||
.at(
|
||||
"/gateway/agents/:id",
|
||||
poem::delete(gateway_remove_agent_handler),
|
||||
)
|
||||
.data(state.clone());
|
||||
let cli = poem::test::TestClient::new(app);
|
||||
let resp = cli.delete("/gateway/agents/del-id").send().await;
|
||||
assert_eq!(resp.0.status(), poem::http::StatusCode::NO_CONTENT);
|
||||
assert!(state.joined_agents.read().await.is_empty());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn remove_agent_unknown_id_returns_not_found() {
|
||||
let state = make_test_state();
|
||||
let app = poem::Route::new()
|
||||
.at(
|
||||
"/gateway/agents/:id",
|
||||
poem::delete(gateway_remove_agent_handler),
|
||||
)
|
||||
.data(state.clone());
|
||||
let cli = poem::test::TestClient::new(app);
|
||||
let resp = cli.delete("/gateway/agents/no-such-id").send().await;
|
||||
assert_eq!(resp.0.status(), poem::http::StatusCode::NOT_FOUND);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn heartbeat_updates_last_seen() {
|
||||
let state = make_test_state();
|
||||
state
|
||||
.joined_agents
|
||||
.write()
|
||||
.await
|
||||
.push(gateway::JoinedAgent {
|
||||
id: "hb-id".into(),
|
||||
label: "hb-agent".into(),
|
||||
address: "ws://hb:3001/crdt-sync".into(),
|
||||
registered_at: 0.0,
|
||||
last_seen: 0.0,
|
||||
assigned_project: None,
|
||||
});
|
||||
let app = poem::Route::new()
|
||||
.at(
|
||||
"/gateway/agents/:id/heartbeat",
|
||||
poem::post(gateway_heartbeat_handler),
|
||||
)
|
||||
.data(state.clone());
|
||||
let cli = poem::test::TestClient::new(app);
|
||||
let resp = cli.post("/gateway/agents/hb-id/heartbeat").send().await;
|
||||
assert_eq!(resp.0.status(), poem::http::StatusCode::NO_CONTENT);
|
||||
let agents = state.joined_agents.read().await;
|
||||
assert!(agents[0].last_seen > 0.0);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn heartbeat_unknown_id_returns_not_found() {
|
||||
let state = make_test_state();
|
||||
let app = poem::Route::new()
|
||||
.at(
|
||||
"/gateway/agents/:id/heartbeat",
|
||||
poem::post(gateway_heartbeat_handler),
|
||||
)
|
||||
.data(state.clone());
|
||||
let cli = poem::test::TestClient::new(app);
|
||||
let resp = cli
|
||||
.post("/gateway/agents/no-such-id/heartbeat")
|
||||
.send()
|
||||
.await;
|
||||
assert_eq!(resp.0.status(), poem::http::StatusCode::NOT_FOUND);
|
||||
}
|
||||
|
||||
// ── Notification poller integration tests ────────────────────────────
|
||||
|
||||
#[tokio::test]
|
||||
|
||||
Reference in New Issue
Block a user