@@ -315,6 +315,285 @@ impl StateManagerReceiver {
315315 }
316316}
317317
318+ #[ cfg( test) ]
319+ mod tests {
320+ use super :: * ;
321+ use common:: monitoringserver:: ContainerList ;
322+ use common:: statemanager:: { ErrorCode , ResourceType , StateChange } ;
323+ use tonic:: Request ;
324+
325+ #[ test]
326+ fn test_validate_state_change_and_resource_type_to_string ( ) {
327+ let ( tx, _rx) = mpsc:: channel :: < ContainerList > ( 1 ) ;
328+ let ( tx_state_change, _rx2) = mpsc:: channel :: < StateChange > ( 1 ) ;
329+ let receiver = StateManagerReceiver {
330+ tx,
331+ tx_state_change,
332+ } ;
333+
334+ // Valid state change
335+ let sc = StateChange {
336+ resource_type : ResourceType :: Scenario as i32 ,
337+ resource_name : "res1" . to_string ( ) ,
338+ current_state : "Idle" . to_string ( ) ,
339+ target_state : "Waiting" . to_string ( ) ,
340+ transition_id : "t1" . to_string ( ) ,
341+ timestamp_ns : 1 ,
342+ source : "unittest" . to_string ( ) ,
343+ } ;
344+ assert ! ( receiver. validate_state_change( & sc) . is_ok( ) ) ;
345+
346+ // Invalid timestamp
347+ let mut sc2 = sc. clone ( ) ;
348+ sc2. timestamp_ns = 0 ;
349+ assert ! ( receiver. validate_state_change( & sc2) . is_err( ) ) ;
350+
351+ // Empty resource_name
352+ let mut sc3 = sc. clone ( ) ;
353+ sc3. resource_name = "" . to_string ( ) ;
354+ assert ! ( receiver. validate_state_change( & sc3) . is_err( ) ) ;
355+
356+ // resource_type_to_string checks
357+ assert_eq ! (
358+ receiver. resource_type_to_string( ResourceType :: Scenario as i32 ) ,
359+ "Scenario"
360+ ) ;
361+ assert_eq ! ( receiver. resource_type_to_string( 9999 ) , "Unknown" ) ;
362+ }
363+
364+ #[ tokio:: test]
365+ async fn test_send_changed_container_list_success_and_failure ( ) {
366+ // Success path: receiver present
367+ let ( tx, _rx) = mpsc:: channel :: < ContainerList > ( 1 ) ;
368+ let ( tx_state_change, _rx2) = mpsc:: channel :: < StateChange > ( 1 ) ;
369+ let receiver = StateManagerReceiver {
370+ tx : tx. clone ( ) ,
371+ tx_state_change : tx_state_change. clone ( ) ,
372+ } ;
373+
374+ let cl = ContainerList {
375+ node_name : "n1" . to_string ( ) ,
376+ containers : vec ! [ ] ,
377+ } ;
378+ let resp = receiver. send_changed_container_list ( Request :: new ( cl) ) . await ;
379+ assert ! ( resp. is_ok( ) ) ;
380+
381+ // Failure path: dropped receiver for tx
382+ let ( bad_tx, bad_rx) = mpsc:: channel :: < ContainerList > ( 1 ) ;
383+ drop ( bad_rx) ;
384+ let receiver2 = StateManagerReceiver {
385+ tx : bad_tx,
386+ tx_state_change : tx_state_change. clone ( ) ,
387+ } ;
388+ let cl2 = ContainerList {
389+ node_name : "n2" . to_string ( ) ,
390+ containers : vec ! [ ] ,
391+ } ;
392+ let resp2 = receiver2
393+ . send_changed_container_list ( Request :: new ( cl2) )
394+ . await ;
395+ assert ! ( resp2. is_err( ) ) ;
396+ }
397+
398+ #[ tokio:: test]
399+ async fn test_send_changed_container_list_response_content ( ) {
400+ let ( tx, _rx) = mpsc:: channel :: < ContainerList > ( 1 ) ;
401+ let ( tx_state_change, _rx2) = mpsc:: channel :: < StateChange > ( 1 ) ;
402+ let receiver = StateManagerReceiver {
403+ tx : tx. clone ( ) ,
404+ tx_state_change : tx_state_change. clone ( ) ,
405+ } ;
406+
407+ let cl = ContainerList {
408+ node_name : "n1" . to_string ( ) ,
409+ containers : vec ! [ ] ,
410+ } ;
411+ let resp = receiver
412+ . send_changed_container_list ( Request :: new ( cl) )
413+ . await
414+ . unwrap ( ) ;
415+ let body = resp. into_inner ( ) ;
416+ assert_eq ! ( body. resp, "Successfully processed ContainerList" ) ;
417+
418+ // Failure message should contain 'cannot send changed container list'
419+ let ( bad_tx, bad_rx) = mpsc:: channel :: < ContainerList > ( 1 ) ;
420+ drop ( bad_rx) ;
421+ let receiver2 = StateManagerReceiver {
422+ tx : bad_tx,
423+ tx_state_change,
424+ } ;
425+ let cl2 = ContainerList {
426+ node_name : "n2" . to_string ( ) ,
427+ containers : vec ! [ ] ,
428+ } ;
429+ let resp2 = receiver2
430+ . send_changed_container_list ( Request :: new ( cl2) )
431+ . await ;
432+ assert ! ( resp2. is_err( ) ) ;
433+ let status = resp2. err ( ) . unwrap ( ) ;
434+ assert_eq ! ( status. code( ) , tonic:: Code :: Unavailable ) ;
435+ assert ! ( status
436+ . message( )
437+ . contains( "cannot send changed container list" ) ) ;
438+ }
439+
440+ #[ tokio:: test]
441+ async fn test_send_state_change_success_and_unavailable ( ) {
442+ // Success: tx_state_change has receiver
443+ let ( tx, _rx) = mpsc:: channel :: < ContainerList > ( 1 ) ;
444+ let ( tx_state_change, mut rx_state_change) = mpsc:: channel :: < StateChange > ( 1 ) ;
445+ let receiver = StateManagerReceiver {
446+ tx : tx. clone ( ) ,
447+ tx_state_change : tx_state_change. clone ( ) ,
448+ } ;
449+
450+ let sc = StateChange {
451+ resource_type : ResourceType :: Scenario as i32 ,
452+ resource_name : "res2" . to_string ( ) ,
453+ current_state : "Idle" . to_string ( ) ,
454+ target_state : "Waiting" . to_string ( ) ,
455+ transition_id : "t2" . to_string ( ) ,
456+ timestamp_ns : 1 ,
457+ source : "unittest" . to_string ( ) ,
458+ } ;
459+
460+ let resp = receiver. send_state_change ( Request :: new ( sc. clone ( ) ) ) . await ;
461+ assert ! ( resp. is_ok( ) ) ;
462+ let body = resp. unwrap ( ) . into_inner ( ) ;
463+ assert_eq ! ( body. error_code, ErrorCode :: Success as i32 ) ;
464+
465+ // ensure message was forwarded
466+ let forwarded = rx_state_change. recv ( ) . await ;
467+ assert ! ( forwarded. is_some( ) ) ;
468+
469+ // Failure: tx_state_change cannot send (receiver dropped)
470+ let ( bad_tx, bad_rx) = mpsc:: channel :: < StateChange > ( 1 ) ;
471+ drop ( bad_rx) ;
472+ let receiver2 = StateManagerReceiver {
473+ tx : tx. clone ( ) ,
474+ tx_state_change : bad_tx,
475+ } ;
476+
477+ let sc2 = StateChange {
478+ transition_id : "t3" . to_string ( ) ,
479+ ..sc. clone ( )
480+ } ;
481+ let resp2 = receiver2
482+ . send_state_change ( Request :: new ( sc2) )
483+ . await
484+ . unwrap ( ) ;
485+ let inner = resp2. into_inner ( ) ;
486+ assert_eq ! ( inner. error_code, ErrorCode :: ResourceUnavailable as i32 ) ;
487+ }
488+
489+ #[ tokio:: test]
490+ async fn test_send_action_returns_unavailable ( ) {
491+ let ( tx, _rx) = mpsc:: channel :: < ContainerList > ( 1 ) ;
492+ let ( tx_state_change, _rx2) = mpsc:: channel :: < StateChange > ( 1 ) ;
493+ let receiver = StateManagerReceiver {
494+ tx,
495+ tx_state_change,
496+ } ;
497+
498+ let action = common:: statemanager:: Action {
499+ action : "doit" . to_string ( ) ,
500+ } ;
501+ let res = receiver. send_action ( Request :: new ( action) ) . await ;
502+ assert ! ( res. is_err( ) ) ;
503+ let status = res. err ( ) . unwrap ( ) ;
504+ assert_eq ! ( status. code( ) , tonic:: Code :: Unavailable ) ;
505+ assert_eq ! ( status. message( ) , "doit" ) ;
506+ }
507+
508+ #[ tokio:: test]
509+ async fn test_send_state_change_validation_failure_returns_invalid_request ( ) {
510+ // Create receiver; validation should fail before attempting to forward
511+ let ( tx, _rx) = mpsc:: channel :: < ContainerList > ( 1 ) ;
512+ let ( tx_state_change, _rx2) = mpsc:: channel :: < StateChange > ( 1 ) ;
513+ let receiver = StateManagerReceiver {
514+ tx,
515+ tx_state_change,
516+ } ;
517+
518+ // Build an invalid StateChange (timestamp_ns <= 0)
519+ let sc = StateChange {
520+ resource_type : ResourceType :: Scenario as i32 ,
521+ resource_name : "bad" . to_string ( ) ,
522+ current_state : "Idle" . to_string ( ) ,
523+ target_state : "Waiting" . to_string ( ) ,
524+ transition_id : "bad-tid" . to_string ( ) ,
525+ timestamp_ns : 0 ,
526+ source : "unittest" . to_string ( ) ,
527+ } ;
528+
529+ let resp = receiver. send_state_change ( Request :: new ( sc) ) . await ;
530+ assert ! ( resp. is_ok( ) ) ;
531+ let inner = resp. unwrap ( ) . into_inner ( ) ;
532+ assert_eq ! ( inner. error_code, ErrorCode :: InvalidRequest as i32 ) ;
533+ }
534+
535+ #[ tokio:: test]
536+ async fn test_send_state_change_invalid_resource_type_returns_invalid_request ( ) {
537+ let ( tx, _rx) = mpsc:: channel :: < ContainerList > ( 1 ) ;
538+ let ( tx_state_change, _rx2) = mpsc:: channel :: < StateChange > ( 1 ) ;
539+ let receiver = StateManagerReceiver {
540+ tx,
541+ tx_state_change,
542+ } ;
543+
544+ let sc = StateChange {
545+ resource_type : 9999 , // invalid
546+ resource_name : "res_invalid" . to_string ( ) ,
547+ current_state : "Idle" . to_string ( ) ,
548+ target_state : "Waiting" . to_string ( ) ,
549+ transition_id : "tid-invalid" . to_string ( ) ,
550+ timestamp_ns : 1 ,
551+ source : "unittest" . to_string ( ) ,
552+ } ;
553+
554+ let resp = receiver. send_state_change ( Request :: new ( sc) ) . await ;
555+ assert ! ( resp. is_ok( ) ) ;
556+ let inner = resp. unwrap ( ) . into_inner ( ) ;
557+ assert_eq ! ( inner. error_code, ErrorCode :: InvalidRequest as i32 ) ;
558+ }
559+
560+ #[ test]
561+ fn test_resource_type_to_string_variants ( ) {
562+ let ( tx, _rx) = mpsc:: channel :: < ContainerList > ( 1 ) ;
563+ let ( tx_state_change, _rx2) = mpsc:: channel :: < StateChange > ( 1 ) ;
564+ let receiver = StateManagerReceiver {
565+ tx,
566+ tx_state_change,
567+ } ;
568+
569+ assert_eq ! (
570+ receiver. resource_type_to_string( ResourceType :: Scenario as i32 ) ,
571+ "Scenario"
572+ ) ;
573+ assert_eq ! (
574+ receiver. resource_type_to_string( ResourceType :: Package as i32 ) ,
575+ "Package"
576+ ) ;
577+ assert_eq ! (
578+ receiver. resource_type_to_string( ResourceType :: Model as i32 ) ,
579+ "Model"
580+ ) ;
581+ assert_eq ! (
582+ receiver. resource_type_to_string( ResourceType :: Volume as i32 ) ,
583+ "Volume"
584+ ) ;
585+ assert_eq ! (
586+ receiver. resource_type_to_string( ResourceType :: Network as i32 ) ,
587+ "Network"
588+ ) ;
589+ assert_eq ! (
590+ receiver. resource_type_to_string( ResourceType :: Node as i32 ) ,
591+ "Node"
592+ ) ;
593+ assert_eq ! ( receiver. resource_type_to_string( 9999 ) , "Unknown" ) ;
594+ }
595+ }
596+
318597// ========================================
319598// FUTURE IMPLEMENTATION NOTES
320599// ========================================
0 commit comments