# -*- coding: utf-8 -*- from odoo.tests.common import TransactionCase from hypothesis import given, strategies as st, settings class TestHelpdeskTicket(TransactionCase): """Test cases for the extended helpdesk ticket model""" def setUp(self): super(TestHelpdeskTicket, self).setUp() self.HelpdeskTicket = self.env['helpdesk.ticket'] self.Rating = self.env['rating.rating'] self.Partner = self.env['res.partner'] self.User = self.env['res.users'] self.HelpdeskTeam = self.env['helpdesk.team'] # Create test partner self.test_partner = self.Partner.create({ 'name': 'Test Customer', 'email': 'test@example.com', }) # Create test user self.test_user = self.User.create({ 'name': 'Test User', 'login': 'testuser', 'email': 'testuser@example.com', }) # Create helpdesk team self.helpdesk_team = self.HelpdeskTeam.create({ 'name': 'Test Support Team', }) def _create_ticket_with_rating(self, rating_value): """Helper method to create a ticket with a rating""" # Create ticket ticket = self.HelpdeskTicket.create({ 'name': 'Test Ticket', 'partner_id': self.test_partner.id, 'team_id': self.helpdesk_team.id, }) # Create rating for the ticket if rating_value is not None: rating = self.Rating.create({ 'rating': rating_value, 'partner_id': self.test_partner.id, 'rated_partner_id': self.test_user.partner_id.id, 'res_model': 'helpdesk.ticket', 'res_id': ticket.id, }) # No need to invalidate - computed fields will compute on access return ticket # Feature: helpdesk-rating-five-stars, Property 10: Backend displays correct star count @given(rating_value=st.floats(min_value=1.0, max_value=5.0, allow_nan=False, allow_infinity=False)) @settings(max_examples=100, deadline=None) def test_property_backend_displays_correct_star_count(self, rating_value): """ Property 10: Backend displays correct star count For any rating value, the backend view should display the number of filled stars equal to the rating value (rounded). Validates: Requirements 4.3 """ # Create ticket with rating ticket = self._create_ticket_with_rating(rating_value) # Get the HTML representation html = ticket.rating_stars_html # Verify HTML is generated self.assertTrue(html, "HTML should be generated for rated ticket") # Count filled and empty stars in HTML filled_count = html.count('★') empty_count = html.count('☆') # Expected filled stars (rounded rating value) expected_filled = round(rating_value) expected_empty = 5 - expected_filled # Verify star counts match self.assertEqual(filled_count, expected_filled, f"For rating {rating_value}, should display {expected_filled} filled stars, got {filled_count}") self.assertEqual(empty_count, expected_empty, f"For rating {rating_value}, should display {expected_empty} empty stars, got {empty_count}") # Verify total is always 5 stars self.assertEqual(filled_count + empty_count, 5, "Total stars should always be 5") # Feature: helpdesk-rating-five-stars, Property 13: Ticket view displays rating stars @given(rating_value=st.one_of( st.just(0.0), # No rating st.floats(min_value=1.0, max_value=5.0, allow_nan=False, allow_infinity=False) # Valid ratings )) @settings(max_examples=100, deadline=None) def test_property_ticket_view_displays_rating_stars(self, rating_value): """ Property 13: Ticket view displays rating stars For any ticket with a rating, the backend view should display the rating as filled star icons. Validates: Requirements 5.1 """ # Create ticket with rating ticket = self._create_ticket_with_rating(rating_value) # Get the HTML representation html = ticket.rating_stars_html # Verify HTML is generated self.assertTrue(html, "HTML should be generated for ticket") # Verify HTML contains star structure self.assertIn('o_rating_stars', html, "HTML should contain rating stars class") # Verify stars are present has_filled_stars = '★' in html has_empty_stars = '☆' in html self.assertTrue(has_filled_stars or has_empty_stars, "HTML should contain star characters") # For non-zero ratings, verify filled stars match rating if rating_value > 0: filled_count = html.count('★') expected_filled = round(rating_value) self.assertEqual(filled_count, expected_filled, f"For rating {rating_value}, should display {expected_filled} filled stars") else: # For zero rating, should display 5 empty stars empty_count = html.count('☆') self.assertEqual(empty_count, 5, "For zero rating, should display 5 empty stars") def test_ticket_without_rating_displays_empty_stars(self): """Test that tickets without ratings display empty stars or 'Not Rated'""" # Create ticket without rating ticket = self._create_ticket_with_rating(None) # Get the HTML representation html = ticket.rating_stars_html # Verify HTML is generated self.assertTrue(html, "HTML should be generated even without rating") # Should display 5 empty stars empty_count = html.count('☆') self.assertEqual(empty_count, 5, "Ticket without rating should display 5 empty stars") # Should not have filled stars filled_count = html.count('★') self.assertEqual(filled_count, 0, "Ticket without rating should have no filled stars") def test_ticket_with_multiple_ratings_uses_most_recent(self): """Test that when a ticket has multiple ratings, the most recent is displayed""" # Create ticket ticket = self.HelpdeskTicket.create({ 'name': 'Test Ticket', 'partner_id': self.test_partner.id, 'team_id': self.helpdesk_team.id, }) # Create first rating rating1 = self.Rating.create({ 'rating': 2.0, 'partner_id': self.test_partner.id, 'rated_partner_id': self.test_user.partner_id.id, 'res_model': 'helpdesk.ticket', 'res_id': ticket.id, }) # Create second rating (more recent) rating2 = self.Rating.create({ 'rating': 5.0, 'partner_id': self.test_partner.id, 'rated_partner_id': self.test_user.partner_id.id, 'res_model': 'helpdesk.ticket', 'res_id': ticket.id, }) # Don't invalidate - just access the computed field directly # The ORM will handle the relationship correctly # Get the HTML representation html = ticket.rating_stars_html # Should display 5 filled stars (from most recent rating) filled_count = html.count('★') self.assertEqual(filled_count, 5, "Should display stars from most recent rating (5 stars)")