128 lines
4.2 KiB
Python
128 lines
4.2 KiB
Python
"""Parser/dispatcher tests for the PM-expansion chat commands."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import pytest
|
|
|
|
from claire.db import migrate, open_db
|
|
from claire.domain import ChatScope, TaskStatus, TaskType
|
|
from claire.hlc import HLCGenerator
|
|
from claire.web import service
|
|
from claire.web.chat import commands as cmds
|
|
|
|
|
|
def _setup() -> tuple:
|
|
conn = open_db(":memory:")
|
|
migrate(conn)
|
|
gen = HLCGenerator("test-machine")
|
|
return conn, gen
|
|
|
|
|
|
def _orch_ctx() -> cmds.ScopeCtx:
|
|
return cmds.ScopeCtx(scope=ChatScope.ORCHESTRATOR, scope_ref=None)
|
|
|
|
|
|
def test_parse_org_new() -> None:
|
|
cmd = cmds.parse("/org new acme --description big-corp", _orch_ctx())
|
|
assert isinstance(cmd, cmds.OrgNewCmd)
|
|
assert cmd.name == "acme" and cmd.description == "big-corp"
|
|
|
|
|
|
def test_parse_person_new_requires_at_handle() -> None:
|
|
with pytest.raises(Exception): # ValidationError from pydantic
|
|
cmds.parse("/person new quinn Quinn", _orch_ctx())
|
|
cmd = cmds.parse("/person new @quinn Quinn", _orch_ctx())
|
|
assert isinstance(cmd, cmds.PersonNewCmd)
|
|
assert cmd.handle == "@quinn" and cmd.display_name == "Quinn"
|
|
|
|
|
|
def test_parse_state_invalid_value() -> None:
|
|
with pytest.raises(cmds.ParseError):
|
|
cmds.parse("/state abcd1234 flying", _orch_ctx())
|
|
|
|
|
|
def test_parse_state_valid() -> None:
|
|
cmd = cmds.parse("/state abcd1234 in_progress --reason 'started work'", _orch_ctx())
|
|
assert isinstance(cmd, cmds.TaskStateCmd)
|
|
assert cmd.to_state == TaskStatus.IN_PROGRESS
|
|
assert cmd.reason == "started work"
|
|
|
|
|
|
def test_parse_tag_and_untag() -> None:
|
|
tag_cmd = cmds.parse("/tag abcd1234 urgent", _orch_ctx())
|
|
assert isinstance(tag_cmd, cmds.TaskTagCmd) and not tag_cmd.remove
|
|
untag_cmd = cmds.parse("/untag abcd1234 urgent", _orch_ctx())
|
|
assert isinstance(untag_cmd, cmds.TaskTagCmd) and untag_cmd.remove
|
|
|
|
|
|
def test_parse_own_with_and_without_handle() -> None:
|
|
with_handle = cmds.parse("/own abcd1234 @quinn", _orch_ctx())
|
|
assert isinstance(with_handle, cmds.TaskOwnCmd)
|
|
assert with_handle.person_handle == "@quinn"
|
|
without = cmds.parse("/own abcd1234", _orch_ctx())
|
|
assert without.person_handle is None
|
|
|
|
|
|
def test_parse_type_clear() -> None:
|
|
typed = cmds.parse("/type abcd1234 bug", _orch_ctx())
|
|
assert isinstance(typed, cmds.TaskTypeCmd) and typed.task_type == TaskType.BUG
|
|
cleared = cmds.parse("/type abcd1234", _orch_ctx())
|
|
assert cleared.task_type is None
|
|
|
|
|
|
def test_parse_meta_flags() -> None:
|
|
cmd = cmds.parse(
|
|
"/meta abcd1234 --color #fff --emoji 🐛 --apply-color-rule off",
|
|
_orch_ctx(),
|
|
)
|
|
assert isinstance(cmd, cmds.TaskMetaCmd)
|
|
assert cmd.color == "#fff" and cmd.emoji == "🐛"
|
|
assert cmd.apply_color_rule is False
|
|
|
|
|
|
def test_dispatch_epic_new() -> None:
|
|
conn, gen = _setup()
|
|
service.create_project(conn, gen, name="proj")
|
|
cmd = cmds.parse("/epic new 'auth rewrite' --project proj", _orch_ctx())
|
|
result = cmds.dispatch(conn, gen, cmd, _orch_ctx())
|
|
assert "auth rewrite" in result.body
|
|
assert result.meta["kind"] == "epic_new"
|
|
|
|
|
|
def test_dispatch_tag_creates_on_demand_then_untag() -> None:
|
|
conn, gen = _setup()
|
|
service.create_project(conn, gen, name="p")
|
|
task = service.add_task(conn, gen, project="p", title="t")
|
|
short = str(task.id)[:8]
|
|
res1 = cmds.dispatch(
|
|
conn, gen,
|
|
cmds.parse(f"/tag {short} brand-new", _orch_ctx()),
|
|
_orch_ctx(),
|
|
)
|
|
assert "tagged" in res1.body
|
|
res2 = cmds.dispatch(
|
|
conn, gen,
|
|
cmds.parse(f"/untag {short} brand-new", _orch_ctx()),
|
|
_orch_ctx(),
|
|
)
|
|
assert "untagged" in res2.body
|
|
|
|
|
|
def test_dispatch_state_transition_legal_and_illegal() -> None:
|
|
conn, gen = _setup()
|
|
service.create_project(conn, gen, name="p")
|
|
task = service.add_task(conn, gen, project="p", title="t")
|
|
short = str(task.id)[:8]
|
|
res = cmds.dispatch(
|
|
conn, gen,
|
|
cmds.parse(f"/state {short} in_progress", _orch_ctx()),
|
|
_orch_ctx(),
|
|
)
|
|
assert "in_progress" in res.body
|
|
# Illegal: in_progress → todo
|
|
with pytest.raises(service.InvalidInput):
|
|
cmds.dispatch(
|
|
conn, gen,
|
|
cmds.parse(f"/state {short} todo", _orch_ctx()),
|
|
_orch_ctx(),
|
|
)
|