|
| 1 | +"""Unit tests for WorkItemDetail model type compatibility with Plane v1.3.0+. |
| 2 | +
|
| 3 | +Plane v1.3.0 changed the API to return labels and assignees as UUID strings |
| 4 | +instead of expanded objects, even when not using the `fields` sparse parameter. |
| 5 | +These tests verify that WorkItemDetail handles both representations. |
| 6 | +""" |
| 7 | + |
| 8 | +import pytest |
| 9 | +from pydantic import ValidationError |
| 10 | + |
| 11 | +from plane.models.work_items import WorkItemDetail |
| 12 | + |
| 13 | + |
| 14 | +MINIMAL_WORK_ITEM = { |
| 15 | + "id": "ef6bf853-fecb-433e-a4e3-8546e60abebd", |
| 16 | + "name": "Test issue", |
| 17 | + "sequence_id": 1, |
| 18 | +} |
| 19 | + |
| 20 | + |
| 21 | +class TestWorkItemDetailLabelsAssigneesTypes: |
| 22 | + """WorkItemDetail should accept labels/assignees as UUID strings or objects.""" |
| 23 | + |
| 24 | + def test_labels_as_uuid_strings(self) -> None: |
| 25 | + """Plane v1.3.0 returns labels as UUID strings — must not raise.""" |
| 26 | + data = { |
| 27 | + **MINIMAL_WORK_ITEM, |
| 28 | + "labels": ["f6a24a78-a275-4fd1-a777-0e9e0e99dbef"], |
| 29 | + "assignees": [], |
| 30 | + } |
| 31 | + item = WorkItemDetail(**data) |
| 32 | + assert item.labels == ["f6a24a78-a275-4fd1-a777-0e9e0e99dbef"] |
| 33 | + |
| 34 | + def test_assignees_as_uuid_strings(self) -> None: |
| 35 | + """Plane v1.3.0 returns assignees as UUID strings — must not raise.""" |
| 36 | + data = { |
| 37 | + **MINIMAL_WORK_ITEM, |
| 38 | + "labels": [], |
| 39 | + "assignees": ["48b05854-3e71-44f0-9fcb-7a5d6887f5ec"], |
| 40 | + } |
| 41 | + item = WorkItemDetail(**data) |
| 42 | + assert item.assignees == ["48b05854-3e71-44f0-9fcb-7a5d6887f5ec"] |
| 43 | + |
| 44 | + def test_both_as_uuid_strings(self) -> None: |
| 45 | + """Both fields as UUID strings simultaneously.""" |
| 46 | + data = { |
| 47 | + **MINIMAL_WORK_ITEM, |
| 48 | + "labels": [ |
| 49 | + "f6a24a78-a275-4fd1-a777-0e9e0e99dbef", |
| 50 | + "a8509ac8-7c71-4b9e-9a4a-623ef2c2365b", |
| 51 | + ], |
| 52 | + "assignees": ["48b05854-3e71-44f0-9fcb-7a5d6887f5ec"], |
| 53 | + } |
| 54 | + item = WorkItemDetail(**data) |
| 55 | + assert len(item.labels) == 2 |
| 56 | + assert len(item.assignees) == 1 |
| 57 | + |
| 58 | + def test_labels_as_objects(self) -> None: |
| 59 | + """Expanded label objects (pre-v1.3.0 behavior) still parse correctly.""" |
| 60 | + data = { |
| 61 | + **MINIMAL_WORK_ITEM, |
| 62 | + "labels": [ |
| 63 | + { |
| 64 | + "id": "f6a24a78-a275-4fd1-a777-0e9e0e99dbef", |
| 65 | + "name": "v3", |
| 66 | + "color": "#FF6900", |
| 67 | + "project": "9aa02e26-3b44-4fd2-96f9-015aa9ee7a52", |
| 68 | + "workspace": "0f4d413a-a164-4168-aa31-abbdaa0aecd1", |
| 69 | + } |
| 70 | + ], |
| 71 | + "assignees": [], |
| 72 | + } |
| 73 | + item = WorkItemDetail(**data) |
| 74 | + assert len(item.labels) == 1 |
| 75 | + |
| 76 | + def test_assignees_as_objects(self) -> None: |
| 77 | + """Expanded assignee objects (pre-v1.3.0 behavior) still parse correctly.""" |
| 78 | + data = { |
| 79 | + **MINIMAL_WORK_ITEM, |
| 80 | + "labels": [], |
| 81 | + "assignees": [ |
| 82 | + { |
| 83 | + "id": "48b05854-3e71-44f0-9fcb-7a5d6887f5ec", |
| 84 | + "display_name": "vic", |
| 85 | + "avatar": None, |
| 86 | + "is_bot": False, |
| 87 | + } |
| 88 | + ], |
| 89 | + } |
| 90 | + item = WorkItemDetail(**data) |
| 91 | + assert len(item.assignees) == 1 |
| 92 | + |
| 93 | + def test_empty_lists(self) -> None: |
| 94 | + """Empty lists for both fields are valid.""" |
| 95 | + data = {**MINIMAL_WORK_ITEM, "labels": [], "assignees": []} |
| 96 | + item = WorkItemDetail(**data) |
| 97 | + assert item.labels == [] |
| 98 | + assert item.assignees == [] |
| 99 | + |
| 100 | + def test_fields_omitted_defaults_to_empty(self) -> None: |
| 101 | + """Fields absent from response default to empty lists (sparse responses).""" |
| 102 | + item = WorkItemDetail(**MINIMAL_WORK_ITEM) |
| 103 | + assert item.labels == [] |
| 104 | + assert item.assignees == [] |
| 105 | + |
| 106 | + def test_name_still_required(self) -> None: |
| 107 | + """name remains a required field — no over-relaxation.""" |
| 108 | + with pytest.raises(ValidationError): |
| 109 | + WorkItemDetail(id="abc", labels=[], assignees=[]) |
0 commit comments