144 lines
4.1 KiB
Python
144 lines
4.1 KiB
Python
from app.models import Box, Item, SubItem
|
|
from app.notion_import import (
|
|
ImportSummary,
|
|
ParsedBox,
|
|
ParsedItem,
|
|
ParsedSubItem,
|
|
apply_import,
|
|
extract_page_id,
|
|
parse_notion_blocks,
|
|
)
|
|
|
|
|
|
def make_heading_2(text: str) -> dict:
|
|
return {
|
|
"type": "heading_2",
|
|
"heading_2": {"rich_text": [{"plain_text": text}]},
|
|
"_children": [],
|
|
}
|
|
|
|
|
|
def make_bullet(text: str, children: list[dict] | None = None) -> dict:
|
|
return {
|
|
"type": "bulleted_list_item",
|
|
"bulleted_list_item": {"rich_text": [{"plain_text": text}]},
|
|
"_children": children or [],
|
|
}
|
|
|
|
|
|
def make_image_block() -> dict:
|
|
return {"type": "image", "image": {}, "_children": []}
|
|
|
|
|
|
def test_extract_page_id_from_notion_url():
|
|
url = "https://www.notion.so/workspace/My-Page-1234567890abcdef1234567890abcdef?pvs=4"
|
|
|
|
page_id = extract_page_id(url)
|
|
|
|
assert page_id == "12345678-90ab-cdef-1234-567890abcdef"
|
|
|
|
|
|
def test_parse_heading_2_as_box():
|
|
summary = parse_notion_blocks([make_heading_2("厨房箱")])
|
|
|
|
assert summary.box_count == 1
|
|
assert summary.boxes[0].name == "厨房箱"
|
|
|
|
|
|
def test_parse_first_level_bullet_as_item():
|
|
blocks = [make_heading_2("客厅箱"), make_bullet("锅具")]
|
|
|
|
summary = parse_notion_blocks(blocks)
|
|
|
|
assert summary.item_count == 1
|
|
assert summary.boxes[0].items[0].name == "锅具"
|
|
assert summary.boxes[0].items[0].is_container is False
|
|
|
|
|
|
def test_parse_bullet_with_children_as_container_item_and_subitems():
|
|
blocks = [
|
|
make_heading_2("电子箱"),
|
|
make_bullet("配件盒", children=[make_bullet("USB 线"), make_bullet("转接头")]),
|
|
]
|
|
|
|
summary = parse_notion_blocks(blocks)
|
|
|
|
item = summary.boxes[0].items[0]
|
|
assert item.name == "配件盒"
|
|
assert item.is_container is True
|
|
assert [subitem.name for subitem in item.subitems] == ["USB 线", "转接头"]
|
|
|
|
|
|
def test_parse_second_level_bullets_as_subitems():
|
|
blocks = [
|
|
make_heading_2("文件箱"),
|
|
make_bullet("文件袋", children=[make_bullet("合同"), make_bullet("护照复印件")]),
|
|
]
|
|
|
|
summary = parse_notion_blocks(blocks)
|
|
|
|
assert summary.subitem_count == 2
|
|
assert summary.boxes[0].items[0].subitems[1].name == "护照复印件"
|
|
|
|
|
|
def test_parse_deeper_than_supported_levels_adds_warning():
|
|
blocks = [
|
|
make_heading_2("测试箱"),
|
|
make_bullet(
|
|
"外层袋",
|
|
children=[make_bullet("内层物品", children=[make_bullet("更深一层")])],
|
|
),
|
|
]
|
|
|
|
summary = parse_notion_blocks(blocks)
|
|
|
|
assert summary.container_item_count == 1
|
|
assert any("超出支持层级" in warning for warning in summary.warnings)
|
|
|
|
|
|
def test_parse_non_text_media_block_adds_skip_warning():
|
|
blocks = [make_heading_2("照片箱"), make_image_block()]
|
|
|
|
summary = parse_notion_blocks(blocks)
|
|
|
|
assert any("这版不导入图片或媒体" in warning for warning in summary.warnings)
|
|
|
|
|
|
def test_dry_run_parse_does_not_write_database(db_session):
|
|
blocks = [make_heading_2("厨房箱"), make_bullet("锅")]
|
|
|
|
summary = parse_notion_blocks(blocks)
|
|
|
|
assert summary.box_count == 1
|
|
assert db_session.query(Box).count() == 0
|
|
assert db_session.query(Item).count() == 0
|
|
assert db_session.query(SubItem).count() == 0
|
|
|
|
|
|
def test_apply_import_writes_expected_structure(db_session):
|
|
summary = ImportSummary(
|
|
boxes=[
|
|
ParsedBox(
|
|
name="主卧箱",
|
|
items=[
|
|
ParsedItem(name="衣服", is_container=False),
|
|
ParsedItem(
|
|
name="收纳袋",
|
|
is_container=True,
|
|
subitems=[ParsedSubItem(name="袜子"), ParsedSubItem(name="围巾")],
|
|
),
|
|
],
|
|
)
|
|
]
|
|
)
|
|
|
|
counts = apply_import(summary, db_session)
|
|
|
|
assert counts == {"boxes": 1, "items": 2, "subitems": 2}
|
|
assert db_session.query(Box).count() == 1
|
|
assert db_session.query(Item).count() == 2
|
|
assert db_session.query(SubItem).count() == 2
|
|
|
|
container_item = db_session.query(Item).filter_by(name="收纳袋").one()
|
|
assert container_item.is_container is True
|