refactor: 平台改为代码内置 provider 架构,一次性迁移现有数据 - 新增 app/providers:BalanceProvider 基类 + DeepSeek 适配器 + 注册表 - platforms 表去除 method/url/headers/body/balance_path,新增 provider_id - 现有数据库已一次性迁移(备份 data/monitor.db.bak-v1),不保留迁移工具 - 平台 UI 改为选择内置提供方;fetcher/monitor 走 provider 构建请求与解析 - 表达式引擎(运算符/函数)随架构保留,供 provider 内部使用 - 测试更新至 provider 模式,共 92 个
This commit is contained in:
+47
-18
@@ -68,13 +68,32 @@ class FakeResponse:
|
||||
return self._json
|
||||
|
||||
|
||||
PLATFORM_GET = {
|
||||
"method": "GET",
|
||||
"url": "https://x.test/api?key={{apiKey}}",
|
||||
"headers": {"Authorization": "Bearer {{apiKey}}"},
|
||||
"body": "",
|
||||
"balance_path": "data.balance",
|
||||
}
|
||||
class FakeProvider:
|
||||
"""测试用 provider:URL 带 key、余额在 data.balance。"""
|
||||
|
||||
method = "GET"
|
||||
|
||||
def __init__(self, path="data.balance"):
|
||||
self.path = path
|
||||
|
||||
def build_request(self, api_key):
|
||||
return (
|
||||
"https://x.test/api?key=" + api_key,
|
||||
{"Authorization": "Bearer " + api_key},
|
||||
None,
|
||||
)
|
||||
|
||||
def extract_balance(self, data):
|
||||
from app.expr import evaluate_balance
|
||||
|
||||
return evaluate_balance(data, self.path)
|
||||
|
||||
|
||||
class FakePostProvider(FakeProvider):
|
||||
method = "POST"
|
||||
|
||||
def build_request(self, api_key):
|
||||
return ("https://x.test/api", {}, '{"api_key": "' + api_key + '"}')
|
||||
|
||||
|
||||
class TestFetchBalance:
|
||||
@@ -88,12 +107,11 @@ class TestFetchBalance:
|
||||
return FakeResponse(200, json_data={"data": {"balance": 42.5}})
|
||||
|
||||
monkeypatch.setattr("requests.get", fake_get)
|
||||
result = fetch_balance(PLATFORM_GET, "sk-1", retry_count=2, timeout=10)
|
||||
result = fetch_balance(FakeProvider(), "sk-1", retry_count=2, timeout=10)
|
||||
assert result.ok and result.balance == 42.5
|
||||
assert len(calls) == 1
|
||||
|
||||
def test_post_with_body(self, monkeypatch):
|
||||
platform = dict(PLATFORM_GET, method="POST", body='{"api_key": "{{apiKey}}"}')
|
||||
captured = {}
|
||||
|
||||
def fake_post(url, headers=None, data=None, timeout=10):
|
||||
@@ -101,10 +119,21 @@ class TestFetchBalance:
|
||||
return FakeResponse(200, json_data={"data": {"balance": 1}})
|
||||
|
||||
monkeypatch.setattr("requests.post", fake_post)
|
||||
result = fetch_balance(platform, "sk-2", retry_count=2, timeout=10)
|
||||
result = fetch_balance(FakePostProvider(), "sk-2", retry_count=2, timeout=10)
|
||||
assert result.ok
|
||||
assert captured["data"] == '{"api_key": "sk-2"}'
|
||||
|
||||
def test_post_content_type_auto(self, monkeypatch):
|
||||
captured = {}
|
||||
|
||||
def fake_post(url, headers=None, data=None, timeout=10):
|
||||
captured["headers"] = headers
|
||||
return FakeResponse(200, json_data={"data": {"balance": 1}})
|
||||
|
||||
monkeypatch.setattr("requests.post", fake_post)
|
||||
fetch_balance(FakePostProvider(), "sk-2", retry_count=0, timeout=10)
|
||||
assert captured["headers"].get("Content-Type") == "application/json"
|
||||
|
||||
def test_401_retries_then_auth_error(self, monkeypatch):
|
||||
calls = []
|
||||
|
||||
@@ -113,7 +142,7 @@ class TestFetchBalance:
|
||||
return FakeResponse(401, text="unauthorized")
|
||||
|
||||
monkeypatch.setattr("requests.get", fake_get)
|
||||
result = fetch_balance(PLATFORM_GET, "bad", retry_count=2, timeout=10)
|
||||
result = fetch_balance(FakeProvider(), "bad", retry_count=2, timeout=10)
|
||||
assert not result.ok and result.auth_error
|
||||
assert len(calls) == 3 # 401 也按重试次数确认后再判定
|
||||
|
||||
@@ -125,7 +154,7 @@ class TestFetchBalance:
|
||||
return FakeResponse(401) if len(calls) == 1 else FakeResponse(200, json_data={"data": {"balance": 6.6}})
|
||||
|
||||
monkeypatch.setattr("requests.get", fake_get)
|
||||
result = fetch_balance(PLATFORM_GET, "k", retry_count=2, timeout=10)
|
||||
result = fetch_balance(FakeProvider(), "k", retry_count=2, timeout=10)
|
||||
assert result.ok and result.balance == 6.6
|
||||
assert len(calls) == 2 # 瞬时 401 重试后成功,不误判禁用
|
||||
|
||||
@@ -137,13 +166,13 @@ class TestFetchBalance:
|
||||
return FakeResponse(500) if len(calls) < 3 else FakeResponse(200, json_data={"data": {"balance": 5}})
|
||||
|
||||
monkeypatch.setattr("requests.get", fake_get)
|
||||
result = fetch_balance(PLATFORM_GET, "k", retry_count=2, timeout=10)
|
||||
result = fetch_balance(FakeProvider(), "k", retry_count=2, timeout=10)
|
||||
assert result.ok and result.balance == 5
|
||||
assert len(calls) == 3
|
||||
|
||||
def test_5xx_all_fail(self, monkeypatch):
|
||||
monkeypatch.setattr("requests.get", lambda *a, **k: FakeResponse(503))
|
||||
result = fetch_balance(PLATFORM_GET, "k", retry_count=2, timeout=10)
|
||||
result = fetch_balance(FakeProvider(), "k", retry_count=2, timeout=10)
|
||||
assert not result.ok and not result.auth_error
|
||||
|
||||
def test_network_error_retries(self, monkeypatch):
|
||||
@@ -157,7 +186,7 @@ class TestFetchBalance:
|
||||
return FakeResponse(200, json_data={"data": {"balance": 8}})
|
||||
|
||||
monkeypatch.setattr("requests.get", fake_get)
|
||||
result = fetch_balance(PLATFORM_GET, "k", retry_count=2, timeout=10)
|
||||
result = fetch_balance(FakeProvider(), "k", retry_count=2, timeout=10)
|
||||
assert result.ok and result.balance == 8
|
||||
|
||||
def test_other_4xx_no_retry(self, monkeypatch):
|
||||
@@ -168,19 +197,19 @@ class TestFetchBalance:
|
||||
return FakeResponse(404, text="not found")
|
||||
|
||||
monkeypatch.setattr("requests.get", fake_get)
|
||||
result = fetch_balance(PLATFORM_GET, "k", retry_count=2, timeout=10)
|
||||
result = fetch_balance(FakeProvider(), "k", retry_count=2, timeout=10)
|
||||
assert not result.ok and not result.auth_error
|
||||
assert "404" in result.error
|
||||
assert len(calls) == 1
|
||||
|
||||
def test_bad_json_path(self, monkeypatch):
|
||||
monkeypatch.setattr("requests.get", lambda *a, **k: FakeResponse(200, json_data={"x": 1}))
|
||||
result = fetch_balance(PLATFORM_GET, "k", retry_count=0, timeout=10)
|
||||
result = fetch_balance(FakeProvider(), "k", retry_count=0, timeout=10)
|
||||
assert not result.ok
|
||||
assert "路径不存在" in result.error
|
||||
|
||||
def test_invalid_json_body(self, monkeypatch):
|
||||
monkeypatch.setattr("requests.get", lambda *a, **k: FakeResponse(200, text="<html>"))
|
||||
result = fetch_balance(PLATFORM_GET, "k", retry_count=0, timeout=10)
|
||||
result = fetch_balance(FakeProvider(), "k", retry_count=0, timeout=10)
|
||||
assert not result.ok
|
||||
assert "JSON" in result.error
|
||||
Reference in New Issue
Block a user