feat(knowledge): 批量把知识移到目录或其它工作区
This commit is contained in:
@@ -538,3 +538,227 @@ async def test_tags_endpoint_counts_documents(client: AsyncClient) -> None:
|
||||
)
|
||||
counts = {row["name"]: row["count"] for row in got["items"]}
|
||||
assert counts == {"发布": 2, "回滚": 1}
|
||||
|
||||
|
||||
async def _category(
|
||||
client: AsyncClient, user: dict[str, Any], ws_id: str, name: str
|
||||
) -> dict[str, Any]:
|
||||
return unwrap(
|
||||
await client.post(
|
||||
f"{V1}/workspaces/{ws_id}/knowledge/categories",
|
||||
json={"name": name},
|
||||
headers=user["headers"],
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
async def _note(
|
||||
client: AsyncClient, user: dict[str, Any], ws_id: str, title: str, **extra: Any
|
||||
) -> dict[str, Any]:
|
||||
return unwrap(
|
||||
await client.post(
|
||||
f"{V1}/workspaces/{ws_id}/knowledge",
|
||||
json={"title": title, "doc_type": "note", **extra},
|
||||
headers=user["headers"],
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
async def _bulk_move(
|
||||
client: AsyncClient, user: dict[str, Any], ws_id: str, body: dict[str, Any]
|
||||
) -> dict[str, Any]:
|
||||
return unwrap(
|
||||
await client.post(
|
||||
f"{V1}/workspaces/{ws_id}/knowledge/bulk-move",
|
||||
json=body,
|
||||
headers=user["headers"],
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_bulk_move_into_category_of_same_workspace(client: AsyncClient) -> None:
|
||||
owner = await login(client)
|
||||
ws = await create_workspace(client, owner, "整理知识")
|
||||
cat = await _category(client, owner, ws["id"], "制度")
|
||||
first = await _note(client, owner, ws["id"], "报销要贴发票原件")
|
||||
second = await _note(client, owner, ws["id"], "请假提前一天提")
|
||||
|
||||
moved = await _bulk_move(
|
||||
client,
|
||||
owner,
|
||||
ws["id"],
|
||||
{"item_ids": [first["id"], second["id"]], "category_id": cat["id"]},
|
||||
)
|
||||
assert sorted(moved["moved"]) == sorted([first["id"], second["id"]])
|
||||
assert moved["workspace_id"] == ws["id"]
|
||||
assert moved["category_id"] == cat["id"]
|
||||
|
||||
listed = unwrap(
|
||||
await client.get(
|
||||
f"{V1}/workspaces/{ws['id']}/knowledge",
|
||||
params={"category_id": cat["id"]},
|
||||
headers=owner["headers"],
|
||||
)
|
||||
)
|
||||
assert sorted(i["id"] for i in listed["items"]) == sorted([first["id"], second["id"]])
|
||||
assert all(i["category"]["id"] == cat["id"] for i in listed["items"])
|
||||
|
||||
# 不动目录也照样能移(移动到"未分类")
|
||||
uncategorized = await _bulk_move(client, owner, ws["id"], {"item_ids": [first["id"]]})
|
||||
assert uncategorized["category_id"] is None
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_bulk_move_across_workspaces_keeps_tags_and_children(client: AsyncClient) -> None:
|
||||
owner = await login(client)
|
||||
src = await create_workspace(client, owner, "来源库")
|
||||
dst = await create_workspace(client, owner, "目标库")
|
||||
cat = await _category(client, owner, dst["id"], "运维")
|
||||
doc = await _note(client, owner, src["id"], "重启网关的顺序", tag_names=["运维", "网关"])
|
||||
|
||||
moved = await _bulk_move(
|
||||
client,
|
||||
owner,
|
||||
src["id"],
|
||||
{
|
||||
"item_ids": [doc["id"]],
|
||||
"target_workspace_id": dst["id"],
|
||||
"category_id": cat["id"],
|
||||
},
|
||||
)
|
||||
assert moved["workspace_id"] == dst["id"]
|
||||
assert moved["category_id"] == cat["id"]
|
||||
|
||||
gone = await client.get(
|
||||
f"{V1}/workspaces/{src['id']}/knowledge/{doc['id']}", headers=owner["headers"]
|
||||
)
|
||||
assert gone.json()["code"] == 7001
|
||||
|
||||
got = unwrap(
|
||||
await client.get(
|
||||
f"{V1}/workspaces/{dst['id']}/knowledge/{doc['id']}", headers=owner["headers"]
|
||||
)
|
||||
)
|
||||
assert got["id"] == doc["id"]
|
||||
assert got["knowledge"]["category_id"] == cat["id"]
|
||||
assert sorted(t["name"] for t in got["tags"]) == ["网关", "运维"]
|
||||
assert got["knowledge"]["source_kind"] == "manual"
|
||||
|
||||
# 标签本身也在目标工作区里建好了,不是只在关联表里挂了个空壳
|
||||
tags = unwrap(
|
||||
await client.get(f"{V1}/workspaces/{dst['id']}/knowledge/tags", headers=owner["headers"])
|
||||
)
|
||||
assert {row["name"] for row in tags["items"]} == {"运维", "网关"}
|
||||
|
||||
# 历史事件也搬过来了,不然详情页会像凭空出现的一篇
|
||||
actions = {e["action"] for e in got["activities"]}
|
||||
assert {"knowledge_created", "knowledge_moved"} <= actions
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_bulk_move_ticket_source_becomes_manual(client: AsyncClient) -> None:
|
||||
owner = await login(client)
|
||||
src = await create_workspace(client, owner, "有来源的库")
|
||||
dst = await create_workspace(client, owner, "接手的库")
|
||||
helper = await _member(client, owner, src)
|
||||
task = await _task(client, owner, src["id"], "网关老是超时")
|
||||
ticket = await _finish_ticket(client, owner, src["id"], helper, task)
|
||||
# 验收通过时系统已经自动沉淀了一份草稿,直接拿它
|
||||
listed = unwrap(
|
||||
await client.get(
|
||||
f"{V1}/workspaces/{src['id']}/knowledge",
|
||||
params={"source_item_id": ticket["id"]},
|
||||
headers=owner["headers"],
|
||||
)
|
||||
)
|
||||
doc = listed["items"][0]
|
||||
|
||||
moved = await _bulk_move(
|
||||
client,
|
||||
owner,
|
||||
src["id"],
|
||||
{"item_ids": [doc["id"]], "target_workspace_id": dst["id"]},
|
||||
)
|
||||
assert moved["moved"] == [doc["id"]]
|
||||
|
||||
got = unwrap(
|
||||
await client.get(
|
||||
f"{V1}/workspaces/{dst['id']}/knowledge/{doc['id']}", headers=owner["headers"]
|
||||
)
|
||||
)
|
||||
# 目标库里没有那张工单,来源必须降级成手动,否则指向一个看不见的 id
|
||||
assert got["knowledge"]["source_kind"] == "manual"
|
||||
assert got["knowledge"]["source_item_id"] is None
|
||||
assert got["source"] is None
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_bulk_move_requires_membership_in_target(client: AsyncClient) -> None:
|
||||
owner = await login(client)
|
||||
src = await create_workspace(client, owner, "自己的库")
|
||||
stranger = await login(client)
|
||||
dst = await create_workspace(client, stranger, "别人的库")
|
||||
doc = await _note(client, owner, src["id"], "我的笔记")
|
||||
|
||||
resp = await client.post(
|
||||
f"{V1}/workspaces/{src['id']}/knowledge/bulk-move",
|
||||
json={"item_ids": [doc["id"]], "target_workspace_id": dst["id"]},
|
||||
headers=owner["headers"],
|
||||
)
|
||||
assert resp.json()["code"] == 3004
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_bulk_move_rejects_foreign_category_and_missing_docs(client: AsyncClient) -> None:
|
||||
owner = await login(client)
|
||||
src = await create_workspace(client, owner, "甲库")
|
||||
dst = await create_workspace(client, owner, "乙库")
|
||||
src_cat = await _category(client, owner, src["id"], "甲的分类")
|
||||
doc = await _note(client, owner, src["id"], "一篇")
|
||||
|
||||
foreign = await client.post(
|
||||
f"{V1}/workspaces/{src['id']}/knowledge/bulk-move",
|
||||
json={
|
||||
"item_ids": [doc["id"]],
|
||||
"target_workspace_id": dst["id"],
|
||||
"category_id": src_cat["id"],
|
||||
},
|
||||
headers=owner["headers"],
|
||||
)
|
||||
assert foreign.json()["code"] == 7006
|
||||
|
||||
missing = await client.post(
|
||||
f"{V1}/workspaces/{src['id']}/knowledge/bulk-move",
|
||||
json={"item_ids": ["00000000-0000-0000-0000-000000000001"]},
|
||||
headers=owner["headers"],
|
||||
)
|
||||
assert missing.json()["code"] == 7001
|
||||
assert missing.json()["data"]["missing"] == ["00000000-0000-0000-0000-000000000001"]
|
||||
|
||||
# 移出后源库再移一次要报 7001,而不是静默成功
|
||||
await _bulk_move(
|
||||
client, owner, src["id"], {"item_ids": [doc["id"]], "target_workspace_id": dst["id"]}
|
||||
)
|
||||
again = await client.post(
|
||||
f"{V1}/workspaces/{src['id']}/knowledge/bulk-move",
|
||||
json={"item_ids": [doc["id"]]},
|
||||
headers=owner["headers"],
|
||||
)
|
||||
assert again.json()["code"] == 7001
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_bulk_move_cannot_touch_another_tenants_knowledge(client: AsyncClient) -> None:
|
||||
owner = await login(client)
|
||||
ws = await create_workspace(client, owner, "甲的工作区")
|
||||
doc = await _note(client, owner, ws["id"], "甲的秘密")
|
||||
outsider = await login(client)
|
||||
own = await create_workspace(client, outsider, "乙的工作区")
|
||||
|
||||
resp = await client.post(
|
||||
f"{V1}/workspaces/{own['id']}/knowledge/bulk-move",
|
||||
json={"item_ids": [doc["id"]]},
|
||||
headers=outsider["headers"],
|
||||
)
|
||||
assert resp.json()["code"] == 7001
|
||||
|
||||
Reference in New Issue
Block a user