101 lines
3.5 KiB
Python
101 lines
3.5 KiB
Python
#!/usr/bin/env python3
|
|
from __future__ import annotations
|
|
|
|
import unittest
|
|
from datetime import datetime, timedelta, timezone
|
|
|
|
from redmine_ticket_query import QueryError, compile_query, filter_by_query, issue_matches
|
|
from redmine_ticket_view import render_ticket_markdown
|
|
|
|
|
|
class PropertyQueryTest(unittest.TestCase):
|
|
def setUp(self):
|
|
self.issue = {
|
|
"id": 1,
|
|
"subject": "Please send verification code",
|
|
"updated_on": datetime.now(timezone.utc).isoformat().replace("+00:00", "Z"),
|
|
"status": {"name": "New"},
|
|
"assigned_to": None,
|
|
"helpdesk_ticket": {
|
|
"from_address": "same@example.com",
|
|
"contact": {
|
|
"id": 9,
|
|
"email": "same@example.com",
|
|
"company": "same@example.com",
|
|
"name": "Same",
|
|
},
|
|
},
|
|
}
|
|
|
|
def test_includes(self):
|
|
self.assertTrue(issue_matches(self.issue, 'subject.includes("verification code")'))
|
|
self.assertFalse(issue_matches(self.issue, 'subject.includes("magic link")'))
|
|
|
|
def test_path_eq_path(self):
|
|
expr = "helpdesk_ticket.contact.email == helpdesk_ticket.contact.company"
|
|
self.assertTrue(issue_matches(self.issue, expr))
|
|
|
|
def test_is_null(self):
|
|
self.assertTrue(issue_matches(self.issue, "assigned_to IS NULL"))
|
|
self.issue["assigned_to"] = {"name": "Jason T."}
|
|
self.assertFalse(issue_matches(self.issue, "assigned_to IS NULL"))
|
|
self.assertTrue(issue_matches(self.issue, 'assigned_to != "Other"'))
|
|
self.assertFalse(issue_matches(self.issue, 'assigned_to != "Jason T."'))
|
|
|
|
def test_and_or_parens(self):
|
|
expr = (
|
|
'(subject.includes("verification code") OR subject.includes("magic link")) '
|
|
'AND (assigned_to IS NULL)'
|
|
)
|
|
self.assertTrue(issue_matches(self.issue, expr))
|
|
|
|
def test_ampersand_and(self):
|
|
self.assertTrue(
|
|
issue_matches(self.issue, 'subject.includes("verification") && status.name == "New"')
|
|
)
|
|
|
|
def test_since(self):
|
|
self.assertTrue(issue_matches(self.issue, 'updated_on.since("1 hour")'))
|
|
old = {
|
|
**self.issue,
|
|
"updated_on": (datetime.now(timezone.utc) - timedelta(days=10))
|
|
.isoformat()
|
|
.replace("+00:00", "Z"),
|
|
}
|
|
self.assertFalse(issue_matches(old, 'updated_on.since("1 day")'))
|
|
|
|
def test_filter_multiple_and(self):
|
|
issues = [self.issue, {**self.issue, "id": 2, "subject": "hello"}]
|
|
out = filter_by_query(
|
|
issues,
|
|
expressions=['subject.includes("verification")', 'status.name == "New"'],
|
|
)
|
|
self.assertEqual([1], [i["id"] for i in out])
|
|
|
|
def test_bad_expr(self):
|
|
with self.assertRaises(QueryError):
|
|
compile_query("subject ~~ boom")
|
|
|
|
|
|
class MarkdownRenderTest(unittest.TestCase):
|
|
def test_render_contains_core_fields(self):
|
|
md = render_ticket_markdown(
|
|
{
|
|
"id": 42,
|
|
"subject": "Test",
|
|
"status": {"name": "New"},
|
|
"project": {"name": "customer-service"},
|
|
"description": "Body here",
|
|
"journals": [],
|
|
"updated_on": "2026-07-14T00:00:00Z",
|
|
"helpdesk_ticket": {"from_address": "a@b.c", "contact": {"id": 1}},
|
|
}
|
|
)
|
|
self.assertIn("# #42", md)
|
|
self.assertIn("Body here", md)
|
|
self.assertIn("a@b.c", md)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|