Files
soul-yongping/scripts/test/process/test_article_mention_ckb_flow.py

219 lines
7.9 KiB
Python
Raw Normal View History

# -*- 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