Files
matrix-openclaw-bot/tests/test_token_manager.py
2026-02-03 00:54:48 +08:00

278 lines
9.1 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# test_token_manager.py - Token 管理器測試
"""
Tests for TokenManager class.
Uses sandbox testing to avoid modifying real files.
"""
import json
import pytest
import tempfile
from pathlib import Path
from datetime import datetime
# 需要確保可以導入
import sys
sys.path.insert(0, str(Path(__file__).parent.parent))
from src.services.token_manager import TokenManager
class TestTokenManager:
"""TokenManager 測試套件"""
@pytest.fixture
def temp_json_file(self, tmp_path):
"""創建臨時 JSON 文件用於沙箱測試"""
json_file = tmp_path / "openclaw.json"
test_data = {
"channels": {
"matrix": {
"accessToken": "old_token_12345",
"homeserver": "https://matrix.org"
},
"telegram": {
"botToken": "telegram_token"
}
},
"settings": {
"debug": True
}
}
json_file.write_text(json.dumps(test_data, indent=2))
return json_file
@pytest.fixture
def token_manager(self, temp_json_file):
"""創建 TokenManager 實例"""
return TokenManager(
json_path=temp_json_file,
token_key="channels.matrix.accessToken"
)
def test_create_backup(self, token_manager, temp_json_file):
"""測試備份創建"""
backup_path = token_manager.create_backup()
assert backup_path is not None
assert backup_path.exists()
assert temp_json_file.name in backup_path.name
assert ".bak" in backup_path.name
# 驗證備份內容與原文件相同
original = json.loads(temp_json_file.read_text())
backup = json.loads(backup_path.read_text())
assert original == backup
def test_verify_backup_exists(self, token_manager):
"""測試備份驗證"""
# 未創建備份時應該返回 False
assert token_manager.verify_backup_exists() is False
# 創建備份後應該返回 True
token_manager.create_backup()
assert token_manager.verify_backup_exists() is True
def test_get_nested_value(self, token_manager):
"""測試嵌套值讀取"""
data = {
"level1": {
"level2": {
"level3": "value"
}
}
}
assert token_manager._get_nested_value(data, "level1.level2.level3") == "value"
assert token_manager._get_nested_value(data, "level1.level2") is None # 不是字符串
assert token_manager._get_nested_value(data, "nonexistent") is None
def test_set_nested_value(self, token_manager):
"""測試嵌套值設置"""
data = {}
result = token_manager._set_nested_value(data, "a.b.c", "new_value")
assert result["a"]["b"]["c"] == "new_value"
@pytest.mark.asyncio
async def test_read_current_token(self, token_manager):
"""測試讀取當前 token"""
token = await token_manager.read_current_token()
assert token == "old_token_12345"
@pytest.mark.asyncio
async def test_update_access_token(self, token_manager, temp_json_file):
"""測試更新 access token核心功能"""
new_token = "new_token_67890"
# 執行更新
success = await token_manager.update_access_token(new_token)
# 驗證成功
assert success is True
# 驗證備份存在
assert token_manager.verify_backup_exists() is True
# 驗證新 token 已寫入
updated_data = json.loads(temp_json_file.read_text())
assert updated_data["channels"]["matrix"]["accessToken"] == new_token
# 驗證其他數據未被破壞
assert updated_data["channels"]["telegram"]["botToken"] == "telegram_token"
assert updated_data["settings"]["debug"] is True
def test_restore_from_backup(self, token_manager, temp_json_file):
"""測試從備份恢復"""
# 創建備份
backup_path = token_manager.create_backup()
# 修改原文件
temp_json_file.write_text('{"corrupted": true}')
# 恢復
success = token_manager.restore_from_backup()
assert success is True
# 驗證恢復結果
restored_data = json.loads(temp_json_file.read_text())
assert restored_data["channels"]["matrix"]["accessToken"] == "old_token_12345"
class TestTokenManagerEdgeCases:
"""TokenManager 邊緣情況測試"""
def test_backup_nonexistent_file(self, tmp_path):
"""測試備份不存在的文件"""
manager = TokenManager(
json_path=tmp_path / "nonexistent.json",
token_key="test"
)
backup_path = manager.create_backup()
assert backup_path is None
@pytest.mark.asyncio
async def test_update_nonexistent_file(self, tmp_path):
"""測試更新不存在的文件"""
manager = TokenManager(
json_path=tmp_path / "nonexistent.json",
token_key="test"
)
success = await manager.update_access_token("new_token")
assert success is False
class TestBackupRotation:
"""備份輪轉功能測試(沙箱測試)"""
@pytest.fixture
def temp_json_file(self, tmp_path):
"""創建臨時 JSON 文件"""
json_file = tmp_path / "openclaw.json"
test_data = {"channels": {"matrix": {"accessToken": "test_token"}}}
json_file.write_text(json.dumps(test_data))
return json_file
@pytest.fixture
def token_manager(self, temp_json_file):
"""創建 TokenManager 實例"""
return TokenManager(
json_path=temp_json_file,
token_key="channels.matrix.accessToken"
)
def test_max_backups_constant(self):
"""測試最大備份數量常量"""
assert TokenManager.MAX_BACKUPS == 5
def test_get_backup_files_empty(self, token_manager):
"""測試沒有備份時返回空列表"""
backups = token_manager._get_backup_files()
assert backups == []
def test_get_backup_files_with_backups(self, token_manager, tmp_path):
"""測試獲取備份文件列表"""
# 創建幾個備份
for i in range(3):
token_manager.create_backup()
import time
time.sleep(0.01) # 確保時間戳不同
backups = token_manager._get_backup_files()
assert len(backups) == 3
# 確認按文件名排序(時間戳在名稱中,字典序=時間序,最新在前)
for i in range(len(backups) - 1):
assert backups[i].name > backups[i + 1].name
def test_rotation_under_limit(self, token_manager):
"""測試備份數量未超過限制時不刪除"""
# 創建 3 個備份(低於限制)
for i in range(3):
token_manager.create_backup()
import time
time.sleep(0.01)
assert token_manager.get_backup_count() == 3
def test_rotation_at_limit(self, token_manager):
"""測試備份數量剛好達到限制"""
# 創建 5 個備份(剛好等於限制)
for i in range(5):
token_manager.create_backup()
import time
time.sleep(0.01)
assert token_manager.get_backup_count() == 5
def test_rotation_over_limit(self, token_manager):
"""測試備份數量超過限制時自動刪除最舊的"""
# 創建 7 個備份(超過限制)
for i in range(7):
token_manager.create_backup()
import time
time.sleep(0.01)
# 應該只保留 5 個
assert token_manager.get_backup_count() == 5
def test_rotation_preserves_newest(self, token_manager, temp_json_file):
"""測試輪轉保留最新的備份"""
backup_paths = []
# 創建 7 個備份
for i in range(7):
path = token_manager.create_backup()
if path:
backup_paths.append(path)
import time
time.sleep(0.01)
# 檢查最新的 5 個備份仍然存在
remaining_backups = token_manager._get_backup_files()
assert len(remaining_backups) == 5
# 最新的 5 個應該是最後創建的 5 個
newest_5 = backup_paths[-5:]
for path in newest_5:
assert path.exists(), f"{path} should exist"
# 最舊的 2 個應該被刪除
oldest_2 = backup_paths[:2]
for path in oldest_2:
assert not path.exists(), f"{path} should be deleted"
def test_get_backup_count(self, token_manager):
"""測試獲取備份數量"""
assert token_manager.get_backup_count() == 0
token_manager.create_backup()
assert token_manager.get_backup_count() == 1
token_manager.create_backup()
assert token_manager.get_backup_count() == 2