386 lines
15 KiB
Python
386 lines
15 KiB
Python
# -*- 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")
|
|
|