fixed # 修复astrbot日志未使用logging.basicConfig

This commit is contained in:
2026-10-27 11:40:59 +08:00
parent b3455ded58
commit d05b2e5765
20 changed files with 333 additions and 46 deletions
+42
View File
@@ -102,3 +102,45 @@ class BundleTests(unittest.TestCase):
with patch.object(bundle, 'urlopen', return_value=response):
with self.assertRaises(ValueError):
bundle._copy_remote('https://farm.example/a', BytesIO(), 10)
def test_download_reports_throttled_progress_without_urls(self):
class Response(BytesIO):
headers = {'Content-Length': '262144'}
def geturl(self):
return 'https://farm.example/private/assets.zip'
with patch.object(bundle, 'urlopen', return_value=Response(
b'x' * 262144)), patch.object(
bundle.time, 'monotonic',
side_effect=[0, 0.2, 1.1, 1.2, 2.2]), \
self.assertLogs(bundle.logger, level='INFO') as captured:
bundle._copy_remote('https://farm.example/private/assets.zip',
BytesIO(), 262144)
messages = '\n'.join(captured.output)
self.assertIn('下载开始', messages)
self.assertIn('50.0%', messages)
self.assertIn('100.0%', messages)
self.assertIn('下载完成', messages)
self.assertEqual(messages.count('下载进度'), 2)
self.assertNotIn('farm.example', messages)
self.assertNotIn('private', messages)
def test_unknown_length_and_failed_download_report_truthfully(self):
class Response(BytesIO):
headers = {}
def geturl(self):
return 'https://farm.example/a'
with patch.object(bundle, 'urlopen', return_value=Response(b'abc')), \
patch.object(bundle.time, 'monotonic', side_effect=[0, 1.1]), \
self.assertLogs(bundle.logger, level='INFO') as captured:
bundle._copy_remote('https://farm.example/a', BytesIO(), 10)
self.assertIn('下载完成', '\n'.join(captured.output))
self.assertIn('已下载', '\n'.join(captured.output))
self.assertNotIn('%', '\n'.join(captured.output))
with patch.object(bundle, 'urlopen', side_effect=OSError('secret')), \
self.assertLogs(bundle.logger, level='INFO') as captured:
with self.assertRaises(OSError):
bundle._copy_remote('https://farm.example/a', BytesIO(), 10)
messages = '\n'.join(captured.output)
self.assertIn('下载失败', messages)
self.assertNotIn('下载完成', messages)
self.assertNotIn('secret', messages)
+8 -1
View File
@@ -1,8 +1,10 @@
import asyncio
import importlib.util
from pathlib import Path
import sys
from types import ModuleType
import unittest
from unittest.mock import AsyncMock, patch
from unittest.mock import AsyncMock, Mock, patch
ROOT = Path(__file__).resolve().parents[1]
@@ -10,6 +12,11 @@ ROOT = Path(__file__).resolve().parents[1]
class AvatarTests(unittest.IsolatedAsyncioTestCase):
def setUp(self):
api = ModuleType('astrbot.api')
api.logger = Mock()
host = patch.dict(sys.modules, {'astrbot.api': api})
host.start()
self.addCleanup(host.stop)
spec = importlib.util.spec_from_file_location(
'farm_astrbot_avatars',
ROOT / 'integrations/astrbot/avatars.py',
+38 -5
View File
@@ -7,7 +7,7 @@ import sys
import tempfile
from types import ModuleType, SimpleNamespace
import unittest
from unittest.mock import AsyncMock, patch
from unittest.mock import AsyncMock, Mock, patch
ROOT = Path(__file__).resolve().parents[1]
@@ -62,19 +62,43 @@ class AstrBotPackageTests(unittest.IsolatedAsyncioTestCase):
with patch('verify_astrbot.MAX_ARCHIVE_BYTES', 1):
with self.assertRaisesRegex(ValueError, '16 MiB'):
verify_package(first, marketplace=True)
self.assertEqual(files['runtime.py'], (
ROOT / 'integrations/shared/runtime.py'
).read_bytes())
source_tree = ast.parse((ROOT / 'integrations/shared/runtime.py')
.read_bytes())
built_tree = ast.parse(files['runtime.py'])
source_runtime = next(node for node in source_tree.body
if isinstance(node, ast.ClassDef))
built_runtime = next(node for node in built_tree.body
if isinstance(node, ast.ClassDef))
self.assertEqual(ast.dump(source_runtime), ast.dump(built_runtime))
self.assertFalse('_vendor/farm_core/__main__.py' in files)
for name, content in files.items():
self.assertNotIn('.git/', name)
self.assertNotIn('farm.sqlite3', name)
if name.endswith('.py'):
tree = ast.parse(content)
for node in ast.walk(tree):
if isinstance(node, ast.Import):
self.assertFalse(any(
alias.name.split('.')[0] == 'logging'
for alias in node.names
), name)
if isinstance(node, ast.ImportFrom):
self.assertFalse((node.module or '').startswith(
('nonebot', 'zhenxun'),
('nonebot', 'zhenxun', 'logging'),
))
for snippet in (b'\nimport logging\n',
b'\nlogger.setLevel(20)\n'):
invalid = dict(files)
invalid['main.py'] += snippet
invalid_manifest = json.loads(invalid['FARM-MANIFEST.json'])
invalid_manifest['files']['main.py'] = hashlib.sha256(
invalid['main.py'],
).hexdigest()
invalid['FARM-MANIFEST.json'] = json.dumps(
invalid_manifest,
).encode()
with self.assertRaisesRegex(ValueError, 'logging'):
verify_files(invalid)
files['main.py'] += b'\n# tampered\n'
with self.assertRaisesRegex(ValueError, 'checksum'):
verify_files(files)
@@ -91,6 +115,7 @@ class AstrBotPackageTests(unittest.IsolatedAsyncioTestCase):
package.__path__ = [str(self.root / 'host/repository')]
api = ModuleType('astrbot.api')
api.AstrBotConfig = dict
api.logger = Mock()
events = ModuleType('astrbot.api.event')
commands = {}
@@ -152,6 +177,14 @@ class AstrBotPackageTests(unittest.IsolatedAsyncioTestCase):
side_effect=OSError('offline fixture'),
):
await plugin.initialize()
self.assertIs(main.logger, api.logger)
self.assertIsNone(plugin.runtime.log_handler)
self.assertIsNone(plugin.runtime.console_handler)
self.assertFalse((plugin.runtime.data_dir /
'farm-preview.log').exists())
api.logger.info.assert_any_call(
'农场启动:正在检查作物版本和本地缓存',
)
self.assertEqual(set(commands), set(main.COMMANDS))
class Event:
+39
View File
@@ -84,6 +84,45 @@ class ZhenxunRuntimeTests(unittest.IsolatedAsyncioTestCase):
for action in ('seeds', 'crops'):
self.assertIsInstance(await self.call(action, '1'), bytes)
async def test_start_progress_reaches_console_and_file(self):
import logging
from contextlib import redirect_stderr
from io import StringIO
await self.runtime.close()
output = StringIO()
package_log = logging.getLogger(self.package)
with patch.object(logging.getLogger(), 'handlers', []), \
patch.object(package_log, 'handlers', []), \
redirect_stderr(output):
await self.runtime.start()
await self.runtime.close()
self.assertFalse(package_log.handlers)
self.assertIn('正在检查作物版本', output.getvalue())
self.assertIn('使用内置作物版本', output.getvalue())
self.assertIn('字体', output.getvalue())
self.assertIn('农场启动完成', output.getvalue())
saved = (self.runtime.data_dir / 'farm-preview.log').read_text(
encoding='utf-8',
)
self.assertIn('正在检查作物版本', saved)
async def test_start_reuses_existing_console_without_duplicate_output(self):
import logging
from io import StringIO
await self.runtime.close()
output = StringIO()
handler = logging.StreamHandler(output)
handler.setLevel(logging.INFO)
with patch.object(logging.getLogger(), 'handlers', [handler]):
await self.runtime.start()
await self.runtime.start()
self.assertIsNone(self.runtime.console_handler)
await self.runtime.close()
self.assertEqual(output.getvalue().count('正在检查作物版本'), 1)
self.assertEqual(output.getvalue().count('农场启动完成'), 1)
async def test_catalogue_is_loaded_once_before_store_initialization(self):
import json
from unittest.mock import AsyncMock