Files
soul-yongping/scripts/test/process/test_article_mention_ckb_flow.py
卡若 76965adb23 chore: 清理敏感与开发文档,仅同步代码
- 永久忽略并从仓库移除 开发文档/
- 移除并忽略 .env 与小程序私有配置
- 同步小程序/管理端/API与脚本改动

Made-with: Cursor
2026-03-17 17:50:12 +08:00

219 lines
7.9 KiB
Python
Raw 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.

# -*- coding: utf-8 -*-
"""
流程测试:文章编辑 @某人 不存在时自动创建 Person + 存客宝获客计划
需求来源:临时需求池/2026-03-16-文章编辑自动创建@和#.md
验收:编辑文章输入 @新人物(链接人与事中无)→ 保存 → 链接人与事列表出现「新人物」,存客宝有对应计划
流程:管理端 ensureMentionsAndTags 对 content 中 @name 调用 POST /api/db/persons {name}
→ 后端按 name 查找,不存在则创建 Person + 调用存客宝创建获客计划
前置条件:存客宝 API 可连通CKB_OPEN_API_KEY 等配置正确),且存在名为 soul 的设备;否则创建新 Person 会失败
"""
import random
import time
import pytest
import requests
from util import admin_headers
def _unique_name():
"""生成唯一名称,避免与已有 Person 冲突"""
return f"测试自动创建_{int(time.time() * 1000)}"
def test_person_ensure_creates_ckb_plan_when_not_exists(admin_token, base_url):
"""
@某人 不存在时POST /api/db/persons 仅传 name → 应创建 Person 并自动创建存客宝获客计划
"""
if not admin_token:
pytest.skip("admin 登录失败,跳过")
name = _unique_name()
r = requests.post(
f"{base_url}/api/db/persons",
headers=admin_headers(admin_token),
json={"name": name},
timeout=15,
)
assert r.status_code == 200, f"响应: {r.text}"
data = r.json()
assert data.get("success") is True, f"success 应为 true: {data}"
person = data.get("person")
assert person is not None, "应返回 person"
assert person.get("name") == name
assert person.get("personId"), "应有 personId"
assert person.get("token"), "应有 token小程序 @ 点击时兑换密钥)"
# 存客宝获客计划应已创建
ckb_plan_id = person.get("ckbPlanId") or 0
assert ckb_plan_id > 0, f"应自动创建存客宝计划ckbPlanId 应 > 0实际: {ckb_plan_id}"
assert person.get("ckbApiKey"), "应有 ckbApiKey"
def test_person_ensure_returns_existing_when_name_exists(admin_token, base_url):
"""
@某人 已存在时POST /api/db/persons 仅传 name → 应返回已有 Person不重复创建
"""
if not admin_token:
pytest.skip("admin 登录失败,跳过")
name = _unique_name()
# 第一次创建
r1 = requests.post(
f"{base_url}/api/db/persons",
headers=admin_headers(admin_token),
json={"name": name},
timeout=15,
)
assert r1.status_code == 200 and r1.json().get("success")
first_id = r1.json()["person"]["personId"]
# 第二次相同 name应返回已有
r2 = requests.post(
f"{base_url}/api/db/persons",
headers=admin_headers(admin_token),
json={"name": name},
timeout=15,
)
assert r2.status_code == 200 and r2.json().get("success")
second = r2.json()["person"]
assert second["personId"] == first_id, "相同 name 应返回同一 Person"
def test_person_ensure_rejects_empty_name(admin_token, base_url):
"""name 为空时 POST /api/db/persons 应返回错误(不依赖存客宝)"""
if not admin_token:
pytest.skip("admin 登录失败,跳过")
r = requests.post(
f"{base_url}/api/db/persons",
headers=admin_headers(admin_token),
json={"name": ""},
timeout=10,
)
assert r.status_code == 200
data = r.json()
assert data.get("success") is False
assert "name" in (data.get("error") or "").lower() or "必填" in (data.get("error") or "")
def test_article_mention_flow_persons_list_contains_new(admin_token, base_url):
"""
流程:创建新 Person 后GET /api/db/persons 列表应包含该人
"""
if not admin_token:
pytest.skip("admin 登录失败,跳过")
name = _unique_name()
r_create = requests.post(
f"{base_url}/api/db/persons",
headers=admin_headers(admin_token),
json={"name": name},
timeout=15,
)
assert r_create.status_code == 200 and r_create.json().get("success")
person_id = r_create.json()["person"]["personId"]
# 拉列表
r_list = requests.get(
f"{base_url}/api/db/persons",
headers=admin_headers(admin_token),
timeout=10,
)
assert r_list.status_code == 200 and r_list.json().get("success")
persons = r_list.json().get("persons") or []
found = [p for p in persons if p.get("personId") == person_id]
assert len(found) == 1, f"列表应包含新建的 Person {person_id}"
assert found[0].get("ckbPlanId", 0) > 0, "列表中应有 ckbPlanId"
def test_new_article_save_auto_creates_person_and_ckb(admin_token, base_url):
"""
完整流程新建文章content 含 @新人物 → 保存时 ensureMentionsAndTags 自动 POST persons
→ 创建 Person + 存客宝获客计划 → 再 PUT book 保存文章
"""
if not admin_token:
pytest.skip("admin 登录失败,跳过")
ts = int(time.time() * 1000)
rnd = random.randint(100000, 999999)
name = f"测试新人物_{ts}_{rnd}"
# chapters.id 限制 size:20用短 id
section_id = f"t{rnd}"
# 1. 获取 book 结构,取第一个 part/chapter
r_list = requests.get(
f"{base_url}/api/db/book?action=list",
headers=admin_headers(admin_token),
timeout=10,
)
assert r_list.status_code == 200 and r_list.json().get("success")
sections = r_list.json().get("sections") or []
part_id = "part-1"
chapter_id = "chapter-1"
part_title = "未分类"
chapter_title = "未分类"
if sections:
first = sections[0]
part_id = first.get("partId") or part_id
chapter_id = first.get("chapterId") or chapter_id
part_title = first.get("partTitle") or part_title
chapter_title = first.get("chapterTitle") or chapter_title
# 2. 模拟 ensureMentionsAndTagscontent 含 @name 时先 POST persons
content = f"这是一篇测试文章,@{name} 会被自动创建并同步存客宝。"
r_person = requests.post(
f"{base_url}/api/db/persons",
headers=admin_headers(admin_token),
json={"name": name},
timeout=15,
)
assert r_person.status_code == 200, f"创建 Person 失败: {r_person.text}"
person_data = r_person.json()
assert person_data.get("success") is True, f"Person 创建失败: {person_data}"
person = person_data.get("person")
assert person and person.get("ckbPlanId", 0) > 0, "应自动创建存客宝获客计划"
# 3. 新建文章PUT /api/db/book
payload = {
"id": section_id,
"title": f"测试自动创建_{ts}",
"content": content,
"price": 1,
"isFree": False,
"partId": part_id,
"partTitle": part_title,
"chapterId": chapter_id,
"chapterTitle": chapter_title,
"editionStandard": True,
"editionPremium": False,
"isNew": False,
"hotScore": 0,
}
r_put = requests.put(
f"{base_url}/api/db/book",
headers=admin_headers(admin_token),
json=payload,
timeout=15,
)
assert r_put.status_code == 200, f"保存文章失败: {r_put.text}"
put_data = r_put.json()
assert put_data.get("success") is True, f"保存文章失败: {put_data}"
# 4. 验证 persons 列表包含新人物
r_persons = requests.get(
f"{base_url}/api/db/persons",
headers=admin_headers(admin_token),
timeout=10,
)
assert r_persons.status_code == 200
persons = r_persons.json().get("persons") or []
found = [p for p in persons if p.get("name") == name]
assert len(found) == 1, f"链接人与事列表应包含「{name}"
assert found[0].get("ckbPlanId", 0) > 0, "应有存客宝获客计划"
# 5. 清理:删除测试文章(避免重复运行冲突)
try:
requests.delete(
f"{base_url}/api/db/book?id={section_id}",
headers=admin_headers(admin_token),
timeout=10,
)
except Exception:
pass