Files
wehub-resource-sync 2114b14ee0
Sync main into demo / sync (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:35:26 +08:00

639 lines
29 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.
"""
Account specialized task definitions.
覆盖注册、登录、改密、找回密码、验证码、设备信任、注销等账号流程。
"""
# -- Task Index (auto-generated, do not edit) --
# 5 tasks | L2×2 L3×2 L4×1
#
# [L3] Railway12306LoginWithAccount 备忘录里标题为{noteTitle}的笔记记着铁路12306账号{username}的几个候选密码,帮我试出哪个能登录,并把笔记改成只保留正确密码
# [L4] Railway12306RegisterThenLogin 帮我注册一个新的铁路12306账号{username},密码是{password},姓名{name},身份证{idNo},手机号{phone},邮箱{email},然后登陆
# [L2] Railway12306ChangePassword 帮我把铁路12306的登录密码从{oldPassword}改成{newPassword}
# [L2] WechatAccountCancellation 帮我把微信账号注销掉
# [L3] Railway12306ForgotPasswordReset 帮我用手机号{accountPhone}和证件号{idNo}找回 12306 密码,把新密码设成{newPassword},再用新密码登录一次
# -- End Task Index --
from __future__ import annotations
from typing import Any
from bench_env.task.base import BaseTask
from bench_env.task.judge import JudgeInput
from bench_env.task.notes.app import Notes
from bench_env.task.common_tasks import CriteriaTask
from bench_env.task.railway12306.app import Railway12306
from bench_env.task.utils import (
now_ms,
)
class Railway12306LoginWithAccount(BaseTask):
"""判定:12306 以目标用户名登录成功 + 笔记只保留正确密码。"""
templates = [
"备忘录里标题为{noteTitle}的笔记记着铁路12306账号{username}的几个候选密码,帮我试出哪个能登录,并把笔记改成只保留正确密码",
"The note titled {noteTitle} contains several candidate passwords for Railway 12306 account {username}. Please try them to find which one works, then edit the note to keep only the correct password",
]
scope = "S2"
objective = "operate"
composition = "transfer"
difficulty = "L3"
max_steps = 60
capabilities = ["search", "edit", "handoff"]
apps = ["railway12306", "notes"]
expected_changes = [
"railway12306.isLoggedIn",
"railway12306.loginUser",
"railway12306.auth",
"railway12306.lastLoginHint",
"railway12306.account",
"notes.notes",
]
parameters = {
"noteTitle": {"type": "string", "default": "12306账号密码", "description": "备忘录标题"},
"username": {"type": "string", "default": "user_123", "description": "登录用户名"},
"correctPassword": {"type": "string", "default": "123456", "description": "正确密码"},
"otherPasswords": {"type": "string", "default": "111111,888888,password", "description": "错误密码列表(逗号分隔)"},
}
async def _post_sample(self, env: Any) -> None:
state = await env.get_state()
now = now_ms(state["os"])
user = str(self.p.username)
correct = str(self.p.correctPassword)
others = [x.strip() for x in str(self.p.otherPasswords).split(",") if x.strip()]
all_pwds = others + [correct]
content = f"账号:{user}\n密码:\n" + "\n".join(all_pwds)
await env.set_state({
"apps": {
"notes": {
"notes": [
{
"id": "note_12306_login",
"title": str(self.p.noteTitle),
"content": content,
"updatedAt": now,
}
]
},
"railway12306": {
"isLoggedIn": False,
"loginUser": None,
"auth": {
"accounts": [
{"username": user, "password": correct, "phone": "13800000000", "email": f"{user}@example.com"},
],
"loginAttempts": [],
"lastLoginHint": "",
},
}
}
}, deep=True, reload=False)
def check_goals(self, input: JudgeInput) -> list[dict[str, Any]]:
rail = Railway12306(input.apps["railway12306"])
notes = Notes(input.apps["notes"])
note = notes.latest_note_by_title(str(self.p.noteTitle))
note_content = str(note["content"]) if note else "(笔记不存在)"
target_pwd = str(self.p.correctPassword)
others = [x.strip() for x in str(self.p.otherPasswords).split(",") if x.strip()]
has_correct = target_pwd in note_content
has_others = any(bad in note_content for bad in others if bad and bad != target_pwd)
note_ok = note is not None and has_correct and not has_others
return [
rail.check_login_success(str(self.p.username)),
{
"field": "notes.content.filtered",
"expected": f"只保留正确密码 '{target_pwd}'",
"actual": note_content[:200] if note else "(笔记不存在)",
"passed": note_ok,
},
]
class Railway12306RegisterThenLogin(BaseTask):
"""判定:12306 账号已创建(密码匹配)、已登录、profile 信息脱敏后正确。"""
templates = [
"帮我注册一个新的铁路12306账号{username},密码是{password},姓名{name},身份证{idNo},手机号{phone},邮箱{email},然后登陆",
"Register a new Railway 12306 account {username} with password {password}, name {name}, ID number {idNo}, phone {phone}, email {email}, then log in",
]
scope = "S1"
objective = "operate"
composition = "sequential"
difficulty = "L4"
capabilities = ["create", "edit"]
apps = ["railway12306"]
expected_changes = [
"isLoggedIn",
"loginUser",
"auth",
"user",
"passengers",
"lastLoginHint",
"userProfile",
"account",
"os.providers.sms",
"os.notifications",
"apps.sms",
]
parameters = {
"username": {"type": "string", "default": "new_user_001", "description": "注册用户名"},
"password": {"type": "string", "default": "Reg2026x", "description": "注册密码"},
"name": {"type": "string", "default": "张三", "description": "姓名"},
"idNo": {"type": "string", "default": "110101199001011234", "description": "身份证号"},
"phone": {"type": "string", "default": "13800000000", "description": "手机号"},
"email": {"type": "string", "default": "new_user_001@example.com", "description": "邮箱"},
}
async def _prepare(self, env: Any) -> None:
await env.set_state({
"apps": {
"railway12306": {
"isLoggedIn": False,
"loginUser": None,
}
}
}, deep=True, reload=False)
def check_goals(self, input: JudgeInput) -> list[dict[str, Any]]:
rail = Railway12306(input.apps["railway12306"])
return [
rail.check_registration(
username=str(self.p.username),
password=str(self.p.password),
name=str(self.p.name),
phone=str(self.p.phone),
),
]
class Railway12306ChangePassword(BaseTask):
"""判定:loginUser 和 auth.accounts 中密码均已更新为新密码,且保持登录状态。"""
templates = [
"帮我把铁路12306的登录密码从{oldPassword}改成{newPassword}",
"Change my Railway 12306 login password from {oldPassword} to {newPassword}",
]
scope = "S1"
objective = "operate"
composition = "sequential"
difficulty = "L2"
max_steps = 45
capabilities = ["settings", "edit"]
apps = ["railway12306"]
expected_changes = [
"loginUser",
"auth",
"os.providers.sms.messagesByConversationId",
"os.providers.sms.conversations",
"os.notifications",
]
parameters = {
"oldPassword": {"type": "string", "default": "123456", "description": "原密码"},
"newPassword": {"type": "string", "default": "Abc@5678", "description": "新密码"},
}
async def _post_sample(self, env: Any) -> None:
await env.set_state({
"apps": {
"railway12306": {
"isLoggedIn": True,
"loginUser": {
"username": "user_123",
"password": str(self.p.oldPassword),
},
"auth": {
"accounts[username=user_123]": None,
"accounts[]": {
"username": "user_123",
"password": str(self.p.oldPassword),
"name": "测试用户",
"phone": "13812348520",
"idNo": "310101199501017821",
"email": "test@example.com",
},
},
}
}
}, deep=True, reload=False)
def check_goals(self, input: JudgeInput) -> list[dict[str, Any]]:
rail = Railway12306(input.apps["railway12306"])
return [
rail.check_password_changed(str(self.p.newPassword)),
]
class WechatAccountCancellation(CriteriaTask):
"""判定:user.accountStatus 变为 cancelled。"""
templates = [
"帮我把微信账号注销掉",
"Cancel my WeChat account",
]
scope = "S1"
objective = "operate"
composition = "sequential"
difficulty = "L2"
capabilities = ["settings", "delete"]
apps = ["wechat"]
criteria = {"user.accountStatus": "cancelled"}
expected_changes = ["user", "auth"]
class Railway12306ForgotPasswordReset(BaseTask):
"""注入:创建目标手机号的账号并设初始密码,清空登录状态。
判定:密码已更新 + 验证码已验证(必经之路)+ 用新密码登录成功。
"""
templates = [
"帮我用手机号{accountPhone}和证件号{idNo}找回 12306 密码,把新密码设成{newPassword},再用新密码登录一次",
"Use phone number {accountPhone} and ID number {idNo} to recover my 12306 password, set the new password to {newPassword}, then log in once with the new password",
]
scope = "S1"
objective = "operate"
composition = "sequential"
difficulty = "L3"
max_steps = 60
capabilities = ["settings", "edit"]
apps = ["railway12306"]
expected_changes = [
"auth",
"isLoggedIn",
"loginUser",
"lastLoginHint",
"userProfile",
"user",
"account",
"os.providers.sms",
"os.notifications",
]
parameters = {
"accountPhone": {"type": "string", "default": "17366666695", "description": "账号手机号"},
"idNo": {"type": "string", "default": "110101199001011234", "description": "证件号码"},
"newPassword": {"type": "string", "default": "NewP@ssw0rd123", "description": "新密码"},
}
async def _post_sample(self, env: Any) -> None:
phone = str(self.p.accountPhone)
id_no = str(self.p.idNo)
await env.set_state({
"apps": {
"railway12306": {
"isLoggedIn": False,
"loginUser": None,
"auth": {
f"accounts[phone={phone}]": None,
"accounts[]": {
"username": "user_" + phone,
"phone": phone,
"password": "railway_pwd_123",
"name": "Test User",
"idNo": id_no,
"email": "test@example.com",
},
"loginAttempts": [],
"resetVerification": None,
"resetVerificationAttempts": [],
}
}
}
}, deep=True, reload=False)
def check_goals(self, input: JudgeInput) -> list[dict[str, Any]]:
rail = Railway12306(input.apps["railway12306"])
phone = str(self.p.accountPhone)
new_pwd = str(self.p.newPassword)
acc = rail.account_by_phone(phone)
pwd_now = str(acc["password"]) if acc else "(账号不存在)"
pwd_updated = acc is not None and pwd_now == new_pwd
reset_attempts = rail.auth["resetVerificationAttempts"]
code_verified = any(
str(item["phone"]) == phone and bool(item["ok"])
for item in reset_attempts
)
login_user = rail.login_user
logged_ok = (
rail.is_logged_in
and login_user is not None
and str(login_user["password"]) == new_pwd
and str(login_user["phone"]) == phone
)
return [
{
"field": "password_updated",
"expected": new_pwd,
"actual": pwd_now,
"passed": pwd_updated,
},
{
"field": "code_verified",
"expected": f"phone={phone} 验证码已验证",
"actual": f"已验证" if code_verified else "未完成验证",
"passed": code_verified,
},
{
"field": "logged_with_new_password",
"expected": f"已登录 phone={phone} pwd={new_pwd}",
"actual": (
f"loggedIn={rail.is_logged_in}, user={login_user['phone'] if login_user else '(无)'}"
),
"passed": logged_ok,
},
]
## 微信登录页面未完善,相关任务全部搁置
# class WechatWrongPasswordThenCaptchaLogin(BaseTask):
# """
# 让Agent从备忘录读取候选密码,逐个尝试登录微信(期间触发安全验证),
# 成功后把笔记改成只留下正确密码。
# """
# templates = ["备忘录里有几个我用过的微信密码,但是我不确定哪个是现在用的,帮我试出哪个能登录,然后登陆微信,并把笔记改成只留下正确密码"]
# scope = "S2"
# objective = "operate"
# composition = "transfer"
# difficulty = "L3"
# capabilities = ["search", "edit", "transfer"]
# apps = ["wechat", "notes"]
# expected_changes = [
# "apps.wechat.auth",
# "apps.notes.notes",
# ]
# parameters = {
# "password": {"type": "string", "default": "wechat_pwd_123", "description": "正确密码"},
# "otherPasswords": {"type": "string", "default": "abc123,qwerty,pass999", "description": "错误密码列表(逗号分隔)"},
# }
# async def _post_sample(self, env: Any) -> None:
# state = await env.get_state()
# device_id = _os_device_id(state["os"])
# now = now_ms(state["os"])
# auth = state["apps"]["wechat"]["auth"]
# session_phone = auth["session"]["phone"]
# user_phone = state["apps"]["wechat"]["user"]["phone"]
# phone = str(session_phone or user_phone or "17366666695")
# accounts = [account for account in auth["accounts"] if str(account["phone"]) != phone]
# accounts.append({"phone": phone, "password": str(self.p.password), "createdAt": now, "activated": True, "failedAttempts": 0, "requireCaptcha": False})
# trusted = dict(auth["trustedDevicesByPhone"])
# trusted[phone] = [{"deviceId": device_id, "deviceName": device_id, "trustedAt": 0}]
# others = [x.strip() for x in str(self.p.otherPasswords).split(",") if x.strip()]
# all_pwds = others + [str(self.p.password)]
# note_content = "\n".join(all_pwds)
# await env.set_state({
# "apps": {
# "wechat": {
# "auth": {
# "accounts": accounts,
# "session": {"loggedIn": False, "phone": None, "expiresAt": None, "currentDeviceId": device_id},
# "trustedDevicesByPhone": trusted,
# "loginAttempts": [],
# "pendingTrustDevice": None,
# }
# },
# "notes": {
# "notes": [
# {
# "id": "note_wechat_pwd",
# "title": "微信密码",
# "content": note_content,
# "updatedAt": now,
# }
# ]
# },
# }
# }, deep=True, reload=False)
# def check_goals(self, input: JudgeInput) -> list[dict[str, Any]]:
# wechat = Wechat(input.apps["wechat"])
# session = wechat.session
# logged_in = bool(session.get("loggedIn"))
# phone = str(session.get("phone") or wechat.user.get("phone") or "")
# login_ok = logged_in and bool(phone)
# notes = Notes(input.apps["notes"])
# all_notes = notes.notes
# target_pwd = str(self.p.password)
# others = [x.strip() for x in str(self.p.otherPasswords).split(",") if x.strip()]
# matched_note = next(
# (n for n in all_notes if target_pwd in str(n.get("content") or "")),
# None,
# )
# note_content = str(matched_note.get("content") or "") if matched_note else ""
# has_correct = target_pwd in note_content
# has_others = any(bad in note_content for bad in others if bad and bad != target_pwd)
# note_ok = matched_note is not None and has_correct and not has_others
# return [
# {"field": "wechat.login.success", "expected": f"loggedIn with phone={phone}", "actual": f"loggedIn={logged_in}, phone={phone}", "passed": login_ok},
# {"field": "notes.content.filtered", "expected": f"only correct password '{target_pwd}'", "actual": {"has_correct": has_correct, "has_others": has_others, "content": note_content[:200]}, "passed": note_ok},
# ]
# class WechatNewDeviceLoginTrustThenVerifyInDeviceManagement(BaseTask):
# templates = ["帮我用密码{password}登录手机号为{newDeviceLogin}的微信账号,如果出现设备识别提示就信任这台设备,并确认它已经出现在设备管理里"]
# scope = "S1"
# objective = "operate"
# composition = "sequential"
# difficulty = "L3"
# capabilities = ["settings"]
# apps = ["wechat"]
# expected_changes = ["auth"]
# parameters = {
# "newDeviceLogin": {"type": "string", "default": "17366666695", "description": "登录手机号"},
# "password": {"type": "string", "default": "wechat_pwd_123", "description": "密码"},
# }
# async def _post_sample(self, env: Any) -> None:
# state = await env.get_state()
# device_id = _os_device_id(state["os"])
# phone = str(self.p.newDeviceLogin)
# auth = state["apps"]["wechat"]["auth"]
# accounts = list(auth["accounts"])
# if not any(str(account["phone"]) == phone for account in accounts):
# accounts.append({"phone": phone, "password": str(self.p.password), "createdAt": now_ms(state["os"]), "activated": True, "failedAttempts": 0, "requireCaptcha": False})
# trusted = dict(auth["trustedDevicesByPhone"])
# trusted[phone] = [{"deviceId": "old_device_x", "deviceName": "旧设备", "trustedAt": 0}]
# await env.set_state({
# "apps": {
# "wechat": {
# "auth": {
# "accounts": accounts,
# "session": {"loggedIn": False, "phone": None, "expiresAt": None, "currentDeviceId": device_id},
# "trustedDevicesByPhone": trusted,
# "loginAttempts": [],
# "pendingTrustDevice": None,
# "captcha": {"requiredAfterFailures": 3},
# }
# }
# }
# }, deep=True, reload=False)
# def check_goals(self, input: JudgeInput) -> list[dict[str, Any]]:
# wechat = Wechat(input.apps["wechat"])
# phone = str(self.p.newDeviceLogin)
# attempts = wechat.login_attempts
# prompted = any(str(item["phone"]) == phone and str(item["reason"]) == "untrusted_device" for item in attempts)
# session = wechat.session
# logged_ok = bool(session["loggedIn"]) and str(session["phone"]) == phone
# device_id = _os_device_id(input.os)
# trusted_list = (wechat.auth.get("trustedDevicesByPhone") or {}).get(phone, [])
# trusted_ok = any(str(item["deviceId"]) == device_id for item in trusted_list)
# pending_cleared = wechat.auth.get("pendingTrustDevice") is None
# return [
# {"field": "apps.wechat.auth.device_prompt.shown", "expected": True, "actual": prompted, "passed": prompted},
# {"field": "apps.wechat.auth.session.loggedIn", "expected": phone, "actual": session, "passed": logged_ok},
# {"field": "apps.wechat.auth.trustedDevices.contains_current", "expected": device_id, "actual": trusted_list, "passed": trusted_ok},
# {"field": "apps.wechat.auth.pendingTrustDevice.cleared", "expected": True, "actual": wechat.auth.get("pendingTrustDevice"), "passed": pending_cleared},
# ]
# class WechatVisitMeAndOpenAccountSecurityReloginIfExpired(BaseTask):
# templates = ["帮我进入微信里的“账号与安全”页面;如果登录失效了,就用密码{password}重新登录后继续进入"]
# scope = "S1"
# objective = "operate"
# composition = "sequential"
# difficulty = "L3"
# capabilities = ["settings"]
# apps = ["wechat"]
# expected_changes = ["auth"]
# parameters = {
# "_expireAfterSec": {"type": "int", "default": 2, "min": 1, "max": 10, "description": "内部登录失效秒数"},
# "password": {"type": "string", "default": "wechat_pwd_123", "description": "微信登录密码"},
# }
# async def _post_sample(self, env: Any) -> None:
# state = await env.get_state()
# os_state = state["os"]
# now = now_ms(os_state)
# expires_at = now + int(self.p._expireAfterSec) * 1000
# device_id = _os_device_id(os_state)
# auth = state["apps"]["wechat"]["auth"]
# phone = str(auth["session"]["phone"] or state["apps"]["wechat"]["user"]["phone"] or "17366666695")
# accounts = list(auth["accounts"])
# if not any(str(account["phone"]) == phone for account in accounts):
# accounts.append({"phone": phone, "password": str(self.p.password), "createdAt": now, "activated": True, "failedAttempts": 0, "requireCaptcha": False})
# trusted = dict(auth["trustedDevicesByPhone"])
# trusted[phone] = [{"deviceId": device_id, "deviceName": device_id, "trustedAt": 0}]
# await env.set_state({
# "apps": {
# "wechat": {
# "auth": {
# "accounts": accounts,
# "session": {"loggedIn": True, "phone": phone, "expiresAt": expires_at, "currentDeviceId": device_id},
# "trustedDevicesByPhone": trusted,
# "loginAttempts": [],
# "captcha": {"requiredAfterFailures": 3},
# "pendingTrustDevice": None,
# }
# }
# }
# }, deep=True, reload=False)
# def check_goals(self, input: JudgeInput) -> list[dict[str, Any]]:
# wechat = Wechat(input.apps["wechat"])
# session = wechat.session
# expired_at = session.get("lastExpiredAt")
# expired_once = bool(expired_at)
# logged_in = bool(session["loggedIn"])
# path = str(input.route["path"]) if "path" in input.route else ""
# on_target = path == "/settings/security"
# relogin_success = any(
# bool(item.get("ok"))
# and str(item.get("reason")) == "ok"
# and str(item.get("password")) == str(self.p.password)
# for item in wechat.login_attempts
# )
# relogin_if_needed = (not expired_once) or relogin_success
# return [
# {"field": "route", "expected": "/settings/security", "actual": path, "passed": on_target},
# {"field": "apps.wechat.auth.session.loggedIn", "expected": True, "actual": logged_in, "passed": logged_in},
# {"field": "apps.wechat.auth.session.relogin_if_expired", "expected": True, "actual": {"expired": expired_once, "reloginSuccess": relogin_success}, "passed": relogin_if_needed},
# ]
# class WechatRegisterNewAccountWithPhoneVerificationAndRealName(BaseTask):
# templates = ["帮我用手机号{phoneNumber}注册一个新微信账号,昵称叫{nickname},密码设成{password},并完成实名认证:姓名{realName}、身份证{idNumber}"]
# scope = "S1"
# objective = "operate"
# composition = "sequential"
# difficulty = "L4"
# capabilities = ["create", "settings"]
# apps = ["wechat"]
# expected_changes = ["auth"]
# parameters = {
# "phoneNumber": {"type": "string", "default": "13800000000", "description": "注册手机号"},
# "nickname": {"type": "string", "default": "小新用户", "description": "注册昵称"},
# "_verificationCodeLength": {"type": "int", "default": 6, "min": 4, "max": 8, "description": "内部验证码长度"},
# "password": {"type": "string", "default": "P@ssw0rd123", "description": "密码"},
# "realName": {"type": "string", "default": "张三", "description": "实名认证姓名"},
# "idNumber": {"type": "string", "default": "110101199001011234", "description": "身份证号"},
# }
# async def _post_sample(self, env: Any) -> None:
# state = await env.get_state()
# device_id = _os_device_id(state["os"])
# phone = str(self.p.phoneNumber)
# auth = state["apps"]["wechat"]["auth"]
# accounts = [account for account in auth["accounts"] if str(account["phone"]) != phone]
# trusted = dict(auth["trustedDevicesByPhone"])
# trusted[phone] = [{"deviceId": device_id, "deviceName": device_id, "trustedAt": 0}]
# await env.set_state({
# "apps": {
# "wechat": {
# "auth": {
# "accounts": accounts,
# "session": {"loggedIn": False, "phone": None, "expiresAt": None, "currentDeviceId": device_id},
# "trustedDevicesByPhone": trusted,
# "verificationCodes": [],
# "verificationAttempts": [],
# "loginAttempts": [],
# "verificationCodeLength": int(self.p._verificationCodeLength),
# "verificationCodeExpirySec": 60,
# "captcha": {"requiredAfterFailures": 3},
# "pendingTrustDevice": None,
# }
# }
# }
# }, deep=True, reload=False)
# def check_goals(self, input: JudgeInput) -> list[dict[str, Any]]:
# wechat = Wechat(input.apps["wechat"])
# wechat_init = Wechat(input.apps_init["wechat"])
# phone = str(self.p.phoneNumber)
# acc_before = wechat_init.account_by_phone(phone)
# acc = wechat.account_by_phone(phone)
# created_new = acc_before is None and acc is not None
# activated = acc is not None and bool(acc["activated"])
# real_ok = acc is not None and str(acc["realName"]) == str(self.p.realName)
# id_ok = acc is not None and str(acc["idNumber"]) == str(self.p.idNumber)
# pwd_ok = acc is not None and str(acc["password"]) == str(self.p.password)
# session = wechat.session
# logged_ok = bool(session["loggedIn"]) and str(session["phone"]) == phone
# used_ok = any(str(item["phone"]) == phone and bool(item["ok"]) for item in wechat.verification_attempts)
# used_code_ok = any(
# str(item.get("phone")) == phone and bool(item.get("used"))
# for item in wechat.verification_codes_for_phone(phone)
# )
# return [
# {"field": "apps.wechat.auth.accounts[new]", "expected": True, "actual": {"before": acc_before is not None, "after": acc is not None}, "passed": created_new},
# {"field": "apps.wechat.auth.account.activated", "expected": True, "actual": activated, "passed": activated},
# {"field": "apps.wechat.auth.account.realName", "expected": str(self.p.realName), "actual": None if acc is None else acc["realName"], "passed": real_ok},
# {"field": "apps.wechat.auth.account.idNumber", "expected": str(self.p.idNumber), "actual": None if acc is None else acc["idNumber"], "passed": id_ok},
# {"field": "apps.wechat.auth.account.password", "expected": str(self.p.password), "actual": None if acc is None else acc["password"], "passed": pwd_ok},
# {"field": "apps.wechat.auth.session.loggedIn", "expected": phone, "actual": session, "passed": logged_ok},
# {"field": "apps.wechat.auth.verification.used", "expected": True, "actual": used_ok, "passed": used_ok},
# {"field": "apps.wechat.auth.verification.code_used", "expected": True, "actual": used_code_ok, "passed": used_code_ok},
# ]