Add Handbook.md benchmark tasks
This commit is contained in:
@@ -0,0 +1,204 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import base64
|
||||
|
||||
import pytest
|
||||
from jira_mock.server import (
|
||||
add_attachment,
|
||||
add_comment,
|
||||
add_watcher,
|
||||
create_issue,
|
||||
create_status,
|
||||
create_user,
|
||||
export_state,
|
||||
get_attachments,
|
||||
get_issue,
|
||||
get_transitions,
|
||||
get_watchers,
|
||||
import_state,
|
||||
remove_watcher,
|
||||
transition_issue,
|
||||
upsert_workflow_transition,
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_transitions_follow_default_workflow() -> None:
|
||||
await create_issue("MOCK", "Workflow", "Task")
|
||||
|
||||
transitions = (await get_transitions("MOCK-1"))["transitions"]
|
||||
assert transitions[0]["id"] == "1"
|
||||
|
||||
issue = await transition_issue("MOCK-1", "1")
|
||||
assert issue["fields"]["status"]["name"] == "In Progress"
|
||||
|
||||
with pytest.raises(ValueError, match="not available"):
|
||||
await transition_issue("MOCK-1", "1")
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_empty_statuses_and_workflow_inherit_defaults() -> None:
|
||||
state = await export_state()
|
||||
state["statuses"] = {}
|
||||
state["workflow"] = {}
|
||||
state.pop("defaultStatusValue", None)
|
||||
|
||||
await import_state(state)
|
||||
|
||||
exported = await export_state()
|
||||
assert exported["defaultStatusValue"] == "To Do"
|
||||
assert exported["statuses"]["10001"]["name"] == "To Do"
|
||||
assert "To Do" in exported["workflow"]
|
||||
created = await create_issue("MOCK", "Default status after empty maps", "Task")
|
||||
assert (await get_issue(created["key"], fields="status"))["fields"]["status"]["name"] == "To Do"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_empty_default_status_value_inherits_default() -> None:
|
||||
state = await export_state()
|
||||
state["defaultStatusValue"] = ""
|
||||
|
||||
await import_state(state)
|
||||
|
||||
assert (await export_state())["defaultStatusValue"] == "To Do"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_create_issue_uses_configured_default_status_value() -> None:
|
||||
state = await export_state()
|
||||
state["defaultStatusValue"] = "Backlog"
|
||||
state["statuses"] = {
|
||||
"20001": {
|
||||
"id": "20001",
|
||||
"name": "Backlog",
|
||||
"description": "Ready for triage",
|
||||
"statusCategory": {"id": 2, "key": "new", "name": "To Do", "colorName": "blue-gray"},
|
||||
},
|
||||
"20002": {
|
||||
"id": "20002",
|
||||
"name": "Selected",
|
||||
"description": "Ready to start",
|
||||
"statusCategory": {"id": 4, "key": "indeterminate", "name": "In Progress", "colorName": "yellow"},
|
||||
},
|
||||
}
|
||||
state["workflow"] = {"Backlog": [{"id": "21", "name": "Select for Work", "to": "Selected"}], "Selected": []}
|
||||
await import_state(state)
|
||||
|
||||
created = await create_issue("MOCK", "Starts in custom default", "Task")
|
||||
|
||||
assert (await get_issue(created["key"], fields="status"))["fields"]["status"]["name"] == "Backlog"
|
||||
transitions = (await get_transitions(created["key"]))["transitions"]
|
||||
assert transitions == [
|
||||
{
|
||||
"id": "21",
|
||||
"name": "Select for Work",
|
||||
"to": {
|
||||
"id": "20002",
|
||||
"name": "Selected",
|
||||
"description": "Ready to start",
|
||||
"iconUrl": None,
|
||||
"statusCategory": {
|
||||
"id": 4,
|
||||
"key": "indeterminate",
|
||||
"name": "In Progress",
|
||||
"colorName": "yellow",
|
||||
},
|
||||
},
|
||||
"hasScreen": False,
|
||||
"isGlobal": False,
|
||||
"isInitial": False,
|
||||
"isAvailable": True,
|
||||
"isConditional": False,
|
||||
"isLooped": False,
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_default_status_value_must_reference_status_and_workflow() -> None:
|
||||
state = await export_state()
|
||||
state["defaultStatusValue"] = "Backlog"
|
||||
|
||||
with pytest.raises(ValueError, match="defaultStatusValue 'Backlog' does not reference a configured status"):
|
||||
await import_state(state)
|
||||
|
||||
state["statuses"]["20001"] = {
|
||||
"id": "20001",
|
||||
"name": "Backlog",
|
||||
"description": "Ready for triage",
|
||||
"statusCategory": {"id": 2, "key": "new", "name": "To Do", "colorName": "blue-gray"},
|
||||
}
|
||||
with pytest.raises(ValueError, match="defaultStatusValue 'Backlog' does not have a workflow entry"):
|
||||
await import_state(state)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_admin_can_create_custom_status_and_workflow_transition() -> None:
|
||||
state = await export_state()
|
||||
state["is_admin"] = True
|
||||
await import_state(state)
|
||||
await create_issue("MOCK", "Blocked work", "Task")
|
||||
|
||||
status = await create_status("10005", "Blocked", "indeterminate", description="Work is blocked")
|
||||
transition = await upsert_workflow_transition("To Do", "8", "Mark Blocked", "Blocked")
|
||||
|
||||
assert status["name"] == "Blocked"
|
||||
assert transition == {"id": "8", "name": "Mark Blocked", "to": "Blocked"}
|
||||
assert any(item["id"] == "8" for item in (await get_transitions("MOCK-1"))["transitions"])
|
||||
assert (await transition_issue("MOCK-1", "8"))["fields"]["status"]["name"] == "Blocked"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_workflow_transition_upsert_canonicalizes_status_names() -> None:
|
||||
state = await export_state()
|
||||
state["is_admin"] = True
|
||||
await import_state(state)
|
||||
await create_issue("MOCK", "Blocked work", "Task")
|
||||
await create_status("10005", "Blocked", "indeterminate", description="Work is blocked")
|
||||
|
||||
transition = await upsert_workflow_transition("to do", "8", "Mark Blocked", "blocked")
|
||||
|
||||
assert transition == {"id": "8", "name": "Mark Blocked", "to": "Blocked"}
|
||||
exported = await export_state()
|
||||
assert "to do" not in exported["workflow"]
|
||||
assert exported["workflow"]["To Do"][-1] == {"id": "8", "name": "Mark Blocked", "to": "Blocked"}
|
||||
assert any(item["id"] == "8" for item in (await get_transitions("MOCK-1"))["transitions"])
|
||||
assert (await transition_issue("MOCK-1", "8"))["fields"]["status"]["name"] == "Blocked"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_non_admin_cannot_configure_statuses_or_workflows() -> None:
|
||||
with pytest.raises(PermissionError, match="Admin privileges"):
|
||||
await create_status("10005", "Blocked", "indeterminate")
|
||||
|
||||
with pytest.raises(PermissionError, match="Admin privileges"):
|
||||
await upsert_workflow_transition("To Do", "8", "Mark Blocked", "Blocked")
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_comments_watchers_and_attachments() -> None:
|
||||
await create_issue("MOCK", "Collaboration", "Task")
|
||||
|
||||
comment = await add_comment("MOCK-1", "Looks good")
|
||||
assert comment["body"]["content"][0]["content"][0]["text"] == "Looks good"
|
||||
assert (await get_issue("MOCK-1", fields="comment"))["fields"]["comment"]["total"] == 1
|
||||
await add_comment("MOCK-1", "Second")
|
||||
assert "comment" not in (await export_state())["issues"]["MOCK-1"]["fields"]
|
||||
assert (await get_issue("MOCK-1", fields="comment"))["fields"]["comment"]["total"] == 2
|
||||
|
||||
state = await export_state()
|
||||
state["is_admin"] = True
|
||||
await import_state(state)
|
||||
await create_user("alice", "Alice")
|
||||
|
||||
assert (await add_watcher("MOCK-1", "alice"))["message"] == "Added watcher alice to MOCK-1"
|
||||
assert (await add_watcher("MOCK-1", "alice"))["message"] == "User alice is already watching MOCK-1"
|
||||
assert (await get_watchers("MOCK-1"))["watchCount"] == 1
|
||||
assert (await remove_watcher("MOCK-1", "alice"))["message"] == "Removed watcher alice from MOCK-1"
|
||||
|
||||
payload = base64.b64encode(b"hello").decode()
|
||||
attachment = await add_attachment("MOCK-1", "note.txt", payload)
|
||||
assert attachment["mimeType"] == "text/plain"
|
||||
attachments = await get_attachments("MOCK-1")
|
||||
assert attachments["total"] == 1
|
||||
assert "content" not in attachments["attachments"][0]
|
||||
Reference in New Issue
Block a user