zotero-kb/tests/test_projects.py
Saberlve cf7e2feb19 feat: Implement project management features including rename and delete functionality
- Added project renaming capability in the workspace with appropriate API endpoints.
- Implemented project deletion functionality, ensuring project directories are removed.
- Updated UI to support project renaming and deletion, including inline editing and confirmation dialogs.
- Enhanced batch generation feature for project items with selection and progress tracking.
- Added tests for project renaming and deletion to ensure functionality and integrity.
2026-04-21 16:55:42 +08:00

109 lines
4.1 KiB
Python

import json
from pathlib import Path
from zotero_kb.cards import CardBuilder
from zotero_kb.config import AppConfig
from zotero_kb.projects import ProjectService
from zotero_kb.workspace import Workspace
from zotero_kb.zotero_reader import ZoteroItemRecord
class FakeLlmClient:
def generate_card(self, source_bundle: dict[str, object]) -> dict[str, object]:
title = str(source_bundle["title"])
if "Scoped" in title:
summary = "Project-scoped retrieval improves drafting."
claims = ["Project scoping improves citation precision."]
else:
summary = "Unrelated retrieval baseline."
claims = ["Baseline retrieval is broad."]
return {
"summary": summary,
"core_claims": claims,
"methods": ["Method details."],
"evidence": ["Evidence details."],
"quotable_passages": claims,
"writing_hints": [summary],
"keywords": ["retrieval"],
}
def _build_item(item_key: str, title: str) -> ZoteroItemRecord:
return ZoteroItemRecord(
item_key=item_key,
title=title,
creators=["Alice Smith"],
year="2024",
item_type="journalArticle",
abstract=title,
tags=["retrieval"],
collection_paths=[["Theory", "Drafting"]],
notes=[title],
attachment_texts=[title],
)
def test_add_item_to_project_updates_selected_items_and_project_index(tmp_path: Path) -> None:
config = AppConfig(workspace_dir=tmp_path / "workspace", zotero_data_dir=tmp_path / "zotero")
workspace = Workspace(config)
workspace.create_project("thesis-ch2", "Thesis Chapter 2", "openai", "gpt-5-mini")
builder = CardBuilder(config.workspace_dir, FakeLlmClient())
builder.build_or_update(_build_item("PAPER0001", "Scoped Retrieval for Drafting"))
service = ProjectService(config.workspace_dir)
payload = service.add_items("thesis-ch2", ["PAPER0001"])
assert payload["selected_items"] == ["PAPER0001"]
assert payload["cards"][0]["item_key"] == "PAPER0001"
selected_items = json.loads(
(config.workspace_dir / "projects" / "thesis-ch2" / "selected-items.json").read_text(encoding="utf-8")
)
assert selected_items == ["PAPER0001"]
def test_remove_item_from_project_updates_selected_items(tmp_path: Path) -> None:
config = AppConfig(workspace_dir=tmp_path / "workspace", zotero_data_dir=tmp_path / "zotero")
workspace = Workspace(config)
workspace.create_project("thesis-ch2", "Thesis Chapter 2", "openai", "gpt-5-mini")
builder = CardBuilder(config.workspace_dir, FakeLlmClient())
builder.build_or_update(_build_item("PAPER0001", "Scoped Retrieval for Drafting"))
service = ProjectService(config.workspace_dir)
service.add_items("thesis-ch2", ["PAPER0001"])
payload = service.remove_item("thesis-ch2", "PAPER0001")
assert payload["selected_items"] == []
def test_add_item_to_project_returns_pending_item_when_card_not_generated(tmp_path: Path) -> None:
config = AppConfig(workspace_dir=tmp_path / "workspace", zotero_data_dir=tmp_path / "zotero")
workspace = Workspace(config)
workspace.create_project("thesis-ch2", "Thesis Chapter 2", "openai", "gpt-5-mini")
items_index_path = config.workspace_dir / "library" / "index" / "items.json"
items_index_path.parent.mkdir(parents=True, exist_ok=True)
items_index_path.write_text(
json.dumps(
{
"PAPER0001": {
"item_key": "PAPER0001",
"title": "Pending Item",
"creators": ["Alice Smith"],
"year": "2024",
"item_type": "journalArticle",
}
},
ensure_ascii=False,
),
encoding="utf-8",
)
service = ProjectService(config.workspace_dir)
payload = service.add_items("thesis-ch2", ["PAPER0001"])
assert payload["selected_items"] == ["PAPER0001"]
assert len(payload["items"]) == 1
assert payload["items"][0]["item_key"] == "PAPER0001"
assert payload["items"][0]["card_status"] == "pending"