fix(nox-api): address duplicate creation risks, pending upgrading issues, cache warm error handling, test speedups, and frontend display support for pending keys
This commit is contained in:
@@ -1570,3 +1570,168 @@ def test_ensure_group_key_nox_pending_re_invoke_backfills(db_session):
|
||||
assert row.status == "exists"
|
||||
assert row.key_id == "remote-backfill-id"
|
||||
assert row.key_value == "sk-backfilled-plain-key"
|
||||
|
||||
|
||||
def test_ensure_group_key_remote_query_failure_aborts(db_session):
|
||||
"""验证当远端查询抛出异常时,直接中断,不重建或重置本地记录。"""
|
||||
from app.routers.upstreams import _ensure_group_key
|
||||
from app.schemas.upstream import GenerateKeysByGroupsRequest
|
||||
from app.models.upstream_key import UpstreamGeneratedKey
|
||||
from app.services.upstream_client import UpstreamError
|
||||
|
||||
upstream = Upstream(
|
||||
name="NoxAPI",
|
||||
base_url="http://nox.local",
|
||||
api_prefix="",
|
||||
auth_type="nox_token",
|
||||
auth_config_json=json.dumps({"token": "abc", "new_api_user": "7"}),
|
||||
groups_endpoint="/api/user/self/groups",
|
||||
rate_endpoint="/api/user/self/groups",
|
||||
)
|
||||
db_session.add(upstream)
|
||||
db_session.commit()
|
||||
db_session.refresh(upstream)
|
||||
|
||||
# 1. 本地有记录的情形
|
||||
old_key_name = f"SmartUp-{upstream.id}-VIP-vip"
|
||||
db_session.add(UpstreamGeneratedKey(
|
||||
upstream_id=upstream.id,
|
||||
group_id="vip",
|
||||
group_name="VIP",
|
||||
key_name=old_key_name,
|
||||
key_value="sk-local",
|
||||
managed_prefix="SmartUp",
|
||||
key_id="123",
|
||||
status="exists",
|
||||
))
|
||||
db_session.commit()
|
||||
|
||||
class MockClient:
|
||||
def find_smartup_group_key(self, *args, **kwargs):
|
||||
raise UpstreamError("429 Too Many Requests")
|
||||
|
||||
def create_api_key(self, *args, **kwargs):
|
||||
raise AssertionError("create_api_key should not be called on query failure")
|
||||
|
||||
group = {"id": "vip", "name": "VIP"}
|
||||
body = GenerateKeysByGroupsRequest(group_ids=["vip"], name_prefix="SmartUp", quota=0)
|
||||
|
||||
result = _ensure_group_key(db_session, MockClient(), upstream, group, "SmartUp", body)
|
||||
|
||||
assert result.status == "failed"
|
||||
assert "429 Too Many Requests" in (result.error or "")
|
||||
|
||||
# 本地状态应该保持 exists, 不应该被置为 replaced
|
||||
row = db_session.query(UpstreamGeneratedKey).filter(
|
||||
UpstreamGeneratedKey.upstream_id == upstream.id,
|
||||
UpstreamGeneratedKey.group_id == "vip",
|
||||
).one()
|
||||
assert row.status == "exists"
|
||||
assert row.key_value == "sk-local"
|
||||
|
||||
|
||||
def test_ensure_group_key_pending_re_invoke_no_key_stays_pending(db_session):
|
||||
"""当重试 pending 记录时,如果远端找到 token 但无法获取到明文,应保持 created_pending_key 状态。"""
|
||||
from app.routers.upstreams import _ensure_group_key
|
||||
from app.schemas.upstream import GenerateKeysByGroupsRequest
|
||||
from app.models.upstream_key import UpstreamGeneratedKey
|
||||
|
||||
upstream = Upstream(
|
||||
name="NoxAPI",
|
||||
base_url="http://nox.local",
|
||||
api_prefix="",
|
||||
auth_type="nox_token",
|
||||
auth_config_json=json.dumps({"token": "abc", "new_api_user": "7"}),
|
||||
groups_endpoint="/api/user/self/groups",
|
||||
rate_endpoint="/api/user/self/groups",
|
||||
)
|
||||
db_session.add(upstream)
|
||||
db_session.commit()
|
||||
db_session.refresh(upstream)
|
||||
|
||||
old_key_name = f"SmartUp-{upstream.id}-PendingGrp-pending-grp"
|
||||
db_session.add(UpstreamGeneratedKey(
|
||||
upstream_id=upstream.id,
|
||||
group_id="pending-grp",
|
||||
group_name="PendingGrp",
|
||||
key_name=old_key_name,
|
||||
key_value="",
|
||||
masked_key="",
|
||||
managed_prefix="SmartUp",
|
||||
key_id=None,
|
||||
status="created_pending_key",
|
||||
))
|
||||
db_session.commit()
|
||||
|
||||
class MockClient:
|
||||
def find_smartup_group_key(self, group_id, name, prefix):
|
||||
# 查到了 token,但无明文 key (例如只返回了 masked_key)
|
||||
if name == old_key_name:
|
||||
return {"id": "remote-backfill-id", "name": old_key_name, "masked_key": "sk-****"}
|
||||
return None
|
||||
|
||||
def create_api_key(self, *args, **kwargs):
|
||||
raise AssertionError("create_api_key should not be called")
|
||||
|
||||
group = {"id": "pending-grp", "name": "PendingGrp"}
|
||||
body = GenerateKeysByGroupsRequest(group_ids=["pending-grp"], name_prefix="SmartUp", quota=0)
|
||||
|
||||
result = _ensure_group_key(db_session, MockClient(), upstream, group, "SmartUp", body)
|
||||
|
||||
assert result.status == "created_pending_key"
|
||||
assert "未能获取到明文 Key" in (result.error or "")
|
||||
|
||||
# 本地状态应该保持 pending
|
||||
row = db_session.query(UpstreamGeneratedKey).filter(
|
||||
UpstreamGeneratedKey.upstream_id == upstream.id,
|
||||
UpstreamGeneratedKey.group_id == "pending-grp",
|
||||
).one()
|
||||
assert row.status == "created_pending_key"
|
||||
assert row.key_id == "remote-backfill-id"
|
||||
assert row.key_value == ""
|
||||
|
||||
|
||||
def test_generate_keys_by_groups_warm_cache_failure_fails_fast(db_session, monkeypatch):
|
||||
"""预热缓存失败时,generate_keys_by_groups 应该直接 abort 抛出 502。"""
|
||||
from app.routers.upstreams import generate_keys_by_groups
|
||||
from app.schemas.upstream import GenerateKeysByGroupsRequest
|
||||
from fastapi import HTTPException
|
||||
|
||||
upstream = Upstream(
|
||||
name="NoxAPI",
|
||||
base_url="http://nox.local",
|
||||
api_prefix="",
|
||||
auth_type="nox_token",
|
||||
auth_config_json=json.dumps({"token": "abc", "new_api_user": "7"}),
|
||||
groups_endpoint="/api/user/self/groups",
|
||||
rate_endpoint="/api/user/self/groups",
|
||||
)
|
||||
db_session.add(upstream)
|
||||
db_session.commit()
|
||||
db_session.refresh(upstream)
|
||||
|
||||
class MockClient:
|
||||
def __init__(self, *args, **kwargs):
|
||||
pass
|
||||
def __enter__(self):
|
||||
return self
|
||||
def __exit__(self, *args):
|
||||
pass
|
||||
def login(self):
|
||||
pass
|
||||
def get_available_groups(self, endpoint):
|
||||
return [{"id": "vip", "name": "VIP"}]
|
||||
def warm_token_list_cache(self):
|
||||
raise UpstreamError("429 Too Many Requests")
|
||||
|
||||
from app.routers import upstreams
|
||||
monkeypatch.setattr(upstreams, "UpstreamClient", MockClient)
|
||||
|
||||
body = GenerateKeysByGroupsRequest(group_ids=["vip"], name_prefix="SmartUp", quota=0)
|
||||
|
||||
with pytest.raises(HTTPException) as excinfo:
|
||||
generate_keys_by_groups(upstream.id, body, db=db_session)
|
||||
|
||||
assert excinfo.value.status_code == 502
|
||||
assert "上游连接或缓存预热失败" in excinfo.value.detail
|
||||
|
||||
|
||||
Reference in New Issue
Block a user