Files
AstrBot/tests_v4/test_legacy_loader.py
whatevertogo 1672bc3227 Add v4 compat layer and legacy shims
- Introduce private v4 compatibility surface using
  _legacy_api.py, _legacy_runtime.py, _legacy_loader.py plus new
  _legacy_context.py and _legacy_star.py to centralize legacy adapters
  while keeping public APIs thin.
- Extend InitializeOutput to carry protocol_version for negotiated
  protocol, enabling runtime to adapt to the chosen v4 version.
- Add lightweight legacy support for Star/Context via new LegacyStar and
  LegacyContext shims and expose legacy API through the aggregate
  _legacy_api entry point.
- Ensure legacy loader preserves class declaration order by iterating
  module.__dict__ instead of relying on alphabetical sorting.
- Add tests: protocol_version handling in InitializeOutput, legacy
  main component order preservation, and embedded-newline framing in
  transport tests.
2026-03-14 17:16:42 +08:00

171 lines
5.2 KiB
Python

"""Tests for the private legacy loader helpers."""
from __future__ import annotations
import tempfile
import textwrap
from pathlib import Path
import yaml
from astrbot_sdk._legacy_loader import (
build_legacy_manifest,
load_legacy_main_component_classes,
load_plugin_manifest_payload,
looks_like_legacy_plugin,
resolve_plugin_component_classes,
)
def _read_yaml(path: Path) -> dict:
return yaml.safe_load(path.read_text(encoding="utf-8")) or {}
def test_looks_like_legacy_plugin_requires_main_without_manifest():
with tempfile.TemporaryDirectory() as temp_dir:
plugin_dir = Path(temp_dir) / "legacy_plugin"
plugin_dir.mkdir()
(plugin_dir / "main.py").write_text("print('x')", encoding="utf-8")
assert looks_like_legacy_plugin(plugin_dir) is True
(plugin_dir / "plugin.yaml").write_text("name: modern", encoding="utf-8")
assert looks_like_legacy_plugin(plugin_dir) is False
def test_build_legacy_manifest_uses_metadata_and_marks_legacy_main():
with tempfile.TemporaryDirectory() as temp_dir:
plugin_dir = Path(temp_dir) / "legacy_plugin"
plugin_dir.mkdir()
metadata_path = plugin_dir / "metadata.yaml"
metadata_path.write_text(
yaml.dump({"name": "legacy_demo", "author": "tester", "version": "1.0.0"}),
encoding="utf-8",
)
manifest_path, manifest_data = build_legacy_manifest(
plugin_dir,
read_yaml=_read_yaml,
default_python_version="3.12",
manifest_flag_key="__legacy_main__",
)
assert manifest_path == metadata_path
assert manifest_data["name"] == "legacy_demo"
assert manifest_data["runtime"]["python"] == "3.12"
assert manifest_data["__legacy_main__"] is True
def test_load_legacy_main_component_classes_supports_relative_imports():
with tempfile.TemporaryDirectory() as temp_dir:
plugin_dir = Path(temp_dir) / "legacy_plugin"
plugin_dir.mkdir()
(plugin_dir / "src").mkdir()
(plugin_dir / "src" / "helper.py").write_text(
'VALUE = "legacy-ok"\n',
encoding="utf-8",
)
(plugin_dir / "main.py").write_text(
textwrap.dedent(
"""\
from astrbot_sdk.api.star import Star
from .src.helper import VALUE
class LegacyPlugin(Star):
helper_value = VALUE
"""
),
encoding="utf-8",
)
classes = load_legacy_main_component_classes(
plugin_name="legacy-plugin",
plugin_dir=plugin_dir,
)
assert [cls.__name__ for cls in classes] == ["LegacyPlugin"]
assert classes[0].helper_value == "legacy-ok"
def test_load_legacy_main_component_classes_preserves_definition_order():
with tempfile.TemporaryDirectory() as temp_dir:
plugin_dir = Path(temp_dir) / "legacy_plugin"
plugin_dir.mkdir()
(plugin_dir / "main.py").write_text(
textwrap.dedent(
"""\
from astrbot_sdk.api.star import Star
class ZebraComponent(Star):
pass
class AlphaComponent(Star):
pass
"""
),
encoding="utf-8",
)
classes = load_legacy_main_component_classes(
plugin_name="legacy-plugin",
plugin_dir=plugin_dir,
)
assert [cls.__name__ for cls in classes] == [
"ZebraComponent",
"AlphaComponent",
]
def test_load_plugin_manifest_payload_prefers_plugin_yaml_when_present():
with tempfile.TemporaryDirectory() as temp_dir:
plugin_dir = Path(temp_dir) / "plugin"
plugin_dir.mkdir()
manifest_path = plugin_dir / "plugin.yaml"
manifest_path.write_text(
yaml.dump({"name": "modern_plugin", "runtime": {"python": "3.12"}}),
encoding="utf-8",
)
resolved_path, manifest_data = load_plugin_manifest_payload(
plugin_dir,
read_yaml=_read_yaml,
default_python_version="3.13",
manifest_flag_key="__legacy_main__",
)
assert resolved_path == manifest_path
assert manifest_data["name"] == "modern_plugin"
assert "__legacy_main__" not in manifest_data
def test_resolve_plugin_component_classes_falls_back_to_legacy_main():
with tempfile.TemporaryDirectory() as temp_dir:
plugin_dir = Path(temp_dir) / "legacy_plugin"
plugin_dir.mkdir()
(plugin_dir / "main.py").write_text(
textwrap.dedent(
"""\
from astrbot_sdk.api.star import Star
class LegacyPlugin(Star):
pass
"""
),
encoding="utf-8",
)
classes = resolve_plugin_component_classes(
plugin_name="legacy_plugin",
plugin_dir=plugin_dir,
manifest_data={"components": [], "__legacy_main__": True},
manifest_flag_key="__legacy_main__",
import_string=lambda path, plugin_dir=None: None,
)
assert [cls.__name__ for cls in classes] == ["LegacyPlugin"]