Skip to content

Commit 6ef6f37

Browse files
authored
Merge pull request #730 from zouyonghe/fix/validator-null-stub-db-config
support validator null stub db and config access
2 parents fa70a76 + 3870231 commit 6ef6f37

2 files changed

Lines changed: 129 additions & 0 deletions

File tree

scripts/validate_plugins/run.py

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -630,16 +630,69 @@ async def _return_self():
630630

631631
return _return_self().__await__()
632632

633+
async def __aenter__(self) -> "NullStub":
634+
return self
635+
636+
async def __aexit__(self, exc_type, exc, tb) -> bool:
637+
del exc_type, exc, tb
638+
return False
639+
640+
def get(self, key=None, default=None):
641+
del key
642+
return default
643+
644+
def pop(self, key=None, default=None):
645+
del key
646+
return default
647+
633648
def __iter__(self):
634649
return iter(())
635650

636651
def __bool__(self) -> bool:
637652
return False
638653

639654

655+
class DummyConfig(dict):
656+
def __init__(self, initial=None) -> None:
657+
super().__init__(initial or {})
658+
659+
def __missing__(self, key):
660+
del key
661+
return NullStub()
662+
663+
def __getattr__(self, name: str):
664+
return self[name]
665+
666+
640667
class DummyContext:
641668
def __init__(self) -> None:
642669
self._star_manager = None
670+
self._astrbot_root = Path(os.environ.get("ASTRBOT_ROOT", Path.cwd())).resolve()
671+
self._data_root = self._astrbot_root / "data"
672+
self._plugin_data_dir = self._data_root / "plugin_data"
673+
self._config = DummyConfig(
674+
{
675+
"wake_prefix": [],
676+
"dashboard": DummyConfig(),
677+
"admins_id": [],
678+
"admin_ids": [],
679+
"platform_settings": DummyConfig(
680+
{
681+
"aiocqhttp": {},
682+
"qqofficial": {},
683+
"telegram": {},
684+
"gewechat": {},
685+
"wechatpadpro": {},
686+
}
687+
),
688+
"data_dir": str(self._data_root),
689+
}
690+
)
691+
self.config = self._config
692+
693+
def _ensure_plugin_data_dir(self) -> Path:
694+
self._plugin_data_dir.mkdir(parents=True, exist_ok=True)
695+
return self._plugin_data_dir
643696

644697
def get_all_stars(self):
645698
try:
@@ -669,6 +722,16 @@ def register_llm_tool(self, name: str, func_args, desc: str, func_obj) -> None:
669722
def unregister_llm_tool(self, name: str) -> None:
670723
del name
671724

725+
def get_config(self, umo: str | None = None):
726+
del umo
727+
return self._config
728+
729+
def get_context_config(self):
730+
return self.get_config()
731+
732+
def get_data_dir(self) -> str:
733+
return str(self._ensure_plugin_data_dir())
734+
672735
def __getattr__(self, name: str) -> NullStub:
673736
del name
674737
return NullStub()

tests/test_validate_plugins.py

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -385,6 +385,72 @@ def test_load_plugins_index_rejects_non_dict_values(self):
385385
os.remove(index_path)
386386

387387

388+
class DummyContextStubTests(unittest.IsolatedAsyncioTestCase):
389+
def test_dummy_context_defers_plugin_data_dir_creation_until_requested(self):
390+
module = load_validator_module()
391+
392+
with tempfile.TemporaryDirectory() as tmp_dir:
393+
astrbot_root = Path(tmp_dir) / "astrbot-root"
394+
plugin_data_dir = astrbot_root / "data" / "plugin_data"
395+
396+
with mock.patch.dict(os.environ, {"ASTRBOT_ROOT": str(astrbot_root)}, clear=True):
397+
context = module.DummyContext()
398+
dir_exists_before = plugin_data_dir.exists()
399+
created_dir = Path(context.get_data_dir())
400+
dir_exists_after = plugin_data_dir.is_dir()
401+
402+
self.assertFalse(dir_exists_before)
403+
self.assertEqual(created_dir.resolve(), plugin_data_dir.resolve())
404+
self.assertTrue(dir_exists_after)
405+
406+
def test_dummy_context_returns_worker_data_dir_for_plugin_storage(self):
407+
module = load_validator_module()
408+
409+
with tempfile.TemporaryDirectory() as tmp_dir:
410+
astrbot_root = Path(tmp_dir) / "astrbot-root"
411+
with mock.patch.dict(os.environ, {"ASTRBOT_ROOT": str(astrbot_root)}, clear=True):
412+
data_dir = Path(module.DummyContext().get_data_dir())
413+
data_dir_exists = data_dir.is_dir()
414+
415+
self.assertEqual(data_dir.resolve(), (astrbot_root / "data" / "plugin_data").resolve())
416+
self.assertTrue(data_dir_exists)
417+
418+
async def test_null_stub_supports_async_database_context_pattern(self):
419+
module = load_validator_module()
420+
421+
db = module.DummyContext().get_db()
422+
423+
async with db.get_db() as session:
424+
self.assertIsInstance(session, module.NullStub)
425+
async with session.begin() as transaction:
426+
self.assertIs(transaction, session)
427+
result = await session.execute("SELECT 1")
428+
429+
self.assertIs(result, session)
430+
431+
async def test_null_stub_returns_defaults_for_restart_style_config_access(self):
432+
module = load_validator_module()
433+
434+
with mock.patch.dict(os.environ, {}, clear=True):
435+
dashboard_config = module.DummyContext().get_config().get("dashboard", {})
436+
437+
self.assertEqual(dashboard_config.get("host", "127.0.0.1"), "127.0.0.1")
438+
self.assertEqual(
439+
int(os.environ.get("DASHBOARD_PORT", dashboard_config.get("port", 6185))),
440+
6185,
441+
)
442+
443+
def test_dummy_context_exposes_dict_like_config_defaults(self):
444+
module = load_validator_module()
445+
446+
with mock.patch.dict(os.environ, {}, clear=True):
447+
context = module.DummyContext()
448+
449+
self.assertEqual(context.get_config()["wake_prefix"], [])
450+
self.assertEqual(context.get_config()["dashboard"].get("port", 6185), 6185)
451+
self.assertEqual(context._config.get("expire_seconds", 300), 300)
452+
453+
388454
class ValidationProgressTests(unittest.TestCase):
389455
def test_build_parser_defaults_max_workers_to_sixteen(self):
390456
module = load_validator_module()

0 commit comments

Comments
 (0)