@@ -830,7 +830,20 @@ impl InspectionUnavailable {
830830 remedy : remedy. to_owned ( ) ,
831831 }
832832 }
833+ }
833834
835+ /// An inspection that could not complete, keeping the cause in the reason. The
836+ /// cause comes from spawning the tool, its timeout, temporary files and the
837+ /// report parse; none of those messages carries a configured value.
838+ fn inspection_failed ( field : & str , action : & str , error : & anyhow:: Error ) -> InspectionUnavailable {
839+ InspectionUnavailable :: new (
840+ field,
841+ & format ! ( "The selected `caseworkctl` helper could not {action}: {error:#}." ) ,
842+ "Install the matching Casework tools or provide RSTACK_CASEWORKCTL_BIN, then run the check again." ,
843+ )
844+ }
845+
846+ impl InspectionUnavailable {
834847 pub ( super ) fn field ( & self ) -> & str {
835848 & self . field
836849 }
@@ -1075,12 +1088,10 @@ fn casework_principals(
10751088 "Run `rstack casework check` for authored diagnostics, then correct the Casework project." ,
10761089 ) . into ( ) ) ;
10771090 }
1078- Err ( _) => {
1079- return Err ( InspectionUnavailable :: new (
1080- & project. field ,
1081- "The selected `caseworkctl` helper could not inspect the Casework project." ,
1082- "Install the matching Casework tools or provide RSTACK_CASEWORKCTL_BIN, then run the check again." ,
1083- ) . into ( ) ) ;
1091+ Err ( error) => {
1092+ return Err (
1093+ inspection_failed ( & project. field , "inspect the Casework project" , & error) . into ( ) ,
1094+ ) ;
10841095 }
10851096 } ;
10861097 let status_profile = plan
@@ -1238,13 +1249,7 @@ fn check_casework_binding(
12381249 binding : & super :: bindings:: CaseworkContextProvider ,
12391250 terminate : & AtomicBool ,
12401251) -> Result < ( ) > {
1241- let unavailable = || {
1242- InspectionUnavailable :: new (
1243- & project. field ,
1244- "The selected `caseworkctl` helper could not check the Casework configuration the run writes." ,
1245- "Install the matching Casework tools or provide RSTACK_CASEWORKCTL_BIN, then run the check again." ,
1246- )
1247- } ;
1252+ let action = "check the Casework configuration the run writes" ;
12481253 let checked = ( || {
12491254 let directory = tempfile:: tempdir ( ) ?;
12501255 // The runtime loader refuses a file reached through a symbolic link,
@@ -1272,8 +1277,9 @@ fn check_casework_binding(
12721277 terminate,
12731278 )
12741279 } ) ( ) ;
1275- let Ok ( ( status, report) ) = checked else {
1276- return Err ( unavailable ( ) . into ( ) ) ;
1280+ let ( status, report) = match checked {
1281+ Ok ( checked) => checked,
1282+ Err ( error) => return Err ( inspection_failed ( & project. field , action, & error) . into ( ) ) ,
12771283 } ;
12781284 match casework_binding_check ( status. code ( ) , & report, & binding. id ) {
12791285 BindingCheck :: Accepted => Ok ( ( ) ) ,
@@ -1286,7 +1292,12 @@ fn check_casework_binding(
12861292 "Correct the value so the product accepts the configuration the run writes for this binding." ,
12871293 )
12881294 . into ( ) ) ,
1289- BindingCheck :: Unchecked => Err ( unavailable ( ) . into ( ) ) ,
1295+ BindingCheck :: Unchecked => Err ( inspection_failed (
1296+ & project. field ,
1297+ action,
1298+ & anyhow:: anyhow!( "caseworkctl check did not report a result for the binding" ) ,
1299+ )
1300+ . into ( ) ) ,
12901301 }
12911302}
12921303
@@ -2532,6 +2543,30 @@ mod tests {
25322543 assert_ne ! ( fs:: read_to_string( other. join( FIELD_KEY ) ) . unwrap( ) , encoded) ;
25332544 }
25342545
2546+ #[ test]
2547+ fn an_unavailable_inspection_is_operational_and_names_its_cause ( ) {
2548+ let class =
2549+ |error : & anyhow:: Error | super :: super :: error_report ( error) . 0 [ "error" ] [ "class" ] . clone ( ) ;
2550+ let unavailable = anyhow:: Error :: new ( InspectionUnavailable :: new (
2551+ "products[0]" ,
2552+ "The Casework project cannot be inspected." ,
2553+ "Install the tools." ,
2554+ ) ) ;
2555+ assert_eq ! ( class( & unavailable) , "operational" ) ;
2556+
2557+ let terminate = AtomicBool :: new ( false ) ;
2558+ let missing = Path :: new ( "/nonexistent/caseworkctl" ) ;
2559+ let error = explain_casework_facts ( missing, Path :: new ( "." ) , & terminate) . unwrap_err ( ) ;
2560+ let reason = inspection_failed ( "products[0]" , "inspect the Casework project" , & error) ;
2561+ assert ! (
2562+ reason. reason( ) . contains(
2563+ "could not inspect the Casework project: cannot start caseworkctl explain"
2564+ ) ,
2565+ "{}" ,
2566+ reason. reason( )
2567+ ) ;
2568+ }
2569+
25352570 #[ test]
25362571 fn missed_readiness_and_start_deadlines_are_operational ( ) {
25372572 let class =
0 commit comments