@@ -393,6 +393,18 @@ def test_build_parser_defaults_max_workers_to_eight(self):
393393
394394 self .assertEqual (args .max_workers , 8 )
395395
396+ def test_build_parser_rejects_non_positive_worker_and_timeout_values (self ):
397+ module = load_validator_module ()
398+
399+ with self .assertRaises (SystemExit ):
400+ module .build_parser ().parse_args (["--astrbot-path" , "/tmp/AstrBot" , "--max-workers" , "0" ])
401+
402+ with self .assertRaises (SystemExit ):
403+ module .build_parser ().parse_args (["--astrbot-path" , "/tmp/AstrBot" , "--clone-timeout" , "0" ])
404+
405+ with self .assertRaises (SystemExit ):
406+ module .build_parser ().parse_args (["--astrbot-path" , "/tmp/AstrBot" , "--load-timeout" , "0" ])
407+
396408 def test_validate_selected_plugins_emits_progress_and_result_lines (self ):
397409 module = load_validator_module ()
398410 selected = [
@@ -546,6 +558,51 @@ def test_precheck_failure_is_mapped_into_result(self):
546558 self .assertEqual (result ["message" ], "invalid metadata" )
547559 self .assertEqual (result ["details" ], "line 3" )
548560
561+ def test_precheck_warning_is_non_fatal_in_final_result (self ):
562+ with mock .patch .object (self .module , "clone_plugin_repo" ):
563+ with mock .patch .object (
564+ self .module ,
565+ "precheck_plugin_directory" ,
566+ return_value = {
567+ "ok" : False ,
568+ "severity" : "warn" ,
569+ "stage" : "metadata" ,
570+ "message" : "missing required metadata fields: desc" ,
571+ },
572+ ):
573+ result = self .call_validate_plugin ()
574+
575+ self .assertTrue (result ["ok" ])
576+ self .assertEqual (result ["severity" ], "warn" )
577+ self .assertEqual (result ["stage" ], "metadata" )
578+
579+ def test_load_timeout_uses_process_output_details (self ):
580+ timeout = subprocess .TimeoutExpired (
581+ cmd = [sys .executable , str (self .script_path )],
582+ timeout = 60 ,
583+ output = "timeout-stdout" ,
584+ stderr = "timeout-stderr" ,
585+ )
586+
587+ with mock .patch .object (
588+ self .module ,
589+ "precheck_plugin_directory" ,
590+ return_value = {"ok" : True , "plugin_dir_name" : "demo-plugin" , "message" : "ok" , "stage" : "precheck" },
591+ ):
592+ with mock .patch .object (self .module , "clone_plugin_repo" ):
593+ with mock .patch .object (subprocess , "run" , side_effect = timeout ):
594+ with mock .patch .object (
595+ self .module ,
596+ "build_process_output_details" ,
597+ return_value = {"stdout" : "timeout-stdout" , "stderr" : "timeout-stderr" },
598+ ) as details_mock :
599+ result = self .call_validate_plugin ()
600+
601+ self .assertEqual (result ["stage" ], "timeout" )
602+ self .assertEqual (result ["plugin_dir_name" ], "demo-plugin" )
603+ self .assertEqual (result ["details" ], {"stdout" : "timeout-stdout" , "stderr" : "timeout-stderr" })
604+ details_mock .assert_called_once_with (stdout = "timeout-stdout" , stderr = "timeout-stderr" )
605+
549606 def test_successful_clone_and_precheck_invokes_worker_and_parses_output (self ):
550607 completed = subprocess .CompletedProcess (
551608 args = ["python3" , "run.py" ],
0 commit comments