116 lines
4.5 KiB
Python
116 lines
4.5 KiB
Python
import unittest
|
|
from unittest.mock import patch, MagicMock
|
|
import json
|
|
import conductor_tech_lead
|
|
|
|
class TestConductorTechLead(unittest.TestCase):
|
|
@patch('ai_client.send')
|
|
@patch('ai_client.set_provider')
|
|
@patch('ai_client.reset_session')
|
|
def test_generate_tickets_success(self, mock_reset_session, mock_set_provider, mock_send):
|
|
# Setup mock response
|
|
mock_tickets = [
|
|
{
|
|
"id": "ticket_1",
|
|
"type": "Ticket",
|
|
"goal": "Test goal",
|
|
"target_file": "test.py",
|
|
"depends_on": [],
|
|
"context_requirements": []
|
|
}
|
|
]
|
|
mock_send.return_value = "```json\n" + json.dumps(mock_tickets) + "\n```"
|
|
|
|
track_brief = "Test track brief"
|
|
module_skeletons = "Test skeletons"
|
|
# Call the function
|
|
tickets = conductor_tech_lead.generate_tickets(track_brief, module_skeletons)
|
|
|
|
# Verify set_provider was called
|
|
mock_set_provider.assert_called_with('gemini', 'gemini-2.5-flash-lite')
|
|
mock_reset_session.assert_called_once()
|
|
|
|
# Verify send was called
|
|
mock_send.assert_called_once()
|
|
args, kwargs = mock_send.call_args
|
|
self.assertEqual(kwargs['md_content'], "")
|
|
self.assertIn(track_brief, kwargs['user_message'])
|
|
self.assertIn(module_skeletons, kwargs['user_message'])
|
|
|
|
# Verify tickets were parsed correctly
|
|
self.assertEqual(tickets, mock_tickets)
|
|
|
|
@patch('ai_client.send')
|
|
@patch('ai_client.set_provider')
|
|
@patch('ai_client.reset_session')
|
|
def test_generate_tickets_parse_error(self, mock_reset_session, mock_set_provider, mock_send):
|
|
# Setup mock invalid response
|
|
mock_send.return_value = "Invalid JSON"
|
|
|
|
# Call the function
|
|
tickets = conductor_tech_lead.generate_tickets("brief", "skeletons")
|
|
|
|
# Verify it returns an empty list on parse error
|
|
self.assertEqual(tickets, [])
|
|
|
|
class TestTopologicalSort(unittest.TestCase):
|
|
def test_topological_sort_empty(self):
|
|
tickets = []
|
|
sorted_tickets = conductor_tech_lead.topological_sort(tickets)
|
|
self.assertEqual(sorted_tickets, [])
|
|
|
|
def test_topological_sort_linear(self):
|
|
tickets = [
|
|
{"id": "t2", "depends_on": ["t1"]},
|
|
{"id": "t1", "depends_on": []},
|
|
{"id": "t3", "depends_on": ["t2"]},
|
|
]
|
|
sorted_tickets = conductor_tech_lead.topological_sort(tickets)
|
|
ids = [t["id"] for t in sorted_tickets]
|
|
self.assertEqual(ids, ["t1", "t2", "t3"])
|
|
|
|
def test_topological_sort_complex(self):
|
|
# t1
|
|
# | \
|
|
# t2 t3
|
|
# | /
|
|
# t4
|
|
tickets = [
|
|
{"id": "t4", "depends_on": ["t2", "t3"]},
|
|
{"id": "t3", "depends_on": ["t1"]},
|
|
{"id": "t2", "depends_on": ["t1"]},
|
|
{"id": "t1", "depends_on": []},
|
|
]
|
|
sorted_tickets = conductor_tech_lead.topological_sort(tickets)
|
|
ids = [t["id"] for t in sorted_tickets]
|
|
# Possible valid orders: [t1, t2, t3, t4] or [t1, t3, t2, t4]
|
|
self.assertEqual(ids[0], "t1")
|
|
self.assertEqual(ids[-1], "t4")
|
|
self.assertSetEqual(set(ids[1:3]), {"t2", "t3"})
|
|
|
|
def test_topological_sort_cycle(self):
|
|
tickets = [
|
|
{"id": "t1", "depends_on": ["t2"]},
|
|
{"id": "t2", "depends_on": ["t1"]},
|
|
]
|
|
with self.assertRaises(ValueError) as cm:
|
|
conductor_tech_lead.topological_sort(tickets)
|
|
self.assertIn("Circular dependency detected", str(cm.exception))
|
|
|
|
def test_topological_sort_missing_dependency(self):
|
|
# If a ticket depends on something not in the list, we should probably handle it or let it fail.
|
|
# Usually in our context, we only care about dependencies within the same track.
|
|
tickets = [
|
|
{"id": "t1", "depends_on": ["missing"]},
|
|
]
|
|
# For now, let's assume it should raise an error if a dependency is missing within the set we are sorting,
|
|
# OR it should just treat it as "ready" if it's external?
|
|
# Actually, let's just test that it doesn't crash if it's not a cycle.
|
|
# But if 'missing' is not in tickets, it will never be satisfied.
|
|
# Let's say it raises ValueError for missing internal dependencies.
|
|
with self.assertRaises(ValueError):
|
|
conductor_tech_lead.topological_sort(tickets)
|
|
|
|
if __name__ == '__main__':
|
|
unittest.main()
|