@@ -6,7 +6,7 @@ use serde_json::json;
66use tower:: ServiceExt ;
77
88use common:: {
9- body_json, build_app, build_app_with_mock_agents, delete_with_token, get_with_token, json_with_token,
9+ body_json, build_app, build_app_with_mock_agents, delete_with_token, get_request , get_with_token, json_with_token,
1010 setup_and_login,
1111} ;
1212
@@ -422,6 +422,98 @@ async fn pause_team_slot_endpoint_requires_owned_team_and_active_run() {
422422 assert ! ( body[ "success" ] . as_bool( ) . is_some_and( |success| !success) ) ;
423423}
424424
425+ #[ tokio:: test]
426+ async fn trs1_run_state_returns_null_for_existing_team_without_active_run ( ) {
427+ let ( mut app, services) = build_app_with_mock_agents ( ) . await ;
428+ let ( token, csrf) = setup_and_login ( & mut app, & services, "admin" , "StrongP@ss1" ) . await ;
429+ let data = create_team ( & mut app, & token, & csrf) . await ;
430+ let team_id = data[ "id" ] . as_str ( ) . unwrap ( ) ;
431+
432+ let stop_req = delete_with_token ( & format ! ( "/api/teams/{team_id}/session" ) , & token, & csrf) ;
433+ let stop_resp = app. clone ( ) . oneshot ( stop_req) . await . unwrap ( ) ;
434+ assert_eq ! ( stop_resp. status( ) , StatusCode :: OK ) ;
435+
436+ let req = get_with_token ( & format ! ( "/api/teams/{team_id}/run-state" ) , & token) ;
437+ let resp = app. oneshot ( req) . await . unwrap ( ) ;
438+
439+ assert_eq ! ( resp. status( ) , StatusCode :: OK ) ;
440+ let body = body_json ( resp) . await ;
441+ assert_eq ! ( body[ "success" ] , true ) ;
442+ assert ! ( body[ "data" ] [ "active_run" ] . is_null( ) ) ;
443+ }
444+
445+ #[ tokio:: test]
446+ async fn trs2_run_state_returns_active_run_payload ( ) {
447+ let ( mut app, services) = build_app_with_mock_agents ( ) . await ;
448+ let ( token, csrf) = setup_and_login ( & mut app, & services, "admin" , "StrongP@ss1" ) . await ;
449+ let data = create_team ( & mut app, & token, & csrf) . await ;
450+ let team_id = data[ "id" ] . as_str ( ) . unwrap ( ) ;
451+
452+ let send_req = json_with_token (
453+ "POST" ,
454+ & format ! ( "/api/teams/{team_id}/messages" ) ,
455+ json ! ( { "content" : "hello" , "files" : [ ] } ) ,
456+ & token,
457+ & csrf,
458+ ) ;
459+ let send_resp = app. clone ( ) . oneshot ( send_req) . await . unwrap ( ) ;
460+ assert_eq ! ( send_resp. status( ) , StatusCode :: OK ) ;
461+ let send_body = body_json ( send_resp) . await ;
462+ let team_run_id = send_body[ "data" ] [ "team_run_id" ] . as_str ( ) . unwrap ( ) ;
463+
464+ let req = get_with_token ( & format ! ( "/api/teams/{team_id}/run-state" ) , & token) ;
465+ let resp = app. oneshot ( req) . await . unwrap ( ) ;
466+
467+ assert_eq ! ( resp. status( ) , StatusCode :: OK ) ;
468+ let body = body_json ( resp) . await ;
469+ assert_eq ! ( body[ "success" ] , true ) ;
470+ assert_eq ! ( body[ "data" ] [ "active_run" ] [ "team_id" ] , team_id) ;
471+ assert_eq ! ( body[ "data" ] [ "active_run" ] [ "team_run_id" ] , team_run_id) ;
472+ assert_eq ! ( body[ "data" ] [ "active_run" ] [ "source" ] , "user_message" ) ;
473+ assert_eq ! ( body[ "data" ] [ "active_run" ] [ "has_user_intervention" ] , true ) ;
474+ assert_eq ! ( body[ "data" ] [ "active_run" ] [ "status" ] , "accepted" ) ;
475+ assert_eq ! ( body[ "data" ] [ "active_run" ] [ "pending_wake_count" ] , 1 ) ;
476+ assert ! ( body[ "data" ] [ "active_run" ] [ "slot_work" ] . as_array( ) . unwrap( ) . len( ) >= 1 ) ;
477+ }
478+
479+ #[ tokio:: test]
480+ async fn trs3_run_state_unauthenticated_returns_401 ( ) {
481+ let ( app, _services) = build_app ( ) . await ;
482+
483+ let resp = app. oneshot ( get_request ( "/api/teams/team-1/run-state" ) ) . await . unwrap ( ) ;
484+
485+ assert_eq ! ( resp. status( ) , StatusCode :: UNAUTHORIZED ) ;
486+ let body = body_json ( resp) . await ;
487+ assert_eq ! ( body[ "code" ] , "UNAUTHORIZED" ) ;
488+ }
489+
490+ #[ tokio:: test]
491+ async fn trs4_run_state_missing_team_returns_404 ( ) {
492+ let ( mut app, services) = build_app ( ) . await ;
493+ let ( token, _csrf) = setup_and_login ( & mut app, & services, "admin" , "StrongP@ss1" ) . await ;
494+
495+ let req = get_with_token ( "/api/teams/team-missing/run-state" , & token) ;
496+ let resp = app. oneshot ( req) . await . unwrap ( ) ;
497+
498+ assert_eq ! ( resp. status( ) , StatusCode :: NOT_FOUND ) ;
499+ }
500+
501+ #[ tokio:: test]
502+ async fn trs5_run_state_rejects_cross_user_access ( ) {
503+ let ( mut app, services) = build_app_with_mock_agents ( ) . await ;
504+ let ( admin_token, admin_csrf) = setup_and_login ( & mut app, & services, "admin" , "StrongP@ss1" ) . await ;
505+ let data = create_team ( & mut app, & admin_token, & admin_csrf) . await ;
506+ let team_id = data[ "id" ] . as_str ( ) . unwrap ( ) ;
507+
508+ let ( other_token, _other_csrf) = setup_and_login ( & mut app, & services, "other" , "StrongP@ss2" ) . await ;
509+ let req = get_with_token ( & format ! ( "/api/teams/{team_id}/run-state" ) , & other_token) ;
510+ let resp = app. oneshot ( req) . await . unwrap ( ) ;
511+
512+ assert_eq ! ( resp. status( ) , StatusCode :: FORBIDDEN ) ;
513+ let body = body_json ( resp) . await ;
514+ assert_eq ! ( body[ "code" ] , "FORBIDDEN" ) ;
515+ }
516+
425517// TL-3: Each team contains full assistants info
426518#[ tokio:: test]
427519async fn tl3_teams_contain_full_agent_info ( ) {
0 commit comments