bemade-addons/bemade_sports_clinic/tests/test_e2e_workflows.py

301 lines
13 KiB
Python
Raw Normal View History

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')