Files
2026-05-04 09:50:03 -04:00

139 lines
6.4 KiB
Python

import unittest
from semantic_index.models import IndexDocument
from semantic_index.redmine import RedmineMapper
class RedmineMapperTest(unittest.TestCase):
def test_issue_chunks_have_stable_ids_and_metadata(self):
issue = {
"id": 42,
"subject": "Widget order ORD-12345 cannot ship",
"description": "Customer reports that widget order ORD-12345 is blocked.",
"project": {"id": 7, "identifier": "fud-helpdesk"},
"contact": {"id": 9, "email": "ada@example.com", "name": "Ada Lovelace"},
"created_on": "2026-04-01T10:00:00Z",
"updated_on": "2026-04-02T10:00:00Z",
"url": "http://redmine.local/issues/42",
}
first = RedmineMapper(redmine_url="http://redmine.local").issue_to_documents(issue)
second = RedmineMapper(redmine_url="http://redmine.local").issue_to_documents(issue)
self.assertEqual([doc.id for doc in first], [doc.id for doc in second])
self.assertEqual("redmine:issue:42:chunk:0", first[0].id)
self.assertEqual("issue", first[0].payload["doc_type"])
self.assertEqual(42, first[0].payload["issue_id"])
self.assertEqual("fud-helpdesk", first[0].payload["project_identifier"])
self.assertIsNone(first[0].payload["project_name"])
self.assertFalse(first[0].payload["has_helpdesk_ticket"])
self.assertEqual("ada@example.com", first[0].payload["contact_email"])
self.assertEqual("Ada Lovelace", first[0].payload["contact_name"])
self.assertEqual("http://redmine.local/issues/42", first[0].payload["redmine_url"])
self.assertIn("source_hash", first[0].payload)
def test_helpdesk_ticket_contact_is_mapped_to_all_issue_chunks(self):
issue = {
"id": 39779,
"subject": "Goods return",
"description": "Please arrange to return these goods.",
"project": {"id": 1, "identifier": "customer-service"},
"helpdesk_ticket": {
"id": 35159,
"contact_id": 1890,
"from_address": "callum@safetagtracking.com",
"contact": {
"id": 1890,
"name": "Callum Mackeonis",
"company": "SafeTag Tracking",
"email": "callum@safetagtracking.com",
},
},
"journals": [
{"id": 71570, "notes": "Hello, yes we can arrange this today.", "created_on": "2026-04-14T14:29:49Z"}
],
}
docs = RedmineMapper(redmine_url="http://redmine.local").issue_to_documents(issue)
issue_doc = next(doc for doc in docs if doc.payload["doc_type"] == "issue")
journal_doc = next(doc for doc in docs if doc.payload["doc_type"] == "journal")
contact_doc = next(doc for doc in docs if doc.payload["doc_type"] == "contact")
for doc in (issue_doc, journal_doc, contact_doc):
self.assertEqual(35159, doc.payload["helpdesk_ticket_id"])
self.assertTrue(doc.payload["has_helpdesk_ticket"])
self.assertEqual(1890, doc.payload["contact_id"])
self.assertEqual("Callum Mackeonis", doc.payload["contact_name"])
self.assertEqual("SafeTag Tracking", doc.payload["contact_company"])
self.assertEqual("callum@safetagtracking.com", doc.payload["contact_email"])
self.assertIn("Callum Mackeonis", issue_doc.text)
self.assertIn("callum@safetagtracking.com", contact_doc.text)
def test_configured_project_identifier_is_used_when_issue_payload_omits_identifier(self):
issue = {
"id": 42,
"subject": "Widget order",
"description": "Body",
"project": {"id": 1, "name": "Customer Service"},
}
docs = RedmineMapper(
redmine_url="http://redmine.local",
project_identifier="customer-service",
).issue_to_documents(issue)
self.assertEqual("customer-service", docs[0].payload["project_identifier"])
self.assertEqual("Customer Service", docs[0].payload["project_name"])
def test_internal_non_helpdesk_issue_keeps_project_metadata_without_contact(self):
issue = {
"id": 55,
"subject": "Internal hiring task",
"description": "Follow up with candidate.",
"project": {"id": 68, "identifier": "hiring", "name": "Hiring"},
}
docs = RedmineMapper(redmine_url="http://redmine.local").issue_to_documents(issue)
self.assertEqual(1, len(docs))
self.assertEqual("hiring", docs[0].payload["project_identifier"])
self.assertEqual("Hiring", docs[0].payload["project_name"])
self.assertFalse(docs[0].payload["has_helpdesk_ticket"])
self.assertIsNone(docs[0].payload["contact_id"])
def test_issue_journals_messages_and_contact_are_mapped(self):
issue = {
"id": 42,
"subject": "Widget order",
"description": "Ticket envelope",
"project": {"id": 7, "identifier": "fud-helpdesk"},
"contact": {"id": 9, "email": "ada@example.com", "name": "Ada Lovelace"},
"journals": [
{"id": 5, "notes": "Private escalation note", "private_notes": True, "created_on": "2026-04-03T10:00:00Z"}
],
"messages": [
{"id": 6, "body": "Customer reply body", "direction": "incoming", "created_on": "2026-04-03T11:00:00Z"}
],
}
docs = RedmineMapper(redmine_url="http://redmine.local").issue_to_documents(issue)
ids = {doc.id for doc in docs}
types = {doc.payload["doc_type"] for doc in docs}
self.assertIn("redmine:issue:42:journal:5:chunk:0", ids)
self.assertIn("redmine:issue:42:message:6:chunk:0", ids)
self.assertIn("redmine:contact:9:issue:42:chunk:0", ids)
self.assertEqual({"issue", "journal", "message", "contact"}, types)
journal = next(doc for doc in docs if doc.payload["doc_type"] == "journal")
message = next(doc for doc in docs if doc.payload["doc_type"] == "message")
self.assertEqual("private", journal.payload["visibility"])
self.assertEqual("incoming", message.payload["direction"])
def test_empty_documents_are_rejected(self):
with self.assertRaises(ValueError):
IndexDocument(id="x", text=" ", payload={})
if __name__ == "__main__":
unittest.main()