diff --git a/CHANGELOG.md b/CHANGELOG.md index 66ffbab..5e3652f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/). ### Added - `planecli intake` command group: `ls`, `create`, `accept`, `decline`, `delete`, `enabled` for project intake queues. Mutations take the work item UUID shown in the `Issue ID` column of `intake ls`. `accept`/`decline` require the project Admin role (the API silently ignores the change for lower roles, so the CLI verifies it and fails loudly). `delete` also permanently deletes the underlying work item for any status other than `accepted` +### Fixed +- `document create` no longer crashes before the API call: `CreatePage` now receives the required `description_html` at construction (`
` when `--content` is omitted) + ## [0.5.1] - 2026-07-03 ### Added diff --git a/src/planecli/commands/documents.py b/src/planecli/commands/documents.py index 1ceaa53..4ef19c2 100644 --- a/src/planecli/commands/documents.py +++ b/src/planecli/commands/documents.py @@ -165,9 +165,8 @@ async def create( client = get_client() workspace = get_workspace() - page_data = CreatePage(name=title) - if content: - page_data.description_html = f"{content}
" + description_html = f"{content}
" if content else "" + page_data = CreatePage(name=title, description_html=description_html) if project: proj = await resolve_project_async(project, client, workspace) diff --git a/tests/test_commands/test_documents.py b/tests/test_commands/test_documents.py new file mode 100644 index 0000000..118627c --- /dev/null +++ b/tests/test_commands/test_documents.py @@ -0,0 +1,70 @@ +"""Tests for document / page commands.""" + +from __future__ import annotations + +from unittest.mock import AsyncMock, MagicMock, patch + +from plane.models.pages import CreatePage + +from planecli.commands.documents import DOC_FIELDS, create + + +def _page_model(**overrides) -> MagicMock: + """A fake Page: only `.model_dump()` is used by the create command.""" + payload = { + "id": "page-1", + "name": "Test doc", + "description_html": "", + "created_at": "2026-08-28T10:00:00Z", + "updated_at": "2026-08-28T10:00:00Z", + } + payload.update(overrides) + m = MagicMock() + m.model_dump.return_value = payload + return m + + +@patch("planecli.commands.documents.output_single") +@patch("planecli.commands.documents.run_sdk", new_callable=AsyncMock) +@patch("planecli.commands.documents.resolve_project_async", new_callable=AsyncMock) +@patch("planecli.commands.documents.get_workspace", return_value="ws") +@patch("planecli.commands.documents.get_client") +async def test_create_passes_description_html_when_content_provided( + mock_client, mock_ws, mock_resolve, mock_run_sdk, mock_output_single +): + """CreatePage is constructed with wrapped --content, not assigned after the fact.""" + mock_resolve.return_value = {"id": "p1", "identifier": "ABC"} + mock_run_sdk.return_value = _page_model(name="Test doc", description_html="hello
") + + await create(title="Test doc", content="hello", project="ABC") + + args = mock_run_sdk.call_args[0] + assert args[0] is mock_client.return_value.pages.create_project_page + assert args[1:3] == ("ws", "p1") + payload = args[3] + assert isinstance(payload, CreatePage) + assert payload.name == "Test doc" + assert payload.description_html == "hello
" + data, fields = mock_output_single.call_args[0] + assert fields is DOC_FIELDS + assert data["name"] == "Test doc" + + +@patch("planecli.commands.documents.output_single") +@patch("planecli.commands.documents.run_sdk", new_callable=AsyncMock) +@patch("planecli.commands.documents.resolve_project_async", new_callable=AsyncMock) +@patch("planecli.commands.documents.get_workspace", return_value="ws") +@patch("planecli.commands.documents.get_client") +async def test_create_passes_empty_html_body_when_content_omitted( + mock_client, mock_ws, mock_resolve, mock_run_sdk, mock_output_single +): + """Omitting --content still supplies a valid empty HTML body at construction.""" + mock_resolve.return_value = {"id": "p1"} + mock_run_sdk.return_value = _page_model() + + await create(title="Test doc", project="ABC") + + payload = mock_run_sdk.call_args[0][3] + assert isinstance(payload, CreatePage) + assert payload.name == "Test doc" + assert payload.description_html == ""