Files
tickets/tests/test_tickets.py
Arcline Dev c6dc1a2acf Initial commit: lightweight CLI ticket tracker
A JSON-backed ticket management system with full CRUD operations,
status/priority filtering, prefix-based ID resolution, and test suite.
2026-07-03 21:35:32 -05:00

155 lines
5.2 KiB
Python

"""Tests for the tickets package."""
from __future__ import annotations
import json
import tempfile
from pathlib import Path
import pytest
from tickets.cli import main
from tickets.models import Priority, Status, Ticket
from tickets.store import TicketStore
class TestTicketModel:
def test_create_minimal_ticket(self) -> None:
t = Ticket(title="Fix login bug")
assert t.title == "Fix login bug"
assert t.description == ""
assert t.priority == Priority.MEDIUM
assert t.status == Status.TODO
assert t.id and len(t.id) == 8
def test_create_full_ticket(self) -> None:
t = Ticket(
title="Deploy v2.0",
description="Ship the new release",
priority=Priority.CRITICAL,
status=Status.IN_PROGRESS,
assignee="alice",
tags=["deploy", "urgent"],
)
assert t.priority == Priority.CRITICAL
assert t.status == Status.IN_PROGRESS
assert t.assignee == "alice"
assert "urgent" in t.tags
def test_roundtrip_dict(self) -> None:
t = Ticket(title="Test", description="desc", tags=["a", "b"])
rt = Ticket.from_dict(t.to_dict())
assert rt.title == t.title
assert rt.description == t.description
assert rt.tags == t.tags
assert rt.priority == t.priority
def test_update_fields(self) -> None:
t = Ticket(title="Old")
t.update(title="New", status="done", priority="high")
assert t.title == "New"
assert t.status == Status.DONE
assert t.priority == Priority.HIGH
assert t.updated_at != t.created_at
def test_accept_enum_or_string(self) -> None:
t = Ticket(title="X", priority=Priority.LOW, status=Status.DONE)
assert t.priority == Priority.LOW
t.update(priority="critical", status="cancelled")
assert t.priority == Priority.CRITICAL
assert t.status == Status.CANCELLED
class TestTicketStore:
def test_add_and_retrieve(self) -> None:
with tempfile.TemporaryDirectory() as d:
fp = Path(d) / "t.json"
store = TicketStore(fp)
t = Ticket(title="Hello")
store.add(t)
assert t.id in store
assert store[t.id].title == "Hello"
def test_delete(self) -> None:
with tempfile.TemporaryDirectory() as d:
fp = Path(d) / "t.json"
store = TicketStore(fp)
store.add(Ticket(title="rm me"))
tid = next(iter(store))
del store[tid]
assert tid not in store
def test_list_filtering(self) -> None:
with tempfile.TemporaryDirectory() as d:
fp = Path(d) / "t.json"
store = TicketStore(fp)
store.add(Ticket("a", status=Status.TODO, priority=Priority.LOW))
store.add(Ticket("b", status=Status.DONE, priority=Priority.HIGH))
store.add(Ticket("c", status=Status.DONE, priority=Priority.LOW))
assert len(store.list_tickets(status="done")) == 2
assert len(store.list_tickets(priority="high")) == 1
def test_persists_to_disk(self) -> None:
with tempfile.TemporaryDirectory() as d:
fp = Path(d) / "t.json"
store = TicketStore(fp)
store.add(Ticket("persist"))
assert fp.exists()
# Reload
store2 = TicketStore(fp)
assert len(store2) == 1
class TestCLI:
def test_create_and_view(self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
fp = tmp_path / "t.json"
monkeypatch.setattr("tickets.cli.TicketStore", lambda: TicketStore(fp))
main(["create", "My ticket", "-d", "desc", "-p", "high"])
store = TicketStore(fp)
assert len(store) == 1
tid = next(iter(store))
main(["view", tid])
main(["list", "-s", "todo"])
main(["list"])
def test_update(self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
fp = tmp_path / "t.json"
monkeypatch.setattr("tickets.cli.TicketStore", lambda: TicketStore(fp))
main(["create", "Old title"])
store = TicketStore(fp)
tid = next(iter(store))
main(["update", tid, "-t", "New title", "-s", "done"])
store2 = TicketStore(fp)
assert store2[tid].title == "New title"
assert store2[tid].status == Status.DONE
def test_delete_with_force(self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
fp = tmp_path / "t.json"
monkeypatch.setattr("tickets.cli.TicketStore", lambda: TicketStore(fp))
main(["create", "To delete"])
store = TicketStore(fp)
assert len(store) == 1
tid = next(iter(store))
main(["delete", tid, "--force"])
store2 = TicketStore(fp)
assert len(store2) == 0
def test_prefix_match(self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
fp = tmp_path / "t.json"
monkeypatch.setattr("tickets.cli.TicketStore", lambda: TicketStore(fp))
main(["create", "Ticket 1"])
store = TicketStore(fp)
tid = next(iter(store))
prefix = tid[:4]
# Should resolve via prefix
main(["view", prefix])