@@ -387,252 +387,5 @@ async fn handle_connection(stream: UnixStream, state: ServerState) -> crate::Res
387387}
388388
389389#[ cfg( test) ]
390- mod tests {
391- use super :: * ;
392- use serde_json:: json;
393- use std:: path:: PathBuf ;
394- use tokio:: io:: { AsyncBufReadExt , AsyncWriteExt , BufReader } ;
395- use tokio:: net:: { UnixListener , UnixStream } ;
396- use tokio:: time:: timeout;
397-
398- /// Spawns a test server and returns (temp_dir, socket_path, state).
399- /// The temp_dir must be kept in scope for the socket to remain valid.
400- async fn spawn_test_server ( test_name : & str ) -> ( tempfile:: TempDir , PathBuf , Arc < ServerState > ) {
401- let dir = tempfile:: tempdir ( ) . expect ( "temp dir" ) ;
402- let socket_path = dir. path ( ) . join ( format ! ( "{}.sock" , test_name) ) ;
403-
404- let workload_manager = Arc :: new (
405- WorkloadManager :: new ( 2 )
406- . await
407- . expect ( "create workload manager" ) ,
408- ) ;
409- let state = Arc :: new ( ServerState {
410- start_time : Instant :: now ( ) ,
411- workload_manager,
412- } ) ;
413-
414- if socket_path. exists ( ) {
415- std:: fs:: remove_file ( & socket_path) . expect ( "remove existing" ) ;
416- }
417- if let Some ( parent) = socket_path. parent ( ) {
418- std:: fs:: create_dir_all ( parent) . expect ( "create parent" ) ;
419- }
420- let listener = UnixListener :: bind ( & socket_path) . expect ( "bind" ) ;
421-
422- let state_clone = Arc :: clone ( & state) ;
423- tokio:: spawn ( async move {
424- loop {
425- if let Ok ( ( stream, _) ) = listener. accept ( ) . await {
426- let state_clone = Arc :: clone ( & state_clone) ;
427- tokio:: spawn ( async move {
428- let _ =
429- super :: handle_connection ( stream, state_clone. as_ref ( ) . clone ( ) ) . await ;
430- } ) ;
431- }
432- }
433- } ) ;
434-
435- tokio:: task:: yield_now ( ) . await ;
436-
437- ( dir, socket_path, state)
438- }
439-
440- fn jsonrpc_request ( method : & str , params : Value , id : Value ) -> String {
441- serde_json:: to_string ( & json ! ( {
442- "jsonrpc" : "2.0" ,
443- "method" : method,
444- "params" : params,
445- "id" : id
446- } ) )
447- . expect ( "serialize request" )
448- }
449-
450- async fn connect_and_send ( socket_path : & std:: path:: Path , request : & str ) -> String {
451- let stream = timeout ( std:: time:: Duration :: from_secs ( 2 ) , async {
452- for _ in 0 ..50 {
453- match UnixStream :: connect ( socket_path) . await {
454- Ok ( s) => return Ok ( s) ,
455- Err ( _) => tokio:: task:: yield_now ( ) . await ,
456- }
457- }
458- Err ( std:: io:: Error :: new (
459- std:: io:: ErrorKind :: ConnectionRefused ,
460- "could not connect" ,
461- ) )
462- } )
463- . await
464- . expect ( "connect timeout" )
465- . expect ( "connect" ) ;
466-
467- let ( reader, mut writer) = stream. into_split ( ) ;
468- writer
469- . write_all ( request. as_bytes ( ) )
470- . await
471- . expect ( "write request" ) ;
472- writer. write_all ( b"\n " ) . await . expect ( "write newline" ) ;
473- writer. flush ( ) . await . expect ( "flush" ) ;
474-
475- let mut reader = BufReader :: new ( reader) ;
476- let mut line = String :: new ( ) ;
477- timeout (
478- std:: time:: Duration :: from_secs ( 2 ) ,
479- reader. read_line ( & mut line) ,
480- )
481- . await
482- . expect ( "read timeout" )
483- . expect ( "read" ) ;
484- line
485- }
486-
487- #[ test]
488- fn test_jsonrpc_request_parsing ( ) {
489- let json = r#"{"jsonrpc":"2.0","method":"daemon.health","params":{},"id":1}"# ;
490- let request: JsonRpcRequest = serde_json:: from_str ( json) . unwrap ( ) ;
491- assert_eq ! ( request. jsonrpc, "2.0" ) ;
492- assert_eq ! ( request. method, "daemon.health" ) ;
493- }
494-
495- #[ test]
496- fn test_jsonrpc_response_serialization ( ) {
497- let response = JsonRpcResponse {
498- jsonrpc : toadstool_common:: constants:: jsonrpc:: VERSION . to_string ( ) ,
499- result : Some ( json ! ( { "status" : "ok" } ) ) ,
500- error : None ,
501- id : Some ( json ! ( 1 ) ) ,
502- } ;
503- let json = serde_json:: to_string ( & response) . unwrap ( ) ;
504- assert ! ( json. contains( "2.0" ) ) ;
505- assert ! ( json. contains( "result" ) ) ;
506- }
507-
508- #[ tokio:: test]
509- async fn test_server_construct_and_health ( ) {
510- let ( _dir, socket_path, _state) = spawn_test_server ( "test" ) . await ;
511-
512- let req = jsonrpc_request ( "daemon.health" , json ! ( { } ) , json ! ( 1 ) ) ;
513- let resp = connect_and_send ( & socket_path, & req) . await ;
514- let parsed: Value = serde_json:: from_str ( resp. trim ( ) ) . expect ( "parse response" ) ;
515- assert_eq ! ( parsed[ "result" ] [ "status" ] , "ok" ) ;
516- assert ! ( parsed[ "result" ] [ "uptime_secs" ] . as_u64( ) . is_some( ) ) ;
517- assert_eq ! ( parsed[ "id" ] , 1 ) ;
518- }
519-
520- #[ tokio:: test]
521- async fn test_method_routing_metrics ( ) {
522- let ( _dir, socket_path, _state) = spawn_test_server ( "test_metrics" ) . await ;
523-
524- let req = jsonrpc_request ( "daemon.metrics" , json ! ( { } ) , json ! ( 2 ) ) ;
525- let resp = connect_and_send ( & socket_path, & req) . await ;
526- let parsed: Value = serde_json:: from_str ( resp. trim ( ) ) . expect ( "parse response" ) ;
527- assert ! ( parsed[ "result" ] [ "workloads" ] . is_object( ) ) ;
528- assert ! ( parsed[ "result" ] [ "uptime_secs" ] . as_u64( ) . is_some( ) ) ;
529- }
530-
531- #[ tokio:: test]
532- async fn test_method_routing_list_workloads ( ) {
533- let ( _dir, socket_path, _state) = spawn_test_server ( "test_list" ) . await ;
534-
535- let req = jsonrpc_request ( "daemon.list_workloads" , json ! ( { } ) , json ! ( 3 ) ) ;
536- let resp = connect_and_send ( & socket_path, & req) . await ;
537- let parsed: Value = serde_json:: from_str ( resp. trim ( ) ) . expect ( "parse response" ) ;
538- assert ! ( parsed[ "result" ] [ "workloads" ] . is_array( ) ) ;
539- assert ! ( parsed[ "result" ] [ "count" ] . as_u64( ) . is_some( ) ) ;
540- }
541-
542- #[ tokio:: test]
543- async fn test_submit_workload_request_response ( ) {
544- let ( _dir, socket_path, _state) = spawn_test_server ( "test_submit" ) . await ;
545-
546- let params = json ! ( {
547- "biome_yaml" : "version: 1.0" ,
548- "requester" : "test-client" ,
549- "environment" : { } ,
550- "timeout_secs" : 60 ,
551- "persistent" : false
552- } ) ;
553- let req = jsonrpc_request ( "daemon.submit_workload" , params, json ! ( 4 ) ) ;
554- let resp = connect_and_send ( & socket_path, & req) . await ;
555- let parsed: Value = serde_json:: from_str ( resp. trim ( ) ) . expect ( "parse response" ) ;
556- if let Some ( err) = parsed. get ( "error" ) {
557- unreachable ! ( "submit_workload failed: {err}" ) ;
558- }
559- // JSON-RPC handler returns workload_id string directly from WorkloadManager
560- let workload_id = parsed[ "result" ]
561- . as_str ( )
562- . or_else ( || parsed[ "result" ] [ "workload_id" ] . as_str ( ) ) ;
563- assert ! (
564- workload_id. is_some_and( |id| !id. is_empty( ) ) ,
565- "expected workload_id in result: {}" ,
566- parsed
567- ) ;
568- }
569-
570- #[ tokio:: test]
571- async fn test_get_workload_not_found ( ) {
572- let ( _dir, socket_path, _state) = spawn_test_server ( "test_get" ) . await ;
573-
574- let params = json ! ( { "id" : "nonexistent-uuid" } ) ;
575- let req = jsonrpc_request ( "daemon.get_workload" , params, json ! ( 5 ) ) ;
576- let resp = connect_and_send ( & socket_path, & req) . await ;
577- let parsed: Value = serde_json:: from_str ( resp. trim ( ) ) . expect ( "parse response" ) ;
578- assert ! ( parsed[ "error" ] . is_object( ) ) ;
579- assert_eq ! ( parsed[ "error" ] [ "code" ] , error_codes:: WORKLOAD_NOT_FOUND ) ;
580- }
581-
582- #[ tokio:: test]
583- async fn test_parse_error_invalid_json ( ) {
584- let ( _dir, socket_path, _state) = spawn_test_server ( "test_parse" ) . await ;
585-
586- let resp = connect_and_send ( & socket_path, "not valid json\n " ) . await ;
587- let parsed: Value = serde_json:: from_str ( resp. trim ( ) ) . expect ( "parse response" ) ;
588- assert ! ( parsed[ "error" ] . is_object( ) ) ;
589- assert_eq ! ( parsed[ "error" ] [ "code" ] , error_codes:: PARSE_ERROR ) ;
590- }
591-
592- #[ tokio:: test]
593- async fn test_invalid_jsonrpc_version ( ) {
594- let ( _dir, socket_path, _state) = spawn_test_server ( "test_version" ) . await ;
595-
596- let req = jsonrpc_request ( "daemon.health" , json ! ( { } ) , json ! ( 1 ) ) ;
597- let bad_req = req. replace ( "\" 2.0\" " , "\" 1.0\" " ) ;
598- let resp = connect_and_send ( & socket_path, & bad_req) . await ;
599- let parsed: Value = serde_json:: from_str ( resp. trim ( ) ) . expect ( "parse response" ) ;
600- assert ! ( parsed[ "error" ] . is_object( ) ) ;
601- assert_eq ! ( parsed[ "error" ] [ "code" ] , error_codes:: INVALID_REQUEST ) ;
602- }
603-
604- #[ tokio:: test]
605- async fn test_method_not_found ( ) {
606- let ( _dir, socket_path, _state) = spawn_test_server ( "test_method" ) . await ;
607-
608- let req = jsonrpc_request ( "daemon.nonexistent" , json ! ( { } ) , json ! ( 6 ) ) ;
609- let resp = connect_and_send ( & socket_path, & req) . await ;
610- let parsed: Value = serde_json:: from_str ( resp. trim ( ) ) . expect ( "parse response" ) ;
611- assert ! ( parsed[ "error" ] . is_object( ) ) ;
612- assert_eq ! ( parsed[ "error" ] [ "code" ] , error_codes:: METHOD_NOT_FOUND ) ;
613- }
614-
615- #[ tokio:: test]
616- async fn test_invalid_params_submit_workload ( ) {
617- let ( _dir, socket_path, _state) = spawn_test_server ( "test_invalid_submit" ) . await ;
618-
619- let params = json ! ( { "invalid" : "params" } ) ;
620- let req = jsonrpc_request ( "daemon.submit_workload" , params, json ! ( 7 ) ) ;
621- let resp = connect_and_send ( & socket_path, & req) . await ;
622- let parsed: Value = serde_json:: from_str ( resp. trim ( ) ) . expect ( "parse response" ) ;
623- assert ! ( parsed[ "error" ] . is_object( ) ) ;
624- assert_eq ! ( parsed[ "error" ] [ "code" ] , error_codes:: INVALID_PARAMS ) ;
625- }
626-
627- #[ tokio:: test]
628- async fn test_invalid_params_get_workload_missing_id ( ) {
629- let ( _dir, socket_path, _state) = spawn_test_server ( "test_get_missing" ) . await ;
630-
631- let params = json ! ( { } ) ;
632- let req = jsonrpc_request ( "daemon.get_workload" , params, json ! ( 8 ) ) ;
633- let resp = connect_and_send ( & socket_path, & req) . await ;
634- let parsed: Value = serde_json:: from_str ( resp. trim ( ) ) . expect ( "parse response" ) ;
635- assert ! ( parsed[ "error" ] . is_object( ) ) ;
636- assert_eq ! ( parsed[ "error" ] [ "code" ] , error_codes:: INVALID_PARAMS ) ;
637- }
638- }
390+ #[ path = "jsonrpc_server_tests.rs" ]
391+ mod tests;
0 commit comments