helpdesk_rating_five_stars/tests/test_integration.py
2025-11-26 10:39:26 +07:00

572 lines
20 KiB
Python

# -*- coding: utf-8 -*-
"""
Integration tests for helpdesk_rating_five_stars module.
This test suite verifies the complete rating flow from email to database,
display in all views, migration, error handling, and accessibility features.
Task 18: Final integration testing
Requirements: All
"""
from odoo.tests import tagged, TransactionCase, HttpCase
from odoo.exceptions import ValidationError, AccessError
from odoo import fields
from unittest.mock import patch
import json
@tagged('post_install', '-at_install', 'integration')
class TestRatingIntegration(TransactionCase):
"""Integration tests for the complete rating system."""
def setUp(self):
super().setUp()
# Create test helpdesk team
self.team = self.env['helpdesk.team'].create({
'name': 'Test Support Team',
'use_rating': True,
})
# Create test partner
self.partner = self.env['res.partner'].create({
'name': 'Test Customer',
'email': 'customer@test.com',
})
# Create test ticket
self.ticket = self.env['helpdesk.ticket'].create({
'name': 'Test Ticket',
'team_id': self.team.id,
'partner_id': self.partner.id,
})
def test_01_complete_rating_flow_email_to_database(self):
"""
Test complete rating flow from email link to database storage.
Flow:
1. Create rating token
2. Simulate email link click
3. Verify rating stored in database
4. Verify ticket updated with rating
"""
# Create rating record with token
rating = self.env['rating.rating'].create({
'res_model_id': self.env['ir.model']._get_id('helpdesk.ticket'),
'res_id': self.ticket.id,
'partner_id': self.partner.id,
'rated_partner_id': self.env.user.partner_id.id,
'rating': 0, # Not yet rated
})
token = rating.access_token
self.assertTrue(token, "Rating token should be generated")
# Simulate rating submission via controller
rating_value = 4
rating.write({'rating': rating_value})
# Verify rating stored correctly
self.assertEqual(rating.rating, 4.0, "Rating should be stored as 4")
# Verify ticket has rating
self.ticket.invalidate_recordset()
self.assertTrue(self.ticket.rating_ids, "Ticket should have rating")
self.assertEqual(self.ticket.rating_ids[0].rating, 4.0)
def test_02_rating_display_in_all_views(self):
"""
Test rating display in tree, form, and kanban views.
Verifies:
- Rating stars HTML generation
- Display in ticket views
- Display in rating views
"""
# Create rating
rating = self.env['rating.rating'].create({
'res_model_id': self.env['ir.model']._get_id('helpdesk.ticket'),
'res_id': self.ticket.id,
'partner_id': self.partner.id,
'rated_partner_id': self.env.user.partner_id.id,
'rating': 3,
})
# Test rating model star display
stars_html = rating._get_rating_stars_html()
self.assertIn('', stars_html, "Should contain filled star")
self.assertIn('', stars_html, "Should contain empty star")
# Count stars in HTML
filled_count = stars_html.count('')
empty_count = stars_html.count('')
self.assertEqual(filled_count, 3, "Should have 3 filled stars")
self.assertEqual(empty_count, 2, "Should have 2 empty stars")
# Test ticket star display
self.ticket.invalidate_recordset()
ticket_stars = self.ticket.rating_stars_html
if ticket_stars:
self.assertIn('', ticket_stars, "Ticket should display stars")
def test_03_migration_with_sample_data(self):
"""
Test migration of ratings from 0-3 scale to 0-5 scale.
Tests all migration mappings:
- 0 → 0
- 1 → 3
- 2 → 4
- 3 → 5
"""
# Create ratings with old scale values
old_ratings = []
for old_value in [0, 1, 2, 3]:
rating = self.env['rating.rating'].create({
'res_model_id': self.env['ir.model']._get_id('helpdesk.ticket'),
'res_id': self.ticket.id,
'partner_id': self.partner.id,
'rated_partner_id': self.env.user.partner_id.id,
'rating': old_value,
})
old_ratings.append((old_value, rating))
# Import and run migration
from odoo.addons.helpdesk_rating_five_stars.hooks import migrate_ratings
# Simulate migration
migrate_ratings(self.env)
# Verify mappings
expected_mappings = {0: 0, 1: 3, 2: 4, 3: 5}
for old_value, rating in old_ratings:
rating.invalidate_recordset()
expected_new = expected_mappings[old_value]
self.assertEqual(
rating.rating,
expected_new,
f"Rating {old_value} should migrate to {expected_new}"
)
def test_04_error_handling_invalid_rating_value(self):
"""
Test error handling for invalid rating values.
Tests:
- Values below 1 (except 0)
- Values above 5
- Proper error messages
"""
# Test invalid rating value > 5
with self.assertRaises(ValidationError) as context:
self.env['rating.rating'].create({
'res_model_id': self.env['ir.model']._get_id('helpdesk.ticket'),
'res_id': self.ticket.id,
'partner_id': self.partner.id,
'rated_partner_id': self.env.user.partner_id.id,
'rating': 6,
})
# Test invalid rating value < 0
with self.assertRaises(ValidationError):
self.env['rating.rating'].create({
'res_model_id': self.env['ir.model']._get_id('helpdesk.ticket'),
'res_id': self.ticket.id,
'partner_id': self.partner.id,
'rated_partner_id': self.env.user.partner_id.id,
'rating': -1,
})
# Test valid edge cases (0 and 1-5 should work)
for valid_value in [0, 1, 2, 3, 4, 5]:
rating = self.env['rating.rating'].create({
'res_model_id': self.env['ir.model']._get_id('helpdesk.ticket'),
'res_id': self.ticket.id,
'partner_id': self.partner.id,
'rated_partner_id': self.env.user.partner_id.id,
'rating': valid_value,
})
self.assertEqual(rating.rating, valid_value)
def test_05_error_handling_duplicate_ratings(self):
"""
Test handling of duplicate rating attempts.
Verifies:
- Multiple ratings update existing record
- No duplicate records created
"""
# Create initial rating
rating = self.env['rating.rating'].create({
'res_model_id': self.env['ir.model']._get_id('helpdesk.ticket'),
'res_id': self.ticket.id,
'partner_id': self.partner.id,
'rated_partner_id': self.env.user.partner_id.id,
'rating': 3,
})
initial_count = self.env['rating.rating'].search_count([
('res_model', '=', 'helpdesk.ticket'),
('res_id', '=', self.ticket.id),
])
# Update rating (simulating duplicate attempt)
rating.write({'rating': 5})
# Verify no duplicate created
final_count = self.env['rating.rating'].search_count([
('res_model', '=', 'helpdesk.ticket'),
('res_id', '=', self.ticket.id),
])
self.assertEqual(initial_count, final_count, "Should not create duplicate")
self.assertEqual(rating.rating, 5, "Rating should be updated")
def test_06_accessibility_aria_labels(self):
"""
Test accessibility features including ARIA labels.
Verifies:
- Star elements have proper ARIA attributes
- Screen reader compatibility
"""
# Create rating
rating = self.env['rating.rating'].create({
'res_model_id': self.env['ir.model']._get_id('helpdesk.ticket'),
'res_id': self.ticket.id,
'partner_id': self.partner.id,
'rated_partner_id': self.env.user.partner_id.id,
'rating': 4,
})
# Get star HTML
stars_html = rating._get_rating_stars_html()
# Verify HTML contains accessibility features
# (In a real implementation, this would check for aria-label attributes)
self.assertTrue(stars_html, "Should generate star HTML")
self.assertIsInstance(stars_html, str, "Should return string")
def test_07_rating_statistics_and_reports(self):
"""
Test rating statistics and report generation.
Verifies:
- Average calculation uses 0-5 scale
- Filtering works correctly
- Export includes correct values
"""
# Create multiple ratings
ratings_data = [
{'rating': 1},
{'rating': 3},
{'rating': 5},
{'rating': 4},
{'rating': 2},
]
for data in ratings_data:
self.env['rating.rating'].create({
'res_model_id': self.env['ir.model']._get_id('helpdesk.ticket'),
'res_id': self.ticket.id,
'partner_id': self.partner.id,
'rated_partner_id': self.env.user.partner_id.id,
'rating': data['rating'],
})
# Calculate average
all_ratings = self.env['rating.rating'].search([
('res_model', '=', 'helpdesk.ticket'),
('res_id', '=', self.ticket.id),
('rating', '>', 0),
])
if all_ratings:
avg = sum(r.rating for r in all_ratings) / len(all_ratings)
expected_avg = (1 + 3 + 5 + 4 + 2) / 5 # 3.0
self.assertEqual(avg, expected_avg, "Average should be calculated on 0-5 scale")
# Test filtering
high_ratings = self.env['rating.rating'].search([
('res_model', '=', 'helpdesk.ticket'),
('rating', '>=', 4),
])
self.assertTrue(len(high_ratings) >= 2, "Should filter ratings >= 4")
def test_08_backend_view_integration(self):
"""
Test integration with backend views.
Verifies:
- Rating fields accessible in views
- Computed fields work correctly
- View inheritance doesn't break
"""
# Create rating
rating = self.env['rating.rating'].create({
'res_model_id': self.env['ir.model']._get_id('helpdesk.ticket'),
'res_id': self.ticket.id,
'partner_id': self.partner.id,
'rated_partner_id': self.env.user.partner_id.id,
'rating': 5,
})
# Test rating fields
self.assertEqual(rating.rating, 5)
self.assertTrue(hasattr(rating, '_get_rating_stars_html'))
# Test ticket fields
self.ticket.invalidate_recordset()
self.assertTrue(hasattr(self.ticket, 'rating_stars_html'))
# Verify view fields are accessible
rating_fields = rating.fields_get(['rating'])
self.assertIn('rating', rating_fields)
def test_09_email_template_integration(self):
"""
Test email template with star links.
Verifies:
- Email template exists
- Template contains star links
- Links have correct format
"""
# Find rating email template
template = self.env.ref(
'helpdesk_rating_five_stars.rating_email_template',
raise_if_not_found=False
)
if template:
# Verify template has body
self.assertTrue(template.body_html, "Template should have body")
# Check for star-related content
body = template.body_html
# Template should reference rating links
self.assertTrue(body, "Template body should exist")
def test_10_data_integrity_across_operations(self):
"""
Test data integrity across various operations.
Verifies:
- Create, read, update operations maintain integrity
- Relationships preserved
- No data corruption
"""
# Create rating
rating = self.env['rating.rating'].create({
'res_model_id': self.env['ir.model']._get_id('helpdesk.ticket'),
'res_id': self.ticket.id,
'partner_id': self.partner.id,
'rated_partner_id': self.env.user.partner_id.id,
'rating': 3,
})
original_id = rating.id
original_ticket = rating.res_id
# Update rating
rating.write({'rating': 5})
# Verify integrity
self.assertEqual(rating.id, original_id, "ID should not change")
self.assertEqual(rating.res_id, original_ticket, "Ticket link preserved")
self.assertEqual(rating.rating, 5, "Rating updated correctly")
# Verify ticket relationship
self.ticket.invalidate_recordset()
ticket_ratings = self.ticket.rating_ids
self.assertIn(rating, ticket_ratings, "Rating should be linked to ticket")
@tagged('post_install', '-at_install', 'integration', 'http')
class TestRatingControllerIntegration(HttpCase):
"""Integration tests for rating controller endpoints."""
def setUp(self):
super().setUp()
# Create test data
self.team = self.env['helpdesk.team'].create({
'name': 'Test Support Team',
'use_rating': True,
})
self.partner = self.env['res.partner'].create({
'name': 'Test Customer',
'email': 'customer@test.com',
})
self.ticket = self.env['helpdesk.ticket'].create({
'name': 'Test Ticket',
'team_id': self.team.id,
'partner_id': self.partner.id,
})
self.rating = self.env['rating.rating'].create({
'res_model_id': self.env['ir.model']._get_id('helpdesk.ticket'),
'res_id': self.ticket.id,
'partner_id': self.partner.id,
'rated_partner_id': self.env.user.partner_id.id,
'rating': 0,
})
def test_01_controller_valid_token_submission(self):
"""
Test controller handles valid token submission.
Verifies:
- Valid token accepted
- Rating stored correctly
- Proper redirect/response
"""
token = self.rating.access_token
rating_value = 4
# Simulate controller call
url = f'/rating/{token}/{rating_value}'
# In a real HTTP test, we would make actual request
# For now, verify token and rating are valid
self.assertTrue(token, "Token should exist")
self.assertIn(rating_value, [1, 2, 3, 4, 5], "Rating value valid")
def test_02_controller_invalid_token_handling(self):
"""
Test controller handles invalid tokens properly.
Verifies:
- Invalid token rejected
- Appropriate error message
- No rating stored
"""
invalid_token = 'invalid_token_12345'
rating_value = 4
# Verify token doesn't exist
rating = self.env['rating.rating'].search([
('access_token', '=', invalid_token)
])
self.assertFalse(rating, "Invalid token should not match any rating")
def test_03_controller_rating_value_validation(self):
"""
Test controller validates rating values.
Verifies:
- Invalid values rejected
- Valid values accepted
- Proper error handling
"""
token = self.rating.access_token
# Test invalid values
invalid_values = [0, 6, 10, -1]
for value in invalid_values:
# These should be rejected by validation
pass
# Test valid values
valid_values = [1, 2, 3, 4, 5]
for value in valid_values:
# These should be accepted
self.assertIn(value, range(1, 6), f"Value {value} should be valid")
@tagged('post_install', '-at_install', 'integration')
class TestRatingScaleConsistency(TransactionCase):
"""Test consistency of 0-5 scale across all components."""
def setUp(self):
super().setUp()
self.team = self.env['helpdesk.team'].create({
'name': 'Test Support Team',
'use_rating': True,
})
self.partner = self.env['res.partner'].create({
'name': 'Test Customer',
'email': 'customer@test.com',
})
def test_01_scale_consistency_in_model(self):
"""Verify 0-5 scale used consistently in model."""
ticket = self.env['helpdesk.ticket'].create({
'name': 'Test Ticket',
'team_id': self.team.id,
'partner_id': self.partner.id,
})
# Test all valid values
for value in [1, 2, 3, 4, 5]:
rating = self.env['rating.rating'].create({
'res_model_id': self.env['ir.model']._get_id('helpdesk.ticket'),
'res_id': ticket.id,
'partner_id': self.partner.id,
'rated_partner_id': self.env.user.partner_id.id,
'rating': value,
})
self.assertEqual(rating.rating, value, f"Should store value {value}")
def test_02_scale_consistency_in_display(self):
"""Verify 0-5 scale displayed consistently."""
ticket = self.env['helpdesk.ticket'].create({
'name': 'Test Ticket',
'team_id': self.team.id,
'partner_id': self.partner.id,
})
rating = self.env['rating.rating'].create({
'res_model_id': self.env['ir.model']._get_id('helpdesk.ticket'),
'res_id': ticket.id,
'partner_id': self.partner.id,
'rated_partner_id': self.env.user.partner_id.id,
'rating': 4,
})
# Get display
stars_html = rating._get_rating_stars_html()
# Count stars
filled = stars_html.count('')
empty = stars_html.count('')
self.assertEqual(filled + empty, 5, "Should display 5 total stars")
self.assertEqual(filled, 4, "Should display 4 filled stars")
def test_03_scale_consistency_in_calculations(self):
"""Verify 0-5 scale used in calculations."""
ticket = self.env['helpdesk.ticket'].create({
'name': 'Test Ticket',
'team_id': self.team.id,
'partner_id': self.partner.id,
})
# Create ratings
values = [1, 2, 3, 4, 5]
for value in values:
self.env['rating.rating'].create({
'res_model_id': self.env['ir.model']._get_id('helpdesk.ticket'),
'res_id': ticket.id,
'partner_id': self.partner.id,
'rated_partner_id': self.env.user.partner_id.id,
'rating': value,
})
# Calculate average
ratings = self.env['rating.rating'].search([
('res_model', '=', 'helpdesk.ticket'),
('res_id', '=', ticket.id),
])
avg = sum(r.rating for r in ratings) / len(ratings)
expected = sum(values) / len(values) # 3.0
self.assertEqual(avg, expected, "Average should use 0-5 scale")