Skip to content

Commit 65556b0

Browse files
committed
tighten validator CLI and warning semantics
1 parent 95aa286 commit 65556b0

2 files changed

Lines changed: 71 additions & 5 deletions

File tree

scripts/validate_plugins/run.py

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,13 @@ class MetadataLoadError(ValueError):
3838
pass
3939

4040

41+
def positive_int(raw_value: str) -> int:
42+
value = int(raw_value)
43+
if value <= 0:
44+
raise argparse.ArgumentTypeError("must be a positive integer")
45+
return value
46+
47+
4148
def build_result(
4249
*,
4350
plugin: str,
@@ -50,14 +57,16 @@ def build_result(
5057
plugin_dir_name: str | None = None,
5158
details: dict | str | None = None,
5259
) -> dict:
60+
resolved_severity = severity or ("pass" if ok else "fail")
61+
resolved_ok = True if resolved_severity in {"pass", "warn"} else ok
5362
result = {
5463
"plugin": plugin,
5564
"repo": repo,
5665
"normalized_repo_url": normalized_repo_url,
57-
"ok": ok,
66+
"ok": resolved_ok,
5867
"stage": stage,
5968
"message": message,
60-
"severity": severity or ("pass" if ok else "fail"),
69+
"severity": resolved_severity,
6170
}
6271
if plugin_dir_name:
6372
result["plugin_dir_name"] = plugin_dir_name
@@ -742,9 +751,9 @@ def build_parser() -> argparse.ArgumentParser:
742751
parser.add_argument("--astrbot-path")
743752
parser.add_argument("--report-path", default="validation-report.json")
744753
parser.add_argument("--work-dir")
745-
parser.add_argument("--clone-timeout", type=int, default=DEFAULT_CLONE_TIMEOUT)
746-
parser.add_argument("--load-timeout", type=int, default=300)
747-
parser.add_argument("--max-workers", type=int, default=DEFAULT_MAX_WORKERS)
754+
parser.add_argument("--clone-timeout", type=positive_int, default=DEFAULT_CLONE_TIMEOUT)
755+
parser.add_argument("--load-timeout", type=positive_int, default=300)
756+
parser.add_argument("--max-workers", type=positive_int, default=DEFAULT_MAX_WORKERS)
748757
parser.add_argument("--worker", action="store_true")
749758
parser.add_argument("--plugin-source-dir")
750759
parser.add_argument("--plugin-dir-name")

tests/test_validate_plugins.py

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)