# -*- coding: utf-8 -*- import base64 import json from odoo.tests import TransactionCase from odoo.exceptions import UserError class TestMultiSurveyTemplateIsolation(TransactionCase): """ Test cases for multi-survey template management and isolation. These tests verify that: - Templates are stored per survey record (Requirement 7.2) - Template loading works correctly by survey_id (Requirement 7.3) - Templates from one survey do not affect other surveys """ def setUp(self): super().setUp() # Create multiple test surveys self.survey_1 = self.env['survey.survey'].create({ 'title': 'Python Programming Course', 'description': 'Learn Python programming', }) self.survey_2 = self.env['survey.survey'].create({ 'title': 'JavaScript Fundamentals', 'description': 'Learn JavaScript basics', }) self.survey_3 = self.env['survey.survey'].create({ 'title': 'Data Science Bootcamp', 'description': 'Master data science', }) def _create_test_docx(self, text_content): """ Helper method to create a DOCX file with given text content. Args: text_content: String or list of strings to add as paragraphs Returns: bytes: Binary content of the created DOCX file """ try: from docx import Document from io import BytesIO except ImportError: self.skipTest("python-docx not installed") doc = Document() if isinstance(text_content, str): text_content = [text_content] for text in text_content: doc.add_paragraph(text) # Save to BytesIO doc_stream = BytesIO() doc.save(doc_stream) doc_stream.seek(0) return doc_stream.read() def test_template_storage_per_survey(self): """ Test that templates are stored separately for each survey. Validates Requirement 7.2: Templates should be stored per survey record. """ # Create different templates for each survey template_1 = self._create_test_docx("Certificate for {key.name} - Python Course") template_2 = self._create_test_docx("Certificate for {key.name} - JavaScript Course") template_3 = self._create_test_docx("Certificate for {key.name} - Data Science Course") # Configure survey 1 with template 1 self.survey_1.write({ 'custom_cert_template': base64.b64encode(template_1), 'custom_cert_template_filename': 'python_cert.docx', 'has_custom_certificate': True, 'custom_cert_mappings': json.dumps({ 'placeholders': [ {'key': '{key.name}', 'value_type': 'user_field', 'value_field': 'partner_name'} ] }) }) # Configure survey 2 with template 2 self.survey_2.write({ 'custom_cert_template': base64.b64encode(template_2), 'custom_cert_template_filename': 'javascript_cert.docx', 'has_custom_certificate': True, 'custom_cert_mappings': json.dumps({ 'placeholders': [ {'key': '{key.name}', 'value_type': 'user_field', 'value_field': 'partner_name'} ] }) }) # Configure survey 3 with template 3 self.survey_3.write({ 'custom_cert_template': base64.b64encode(template_3), 'custom_cert_template_filename': 'datascience_cert.docx', 'has_custom_certificate': True, 'custom_cert_mappings': json.dumps({ 'placeholders': [ {'key': '{key.name}', 'value_type': 'user_field', 'value_field': 'partner_name'} ] }) }) # Verify each survey has its own template self.assertTrue(self.survey_1.has_custom_certificate) self.assertTrue(self.survey_2.has_custom_certificate) self.assertTrue(self.survey_3.has_custom_certificate) # Verify filenames are different self.assertEqual(self.survey_1.custom_cert_template_filename, 'python_cert.docx') self.assertEqual(self.survey_2.custom_cert_template_filename, 'javascript_cert.docx') self.assertEqual(self.survey_3.custom_cert_template_filename, 'datascience_cert.docx') # Verify templates are different (by comparing binary content) template_1_stored = base64.b64decode(self.survey_1.custom_cert_template) template_2_stored = base64.b64decode(self.survey_2.custom_cert_template) template_3_stored = base64.b64decode(self.survey_3.custom_cert_template) self.assertNotEqual(template_1_stored, template_2_stored) self.assertNotEqual(template_2_stored, template_3_stored) self.assertNotEqual(template_1_stored, template_3_stored) def test_template_loading_by_survey_id(self): """ Test that the correct template is loaded for each survey. Validates Requirement 7.3: Switching between surveys should load the correct custom template for that survey. """ # Create different templates template_1 = self._create_test_docx("Python Certificate") template_2 = self._create_test_docx("JavaScript Certificate") # Configure survey 1 self.survey_1.write({ 'custom_cert_template': base64.b64encode(template_1), 'custom_cert_template_filename': 'python_cert.docx', 'has_custom_certificate': True, 'custom_cert_mappings': json.dumps({ 'placeholders': [ {'key': '{key.name}', 'value_type': 'user_field', 'value_field': 'partner_name'} ] }) }) # Configure survey 2 self.survey_2.write({ 'custom_cert_template': base64.b64encode(template_2), 'custom_cert_template_filename': 'javascript_cert.docx', 'has_custom_certificate': True, 'custom_cert_mappings': json.dumps({ 'placeholders': [ {'key': '{key.name}', 'value_type': 'user_field', 'value_field': 'partner_name'} ] }) }) # Load survey 1 and verify its template survey_1_loaded = self.env['survey.survey'].browse(self.survey_1.id) self.assertEqual(survey_1_loaded.custom_cert_template_filename, 'python_cert.docx') self.assertTrue(survey_1_loaded.has_custom_certificate) # Load survey 2 and verify its template survey_2_loaded = self.env['survey.survey'].browse(self.survey_2.id) self.assertEqual(survey_2_loaded.custom_cert_template_filename, 'javascript_cert.docx') self.assertTrue(survey_2_loaded.has_custom_certificate) # Verify templates are different self.assertNotEqual( survey_1_loaded.custom_cert_template, survey_2_loaded.custom_cert_template ) def test_template_isolation_no_cross_contamination(self): """ Test that modifying one survey's template does not affect other surveys. This ensures complete isolation between survey templates. """ # Create initial templates template_1 = self._create_test_docx("Original Template 1") template_2 = self._create_test_docx("Original Template 2") # Configure both surveys self.survey_1.write({ 'custom_cert_template': base64.b64encode(template_1), 'custom_cert_template_filename': 'template_1.docx', 'has_custom_certificate': True, 'custom_cert_mappings': json.dumps({ 'placeholders': [ {'key': '{key.name}', 'value_type': 'user_field', 'value_field': 'partner_name'} ] }) }) self.survey_2.write({ 'custom_cert_template': base64.b64encode(template_2), 'custom_cert_template_filename': 'template_2.docx', 'has_custom_certificate': True, 'custom_cert_mappings': json.dumps({ 'placeholders': [ {'key': '{key.name}', 'value_type': 'user_field', 'value_field': 'partner_name'} ] }) }) # Store original values for survey 2 original_template_2 = self.survey_2.custom_cert_template original_filename_2 = self.survey_2.custom_cert_template_filename original_mappings_2 = self.survey_2.custom_cert_mappings # Modify survey 1's template new_template_1 = self._create_test_docx("Modified Template 1") self.survey_1.write({ 'custom_cert_template': base64.b64encode(new_template_1), 'custom_cert_template_filename': 'modified_template_1.docx', 'custom_cert_mappings': json.dumps({ 'placeholders': [ {'key': '{key.name}', 'value_type': 'custom_text', 'custom_text': 'Modified'} ] }) }) # Verify survey 1 was modified self.assertEqual(self.survey_1.custom_cert_template_filename, 'modified_template_1.docx') # Verify survey 2 was NOT affected self.assertEqual(self.survey_2.custom_cert_template, original_template_2) self.assertEqual(self.survey_2.custom_cert_template_filename, original_filename_2) self.assertEqual(self.survey_2.custom_cert_mappings, original_mappings_2) def test_survey_without_template_unaffected(self): """ Test that surveys without custom templates are not affected by other surveys. This ensures that the default behavior is preserved for surveys that don't use custom templates. """ # Configure only survey 1 with a custom template template_1 = self._create_test_docx("Custom Template") self.survey_1.write({ 'custom_cert_template': base64.b64encode(template_1), 'custom_cert_template_filename': 'custom_cert.docx', 'has_custom_certificate': True, 'custom_cert_mappings': json.dumps({ 'placeholders': [ {'key': '{key.name}', 'value_type': 'user_field', 'value_field': 'partner_name'} ] }) }) # Verify survey 1 has custom template self.assertTrue(self.survey_1.has_custom_certificate) self.assertIsNotNone(self.survey_1.custom_cert_template) # Verify survey 2 and 3 do NOT have custom templates self.assertFalse(self.survey_2.has_custom_certificate) self.assertFalse(self.survey_3.has_custom_certificate) self.assertFalse(self.survey_2.custom_cert_template) self.assertFalse(self.survey_3.custom_cert_template) def test_wizard_associates_template_with_correct_survey(self): """ Test that the wizard correctly associates templates with the intended survey. This verifies that the wizard workflow maintains survey-specific isolation. """ # Create template content template_content = self._create_test_docx("Certificate for {key.name}") # Create wizard for survey 1 wizard_1 = self.env['survey.custom.certificate.wizard'].create({ 'survey_id': self.survey_1.id, 'template_file': base64.b64encode(template_content), 'template_filename': 'survey1_cert.docx', }) # Create placeholder for wizard 1 self.env['survey.certificate.placeholder'].create({ 'wizard_id': wizard_1.id, 'source_key': '{key.name}', 'value_type': 'user_field', 'value_field': 'partner_name', 'sequence': 1, }) # Save template via wizard wizard_1.action_save_template() # Verify survey 1 has the template self.assertTrue(self.survey_1.has_custom_certificate) self.assertEqual(self.survey_1.custom_cert_template_filename, 'survey1_cert.docx') # Verify survey 2 does NOT have the template self.assertFalse(self.survey_2.has_custom_certificate) self.assertFalse(self.survey_2.custom_cert_template) # Now create wizard for survey 2 with different template template_content_2 = self._create_test_docx("Different Certificate for {key.name}") wizard_2 = self.env['survey.custom.certificate.wizard'].create({ 'survey_id': self.survey_2.id, 'template_file': base64.b64encode(template_content_2), 'template_filename': 'survey2_cert.docx', }) # Create placeholder for wizard 2 self.env['survey.certificate.placeholder'].create({ 'wizard_id': wizard_2.id, 'source_key': '{key.name}', 'value_type': 'user_field', 'value_field': 'partner_name', 'sequence': 1, }) # Save template via wizard wizard_2.action_save_template() # Verify survey 2 now has its own template self.assertTrue(self.survey_2.has_custom_certificate) self.assertEqual(self.survey_2.custom_cert_template_filename, 'survey2_cert.docx') # Verify survey 1's template is unchanged self.assertEqual(self.survey_1.custom_cert_template_filename, 'survey1_cert.docx') # Verify templates are different self.assertNotEqual( self.survey_1.custom_cert_template, self.survey_2.custom_cert_template ) def test_multiple_surveys_with_same_placeholder_names(self): """ Test that multiple surveys can use the same placeholder names independently. This ensures that placeholder mappings are survey-specific. """ # Create template with same placeholders for both surveys template_content = self._create_test_docx( "Certificate for {key.name} - Course: {key.course}" ) # Configure survey 1 with specific mappings self.survey_1.write({ 'custom_cert_template': base64.b64encode(template_content), 'custom_cert_template_filename': 'cert1.docx', 'has_custom_certificate': True, 'custom_cert_mappings': json.dumps({ 'placeholders': [ {'key': '{key.name}', 'value_type': 'user_field', 'value_field': 'partner_name'}, {'key': '{key.course}', 'value_type': 'survey_field', 'value_field': 'survey_title'} ] }) }) # Configure survey 2 with DIFFERENT mappings for same placeholders self.survey_2.write({ 'custom_cert_template': base64.b64encode(template_content), 'custom_cert_template_filename': 'cert2.docx', 'has_custom_certificate': True, 'custom_cert_mappings': json.dumps({ 'placeholders': [ {'key': '{key.name}', 'value_type': 'custom_text', 'custom_text': 'Student Name'}, {'key': '{key.course}', 'value_type': 'custom_text', 'custom_text': 'Course Name'} ] }) }) # Parse mappings mappings_1 = json.loads(self.survey_1.custom_cert_mappings) mappings_2 = json.loads(self.survey_2.custom_cert_mappings) # Verify survey 1 mappings self.assertEqual(mappings_1['placeholders'][0]['value_type'], 'user_field') self.assertEqual(mappings_1['placeholders'][0]['value_field'], 'partner_name') self.assertEqual(mappings_1['placeholders'][1]['value_type'], 'survey_field') # Verify survey 2 mappings are different self.assertEqual(mappings_2['placeholders'][0]['value_type'], 'custom_text') self.assertEqual(mappings_2['placeholders'][0]['custom_text'], 'Student Name') self.assertEqual(mappings_2['placeholders'][1]['value_type'], 'custom_text') # Verify they are independent self.assertNotEqual( mappings_1['placeholders'][0]['value_type'], mappings_2['placeholders'][0]['value_type'] ) def test_certificate_generation_uses_correct_survey_template(self): """ Test that certificate generation uses the correct template for each survey. This is an integration test that verifies the complete workflow maintains survey-specific template isolation. """ # Create test partner partner = self.env['res.partner'].create({ 'name': 'Test Student', 'email': 'student@example.com', }) # Create user inputs for both surveys user_input_1 = self.env['survey.user_input'].create({ 'survey_id': self.survey_1.id, 'partner_id': partner.id, 'email': partner.email, 'state': 'done', }) user_input_2 = self.env['survey.user_input'].create({ 'survey_id': self.survey_2.id, 'partner_id': partner.id, 'email': partner.email, 'state': 'done', }) # Configure templates for both surveys template_1 = self._create_test_docx("Python Certificate for {key.name}") template_2 = self._create_test_docx("JavaScript Certificate for {key.name}") self.survey_1.write({ 'custom_cert_template': base64.b64encode(template_1), 'custom_cert_template_filename': 'python_cert.docx', 'has_custom_certificate': True, 'custom_cert_mappings': json.dumps({ 'placeholders': [ {'key': '{key.name}', 'value_type': 'user_field', 'value_field': 'partner_name'} ] }) }) self.survey_2.write({ 'custom_cert_template': base64.b64encode(template_2), 'custom_cert_template_filename': 'javascript_cert.docx', 'has_custom_certificate': True, 'custom_cert_mappings': json.dumps({ 'placeholders': [ {'key': '{key.name}', 'value_type': 'user_field', 'value_field': 'partner_name'} ] }) }) # Get certificate data for both surveys data_1 = self.survey_1._get_certificate_data(user_input_1.id) data_2 = self.survey_2._get_certificate_data(user_input_2.id) # Verify data includes correct survey information self.assertEqual(data_1['survey_title'], 'Python Programming Course') self.assertEqual(data_2['survey_title'], 'JavaScript Fundamentals') # Verify both have same participant data self.assertEqual(data_1['partner_name'], 'Test Student') self.assertEqual(data_2['partner_name'], 'Test Student') # Note: Actual certificate generation would require LibreOffice # This test verifies the data retrieval is survey-specific def test_template_deletion_only_affects_target_survey(self): """ Test that deleting a template from one survey doesn't affect others. """ # Configure templates for both surveys template_1 = self._create_test_docx("Template 1") template_2 = self._create_test_docx("Template 2") self.survey_1.write({ 'custom_cert_template': base64.b64encode(template_1), 'custom_cert_template_filename': 'cert1.docx', 'has_custom_certificate': True, 'custom_cert_mappings': json.dumps({ 'placeholders': [ {'key': '{key.name}', 'value_type': 'user_field', 'value_field': 'partner_name'} ] }) }) self.survey_2.write({ 'custom_cert_template': base64.b64encode(template_2), 'custom_cert_template_filename': 'cert2.docx', 'has_custom_certificate': True, 'custom_cert_mappings': json.dumps({ 'placeholders': [ {'key': '{key.name}', 'value_type': 'user_field', 'value_field': 'partner_name'} ] }) }) # Store survey 2's original values original_template_2 = self.survey_2.custom_cert_template original_filename_2 = self.survey_2.custom_cert_template_filename # Delete template from survey 1 self.survey_1.write({ 'custom_cert_template': False, 'custom_cert_template_filename': False, 'has_custom_certificate': False, 'custom_cert_mappings': False, }) # Verify survey 1 template is deleted self.assertFalse(self.survey_1.has_custom_certificate) self.assertFalse(self.survey_1.custom_cert_template) # Verify survey 2 template is unchanged self.assertTrue(self.survey_2.has_custom_certificate) self.assertEqual(self.survey_2.custom_cert_template, original_template_2) self.assertEqual(self.survey_2.custom_cert_template_filename, original_filename_2)