# -*- coding: utf-8 -*- from odoo.tests.common import TransactionCase from hypothesis import given, strategies as st, settings, assume import inspect class TestAPICompatibility(TransactionCase): """Test cases for API compatibility with standard Odoo rating system""" def setUp(self): super(TestAPICompatibility, self).setUp() self.Rating = self.env['rating.rating'] self.Partner = self.env['res.partner'] self.User = self.env['res.users'] self.HelpdeskTicket = self.env['helpdesk.ticket'] self.HelpdeskTeam = self.env['helpdesk.team'] # Create test data self.test_partner = self.Partner.create({ 'name': 'Test Customer', 'email': 'test@example.com', }) self.test_user = self.User.create({ 'name': 'Test User', 'login': 'testuser_api', 'email': 'testuser_api@example.com', }) # Create helpdesk team and ticket for realistic testing self.helpdesk_team = self.HelpdeskTeam.create({ 'name': 'Test Support Team', 'use_rating': True, }) self.test_ticket = self.HelpdeskTicket.create({ 'name': 'Test Ticket for API', 'team_id': self.helpdesk_team.id, 'partner_id': self.test_partner.id, }) def _create_rating(self, rating_value, **kwargs): """Helper method to create a rating with given value""" # Get the ir.model record for helpdesk.ticket res_model_id = self.env['ir.model'].search([('model', '=', 'helpdesk.ticket')], limit=1) vals = { 'rating': rating_value, 'partner_id': self.test_partner.id, 'rated_partner_id': self.test_user.partner_id.id, 'res_model_id': res_model_id.id, 'res_id': self.test_ticket.id, } vals.update(kwargs) return self.Rating.create(vals) # Feature: helpdesk-rating-five-stars, Property 14: API compatibility maintained @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_create_method_signature(self, rating_value): """ Property 14: API compatibility maintained - create method For any overridden rating method, the method signature and return type should remain compatible with the standard Odoo rating API. This test verifies that the create() method: - Accepts the same parameters as the base model - Returns a rating.rating recordset - Properly stores the rating value Validates: Requirements 6.3 """ # Create rating using standard API rating = self._create_rating(rating_value) # Verify return type is a rating.rating recordset self.assertEqual(rating._name, 'rating.rating', "create() should return a rating.rating recordset") # Verify the record exists self.assertTrue(rating.id, "create() should return a record with an ID") # Verify the rating value was stored correctly self.assertGreaterEqual(rating.rating, 1.0, "Rating value should be >= 1.0") self.assertLessEqual(rating.rating, 5.0, "Rating value should be <= 5.0") # Verify standard fields are accessible self.assertTrue(hasattr(rating, 'res_model'), "Standard field 'res_model' should be accessible") self.assertTrue(hasattr(rating, 'res_id'), "Standard field 'res_id' should be accessible") self.assertTrue(hasattr(rating, 'partner_id'), "Standard field 'partner_id' should be accessible") self.assertTrue(hasattr(rating, 'rated_partner_id'), "Standard field 'rated_partner_id' should be accessible") self.assertTrue(hasattr(rating, 'feedback'), "Standard field 'feedback' should be accessible") self.assertTrue(hasattr(rating, 'consumed'), "Standard field 'consumed' should be accessible") self.assertTrue(hasattr(rating, 'access_token'), "Standard field 'access_token' should be accessible") @given( initial_rating=st.floats(min_value=1.0, max_value=5.0, allow_nan=False, allow_infinity=False), new_rating=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_write_method_signature(self, initial_rating, new_rating): """ Property 14: API compatibility maintained - write method For any overridden rating method, the method signature and return type should remain compatible with the standard Odoo rating API. This test verifies that the write() method: - Accepts the same parameters as the base model - Returns True (standard Odoo write behavior) - Properly updates the rating value Validates: Requirements 6.3 """ # Create initial rating rating = self._create_rating(initial_rating) initial_id = rating.id # Update rating using standard API result = rating.write({'rating': new_rating}) # Verify return value is True (standard Odoo behavior) self.assertTrue(result, "write() should return True") # Verify the record still exists with same ID self.assertEqual(rating.id, initial_id, "write() should not change record ID") # Verify the rating value was updated self.assertAlmostEqual(rating.rating, new_rating, places=2, msg=f"Rating should be updated to {new_rating}") # Verify we can update other standard fields rating.write({'feedback': 'Test feedback'}) self.assertEqual(rating.feedback, 'Test feedback', "Standard field 'feedback' should be writable") @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_reset_method_compatibility(self, rating_value): """ Property 14: API compatibility maintained - reset method For any overridden rating method, the method signature and return type should remain compatible with the standard Odoo rating API. This test verifies that the reset() method: - Works as expected (resets rating to 0) - Resets consumed flag - Generates new access token - Clears feedback Validates: Requirements 6.3 """ # Create rating with value and feedback rating = self._create_rating(rating_value, feedback='Test feedback', consumed=True) original_token = rating.access_token # Reset the rating rating.reset() # Verify rating is reset to 0 self.assertEqual(rating.rating, 0.0, "reset() should set rating to 0") # Verify consumed flag is reset self.assertFalse(rating.consumed, "reset() should set consumed to False") # Verify feedback is cleared self.assertFalse(rating.feedback, "reset() should clear feedback") # Verify new access token is generated self.assertNotEqual(rating.access_token, original_token, "reset() should generate new access token") @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_action_open_rated_object_compatibility(self, rating_value): """ Property 14: API compatibility maintained - action_open_rated_object method For any overridden rating method, the method signature and return type should remain compatible with the standard Odoo rating API. This test verifies that the action_open_rated_object() method: - Returns a proper action dictionary - Contains required keys (type, res_model, res_id, views) - Points to the correct record Validates: Requirements 6.3 """ # Create rating rating = self._create_rating(rating_value) # Call action_open_rated_object action = rating.action_open_rated_object() # Verify return type is a dictionary self.assertIsInstance(action, dict, "action_open_rated_object() should return a dictionary") # Verify required keys are present self.assertIn('type', action, "Action should contain 'type' key") self.assertIn('res_model', action, "Action should contain 'res_model' key") self.assertIn('res_id', action, "Action should contain 'res_id' key") self.assertIn('views', action, "Action should contain 'views' key") # Verify action points to correct record self.assertEqual(action['type'], 'ir.actions.act_window', "Action type should be 'ir.actions.act_window'") self.assertEqual(action['res_model'], rating.res_model, "Action res_model should match rating res_model") self.assertEqual(action['res_id'], rating.res_id, "Action res_id should match rating res_id") def test_property_field_compatibility(self): """ Property 14: API compatibility maintained - field compatibility For any overridden rating method, the method signature and return type should remain compatible with the standard Odoo rating API. This test verifies that all standard rating fields are accessible and work as expected. Validates: Requirements 6.3 """ # Create rating rating = self._create_rating(3.0, feedback='Great service!') # Test standard field access standard_fields = [ 'rating', 'res_model', 'res_id', 'partner_id', 'rated_partner_id', 'feedback', 'consumed', 'access_token', 'create_date', 'write_date', 'res_name', 'rating_text', 'message_id', 'is_internal' ] for field_name in standard_fields: self.assertTrue(hasattr(rating, field_name), f"Standard field '{field_name}' should be accessible") # Try to read the field (should not raise exception) try: value = getattr(rating, field_name) # Field access should work self.assertIsNotNone(field_name, f"Field '{field_name}' should be readable") except Exception as e: self.fail(f"Field '{field_name}' access raised exception: {e}") def test_property_computed_fields_compatibility(self): """ Property 14: API compatibility maintained - computed fields For any overridden rating method, the method signature and return type should remain compatible with the standard Odoo rating API. This test verifies that computed fields work correctly. Validates: Requirements 6.3 """ # Create rating rating = self._create_rating(4.0) # Test computed fields self.assertTrue(hasattr(rating, 'res_name'), "Computed field 'res_name' should exist") self.assertTrue(hasattr(rating, 'rating_text'), "Computed field 'rating_text' should exist") # Verify res_name is computed self.assertTrue(rating.res_name, "res_name should be computed and not empty") # Verify rating_text is computed self.assertTrue(rating.rating_text, "rating_text should be computed and not empty") @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_search_compatibility(self, rating_value): """ Property 14: API compatibility maintained - search compatibility For any overridden rating method, the method signature and return type should remain compatible with the standard Odoo rating API. This test verifies that search operations work correctly with the extended rating model. Validates: Requirements 6.3 """ # Create rating rating = self._create_rating(rating_value) # Test search by rating value found_ratings = self.Rating.search([ ('rating', '=', rating_value), ('id', '=', rating.id) ]) self.assertIn(rating, found_ratings, "Search should find the created rating") # Test search by standard fields found_by_partner = self.Rating.search([ ('partner_id', '=', self.test_partner.id), ('id', '=', rating.id) ]) self.assertIn(rating, found_by_partner, "Search by partner_id should work") # Test search by res_model found_by_model = self.Rating.search([ ('res_model', '=', 'helpdesk.ticket'), ('id', '=', rating.id) ]) self.assertIn(rating, found_by_model, "Search by res_model should work") def test_property_unlink_compatibility(self): """ Property 14: API compatibility maintained - unlink compatibility For any overridden rating method, the method signature and return type should remain compatible with the standard Odoo rating API. This test verifies that unlink() works correctly. Validates: Requirements 6.3 """ # Create rating rating = self._create_rating(3.0) rating_id = rating.id # Unlink the rating result = rating.unlink() # Verify return value is True self.assertTrue(result, "unlink() should return True") # Verify rating no longer exists exists = self.Rating.search([('id', '=', rating_id)]) self.assertFalse(exists, "Rating should not exist after unlink()") def test_property_method_signatures_match(self): """ Property 14: API compatibility maintained - method signatures For any overridden rating method, the method signature should match the base model signature. This test verifies that overridden methods have compatible signatures. Validates: Requirements 6.3 """ # Get the extended rating model class extended_rating_class = self.Rating.__class__ # Check that key methods exist key_methods = ['create', 'write', 'reset', 'action_open_rated_object'] for method_name in key_methods: self.assertTrue(hasattr(extended_rating_class, method_name), f"Method '{method_name}' should exist in extended model") method = getattr(extended_rating_class, method_name) self.assertTrue(callable(method), f"'{method_name}' should be callable")