bemade-addons/bemade_sports_clinic/tests/test_e2e_workflows.py
Denis Durepos c0834eb327 [REF] bemade_sports_clinic: Remove injury models and simplify data model
- Removed models/injury_models.py and all its references
- Converted relational fields to character fields:
  - body_location_id → body_location
  - injury_type_id → injury_type
- Updated portal templates to use text inputs instead of dropdowns
- Updated controller code to process the new field formats
- Removed related access rights from security CSV
- Modified test files to accommodate the new structure

This refactoring simplifies the data model by removing unnecessary
classifications that were adding complexity without significant benefit.
The direct text fields maintain the same functionality while reducing
the database overhead and simplifying the UI.
2025-07-16 11:37:50 -04:00

300 lines
13 KiB
Python

from odoo.tests import TransactionCase, tagged
from odoo import Command, fields
from datetime import timedelta
from freezegun import freeze_time
@tagged("-at_install", "post_install")
class TestEndToEndWorkflows(TransactionCase):
"""End-to-end workflow tests for the sports clinic module"""
@classmethod
def setUpClass(cls):
super().setUpClass()
# Create organization and team
cls.organization = cls.env['sports.organization'].create({
'name': 'E2E Test Organization',
})
cls.team = cls.env['sports.team'].create({
'name': 'E2E Test Team',
'organization_id': cls.organization.id,
})
# Create a patient/player
cls.patient = cls.env['sports.patient'].create({
'first_name': 'E2E',
'last_name': 'Test Patient',
'birthdate': fields.Date.today() - timedelta(days=365 * 16), # 16 years old
'team_ids': [(4, cls.team.id)],
})
# Create users with different roles
# 1. Coach
cls.coach_partner = cls.env['res.partner'].create({
'name': 'E2E Coach',
'email': 'e2e.coach@example.com',
})
cls.coach_user = cls.env['res.users'].with_context(no_reset_password=True).create({
'partner_id': cls.coach_partner.id,
'login': 'e2e.coach@example.com',
'password': 'e2ecoach',
'name': cls.coach_partner.name,
'groups_id': [
Command.link(cls.env.ref('bemade_sports_clinic.group_sports_clinic_team_coach').id),
]
})
# 2. Therapist (treatment professional)
cls.therapist_partner = cls.env['res.partner'].create({
'name': 'E2E Therapist',
'email': 'e2e.therapist@example.com',
})
cls.therapist_user = cls.env['res.users'].with_context(no_reset_password=True).create({
'partner_id': cls.therapist_partner.id,
'login': 'e2e.therapist@example.com',
'password': 'e2etherapist',
'name': cls.therapist_partner.name,
'groups_id': [
Command.link(cls.env.ref('bemade_sports_clinic.group_sports_clinic_treatment_professional').id),
]
})
# Create team staff entries
cls.coach_staff = cls.env['sports.team.staff'].create({
'team_id': cls.team.id,
'partner_id': cls.coach_partner.id,
# Role coach doesn't grant treatment professional status
'role': 'coach',
'user_id': cls.coach_user.id,
})
cls.therapist_staff = cls.env['sports.team.staff'].create({
'team_id': cls.team.id,
'partner_id': cls.therapist_partner.id,
# Role therapist automatically grants treatment professional status
'role': 'therapist',
'user_id': cls.therapist_user.id,
})
def test_01_complete_injury_workflow(self):
"""Test complete injury workflow from reporting to resolution"""
# Step 1: Coach reports an injury (unverified)
with freeze_time('2025-01-15'):
# Create injury as coach
injury = self.env['sports.patient.injury'].with_user(self.coach_user).create({
'patient_id': self.patient.id,
'team_id': self.team.id,
'diagnosis': 'Ankle Sprain - Initial',
'external_notes': 'Player twisted ankle during practice.',
'injury_date': fields.Date.today(),
})
# Check that injury is unverified and has default values
self.assertEqual(injury.stage, 'unverified')
self.assertEqual(injury.parental_consent, 'no') # Default for coach-created injuries
self.assertFalse(injury.treatment_professional_ids) # No treatment pros yet
# Check that the player is now marked as injured
self.patient.invalidate_cache()
self.assertTrue(self.patient.is_injured)
# Step 2: Therapist verifies the injury and adds details
with freeze_time('2025-01-16'):
# Update injury as therapist
injury.with_user(self.therapist_user).write({
'diagnosis': 'Grade 2 Ankle Sprain',
'stage': 'active',
'treatment_professional_ids': [(4, self.therapist_partner.id)],
'internal_notes': 'Significant swelling observed. Recommend RICE protocol.',
'parental_consent': 'yes',
})
# Check that injury is now active and therapist is assigned
self.assertEqual(injury.stage, 'active')
self.assertEqual(injury.parental_consent, 'yes')
self.assertIn(self.therapist_partner, injury.treatment_professional_ids)
# Add a message/note to the chatter as the therapist
injury.with_user(self.therapist_user).message_post(
body="Initial assessment complete. Ankle mobility restricted.",
message_type='comment',
subtype_xmlid='mail.mt_comment',
)
# Step 3: Treatment progress and updates
with freeze_time('2025-01-23'):
# Add treatment progress note
injury.with_user(self.therapist_user).write({
'external_notes': 'Player twisted ankle during practice. Follow-up: '
'Swelling reduced, started basic rehabilitation exercises.',
'internal_notes': 'Significant swelling observed. Recommend RICE protocol. '
'Update: ROM improving, pain decreased from 7/10 to 4/10.',
})
# Add another chatter message
injury.with_user(self.therapist_user).message_post(
body="Week 1 follow-up: Patient showing good progress. Cleared for light conditioning.",
message_type='comment',
subtype_xmlid='mail.mt_comment',
)
# Check that coach can see the external updates
as_coach = injury.with_user(self.coach_user)
self.assertIn('started basic rehabilitation exercises', as_coach.external_notes)
# But not the internal notes
with self.assertRaises(Exception):
coach_view_notes = as_coach.internal_notes
# Step 4: More treatment and approaching resolution
with freeze_time('2025-02-06'):
# Add another treatment progress note
injury.with_user(self.therapist_user).write({
'external_notes': injury.external_notes + '\nFollow-up 2: '
'Good progress. Can resume non-contact training.',
'internal_notes': injury.internal_notes + '\nFollow-up 2: '
'Strength tests show 85% compared to uninjured side. Proprioception improving.',
})
# Step 5: Final resolution
with freeze_time('2025-02-15'):
# Resolve the injury
injury.with_user(self.therapist_user).write({
'stage': 'resolved',
'resolution_date': fields.Date.today(),
'external_notes': injury.external_notes + '\nFinal update: '
'Player fully cleared to return to all activities.',
'internal_notes': injury.internal_notes + '\nFinal assessment: '
'Full ROM restored, strength tests at 95%. Cleared for all activities.',
})
# Verify the resolution details
self.assertEqual(injury.stage, 'resolved')
self.assertEqual(injury.resolution_date, fields.Date.from_string('2025-02-15'))
# Check that the player is no longer marked as injured
self.patient.invalidate_cache()
self.assertFalse(self.patient.is_injured)
def test_02_player_removal_workflow(self):
"""Test complete player removal workflow"""
# Create a test player to be removed
player_to_remove = self.env['sports.patient'].create({
'first_name': 'Removal',
'last_name': 'Test Patient',
'birthdate': fields.Date.today() - timedelta(days=365 * 17),
'team_ids': [(4, self.team.id)],
})
# Verify initial state
self.assertIn(self.team, player_to_remove.team_ids)
self.assertFalse(player_to_remove.archived)
# Step 1: Coach requests player removal
removal_request = self.env['sports.patient.team.removal'].with_user(self.coach_user).create({
'patient_id': player_to_remove.id,
'team_id': self.team.id,
'reason': 'Player transferred to another team',
'requested_by_id': self.coach_partner.id,
})
self.assertEqual(removal_request.state, 'draft')
self.assertEqual(removal_request.requested_by_id, self.coach_partner)
# Step 2: Admin approves and processes the removal
admin_user = self.env.ref('base.user_admin')
# Admin processes the request
removal_request.with_user(admin_user).action_approve()
self.assertEqual(removal_request.state, 'approved')
# Execute the removal
removal_request.with_user(admin_user).action_execute()
self.assertEqual(removal_request.state, 'done')
# Verify player has been removed from team
player_to_remove.invalidate_cache()
self.assertNotIn(self.team, player_to_remove.team_ids)
# Check if player is archived when no teams left
self.assertTrue(len(player_to_remove.team_ids) == 0)
player_to_remove.invalidate_cache()
self.assertTrue(player_to_remove.archived)
def test_03_data_export_anonymization_process(self):
"""Test data export and anonymization process"""
# Create test patient with personal information
patient_for_anon = self.env['sports.patient'].create({
'first_name': 'Export',
'last_name': 'Test Patient',
'birthdate': fields.Date.today() - timedelta(days=365 * 15),
'team_ids': [(4, self.team.id)],
'street': '123 Test Street',
'city': 'Test City',
'zip': '12345',
'email': 'test.patient@example.com',
'phone': '555-123-4567',
})
# Create a contact for the patient
patient_contact = self.env['sports.patient.contact'].create({
'patient_id': patient_for_anon.id,
'name': 'Test Parent',
'phone': '555-987-6543',
'email': 'test.parent@example.com',
'relationship': 'parent',
})
# Create an injury with personal medical information
injury_for_anon = self.env['sports.patient.injury'].create({
'patient_id': patient_for_anon.id,
'team_id': self.team.id,
'diagnosis': 'Confidential Medical Condition',
'external_notes': 'Contains personally identifiable information',
'internal_notes': 'Contains sensitive medical details',
'injury_date': fields.Date.today(),
})
# Run anonymization wizard (simulated since actual wizard implementation might vary)
# This is a placeholder for the actual anonymization process
# In a real implementation, we would call the anonymization wizard here
# For testing purposes, we'll manually anonymize the patient
patient_for_anon.write({
'first_name': 'Anonymized',
'last_name': 'Patient',
'street': False,
'city': False,
'zip': False,
'email': False,
'phone': False,
})
patient_contact.write({
'name': 'Anonymized Contact',
'phone': False,
'email': False,
})
injury_for_anon.write({
'external_notes': 'Anonymized external notes',
'internal_notes': 'Anonymized internal notes',
})
# Verify anonymization was effective
self.assertEqual(patient_for_anon.first_name, 'Anonymized')
self.assertEqual(patient_for_anon.last_name, 'Patient')
self.assertFalse(patient_for_anon.email)
self.assertFalse(patient_for_anon.phone)
self.assertEqual(patient_contact.name, 'Anonymized Contact')
self.assertFalse(patient_contact.email)
self.assertFalse(patient_contact.phone)
self.assertEqual(injury_for_anon.external_notes, 'Anonymized external notes')
self.assertEqual(injury_for_anon.internal_notes, 'Anonymized internal notes')