Develop: 升级 NoneBug 版本 (#1725)

This commit is contained in:
Ju4tCode 2023-02-22 23:32:48 +08:00 committed by GitHub
parent 1befd9ffc6
commit 74743e6176
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
28 changed files with 340 additions and 408 deletions

View File

@ -38,7 +38,6 @@ FrontMatter:
"""
import os
import importlib
from importlib.metadata import version
from typing import Any, Dict, Type, Optional
@ -46,9 +45,9 @@ import loguru
from pydantic.env_settings import DotenvType
from nonebot.adapters import Bot
from nonebot.utils import escape_tag
from nonebot.config import Env, Config
from nonebot.log import logger as logger
from nonebot.utils import escape_tag, resolve_dot_notation
from nonebot.drivers import Driver, ReverseDriver, combine_driver
try:
@ -175,31 +174,16 @@ def get_bots() -> Dict[str, Bot]:
return get_driver().bots
def _resolve_dot_notation(
obj_str: str, default_attr: str, default_prefix: Optional[str] = None
) -> Any:
modulename, _, cls = obj_str.partition(":")
if default_prefix is not None and modulename.startswith("~"):
modulename = default_prefix + modulename[1:]
module = importlib.import_module(modulename)
if not cls:
return getattr(module, default_attr)
instance = module
for attr_str in cls.split("."):
instance = getattr(instance, attr_str)
return instance
def _resolve_combine_expr(obj_str: str) -> Type[Driver]:
drivers = obj_str.split("+")
DriverClass = _resolve_dot_notation(
DriverClass = resolve_dot_notation(
drivers[0], "Driver", default_prefix="nonebot.drivers."
)
if len(drivers) == 1:
logger.trace(f"Detected driver {DriverClass} with no mixins.")
return DriverClass
mixins = [
_resolve_dot_notation(mixin, "Mixin", default_prefix="nonebot.drivers.")
resolve_dot_notation(mixin, "Mixin", default_prefix="nonebot.drivers.")
for mixin in drivers[1:]
]
logger.trace(f"Detected driver {DriverClass} with mixins {mixins}.")

View File

@ -9,6 +9,7 @@ import re
import json
import asyncio
import inspect
import importlib
import dataclasses
from pathlib import Path
from functools import wraps, partial
@ -167,13 +168,30 @@ def get_name(obj: Any) -> str:
def path_to_module_name(path: Path) -> str:
rel_path = path.resolve().relative_to(Path(".").resolve())
"""转换路径为模块名"""
rel_path = path.resolve().relative_to(Path.cwd().resolve())
if rel_path.stem == "__init__":
return ".".join(rel_path.parts[:-1])
else:
return ".".join(rel_path.parts[:-1] + (rel_path.stem,))
def resolve_dot_notation(
obj_str: str, default_attr: str, default_prefix: Optional[str] = None
) -> Any:
"""解析并导入点分表示法的对象"""
modulename, _, cls = obj_str.partition(":")
if default_prefix is not None and modulename.startswith("~"):
modulename = default_prefix + modulename[1:]
module = importlib.import_module(modulename)
if not cls:
return getattr(module, default_attr)
instance = module
for attr_str in cls.split("."):
instance = getattr(instance, attr_str)
return instance
class DataclassEncoder(json.JSONEncoder):
"""在JSON序列化 {re}`nonebot.adapters._message.Message` (List[Dataclass]) 时使用的 `JSONEncoder`"""

42
poetry.lock generated
View File

@ -1431,26 +1431,22 @@ setuptools = "*"
[[package]]
name = "nonebug"
version = "0.2.3"
version = "0.3.0"
description = "nonebot2 test framework"
category = "dev"
optional = false
python-versions = "^3.8"
files = []
develop = false
python-versions = ">=3.8,<4.0"
files = [
{file = "nonebug-0.3.0-py3-none-any.whl", hash = "sha256:ba6adef16c73042ac717c840aceb0e68fe06ebca948fe69511139265752b77f9"},
{file = "nonebug-0.3.0.tar.gz", hash = "sha256:189d0d8efbcaf06dd9bdf73b48f270b60a31ec9e76b546ab6607c392929de9bd"},
]
[package.dependencies]
asgiref = "^3.4.0"
async-asgi-testclient = "^1.4.8"
nonebot2 = "^2.0.0-beta.1"
pytest = "^7.0.0"
typing-extensions = "^4.0.0"
[package.source]
type = "git"
url = "https://github.com/nonebot/nonebug.git"
reference = "HEAD"
resolved_reference = "9e40f5717b8f7bf3e19933f9a2b63a470db6005a"
asgiref = ">=3.4.0,<4.0.0"
async-asgi-testclient = ">=1.4.8,<2.0.0"
nonebot2 = ">=2.0.0-rc.2,<3.0.0"
pytest = ">=7.0.0,<8.0.0"
typing-extensions = ">=4.0.0,<5.0.0"
[[package]]
name = "nonemoji"
@ -1571,14 +1567,14 @@ files = [
[[package]]
name = "prompt-toolkit"
version = "3.0.36"
version = "3.0.37"
description = "Library for building powerful interactive command lines in Python"
category = "dev"
optional = false
python-versions = ">=3.6.2"
python-versions = ">=3.7.0"
files = [
{file = "prompt_toolkit-3.0.36-py3-none-any.whl", hash = "sha256:aa64ad242a462c5ff0363a7b9cfe696c20d55d9fc60c11fd8e632d064804d305"},
{file = "prompt_toolkit-3.0.36.tar.gz", hash = "sha256:3e163f254bef5a03b146397d7c1963bd3e2812f0964bb9a24e6ec761fd28db63"},
{file = "prompt_toolkit-3.0.37-py3-none-any.whl", hash = "sha256:6a2948ec427dfcc7c983027b1044b355db6aaa8be374f54ad2015471f7d81c5b"},
{file = "prompt_toolkit-3.0.37.tar.gz", hash = "sha256:d5d73d4b5eb1a92ba884a88962b157f49b71e06c4348b417dd622b25cdd3800b"},
]
[package.dependencies]
@ -1966,14 +1962,14 @@ idna2008 = ["idna"]
[[package]]
name = "setuptools"
version = "67.3.2"
version = "67.4.0"
description = "Easily download, build, install, upgrade, and uninstall Python packages"
category = "dev"
optional = false
python-versions = ">=3.7"
files = [
{file = "setuptools-67.3.2-py3-none-any.whl", hash = "sha256:bb6d8e508de562768f2027902929f8523932fcd1fb784e6d573d2cafac995a48"},
{file = "setuptools-67.3.2.tar.gz", hash = "sha256:95f00380ef2ffa41d9bba85d95b27689d923c93dfbafed4aecd7cf988a25e012"},
{file = "setuptools-67.4.0-py3-none-any.whl", hash = "sha256:f106dee1b506dee5102cc3f3e9e68137bbad6d47b616be7991714b0c62204251"},
{file = "setuptools-67.4.0.tar.gz", hash = "sha256:e5fd0a713141a4a105412233c63dc4e17ba0090c8e8334594ac790ec97792330"},
]
[package.extras]
@ -2491,4 +2487,4 @@ websockets = ["websockets"]
[metadata]
lock-version = "2.0"
python-versions = "^3.8"
content-hash = "9b82bb373f396eb553e6fcc33d26722957ff85590a1bd8b29b89cce4d7ed4489"
content-hash = "d8e1468d5f5e1c5e48a7bf9717cd3f181cc05d4c0153cab3a75f429c6453766f"

View File

@ -45,10 +45,10 @@ nonemoji = "^0.1.2"
pre-commit = "^3.0.0"
[tool.poetry.group.test.dependencies]
nonebug = "^0.3.0"
pytest-cov = "^4.0.0"
pytest-xdist = "^3.0.2"
pytest-asyncio = "^0.20.0"
nonebug = { git = "https://github.com/nonebot/nonebug.git" }
[tool.poetry.group.docs.dependencies]
nb-autodoc = { git = "https://github.com/nonebot/nb-autodoc.git" }
@ -63,7 +63,7 @@ all = ["fastapi", "quart", "aiohttp", "httpx", "websockets", "uvicorn"]
[tool.pytest.ini_options]
asyncio_mode = "auto"
addopts = "--cov=nonebot --cov-report=term-missing"
addopts = "--cov=nonebot --cov-append --cov-report=term-missing"
filterwarnings = [
"error",
"ignore::DeprecationWarning",

View File

@ -1,21 +1,30 @@
import os
from pathlib import Path
from typing import TYPE_CHECKING, Set
import pytest
from nonebug import NONEBOT_INIT_KWARGS
import nonebot
os.environ["CONFIG_FROM_ENV"] = '{"test": "test"}'
os.environ["CONFIG_OVERRIDE"] = "new"
if TYPE_CHECKING:
from nonebot.plugin import Plugin
@pytest.fixture
def load_plugin(nonebug_init: None) -> Set["Plugin"]:
import nonebot
def pytest_configure(config: pytest.Config) -> None:
config.stash[NONEBOT_INIT_KWARGS] = {"config_from_init": "init"}
@pytest.fixture(scope="session", autouse=True)
def load_plugin(nonebug_init: None) -> Set["Plugin"]:
# preload global plugins
return nonebot.load_plugins(str(Path(__file__).parent / "plugins"))
@pytest.fixture
@pytest.fixture(scope="session", autouse=True)
def load_example(nonebug_init: None) -> Set["Plugin"]:
import nonebot
# preload example plugins
return nonebot.load_plugins(str(Path(__file__).parent / "examples"))

0
tests/dynamic/manager.py Normal file
View File

0
tests/dynamic/path.py Normal file
View File

View File

View File

0
tests/dynamic/simple.py Normal file
View File

View File

@ -1,4 +1,4 @@
{
"plugins": [],
"plugin_dirs": ["plugins"]
"plugin_dirs": []
}

View File

@ -1,3 +1,3 @@
[tool.nonebot]
plugins = []
plugin_dirs = ["plugins"]
plugin_dirs = []

View File

@ -101,7 +101,7 @@ def test_message_getitem():
assert message[0] == MessageSegment.text("test")
assert message[0:2] == Message(
assert message[:2] == Message(
[MessageSegment.text("test"), MessageSegment.image("test2")]
)

View File

@ -1,9 +1,8 @@
from nonebot.adapters import MessageTemplate
from utils import escape_text, make_fake_message
def test_template_basis():
from nonebot.adapters import MessageTemplate
template = MessageTemplate("{key:.3%}")
formatted = template.format(key=0.123456789)
assert formatted == "12.346%"

View File

@ -5,30 +5,45 @@ from typing import cast
import pytest
from nonebug import App
import nonebot
from nonebot.config import Env
from nonebot import _resolve_combine_expr
from nonebot.exception import WebSocketClosed
from nonebot.drivers import (
URL,
Driver,
Request,
Response,
WebSocket,
ForwardDriver,
ReverseDriver,
HTTPServerSetup,
WebSocketServerSetup,
)
@pytest.fixture(name="driver")
def load_driver(request: pytest.FixtureRequest) -> Driver:
driver_name = getattr(request, "param", None)
global_driver = nonebot.get_driver()
if driver_name is None:
return global_driver
DriverClass = _resolve_combine_expr(driver_name)
return DriverClass(Env(environment=global_driver.env), global_driver.config)
@pytest.mark.asyncio
@pytest.mark.parametrize(
"nonebug_init",
"driver",
[
pytest.param({"driver": "nonebot.drivers.fastapi:Driver"}, id="fastapi"),
pytest.param({"driver": "nonebot.drivers.quart:Driver"}, id="quart"),
pytest.param("nonebot.drivers.fastapi:Driver", id="fastapi"),
pytest.param("nonebot.drivers.quart:Driver", id="quart"),
],
indirect=True,
)
async def test_reverse_driver(app: App):
import nonebot
from nonebot.exception import WebSocketClosed
from nonebot.drivers import (
URL,
Request,
Response,
WebSocket,
ReverseDriver,
HTTPServerSetup,
WebSocketServerSetup,
)
driver = cast(ReverseDriver, nonebot.get_driver())
async def test_reverse_driver(app: App, driver: Driver):
driver = cast(ReverseDriver, driver)
async def _handle_http(request: Request) -> Response:
assert request.content in (b"test", "test")
@ -61,7 +76,7 @@ async def test_reverse_driver(app: App):
ws_setup = WebSocketServerSetup(URL("/ws_test"), "ws_test", _handle_ws)
driver.setup_websocket_server(ws_setup)
async with app.test_server() as ctx:
async with app.test_server(driver.asgi) as ctx:
client = ctx.get_client()
response = await client.post("/http_test", data="test")
assert response.status_code == 200
@ -86,18 +101,15 @@ async def test_reverse_driver(app: App):
@pytest.mark.asyncio
@pytest.mark.parametrize(
"nonebug_init",
"driver",
[
pytest.param({"driver": "nonebot.drivers.httpx:Driver"}, id="httpx"),
pytest.param({"driver": "nonebot.drivers.aiohttp:Driver"}, id="aiohttp"),
pytest.param("nonebot.drivers.httpx:Driver", id="httpx"),
pytest.param("nonebot.drivers.aiohttp:Driver", id="aiohttp"),
],
indirect=True,
)
async def test_http_driver(app: App):
import nonebot
from nonebot.drivers import Request, ForwardDriver
driver = cast(ForwardDriver, nonebot.get_driver())
async def test_http_driver(driver: Driver):
driver = cast(ForwardDriver, driver)
request = Request(
"POST",
@ -140,23 +152,20 @@ async def test_http_driver(app: App):
@pytest.mark.asyncio
@pytest.mark.parametrize(
"nonebug_init, driver_type",
"driver, driver_type",
[
pytest.param(
{"driver": "nonebot.drivers.fastapi:Driver+nonebot.drivers.aiohttp:Mixin"},
"nonebot.drivers.fastapi:Driver+nonebot.drivers.aiohttp:Mixin",
"fastapi+aiohttp",
id="fastapi+aiohttp",
),
pytest.param(
{"driver": "~httpx:Driver+~websockets"},
"~httpx:Driver+~websockets",
"none+httpx+websockets",
id="httpx+websockets",
),
],
indirect=["nonebug_init"],
indirect=["driver"],
)
async def test_combine_driver(app: App, driver_type: str):
import nonebot
driver = nonebot.get_driver()
async def test_combine_driver(driver: Driver, driver_type: str):
assert driver.type == driver_type

View File

@ -1,13 +1,15 @@
import pytest
from nonebug import App
from utils import make_fake_event, make_fake_message
@pytest.mark.asyncio
async def test_weather(app: App, load_example):
async def test_weather(app: App):
from examples.weather import weather
from utils import make_fake_event, make_fake_message
# 将此处的 make_fake_message() 替换为你要发送的平台消息 Message 类型
# from nonebot.adapters.console import Message
Message = make_fake_message()
async with app.test_matcher(weather) as ctx:
@ -15,6 +17,8 @@ async def test_weather(app: App, load_example):
msg = Message("/天气 上海")
# 将此处的 make_fake_event() 替换为你要发送的平台事件 Event 类型
# from nonebot.adapters.console import MessageEvent
# event = MessageEvent(message=msg, to_me=True, ...)
event = make_fake_event(_message=msg, _to_me=True)()
ctx.receive_event(bot, event)

View File

@ -1,30 +1,16 @@
import os
import pytest
os.environ["CONFIG_FROM_ENV"] = '{"test": "test"}'
os.environ["CONFIG_OVERRIDE"] = "new"
import nonebot
from nonebot.drivers import ReverseDriver
from nonebot import get_app, get_bot, get_asgi, get_bots, get_driver
@pytest.mark.asyncio
@pytest.mark.parametrize(
"nonebug_init",
[
{
"config_from_init": "init",
"driver": "~fastapi+~httpx+~websockets",
},
{"config_from_init": "init", "driver": "~fastapi+~aiohttp"},
],
indirect=True,
)
async def test_init(nonebug_init):
from nonebot import get_driver
env = get_driver().env
async def test_init():
env = nonebot.get_driver().env
assert env == "test"
config = get_driver().config
config = nonebot.get_driver().config
assert config.config_from_env == {"test": "test"}
assert config.config_override == "new"
assert config.config_from_init == "init"
@ -36,15 +22,11 @@ async def test_init(nonebug_init):
@pytest.mark.asyncio
async def test_get(monkeypatch: pytest.MonkeyPatch, nonebug_clear):
import nonebot
from nonebot.drivers import ForwardDriver, ReverseDriver
from nonebot import get_app, get_bot, get_asgi, get_bots, get_driver
with pytest.raises(ValueError):
get_driver()
nonebot.init(driver="nonebot.drivers.fastapi")
async def test_get(monkeypatch: pytest.MonkeyPatch):
with monkeypatch.context() as m:
m.setattr(nonebot, "_driver", None)
with pytest.raises(ValueError):
get_driver()
driver = get_driver()
assert isinstance(driver, ReverseDriver)

View File

@ -1,11 +1,14 @@
import pytest
from nonebug import App
from nonebot.permission import User
from nonebot.message import _check_matcher
from nonebot.matcher import Matcher, matchers
from utils import make_fake_event, make_fake_message
@pytest.mark.asyncio
async def test_matcher(app: App, load_plugin):
async def test_matcher(app: App):
from plugins.matcher.matcher_process import (
test_got,
test_handle,
@ -77,7 +80,7 @@ async def test_matcher(app: App, load_plugin):
@pytest.mark.asyncio
async def test_type_updater(app: App, load_plugin):
async def test_type_updater(app: App):
from plugins.matcher.matcher_type import test_type_updater, test_custom_updater
event = make_fake_event()()
@ -98,8 +101,7 @@ async def test_type_updater(app: App, load_plugin):
@pytest.mark.asyncio
async def test_permission_updater(app: App, load_plugin):
from nonebot.permission import User
async def test_permission_updater(app: App):
from plugins.matcher.matcher_permission import (
default_permission,
test_custom_updater,
@ -143,40 +145,37 @@ async def test_permission_updater(app: App, load_plugin):
@pytest.mark.asyncio
async def test_run(app: App):
from nonebot.matcher import Matcher, matchers
with app.provider.context({}):
assert not matchers
event = make_fake_event()()
assert not matchers
event = make_fake_event()()
async def reject():
await Matcher.reject()
async def reject():
await Matcher.reject()
test_reject = Matcher.new(handlers=[reject])
test_reject = Matcher.new(handlers=[reject])
async with app.test_api() as ctx:
bot = ctx.create_bot()
await test_reject().run(bot, event, {})
assert len(matchers[0]) == 1
assert len(matchers[0][0].handlers) == 1
async with app.test_api() as ctx:
bot = ctx.create_bot()
await test_reject().run(bot, event, {})
assert len(matchers[0]) == 1
assert len(matchers[0][0].handlers) == 1
del matchers[0]
del matchers[0]
async def pause():
await Matcher.pause()
async def pause():
await Matcher.pause()
test_pause = Matcher.new(handlers=[pause])
test_pause = Matcher.new(handlers=[pause])
async with app.test_api() as ctx:
bot = ctx.create_bot()
await test_pause().run(bot, event, {})
assert len(matchers[0]) == 1
assert len(matchers[0][0].handlers) == 0
async with app.test_api() as ctx:
bot = ctx.create_bot()
await test_pause().run(bot, event, {})
assert len(matchers[0]) == 1
assert len(matchers[0][0].handlers) == 0
@pytest.mark.asyncio
async def test_expire(app: App, load_plugin):
from nonebot.matcher import matchers
from nonebot.message import _check_matcher
async def test_expire(app: App):
from plugins.matcher.matcher_expire import (
test_temp_matcher,
test_datetime_matcher,

View File

@ -1,11 +1,14 @@
import pytest
from nonebug import App
from nonebot.matcher import DEFAULT_PROVIDER_CLASS, matchers
@pytest.mark.asyncio
async def test_manager(app: App, load_plugin):
from nonebot.matcher import DEFAULT_PROVIDER_CLASS, matchers
default_provider = matchers.provider
matchers.set_provider(DEFAULT_PROVIDER_CLASS)
assert matchers.provider == default_provider
async def test_manager(app: App):
try:
default_provider = matchers.provider
matchers.set_provider(DEFAULT_PROVIDER_CLASS)
assert default_provider == matchers.provider
finally:
matchers.provider = app.provider

View File

@ -1,12 +1,40 @@
import pytest
from nonebug import App
from nonebot.matcher import Matcher
from nonebot.exception import TypeMisMatch
from utils import make_fake_event, make_fake_message
from nonebot.params import (
ArgParam,
BotParam,
EventParam,
StateParam,
DependParam,
DefaultParam,
MatcherParam,
ExceptionParam,
)
from nonebot.consts import (
CMD_KEY,
REGEX_STR,
PREFIX_KEY,
REGEX_DICT,
SHELL_ARGS,
SHELL_ARGV,
CMD_ARG_KEY,
KEYWORD_KEY,
RAW_CMD_KEY,
REGEX_GROUP,
ENDSWITH_KEY,
CMD_START_KEY,
FULLMATCH_KEY,
REGEX_MATCHED,
STARTSWITH_KEY,
)
@pytest.mark.asyncio
async def test_depend(app: App, load_plugin):
from nonebot.params import DependParam
async def test_depend(app: App):
from plugins.param.param_depend import (
ClassDependency,
runned,
@ -29,14 +57,14 @@ async def test_depend(app: App, load_plugin):
assert len(runned) == 2 and runned[0] == runned[1] == 1
runned.clear()
async with app.test_dependent(class_depend, allow_types=[DependParam]) as ctx:
ctx.should_return(ClassDependency(x=1, y=2))
@pytest.mark.asyncio
async def test_bot(app: App, load_plugin):
from nonebot.params import BotParam
from nonebot.exception import TypeMisMatch
async def test_bot(app: App):
from plugins.param.param_bot import (
FooBot,
get_bot,
@ -82,9 +110,7 @@ async def test_bot(app: App, load_plugin):
@pytest.mark.asyncio
async def test_event(app: App, load_plugin):
from nonebot.exception import TypeMisMatch
from nonebot.params import EventParam, DependParam
async def test_event(app: App):
from plugins.param.param_event import (
FooEvent,
event,
@ -159,25 +185,7 @@ async def test_event(app: App, load_plugin):
@pytest.mark.asyncio
async def test_state(app: App, load_plugin):
from nonebot.params import StateParam, DependParam
from nonebot.consts import (
CMD_KEY,
REGEX_STR,
PREFIX_KEY,
REGEX_DICT,
SHELL_ARGS,
SHELL_ARGV,
CMD_ARG_KEY,
KEYWORD_KEY,
RAW_CMD_KEY,
REGEX_GROUP,
ENDSWITH_KEY,
CMD_START_KEY,
FULLMATCH_KEY,
REGEX_MATCHED,
STARTSWITH_KEY,
)
async def test_state(app: App):
from plugins.param.param_state import (
state,
command,
@ -318,9 +326,7 @@ async def test_state(app: App, load_plugin):
@pytest.mark.asyncio
async def test_matcher(app: App, load_plugin):
from nonebot.matcher import Matcher
from nonebot.params import DependParam, MatcherParam
async def test_matcher(app: App):
from plugins.param.param_matcher import matcher, receive, last_receive
fake_matcher = Matcher()
@ -348,9 +354,7 @@ async def test_matcher(app: App, load_plugin):
@pytest.mark.asyncio
async def test_arg(app: App, load_plugin):
from nonebot.matcher import Matcher
from nonebot.params import ArgParam
async def test_arg(app: App):
from plugins.param.param_arg import arg, arg_str, arg_plain_text
matcher = Matcher()
@ -371,8 +375,7 @@ async def test_arg(app: App, load_plugin):
@pytest.mark.asyncio
async def test_exception(app: App, load_plugin):
from nonebot.params import ExceptionParam
async def test_exception(app: App):
from plugins.param.param_exception import exc
exception = ValueError("test")
@ -382,8 +385,7 @@ async def test_exception(app: App, load_plugin):
@pytest.mark.asyncio
async def test_default(app: App, load_plugin):
from nonebot.params import DefaultParam
async def test_default(app: App):
from plugins.param.param_default import default
async with app.test_dependent(default, allow_types=[DefaultParam]) as ctx:

View File

@ -4,13 +4,26 @@ import pytest
from nonebug import App
from utils import make_fake_event
from nonebot.exception import SkippedException
from nonebot.permission import (
USER,
NOTICE,
MESSAGE,
REQUEST,
METAEVENT,
SUPERUSER,
User,
Notice,
Message,
Request,
MetaEvent,
SuperUser,
Permission,
)
@pytest.mark.asyncio
async def test_permission(app: App):
from nonebot.permission import Permission
from nonebot.exception import SkippedException
async def falsy():
return False
@ -42,20 +55,8 @@ async def test_permission(app: App):
@pytest.mark.asyncio
@pytest.mark.parametrize(
"type,expected",
[
("message", True),
("notice", False),
],
)
async def test_message(
app: App,
type: str,
expected: bool,
):
from nonebot.permission import MESSAGE, Message
@pytest.mark.parametrize("type, expected", [("message", True), ("notice", False)])
async def test_message(type: str, expected: bool):
dependent = list(MESSAGE.checkers)[0]
checker = dependent.call
@ -66,20 +67,8 @@ async def test_message(
@pytest.mark.asyncio
@pytest.mark.parametrize(
"type,expected",
[
("message", False),
("notice", True),
],
)
async def test_notice(
app: App,
type: str,
expected: bool,
):
from nonebot.permission import NOTICE, Notice
@pytest.mark.parametrize("type, expected", [("message", False), ("notice", True)])
async def test_notice(type: str, expected: bool):
dependent = list(NOTICE.checkers)[0]
checker = dependent.call
@ -90,20 +79,8 @@ async def test_notice(
@pytest.mark.asyncio
@pytest.mark.parametrize(
"type,expected",
[
("message", False),
("request", True),
],
)
async def test_request(
app: App,
type: str,
expected: bool,
):
from nonebot.permission import REQUEST, Request
@pytest.mark.parametrize("type, expected", [("message", False), ("request", True)])
async def test_request(type: str, expected: bool):
dependent = list(REQUEST.checkers)[0]
checker = dependent.call
@ -114,20 +91,8 @@ async def test_request(
@pytest.mark.asyncio
@pytest.mark.parametrize(
"type,expected",
[
("message", False),
("meta_event", True),
],
)
async def test_metaevent(
app: App,
type: str,
expected: bool,
):
from nonebot.permission import METAEVENT, MetaEvent
@pytest.mark.parametrize("type, expected", [("message", False), ("meta_event", True)])
async def test_metaevent(type: str, expected: bool):
dependent = list(METAEVENT.checkers)[0]
checker = dependent.call
@ -139,7 +104,7 @@ async def test_metaevent(
@pytest.mark.asyncio
@pytest.mark.parametrize(
"type,user_id,expected",
"type, user_id, expected",
[
("message", "test", True),
("message", "foo", False),
@ -148,14 +113,7 @@ async def test_metaevent(
("notice", "test", True),
],
)
async def test_superuser(
app: App,
type: str,
user_id: str,
expected: bool,
):
from nonebot.permission import SUPERUSER, SuperUser
async def test_superuser(app: App, type: str, user_id: str, expected: bool):
dependent = list(SUPERUSER.checkers)[0]
checker = dependent.call
@ -170,7 +128,7 @@ async def test_superuser(
@pytest.mark.asyncio
@pytest.mark.parametrize(
"session_ids,session_id,expected",
"session_ids, session_id, expected",
[
(("user", "foo"), "user", True),
(("user", "foo"), "bar", False),
@ -180,8 +138,6 @@ async def test_superuser(
async def test_user(
app: App, session_ids: Tuple[str, ...], session_id: Optional[str], expected: bool
):
from nonebot.permission import USER, User
dependent = list(USER(*session_ids).checkers)[0]
checker = dependent.call

View File

@ -1,16 +1,11 @@
from typing import TYPE_CHECKING, Set
import pytest
from nonebug import App
if TYPE_CHECKING:
from nonebot.plugin import Plugin
import nonebot
from nonebot.plugin import PluginManager, _managers
@pytest.mark.asyncio
async def test_get_plugin(app: App, load_plugin: Set["Plugin"]):
import nonebot
async def test_get_plugin():
# check simple plugin
plugin = nonebot.get_plugin("export")
assert plugin
@ -28,12 +23,15 @@ async def test_get_plugin(app: App, load_plugin: Set["Plugin"]):
@pytest.mark.asyncio
async def test_get_available_plugin(app: App):
import nonebot
from nonebot.plugin import PluginManager, _managers
async def test_get_available_plugin():
old_managers = _managers.copy()
_managers.clear()
try:
_managers.append(PluginManager(["plugins.export", "plugin.require"]))
_managers.append(PluginManager(["plugins.export", "plugin.require"]))
# check get available plugins
plugin_names = nonebot.get_available_plugin_names()
assert plugin_names == {"export", "require"}
# check get available plugins
plugin_names = nonebot.get_available_plugin_names()
assert plugin_names == {"export", "require"}
finally:
_managers.clear()
_managers.extend(old_managers)

View File

@ -1,38 +1,32 @@
import sys
from typing import Set
from pathlib import Path
from dataclasses import asdict
from typing import TYPE_CHECKING, Set
import pytest
from nonebug import App
if TYPE_CHECKING:
from nonebot.plugin import Plugin
import nonebot
from nonebot.plugin import Plugin, PluginManager, _managers
@pytest.mark.asyncio
async def test_load_plugin(app: App):
import nonebot
async def test_load_plugin():
# check regular
assert nonebot.load_plugin("plugins.metadata")
assert nonebot.load_plugin("dynamic.simple")
# check path
assert nonebot.load_plugin(Path("plugins/export"))
assert nonebot.load_plugin(Path("dynamic/path.py"))
# check not found
assert nonebot.load_plugin("some_plugin_not_exist") is None
@pytest.mark.asyncio
async def test_load_plugins(app: App, load_plugin: Set["Plugin"]):
import nonebot
from nonebot.plugin import PluginManager
async def test_load_plugins(load_plugin: Set[Plugin], load_example: Set[Plugin]):
loaded_plugins = {
plugin for plugin in nonebot.get_loaded_plugins() if not plugin.parent_plugin
}
assert loaded_plugins == load_plugin
assert loaded_plugins >= load_plugin | load_example
# check simple plugin
assert "plugins.export" in sys.modules
@ -51,9 +45,7 @@ async def test_load_plugins(app: App, load_plugin: Set["Plugin"]):
@pytest.mark.asyncio
async def test_load_nested_plugin(app: App, load_plugin: Set["Plugin"]):
import nonebot
async def test_load_nested_plugin():
parent_plugin = nonebot.get_plugin("nested")
sub_plugin = nonebot.get_plugin("nested_subplugin")
sub_plugin2 = nonebot.get_plugin("nested_subplugin2")
@ -64,9 +56,7 @@ async def test_load_nested_plugin(app: App, load_plugin: Set["Plugin"]):
@pytest.mark.asyncio
async def test_load_json(app: App):
import nonebot
async def test_load_json():
nonebot.load_from_json("./plugins.json")
with pytest.raises(TypeError):
@ -74,9 +64,7 @@ async def test_load_json(app: App):
@pytest.mark.asyncio
async def test_load_toml(app: App):
import nonebot
async def test_load_toml():
nonebot.load_from_toml("./plugins.toml")
with pytest.raises(ValueError):
@ -87,35 +75,27 @@ async def test_load_toml(app: App):
@pytest.mark.asyncio
async def test_bad_plugin(app: App):
import nonebot
async def test_bad_plugin():
nonebot.load_plugins("bad_plugins")
assert nonebot.get_plugin("bad_plugins") is None
@pytest.mark.asyncio
async def test_require_loaded(app: App, monkeypatch: pytest.MonkeyPatch):
import nonebot
async def test_require_loaded(monkeypatch: pytest.MonkeyPatch):
def _patched_find(name: str):
assert False
pytest.fail("require existing plugin should not call find_manager_by_name")
monkeypatch.setattr("nonebot.plugin.load._find_manager_by_name", _patched_find)
nonebot.load_plugin("plugins.export")
nonebot.require("plugins.export")
@pytest.mark.asyncio
async def test_require_not_loaded(app: App, monkeypatch: pytest.MonkeyPatch):
import nonebot
from nonebot.plugin import PluginManager, _managers
m = PluginManager(["plugins.export"])
async def test_require_not_loaded(monkeypatch: pytest.MonkeyPatch):
m = PluginManager(["dynamic.require_not_loaded"])
_managers.append(m)
num_managers = len(_managers)
origin_load = PluginManager.load_plugin
@ -125,33 +105,29 @@ async def test_require_not_loaded(app: App, monkeypatch: pytest.MonkeyPatch):
monkeypatch.setattr(PluginManager, "load_plugin", _patched_load)
nonebot.require("plugins.export")
nonebot.require("dynamic.require_not_loaded")
assert len(_managers) == 1
assert len(_managers) == num_managers
@pytest.mark.asyncio
async def test_require_not_declared(app: App):
import nonebot
from nonebot.plugin import _managers
async def test_require_not_declared():
num_managers = len(_managers)
nonebot.require("plugins.export")
nonebot.require("dynamic.require_not_declared")
assert len(_managers) == 1
assert _managers[-1].plugins == {"plugins.export"}
assert len(_managers) == num_managers + 1
assert _managers[-1].plugins == {"dynamic.require_not_declared"}
@pytest.mark.asyncio
async def test_require_not_found(app: App):
import nonebot
async def test_require_not_found():
with pytest.raises(RuntimeError):
nonebot.require("some_plugin_not_exist")
@pytest.mark.asyncio
async def test_plugin_metadata(app: App, load_plugin: Set["Plugin"]):
import nonebot
async def test_plugin_metadata():
from plugins.metadata import Config
plugin = nonebot.get_plugin("metadata")

View File

@ -1,12 +1,11 @@
import pytest
from nonebug import App
from nonebot.plugin import PluginManager
@pytest.mark.asyncio
async def test_load_plugin_name(app: App):
from nonebot.plugin import PluginManager
m = PluginManager(plugins=["plugins.export"])
module1 = m.load_plugin("export")
module2 = m.load_plugin("plugins.export")
async def test_load_plugin_name():
m = PluginManager(plugins=["dynamic.manager"])
module1 = m.load_plugin("manager")
module2 = m.load_plugin("dynamic.manager")
assert module1 is module2

View File

@ -1,25 +1,25 @@
from typing import Type, Optional
import pytest
from nonebug import App
import nonebot
from nonebot.typing import T_RuleChecker
from nonebot.matcher import Matcher, matchers
from nonebot.rule import (
RegexRule,
IsTypeRule,
CommandRule,
EndswithRule,
KeywordsRule,
FullmatchRule,
StartswithRule,
ShellCommandRule,
)
@pytest.mark.asyncio
async def test_on(app: App, load_plugin):
import nonebot
async def test_on():
import plugins.plugin.matchers as module
from nonebot.typing import T_RuleChecker
from nonebot.matcher import Matcher, matchers
from nonebot.rule import (
RegexRule,
IsTypeRule,
CommandRule,
EndswithRule,
KeywordsRule,
FullmatchRule,
StartswithRule,
ShellCommandRule,
)
from plugins.plugin.matchers import (
TestEvent,
rule,

View File

@ -4,14 +4,51 @@ from typing import Dict, Tuple, Union, Optional
import pytest
from nonebug import App
from nonebot.typing import T_State
from utils import make_fake_event, make_fake_message
from nonebot.exception import ParserExit, SkippedException
from nonebot.consts import (
CMD_KEY,
REGEX_STR,
PREFIX_KEY,
REGEX_DICT,
SHELL_ARGS,
SHELL_ARGV,
CMD_ARG_KEY,
KEYWORD_KEY,
REGEX_GROUP,
ENDSWITH_KEY,
FULLMATCH_KEY,
REGEX_MATCHED,
STARTSWITH_KEY,
)
from nonebot.rule import (
Rule,
ToMeRule,
Namespace,
RegexRule,
IsTypeRule,
CommandRule,
EndswithRule,
KeywordsRule,
FullmatchRule,
ArgumentParser,
StartswithRule,
ShellCommandRule,
regex,
to_me,
command,
is_type,
keyword,
endswith,
fullmatch,
startswith,
shell_command,
)
@pytest.mark.asyncio
async def test_rule(app: App):
from nonebot.rule import Rule
from nonebot.exception import SkippedException
async def falsy():
return False
@ -44,7 +81,7 @@ async def test_rule(app: App):
@pytest.mark.asyncio
@pytest.mark.parametrize(
"msg,ignorecase,type,text,expected",
"msg, ignorecase, type, text, expected",
[
("prefix", False, "message", "prefix_", True),
("prefix", False, "message", "Prefix_", False),
@ -58,16 +95,12 @@ async def test_rule(app: App):
],
)
async def test_startswith(
app: App,
msg: Union[str, Tuple[str, ...]],
ignorecase: bool,
type: str,
text: Optional[str],
expected: bool,
):
from nonebot.consts import STARTSWITH_KEY
from nonebot.rule import StartswithRule, startswith
test_startswith = startswith(msg, ignorecase)
dependent = list(test_startswith.checkers)[0]
checker = dependent.call
@ -87,7 +120,7 @@ async def test_startswith(
@pytest.mark.asyncio
@pytest.mark.parametrize(
"msg,ignorecase,type,text,expected",
"msg, ignorecase, type, text, expected",
[
("suffix", False, "message", "_suffix", True),
("suffix", False, "message", "_Suffix", False),
@ -101,16 +134,12 @@ async def test_startswith(
],
)
async def test_endswith(
app: App,
msg: Union[str, Tuple[str, ...]],
ignorecase: bool,
type: str,
text: Optional[str],
expected: bool,
):
from nonebot.consts import ENDSWITH_KEY
from nonebot.rule import EndswithRule, endswith
test_endswith = endswith(msg, ignorecase)
dependent = list(test_endswith.checkers)[0]
checker = dependent.call
@ -130,7 +159,7 @@ async def test_endswith(
@pytest.mark.asyncio
@pytest.mark.parametrize(
"msg,ignorecase,type,text,expected",
"msg, ignorecase, type, text, expected",
[
("fullmatch", False, "message", "fullmatch", True),
("fullmatch", False, "message", "Fullmatch", False),
@ -144,16 +173,12 @@ async def test_endswith(
],
)
async def test_fullmatch(
app: App,
msg: Union[str, Tuple[str, ...]],
ignorecase: bool,
type: str,
text: Optional[str],
expected: bool,
):
from nonebot.consts import FULLMATCH_KEY
from nonebot.rule import FullmatchRule, fullmatch
test_fullmatch = fullmatch(msg, ignorecase)
dependent = list(test_fullmatch.checkers)[0]
checker = dependent.call
@ -173,7 +198,7 @@ async def test_fullmatch(
@pytest.mark.asyncio
@pytest.mark.parametrize(
"kws,type,text,expected",
"kws, type, text, expected",
[
(("key",), "message", "_key_", True),
(("key", "foo"), "message", "_foo_", True),
@ -183,15 +208,11 @@ async def test_fullmatch(
],
)
async def test_keyword(
app: App,
kws: Tuple[str, ...],
type: str,
text: Optional[str],
expected: bool,
):
from nonebot.consts import KEYWORD_KEY
from nonebot.rule import KeywordsRule, keyword
test_keyword = keyword(*kws)
dependent = list(test_keyword.checkers)[0]
checker = dependent.call
@ -210,10 +231,7 @@ async def test_keyword(
@pytest.mark.parametrize(
"cmds", [(("help",),), (("help", "foo"),), (("help",), ("foo",))]
)
async def test_command(app: App, cmds: Tuple[Tuple[str, ...]]):
from nonebot.rule import CommandRule, command
from nonebot.consts import CMD_KEY, PREFIX_KEY
async def test_command(cmds: Tuple[Tuple[str, ...]]):
test_command = command(*cmds)
dependent = list(test_command.checkers)[0]
checker = dependent.call
@ -227,12 +245,7 @@ async def test_command(app: App, cmds: Tuple[Tuple[str, ...]]):
@pytest.mark.asyncio
async def test_shell_command(app: App):
from nonebot.typing import T_State
from nonebot.exception import ParserExit
from nonebot.consts import CMD_KEY, PREFIX_KEY, SHELL_ARGS, SHELL_ARGV, CMD_ARG_KEY
from nonebot.rule import Namespace, ArgumentParser, ShellCommandRule, shell_command
async def test_shell_command():
state: T_State
CMD = ("test",)
Message = make_fake_message()
@ -328,7 +341,7 @@ async def test_shell_command(app: App):
@pytest.mark.asyncio
@pytest.mark.parametrize(
"pattern,type,text,expected,matched,string,group,dict",
"pattern, type, text, expected, matched, string, group, dict",
[
(
r"(?P<key>key\d)",
@ -345,7 +358,6 @@ async def test_shell_command(app: App):
],
)
async def test_regex(
app: App,
pattern: str,
type: str,
text: Optional[str],
@ -355,10 +367,6 @@ async def test_regex(
group: Optional[Tuple[str, ...]],
dict: Optional[Dict[str, str]],
):
from nonebot.typing import T_State
from nonebot.rule import RegexRule, regex
from nonebot.consts import REGEX_STR, REGEX_DICT, REGEX_GROUP, REGEX_MATCHED
test_regex = regex(pattern)
dependent = list(test_regex.checkers)[0]
checker = dependent.call
@ -378,9 +386,7 @@ async def test_regex(
@pytest.mark.asyncio
@pytest.mark.parametrize("expected", [True, False])
async def test_to_me(app: App, expected: bool):
from nonebot.rule import ToMeRule, to_me
async def test_to_me(expected: bool):
test_to_me = to_me()
dependent = list(test_to_me.checkers)[0]
checker = dependent.call
@ -392,9 +398,7 @@ async def test_to_me(app: App, expected: bool):
@pytest.mark.asyncio
async def test_is_type(app: App):
from nonebot.rule import IsTypeRule, is_type
async def test_is_type():
Event1 = make_fake_event()
Event2 = make_fake_event()
Event3 = make_fake_event()

View File

@ -1,11 +1,10 @@
import json
from utils import make_fake_message
from nonebot.utils import DataclassEncoder
def test_dataclass_encoder():
from nonebot.utils import DataclassEncoder
simple = json.dumps("123", cls=DataclassEncoder)
assert simple == '"123"'

View File

@ -1,9 +1,8 @@
from typing import TYPE_CHECKING, Type, Union, Mapping, Iterable, Optional
from typing import Type, Union, Mapping, Iterable, Optional
from pydantic import create_model
if TYPE_CHECKING:
from nonebot.adapters import Event, Message
from nonebot.adapters import Event, Message, MessageSegment
def escape_text(s: str, *, escape_comma: bool = True) -> str:
@ -14,8 +13,6 @@ def escape_text(s: str, *, escape_comma: bool = True) -> str:
def make_fake_message():
from nonebot.adapters import Message, MessageSegment
class FakeMessageSegment(MessageSegment):
@classmethod
def get_message_class(cls):
@ -61,18 +58,16 @@ def make_fake_message():
def make_fake_event(
_base: Optional[Type["Event"]] = None,
_base: Optional[Type[Event]] = None,
_type: str = "message",
_name: str = "test",
_description: str = "test",
_user_id: Optional[str] = "test",
_session_id: Optional[str] = "test",
_message: Optional["Message"] = None,
_message: Optional[Message] = None,
_to_me: bool = True,
**fields,
) -> Type["Event"]:
from nonebot.adapters import Event
) -> Type[Event]:
_Fake = create_model("_Fake", __base__=_base or Event, **fields)
class FakeEvent(_Fake):