Add upstream_key_account_links table mapping generated keys to remote accounts per website/platform, surface imported_accounts on key responses, and update website sync/routers to manage the links.
666 lines
23 KiB
Python
666 lines
23 KiB
Python
import json
|
||
import sys
|
||
from pathlib import Path
|
||
|
||
import pytest
|
||
from sqlalchemy import create_engine
|
||
from sqlalchemy.orm import sessionmaker
|
||
from sqlalchemy.pool import StaticPool
|
||
|
||
sys.path.insert(0, str(Path(__file__).resolve().parent))
|
||
|
||
from app.database import Base
|
||
from app.models.upstream import Upstream
|
||
from app.models.upstream_key import UpstreamGeneratedKey
|
||
from app.models.website import Website
|
||
from app.routers import websites as websites_router
|
||
from app.schemas.website import ImportAccountsRequest
|
||
|
||
|
||
@pytest.fixture()
|
||
def db_session():
|
||
engine = create_engine(
|
||
"sqlite://",
|
||
connect_args={"check_same_thread": False},
|
||
poolclass=StaticPool,
|
||
)
|
||
Base.metadata.create_all(bind=engine)
|
||
TestingSessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
|
||
db = TestingSessionLocal()
|
||
try:
|
||
yield db
|
||
finally:
|
||
db.close()
|
||
Base.metadata.drop_all(bind=engine)
|
||
|
||
|
||
def seed_account_import_rows(db_session):
|
||
website = Website(
|
||
name="My Sub2API",
|
||
site_type="sub2api",
|
||
base_url="http://sub2api.local",
|
||
api_prefix="/api/v1/admin",
|
||
auth_type="api_key",
|
||
auth_config_json=json.dumps({"key": "admin-key", "header": "x-api-key"}),
|
||
groups_endpoint="/groups",
|
||
group_update_endpoint="/groups/{id}",
|
||
)
|
||
upstream = Upstream(
|
||
name="Packy",
|
||
base_url="http://packy.local",
|
||
api_prefix="/api/v1",
|
||
auth_type="login_password",
|
||
auth_config_json="{}",
|
||
)
|
||
db_session.add_all([website, upstream])
|
||
db_session.commit()
|
||
db_session.refresh(website)
|
||
db_session.refresh(upstream)
|
||
generated = UpstreamGeneratedKey(
|
||
upstream_id=upstream.id,
|
||
group_id="vip",
|
||
group_name="VIP",
|
||
key_id="up-key-id",
|
||
key_name="SmartUp-VIP",
|
||
key_value="sk-upstream-generated",
|
||
masked_key="sk-u...ated",
|
||
raw_json="{}",
|
||
status="created",
|
||
)
|
||
db_session.add(generated)
|
||
db_session.commit()
|
||
db_session.refresh(generated)
|
||
return website, generated
|
||
|
||
|
||
def test_import_upstream_key_creates_sub2api_account_management_apikey(monkeypatch, db_session):
|
||
website, generated = seed_account_import_rows(db_session)
|
||
account_bodies = []
|
||
|
||
class FakeWebsiteClient:
|
||
def __init__(self, **kwargs):
|
||
self.kwargs = kwargs
|
||
|
||
def __enter__(self):
|
||
return self
|
||
|
||
def __exit__(self, exc_type, exc, tb):
|
||
return False
|
||
|
||
def get_groups(self, endpoint="/groups"):
|
||
return [{"id": "7", "name": "Target", "platform": "anthropic"}]
|
||
def list_accounts(self):
|
||
return [{"id": "101", "name": "Existing", "platform": "anthropic", "group_ids": [7]}]
|
||
|
||
def create_account(self, body, endpoint="/accounts"):
|
||
account_bodies.append((endpoint, body))
|
||
return {"id": 101, "name": body["name"]}
|
||
|
||
def account_exists(self, account_id):
|
||
return True
|
||
|
||
@staticmethod
|
||
def extract_id(data):
|
||
return str(data.get("id"))
|
||
|
||
monkeypatch.setattr(websites_router, "Sub2ApiWebsiteClient", FakeWebsiteClient)
|
||
|
||
response = websites_router.import_upstream_keys_as_accounts(
|
||
website.id,
|
||
ImportAccountsRequest(
|
||
upstream_key_ids=[generated.id],
|
||
target_group_map={"vip": "7"},
|
||
account_name_prefix="SmartUp",
|
||
default_platform="anthropic",
|
||
),
|
||
db_session,
|
||
object(),
|
||
)
|
||
|
||
assert "新建 1" in response.message
|
||
assert len(account_bodies) == 1
|
||
endpoint, body = account_bodies[0]
|
||
assert endpoint == "/accounts"
|
||
assert body["type"] == "apikey"
|
||
assert body["platform"] == "anthropic"
|
||
assert body["credentials"]["api_key"] == "sk-upstream-generated"
|
||
assert body["credentials"]["base_url"] == "http://packy.local"
|
||
assert body["group_ids"] == [7]
|
||
assert body["concurrency"] == 100
|
||
assert body["priority"] == 1
|
||
assert body["credentials"]["base_url"] == "http://packy.local"
|
||
|
||
db_session.refresh(generated)
|
||
assert generated.status == "imported"
|
||
assert generated.imported_website_id == website.id
|
||
assert generated.imported_account_id == "101"
|
||
|
||
|
||
def test_import_upstream_key_idempotent_skips_already_imported(monkeypatch, db_session):
|
||
"""已导入过的 Key 再次调用不会重复创建。"""
|
||
website, generated = seed_account_import_rows(db_session)
|
||
generated.imported_website_id = website.id
|
||
generated.imported_account_id = "101"
|
||
db_session.commit()
|
||
|
||
create_called = False
|
||
|
||
class FakeWebsiteClient:
|
||
def __init__(self, **kwargs):
|
||
self.kwargs = kwargs
|
||
def __enter__(self):
|
||
return self
|
||
def __exit__(self, exc_type, exc, tb):
|
||
return False
|
||
def get_groups(self, endpoint="/groups"):
|
||
return [{"id": "7", "name": "Target", "platform": "anthropic"}]
|
||
def list_accounts(self):
|
||
return [{"id": "101", "name": "Existing", "platform": "anthropic", "group_ids": [7]}]
|
||
def create_account(self, body, endpoint="/accounts"):
|
||
nonlocal create_called
|
||
create_called = True
|
||
return {"id": 999, "name": body["name"]}
|
||
def account_exists(self, account_id):
|
||
return True # 模拟远端账号仍存在
|
||
@staticmethod
|
||
def extract_id(data):
|
||
return str(data.get("id"))
|
||
|
||
monkeypatch.setattr(websites_router, "Sub2ApiWebsiteClient", FakeWebsiteClient)
|
||
|
||
response = websites_router.import_upstream_keys_as_accounts(
|
||
website.id,
|
||
ImportAccountsRequest(
|
||
upstream_key_ids=[generated.id],
|
||
target_group_map={"vip": "7"},
|
||
account_name_prefix="SmartUp",
|
||
default_platform="anthropic",
|
||
),
|
||
db_session,
|
||
object(),
|
||
)
|
||
|
||
# create_account 不应被调用
|
||
assert not create_called, "create_account should NOT be called for already imported key"
|
||
# 返回 exists
|
||
assert len(response.items) == 1
|
||
assert response.items[0].status == "exists"
|
||
assert response.items[0].account_id == "101"
|
||
assert response.items[0].message == "已存在并已对齐目标分组"
|
||
# success 应为 true(没有 failed)
|
||
assert response.success
|
||
|
||
|
||
def test_sync_imported_upstream_keys_efficient_and_correct(monkeypatch, db_session):
|
||
"""验证 sync_imported_upstream_keys 优化:只拉一次账号列表,处理 exists 和 stale_cleared。"""
|
||
from app.routers import websites as websites_router
|
||
from app.schemas.website import SyncImportStatusRequest
|
||
|
||
website, generated1 = seed_account_import_rows(db_session)
|
||
generated1.imported_website_id = website.id
|
||
generated1.imported_account_id = "101"
|
||
|
||
# 增加另一个已导入行以测试单次请求 & 多个状态
|
||
generated2 = UpstreamGeneratedKey(
|
||
upstream_id=generated1.upstream_id,
|
||
group_id="normal",
|
||
group_name="normal",
|
||
key_name="normal-key",
|
||
key_value="sk-normal",
|
||
imported_website_id=website.id,
|
||
imported_account_id="102",
|
||
status="imported",
|
||
)
|
||
db_session.add(generated2)
|
||
db_session.commit()
|
||
|
||
list_accounts_calls = 0
|
||
|
||
class FakeClient:
|
||
def __init__(self, **kwargs):
|
||
pass
|
||
def __enter__(self):
|
||
return self
|
||
def __exit__(self, *a):
|
||
return False
|
||
def get_groups(self, endpoint="/groups"):
|
||
return [{"id": "7", "name": "Target", "platform": "anthropic"}]
|
||
def list_accounts(self):
|
||
nonlocal list_accounts_calls
|
||
list_accounts_calls += 1
|
||
# 远端只有 A1 (id: 101),没有 A2 (id: 102)
|
||
return [
|
||
{"id": "101", "name": "SmartUp-A1", "platform": "anthropic", "group_ids": [7]}
|
||
]
|
||
@staticmethod
|
||
def extract_id(data):
|
||
return str(data.get("id"))
|
||
|
||
monkeypatch.setattr(websites_router, "Sub2ApiWebsiteClient", lambda **kw: FakeClient(**kw))
|
||
|
||
response = websites_router.sync_imported_upstream_keys(
|
||
website.id, SyncImportStatusRequest(upstream_id=generated1.upstream_id),
|
||
db_session, object(),
|
||
)
|
||
|
||
# 1. 验证 list_accounts() 只被调用了 1 次
|
||
assert list_accounts_calls == 1
|
||
|
||
# 2. 检查结果
|
||
assert len(response.items) == 2
|
||
item_101 = next(item for item in response.items if item.account_id == "101")
|
||
item_102 = next(item for item in response.items if item.account_id == "102")
|
||
|
||
# 101 仍存在
|
||
assert item_101.status == "exists"
|
||
# 102 已被清除
|
||
assert item_102.status == "stale_cleared"
|
||
|
||
# 3. 校验 DB 反映
|
||
db_session.refresh(generated1)
|
||
db_session.refresh(generated2)
|
||
assert generated1.imported_account_id == "101"
|
||
assert generated2.imported_account_id is None
|
||
|
||
|
||
def test_sync_imported_upstream_keys_check_failed_on_exception(monkeypatch, db_session):
|
||
"""验证 list_accounts 报错或返回 None 时返回 check_failed,且不清空本地导入标记。"""
|
||
from app.routers import websites as websites_router
|
||
from app.schemas.website import SyncImportStatusRequest
|
||
|
||
website, generated = seed_account_import_rows(db_session)
|
||
generated.imported_website_id = website.id
|
||
generated.imported_account_id = "101"
|
||
db_session.commit()
|
||
|
||
class FakeClient:
|
||
def __init__(self, **kwargs):
|
||
pass
|
||
def __enter__(self):
|
||
return self
|
||
def __exit__(self, *a):
|
||
return False
|
||
def get_groups(self, endpoint="/groups"):
|
||
return [{"id": "7", "name": "Target", "platform": "anthropic"}]
|
||
def list_accounts(self):
|
||
raise Exception("List accounts failure")
|
||
@staticmethod
|
||
def extract_id(data):
|
||
return str(data.get("id"))
|
||
|
||
monkeypatch.setattr(websites_router, "Sub2ApiWebsiteClient", lambda **kw: FakeClient(**kw))
|
||
|
||
response = websites_router.sync_imported_upstream_keys(
|
||
website.id, SyncImportStatusRequest(upstream_id=generated.upstream_id),
|
||
db_session, object(),
|
||
)
|
||
|
||
assert len(response.items) == 1
|
||
assert response.items[0].status == "check_failed"
|
||
db_session.refresh(generated)
|
||
assert generated.imported_account_id == "101"
|
||
|
||
|
||
def test_import_rebuilds_when_remote_deleted(monkeypatch, db_session):
|
||
"""导入接口:远端账号已删除时自动清标记并重新创建。"""
|
||
from app.routers import websites as websites_router
|
||
from app.schemas.website import ImportAccountsRequest
|
||
|
||
website, generated = seed_account_import_rows(db_session)
|
||
generated.imported_website_id = website.id
|
||
generated.imported_account_id = "101"
|
||
db_session.commit()
|
||
|
||
class FakeClient:
|
||
def __init__(self, **kwargs):
|
||
self.kwargs = kwargs
|
||
def __enter__(self):
|
||
return self
|
||
def __exit__(self, *a):
|
||
return False
|
||
def get_groups(self, endpoint="/groups"):
|
||
return [{"id": "7", "name": "Target", "platform": "anthropic"}]
|
||
def list_accounts(self):
|
||
return []
|
||
def account_exists(self, account_id):
|
||
return False
|
||
def create_account(self, body, endpoint="/accounts"):
|
||
return {"id": 202, "name": body["name"]}
|
||
@staticmethod
|
||
def extract_id(data):
|
||
return str(data.get("id"))
|
||
|
||
monkeypatch.setattr(websites_router, "Sub2ApiWebsiteClient", lambda **kw: FakeClient(**kw))
|
||
|
||
response = websites_router.import_upstream_keys_as_accounts(
|
||
website.id,
|
||
ImportAccountsRequest(upstream_key_ids=[generated.id], target_group_map={"vip": "7"},
|
||
account_name_prefix="SmartUp", default_platform="openai"),
|
||
db_session, object(),
|
||
)
|
||
|
||
assert len(response.items) == 1
|
||
assert response.items[0].status == "created", f"expected created, got {response.items[0].status}"
|
||
db_session.refresh(generated)
|
||
assert generated.imported_account_id == "202"
|
||
|
||
|
||
def test_import_skips_on_check_failed(monkeypatch, db_session):
|
||
"""导入接口:校验失败时保守跳过,不创建也不清除。"""
|
||
from app.routers import websites as websites_router
|
||
from app.schemas.website import ImportAccountsRequest
|
||
|
||
website, generated = seed_account_import_rows(db_session)
|
||
generated.imported_website_id = website.id
|
||
generated.imported_account_id = "101"
|
||
db_session.commit()
|
||
|
||
class FakeClient:
|
||
def __init__(self, **kwargs):
|
||
self.kwargs = kwargs
|
||
def __enter__(self):
|
||
return self
|
||
def __exit__(self, *a):
|
||
return False
|
||
def get_groups(self, endpoint="/groups"):
|
||
return [{"id": "7", "name": "Target", "platform": "openai"}]
|
||
def list_accounts(self):
|
||
return None
|
||
def account_exists(self, account_id):
|
||
return None
|
||
def create_account(self, body, endpoint="/accounts"):
|
||
raise RuntimeError("should not be called")
|
||
@staticmethod
|
||
def extract_id(data):
|
||
return str(data.get("id"))
|
||
|
||
monkeypatch.setattr(websites_router, "Sub2ApiWebsiteClient", lambda **kw: FakeClient(**kw))
|
||
|
||
response = websites_router.import_upstream_keys_as_accounts(
|
||
website.id,
|
||
ImportAccountsRequest(upstream_key_ids=[generated.id], target_group_map={"vip": "7"},
|
||
account_name_prefix="SmartUp", default_platform="openai"),
|
||
db_session, object(),
|
||
)
|
||
|
||
assert len(response.items) == 1
|
||
assert response.items[0].status == "check_failed"
|
||
db_session.refresh(generated)
|
||
assert generated.imported_account_id == "101" # 未被清除
|
||
assert not response.success
|
||
|
||
|
||
def test_import_upstream_key_with_custom_concurrency_and_priority(monkeypatch, db_session):
|
||
website, generated = seed_account_import_rows(db_session)
|
||
account_bodies = []
|
||
|
||
class FakeWebsiteClient:
|
||
def __init__(self, **kwargs):
|
||
self.kwargs = kwargs
|
||
def __enter__(self):
|
||
return self
|
||
def __exit__(self, exc_type, exc, tb):
|
||
return False
|
||
def get_groups(self, endpoint="/groups"):
|
||
return [{"id": "7", "name": "Target", "platform": "openai"}]
|
||
def create_account(self, body, endpoint="/accounts"):
|
||
account_bodies.append((endpoint, body))
|
||
return {"id": 202, "name": body["name"]}
|
||
def account_exists(self, account_id):
|
||
return True
|
||
@staticmethod
|
||
def extract_id(data):
|
||
return str(data.get("id"))
|
||
|
||
monkeypatch.setattr(websites_router, "Sub2ApiWebsiteClient", FakeWebsiteClient)
|
||
|
||
websites_router.import_upstream_keys_as_accounts(
|
||
website.id,
|
||
ImportAccountsRequest(
|
||
upstream_key_ids=[generated.id],
|
||
target_group_map={"vip": "7"},
|
||
account_name_prefix="SmartUp",
|
||
default_platform="openai",
|
||
concurrency=20,
|
||
priority=5,
|
||
),
|
||
db_session,
|
||
object(),
|
||
)
|
||
|
||
assert len(account_bodies) == 1
|
||
_, body = account_bodies[0]
|
||
assert body["concurrency"] == 20
|
||
assert body["priority"] == 5
|
||
assert body["credentials"]["base_url"] == "http://packy.local"
|
||
|
||
|
||
def test_import_platform_from_snapshot(monkeypatch, db_session):
|
||
"""验证快照 platform=anthropic 且名称不含 Claude 时,创建账号平台必须为 anthropic"""
|
||
from app.models.snapshot import UpstreamRateSnapshot
|
||
website, generated = seed_account_import_rows(db_session)
|
||
generated.key_name = "MyCustomKey" # 不含 Claude
|
||
|
||
# 写入快照指定平台为 anthropic
|
||
snapshot = UpstreamRateSnapshot(
|
||
upstream_id=generated.upstream_id,
|
||
snapshot_json=json.dumps({
|
||
"groups": {
|
||
"vip": {
|
||
"group_name": "VIP",
|
||
"rate": 0.1,
|
||
"platform": "anthropic"
|
||
}
|
||
}
|
||
})
|
||
)
|
||
db_session.add(snapshot)
|
||
db_session.commit()
|
||
|
||
account_bodies = []
|
||
class FakeWebsiteClient:
|
||
def __init__(self, **kwargs):
|
||
pass
|
||
def __enter__(self): return self
|
||
def __exit__(self, *args): return False
|
||
def get_groups(self, endpoint="/groups"):
|
||
return [{"id": "7", "name": "Target", "platform": "anthropic"}]
|
||
def create_account(self, body, endpoint="/accounts"):
|
||
account_bodies.append(body)
|
||
return {"id": 101, "name": body["name"]}
|
||
def account_exists(self, account_id): return True
|
||
@staticmethod
|
||
def extract_id(data): return "101"
|
||
|
||
monkeypatch.setattr(websites_router, "Sub2ApiWebsiteClient", FakeWebsiteClient)
|
||
|
||
websites_router.import_upstream_keys_as_accounts(
|
||
website.id,
|
||
ImportAccountsRequest(
|
||
upstream_key_ids=[generated.id],
|
||
target_group_map={"vip": "7"},
|
||
default_platform="openai",
|
||
platform_mode="auto",
|
||
),
|
||
db_session,
|
||
object(),
|
||
)
|
||
assert len(account_bodies) == 1
|
||
assert account_bodies[0]["platform"] == "anthropic"
|
||
|
||
|
||
def test_import_platform_fallback_to_name(monkeypatch, db_session):
|
||
"""验证快照无 platform 但名称含 Claude 时,仍兜底为 anthropic"""
|
||
from app.models.snapshot import UpstreamRateSnapshot
|
||
website, generated = seed_account_import_rows(db_session)
|
||
generated.key_name = "Claude-Key"
|
||
|
||
# 写入快照但不含 platform 字段
|
||
snapshot = UpstreamRateSnapshot(
|
||
upstream_id=generated.upstream_id,
|
||
snapshot_json=json.dumps({
|
||
"groups": {
|
||
"vip": {
|
||
"group_name": "VIP",
|
||
"rate": 0.1,
|
||
}
|
||
}
|
||
})
|
||
)
|
||
db_session.add(snapshot)
|
||
db_session.commit()
|
||
|
||
account_bodies = []
|
||
class FakeWebsiteClient:
|
||
def __init__(self, **kwargs):
|
||
pass
|
||
def __enter__(self): return self
|
||
def __exit__(self, *args): return False
|
||
def get_groups(self, endpoint="/groups"):
|
||
return [{"id": "7", "name": "Target", "platform": "anthropic"}]
|
||
def create_account(self, body, endpoint="/accounts"):
|
||
account_bodies.append(body)
|
||
return {"id": 101, "name": body["name"]}
|
||
def account_exists(self, account_id): return True
|
||
@staticmethod
|
||
def extract_id(data): return "101"
|
||
|
||
monkeypatch.setattr(websites_router, "Sub2ApiWebsiteClient", FakeWebsiteClient)
|
||
|
||
websites_router.import_upstream_keys_as_accounts(
|
||
website.id,
|
||
ImportAccountsRequest(
|
||
upstream_key_ids=[generated.id],
|
||
target_group_map={"vip": "7"},
|
||
default_platform="openai",
|
||
platform_mode="auto",
|
||
),
|
||
db_session,
|
||
object(),
|
||
)
|
||
assert len(account_bodies) == 1
|
||
assert account_bodies[0]["platform"] == "anthropic"
|
||
|
||
|
||
def test_import_platform_manual_override(monkeypatch, db_session):
|
||
"""手动模式下不使用快照平台,仍按用户指定平台"""
|
||
from app.models.snapshot import UpstreamRateSnapshot
|
||
website, generated = seed_account_import_rows(db_session)
|
||
|
||
# 快照虽然指定为 anthropic
|
||
snapshot = UpstreamRateSnapshot(
|
||
upstream_id=generated.upstream_id,
|
||
snapshot_json=json.dumps({
|
||
"groups": {
|
||
"vip": {
|
||
"group_name": "VIP",
|
||
"rate": 0.1,
|
||
"platform": "anthropic"
|
||
}
|
||
}
|
||
})
|
||
)
|
||
db_session.add(snapshot)
|
||
db_session.commit()
|
||
|
||
account_bodies = []
|
||
class FakeWebsiteClient:
|
||
def __init__(self, **kwargs):
|
||
pass
|
||
def __enter__(self): return self
|
||
def __exit__(self, *args): return False
|
||
def get_groups(self, endpoint="/groups"):
|
||
return [{"id": "7", "name": "Target", "platform": "gemini"}]
|
||
def create_account(self, body, endpoint="/accounts"):
|
||
account_bodies.append(body)
|
||
return {"id": 101, "name": body["name"]}
|
||
def account_exists(self, account_id): return True
|
||
@staticmethod
|
||
def extract_id(data): return "101"
|
||
|
||
monkeypatch.setattr(websites_router, "Sub2ApiWebsiteClient", FakeWebsiteClient)
|
||
|
||
websites_router.import_upstream_keys_as_accounts(
|
||
website.id,
|
||
ImportAccountsRequest(
|
||
upstream_key_ids=[generated.id],
|
||
target_group_map={"vip": "7"},
|
||
default_platform="gemini",
|
||
platform_mode="manual", # 手动模式
|
||
),
|
||
db_session,
|
||
object(),
|
||
)
|
||
assert len(account_bodies) == 1
|
||
assert account_bodies[0]["platform"] == "gemini"
|
||
|
||
|
||
def test_same_key_can_be_imported_into_two_platform_groups(monkeypatch, db_session):
|
||
"""同一 Key 的不同平台映射独立创建,迁移其中一平台不影响另一平台。"""
|
||
website, generated = seed_account_import_rows(db_session)
|
||
|
||
class FakeClient:
|
||
accounts = {}
|
||
next_id = 100
|
||
|
||
def __init__(self, **kwargs):
|
||
pass
|
||
def __enter__(self):
|
||
return self
|
||
def __exit__(self, *args):
|
||
return False
|
||
def get_groups(self, endpoint="/groups"):
|
||
return [
|
||
{"id": "7", "name": "OpenAI", "platform": "openai"},
|
||
{"id": "8", "name": "Claude", "platform": "anthropic"},
|
||
{"id": "9", "name": "OpenAI 2", "platform": "openai"},
|
||
]
|
||
def list_accounts(self):
|
||
return list(self.accounts.values())
|
||
def create_account(self, body, endpoint="/accounts"):
|
||
type(self).next_id += 1
|
||
account = {"id": str(type(self).next_id), **body}
|
||
type(self).accounts[account["id"]] = account
|
||
return account
|
||
def update_account(self, account_id, body):
|
||
type(self).accounts[str(account_id)].update(body)
|
||
return type(self).accounts[str(account_id)]
|
||
@staticmethod
|
||
def extract_id(data):
|
||
return str(data.get("id"))
|
||
|
||
monkeypatch.setattr(websites_router, "Sub2ApiWebsiteClient", FakeClient)
|
||
monkeypatch.setattr(websites_router, "reconcile_upstream_keys_full", lambda db, uid: None)
|
||
|
||
def do_import(target_group_id):
|
||
return websites_router.import_upstream_keys_as_accounts(
|
||
website.id,
|
||
ImportAccountsRequest(
|
||
upstream_key_ids=[generated.id],
|
||
target_group_map={"vip": target_group_id},
|
||
auto_priority_by_rate=False,
|
||
),
|
||
db_session,
|
||
object(),
|
||
)
|
||
|
||
first = do_import("7")
|
||
second = do_import("8")
|
||
third = do_import("9")
|
||
|
||
assert first.items[0].status == "created"
|
||
assert second.items[0].status == "created"
|
||
assert third.items[0].status == "exists"
|
||
assert third.items[0].platform == "openai"
|
||
|
||
links = db_session.query(websites_router.UpstreamKeyAccountLink).filter_by(
|
||
upstream_key_id=generated.id,
|
||
website_id=website.id,
|
||
status="active",
|
||
).all()
|
||
assert {(link.platform, link.target_group) for link in links} == {
|
||
("openai", "9"),
|
||
("anthropic", "8"),
|
||
}
|
||
assert len(FakeClient.accounts) == 2
|