"""回归:只要账号还留着 CK,就不得因为登录批次状态而从工作台消失。 线上事故(2026-10-01):账号「远行者103」被分配给客服后,客服与管理员在 斗鱼工作台都看不到它,只有账号管理里还能搜到。原因是可见性判定写成了 ``LoginTask.status == "success"``,而普通登录批次启动时会把复用的成功记录 置为 ``pending``,CK 重登失败会置为 ``relogin_failed`` —— 两种状态都保留旧 Cookie,却让账号从工作台与 CK 列表里凭空消失。 """ import pytest from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker from web.backend.database import Base from web.backend.models import Account, LoginTask, User from web.backend.routers.cookies import list_cookies from web.backend.routers.douyu import list_task_account_ids, list_task_accounts from web.backend.services.account_service import cookie_account_ids_query from web.backend.services.douyu_service import ( create_douyu_planned_tasks, latest_cookie_login_task, visible_douyu_task_accounts, ) class TestDouyuCookieVisibility: def setup_method(self): self.engine = create_engine("sqlite://") Base.metadata.create_all(self.engine) self.session = sessionmaker(bind=self.engine)() self.admin = User(username="admin", password_hash="hash", role="super_admin") self.support = User(username="kf_7", password_hash="hash", role="support") self.session.add_all([self.admin, self.support]) self.session.commit() # relogin_failed:重登失败但旧 CK 已按设计保留 self.relogin_account = self._make_account("远行者103", self.support.id) self.session.add( LoginTask( batch_id="relogin-failed", account_id=self.relogin_account.id, created_by=self.support.id, status="relogin_failed", cookie="acf_uid=481802039", ck_check_status="valid", ) ) # pending:普通登录批次排队中,复用了成功记录,旧 CK 仍在 self.pending_account = self._make_account("排队中账号", self.support.id) self.session.add( LoginTask( batch_id="queued", account_id=self.pending_account.id, created_by=self.admin.id, status="pending", cookie="acf_uid=2", ) ) # 从未登录成功:没有 CK,必须继续隐藏 self.empty_account = self._make_account("无CK账号", self.support.id) self.session.add( LoginTask( batch_id="failed", account_id=self.empty_account.id, created_by=self.admin.id, status="failed", cookie="", ) ) self.session.commit() def _make_account(self, username: str, assigned_to: int) -> Account: account = Account( username=username, password="password", email=f"{username}@example.com", email_password="mail-password", assigned_to=assigned_to, ) self.session.add(account) self.session.flush() return account def teardown_method(self): self.session.close() Base.metadata.drop_all(self.engine) self.engine.dispose() def _task_account_names(self, current: User) -> set[str]: result = list_task_accounts( search="", tag="", ids="", account_names="", page=None, page_size=50, db=self.session, current=current, ) return {item.username for item in result} @pytest.mark.parametrize("current_attr", ["admin", "support"]) def test_workbench_lists_accounts_that_still_hold_cookie(self, current_attr): current = getattr(self, current_attr) names = self._task_account_names(current) assert {"远行者103", "排队中账号"} <= names assert "无CK账号" not in names def test_workbench_account_ids_keep_cookie_holders(self): result = list_task_account_ids( search="", tag="", account_names="", db=self.session, current=self.admin, ) assert set(result["account_ids"]) == { self.relogin_account.id, self.pending_account.id, } def test_latest_cookie_login_task_ignores_batch_status(self): task = latest_cookie_login_task(self.session, self.relogin_account.id) assert task is not None assert task.cookie == "acf_uid=481802039" def test_visible_task_accounts_include_retained_cookie(self): accounts = visible_douyu_task_accounts( self.session, [self.relogin_account.id, self.empty_account.id], ) assert [account.id for account in accounts] == [self.relogin_account.id] def test_account_has_cookie_filter_matches_workbench(self): ids = { account_id for (account_id,) in cookie_account_ids_query(self.session).all() } assert ids == {self.relogin_account.id, self.pending_account.id} def test_cookie_management_page_lists_retained_cookie(self): items = list_cookies( search="", tag="", account_names="", page=None, page_size=50, include_cookie=True, db=self.session, current=self.admin, ) assert {item["account_username"] for item in items} == { "远行者103", "排队中账号", } def test_retained_cookie_account_can_still_create_task(self): batch_id, created = create_douyu_planned_tasks( self.session, [self.relogin_account.id], "create_gold_qr", "elite", self.admin.id, ) assert batch_id assert created == 1