Files
blackbird/tests/blackbox/test_core_confs.py
T
qing 59c5fe1feb feat(core): confs2 合并入 core,内嵌 confs 配置服务 v0.1.0
- 新增 ConfsTask(ITask,与 HttpTask 同构):ConfigStore + INI/JSON 取值
  + get/list/reload/health 四路由,协议与 Python confs 逐字节兼容
- 新增 ConfsLogC 异步日志客户端(沿用 confs2 LogC,服务名 confs)
- main.cpp 新增 --confs <port> 选项与 CONFS_* 环境变量;不传则行为不变
- Makefile 链接 -lus_msg,版本 0.0.4 → 0.1.0
- _run.sh 启用 --confs 8010;_set.sh 自建 configs → ../confs/configs 链接
- init.txt 移除 confs2 自启动行(由 core 内嵌承担)
- 黑盒对比测试 16/16 通过(tests/blackbox/test_core_confs.py)
2026-08-09 15:36:32 +00:00

121 lines
4.3 KiB
Python

#!/usr/bin/env python3
"""core 内嵌 confs 黑盒测试 — 与 Python confs 行为对比
前置条件:
- Python confs 运行于 :8010(基线)
- core (nidus.out) 以 --confs 8012 启动
用法: python3 test_core_confs.py [core_port] [py_port]
"""
import os
import sys
sys.path.insert(0, '/opt/micro_servers/pymsg')
from pymsg.rpc import RpcClient, RpcError
CORE_PORT = int(sys.argv[1]) if len(sys.argv) > 1 else 8012
PY_PORT = int(sys.argv[2]) if len(sys.argv) > 2 else 8010
CONFIG_DIR = '/opt/micro_servers/confs/configs'
def call(port, route, payload=None):
return RpcClient('127.0.0.1', port, 'test').call(route, payload)
def call_err(port, route, payload=None):
"""返回错误信息字符串;若未报错则返回 None"""
try:
call(port, route, payload)
return None
except RpcError as e:
return str(e)
passed = failed = 0
def check(label, cond):
global passed, failed
if cond:
passed += 1
print(f' [PASS] {label}')
else:
failed += 1
print(f' [FAIL] {label}')
def main():
print('========== 1. health ==========')
h = call(CORE_PORT, 'health')
print(' core:', h)
check('status=ok', h['status'] == 'ok')
check('service=confs', h['service'] == 'confs')
check('config_dir 指向 confs/configs', h['config_dir'].endswith('confs/configs'))
print('========== 2. list ==========')
l2, l0 = call(CORE_PORT, 'list'), call(PY_PORT, 'list')
n2 = sorted(c['name'] for c in l2['configs'])
n0 = sorted(c['name'] for c in l0['configs'])
print(' core:', n2)
check('文件清单一致', n2 == n0)
check('size 一致',
{c['name']: c['size'] for c in l2['configs']}
== {c['name']: c['size'] for c in l0['configs']})
print('========== 3. get 全文 (db.ini) ==========')
g2 = call(CORE_PORT, 'get', {'name': 'db.ini'})
g0 = call(PY_PORT, 'get', {'name': 'db.ini'})
check('content 一致', g2['content'] == g0['content'])
check('size 一致', g2['size'] == g0['size'])
print('========== 4. get INI 键 ==========')
import configparser
cp = configparser.ConfigParser(interpolation=None)
cp.read(os.path.join(CONFIG_DIR, 'db.ini'))
sec = cp.sections()[0]
opt = list(cp[sec].keys())[0]
key = f'{sec}.{opt}'
k2 = call(CORE_PORT, 'get', {'name': 'db.ini', 'key': key})
k0 = call(PY_PORT, 'get', {'name': 'db.ini', 'key': key})
print(f' key={key} core={k2["value"]!r} py={k0["value"]!r}')
check('INI 键值一致', k2['value'] == k0['value'])
print('========== 5. 错误用例(两边都应报错且信息一致) ==========')
for label, payload in [('不存在的配置', {'name': 'nope.ini'}),
('路径穿越', {'name': '../etc/x.ini'}),
('非法类型', {'name': 'x.exe'}),
('缺 name', None),
('键不存在', {'name': 'db.ini', 'key': 'no.such'})]:
e2 = call_err(CORE_PORT, 'get', payload)
e0 = call_err(PY_PORT, 'get', payload)
print(f' {label}: core={e2!r} py={e0!r}')
check(f'{label} 错误一致', e2 is not None and e2 == e0)
print('========== 6. reload ==========')
r2 = call(CORE_PORT, 'reload', {'name': 'db.ini'})
r0 = call(PY_PORT, 'reload', {'name': 'db.ini'})
check('reload 单个', r2 == r0 == {'reloaded': ['db.ini']})
r2, r0 = call(CORE_PORT, 'reload'), call(PY_PORT, 'reload')
check('reload 全部返回集合一致', sorted(r2['reloaded']) == sorted(r0['reloaded']))
print('========== 7. INI option 大小写不敏感 ==========')
ku2 = call(CORE_PORT, 'get', {'name': 'db.ini', 'key': f'{sec}.{opt.upper()}'})
check('大写 option 取值一致', ku2['value'] == k0['value'])
print('========== 8. JSON 配置(若存在) ==========')
jsons = [f for f in os.listdir(CONFIG_DIR) if f.endswith('.json')]
if jsons:
j2 = call(CORE_PORT, 'get', {'name': jsons[0]})
j0 = call(PY_PORT, 'get', {'name': jsons[0]})
check(f'{jsons[0]} content 一致', j2['content'] == j0['content'])
else:
print(' (无 .json 配置,跳过)')
print()
print(f'结果: {passed} 通过, {failed} 失败')
return 1 if failed else 0
if __name__ == '__main__':
sys.exit(main())