Files
JWeBot/tests/test_kernel_manifest.py

174 lines
6.3 KiB
Python

#!/usr/bin/env python3
import sys
import tempfile
import unittest
from pathlib import Path
PROJECT_ROOT = Path(__file__).resolve().parents[1]
SDK_SRC = PROJECT_ROOT / "packages" / "jwebot-sdk" / "src"
KERNEL_SRC = PROJECT_ROOT / "packages" / "jwebot-kernel" / "src"
sys.path[:0] = [str(SDK_SRC), str(KERNEL_SRC)]
from jwebot_kernel import ( # noqa: E402
ManifestError,
PluginType,
load_manifest,
parse_manifest,
)
from jwebot_kernel import manifest as manifest_module # noqa: E402
class ManifestTests(unittest.TestCase):
def manifest(self, **overrides):
value = {
"id": "hello-command",
"author": "jwebot",
"description": "Say hello",
"help": ["/hello [name]"],
"version": "1.0.0",
"api_version": "1",
"entrypoint": "hello_command:plugin",
"type": "command",
"capabilities": {
"commands": ["/hello"],
"storage": False,
"network_profiles": [],
"service_profiles": [],
},
}
value.update(overrides)
return value
def test_command_manifest_is_parsed(self):
manifest = parse_manifest(self.manifest())
self.assertEqual(manifest.plugin_id, "hello-command")
self.assertEqual(manifest.author, "jwebot")
self.assertEqual(manifest.description, "Say hello")
self.assertEqual(manifest.help_entries, ("/hello [name]",))
self.assertEqual(manifest.plugin_type, PluginType.COMMAND)
self.assertEqual(manifest.capabilities.commands, ("/hello",))
def test_author_is_required_and_bounded(self):
value = self.manifest()
del value["author"]
with self.assertRaisesRegex(ManifestError, "author must"):
parse_manifest(value)
with self.assertRaisesRegex(ManifestError, "author has an invalid format"):
parse_manifest(self.manifest(author="../Alice"))
def test_unknown_capability_is_rejected(self):
value = self.manifest()
value["capabilities"]["shell"] = True
with self.assertRaisesRegex(ManifestError, "unknown capabilities"):
parse_manifest(value)
def test_description_and_help_are_declared_together(self):
value = self.manifest()
del value["help"]
with self.assertRaisesRegex(
ManifestError,
"declared together",
):
parse_manifest(value)
def test_command_manifest_requires_visible_help_metadata(self):
value = self.manifest()
del value["description"]
del value["help"]
with self.assertRaisesRegex(
ManifestError,
"must declare description",
):
parse_manifest(value)
def test_help_entries_are_bounded_command_usage(self):
with self.assertRaisesRegex(ManifestError, "invalid display"):
parse_manifest(self.manifest(help=["plain prose"]))
with self.assertRaisesRegex(ManifestError, "display boundary"):
parse_manifest(self.manifest(description="x" * 121))
def test_command_extension_requires_a_namespace(self):
value = self.manifest()
value["capabilities"]["commands"] = []
with self.assertRaisesRegex(ManifestError, "at least one command"):
parse_manifest(value)
def test_non_command_extension_cannot_claim_a_command(self):
with self.assertRaisesRegex(ManifestError, "only command extensions"):
parse_manifest(self.manifest(type="enrichment"))
def test_unsupported_api_version_is_rejected(self):
with self.assertRaisesRegex(ManifestError, "unsupported api_version"):
parse_manifest(self.manifest(api_version="2"))
def test_entrypoint_must_use_plugin_namespace(self):
with self.assertRaisesRegex(ManifestError, "plugin module namespace"):
parse_manifest(self.manifest(entrypoint="json:plugin"))
def test_service_profiles_are_bounded_and_parsed(self):
value = self.manifest()
value["capabilities"]["service_profiles"] = ["coin"]
self.assertEqual(
parse_manifest(value).capabilities.service_profiles,
("coin",),
)
def test_duplicate_commands_are_rejected(self):
value = self.manifest()
value["capabilities"]["commands"] = ["/hello", "/hello"]
with self.assertRaisesRegex(ManifestError, "duplicates"):
parse_manifest(value)
def test_case_insensitive_duplicate_commands_are_rejected(self):
value = self.manifest()
value["capabilities"]["commands"] = ["/Hello", "/hello"]
with self.assertRaisesRegex(ManifestError, "case-insensitive duplicates"):
parse_manifest(value)
def test_command_rejects_manifest_breaking_characters(self):
value = self.manifest()
value["capabilities"]["commands"] = ['/"quoted']
with self.assertRaisesRegex(ManifestError, "invalid value"):
parse_manifest(value)
def test_command_accepts_chinese_names(self):
value = self.manifest()
value["capabilities"]["commands"] = ["/天气"]
self.assertEqual(
parse_manifest(value).capabilities.commands,
("/天气",),
)
def test_load_manifest_reads_toml(self):
if manifest_module.tomllib is None:
self.skipTest("tomli is not installed under local Python")
with tempfile.TemporaryDirectory() as directory:
path = Path(directory) / "plugin.toml"
path.write_text(
"\n".join(
(
'id = "hello-command"',
'author = "Alice"',
'description = "Say hello"',
'help = ["/hello"]',
'version = "1.0.0"',
'api_version = "1"',
'entrypoint = "hello_command:plugin"',
'type = "command"',
"",
"[capabilities]",
'commands = ["/hello"]',
"storage = false",
"network_profiles = []",
)
),
encoding="utf-8",
)
self.assertEqual(load_manifest(path).plugin_id, "hello-command")
if __name__ == "__main__":
unittest.main()