365 lines
11 KiB
Python
365 lines
11 KiB
Python
"""
|
|
Test fixtures and utilities for Django FBF tests.
|
|
"""
|
|
import pytest
|
|
from django.contrib.auth.models import User
|
|
from django.utils import timezone
|
|
from decimal import Decimal
|
|
|
|
from bird.models import Bird, BirdStatus, Circumstance
|
|
from aviary.models import Aviary
|
|
from costs.models import Costs
|
|
from contact.models import Contact
|
|
|
|
|
|
class TestDataMixin:
|
|
"""Mixin class providing common test data setup."""
|
|
|
|
def create_test_user(self, username='testuser', email='test@example.com', is_staff=False):
|
|
"""Create a test user."""
|
|
return User.objects.create_user(
|
|
username=username,
|
|
email=email,
|
|
password='testpass123',
|
|
is_staff=is_staff
|
|
)
|
|
|
|
def create_test_aviary(self, user, name='Test Aviary'):
|
|
"""Create a test aviary."""
|
|
return Aviary.objects.create(
|
|
name=name,
|
|
location='Test Location',
|
|
description='Test description',
|
|
capacity=20,
|
|
current_occupancy=5,
|
|
contact_person='Test Contact',
|
|
contact_phone='123456789',
|
|
contact_email='contact@example.com',
|
|
created_by=user
|
|
)
|
|
|
|
def create_test_bird_status(self, name='Gesund'):
|
|
"""Create a test bird status."""
|
|
return BirdStatus.objects.create(
|
|
name=name,
|
|
description=f'{name} bird status'
|
|
)
|
|
|
|
def create_test_circumstance(self, name='Gefunden'):
|
|
"""Create a test circumstance."""
|
|
return Circumstance.objects.create(
|
|
name=name,
|
|
description=f'{name} circumstance'
|
|
)
|
|
|
|
def create_test_bird(self, user, aviary, status, circumstance, name='Test Bird'):
|
|
"""Create a test bird."""
|
|
return Bird.objects.create(
|
|
name=name,
|
|
species='Test Species',
|
|
age_group='adult',
|
|
gender='unknown',
|
|
weight=Decimal('100.50'),
|
|
wing_span=Decimal('25.00'),
|
|
found_date=timezone.now().date(),
|
|
found_location='Test Location',
|
|
finder_name='Test Finder',
|
|
finder_phone='123456789',
|
|
finder_email='finder@example.com',
|
|
aviary=aviary,
|
|
status=status,
|
|
circumstance=circumstance,
|
|
notes='Test notes',
|
|
created_by=user
|
|
)
|
|
|
|
def create_test_contact(self, user, first_name='John', last_name='Doe'):
|
|
"""Create a test contact."""
|
|
return Contact.objects.create(
|
|
first_name=first_name,
|
|
last_name=last_name,
|
|
email=f'{first_name.lower()}.{last_name.lower()}@example.com',
|
|
phone='123456789',
|
|
address='123 Test Street',
|
|
city='Test City',
|
|
postal_code='12345',
|
|
country='Test Country',
|
|
is_active=True,
|
|
created_by=user
|
|
)
|
|
|
|
def create_test_cost(self, user, bird, amount='50.00', description='Test Cost'):
|
|
"""Create a test cost entry."""
|
|
return Costs.objects.create(
|
|
bird=bird,
|
|
description=description,
|
|
amount=Decimal(amount),
|
|
cost_date=timezone.now().date(),
|
|
category='medical',
|
|
invoice_number=f'INV-{timezone.now().timestamp()}',
|
|
vendor='Test Vendor',
|
|
notes='Test cost notes',
|
|
created_by=user
|
|
)
|
|
|
|
|
|
@pytest.fixture
|
|
def test_user():
|
|
"""Fixture for creating a test user."""
|
|
return User.objects.create_user(
|
|
username='fixtureuser',
|
|
email='fixture@example.com',
|
|
password='fixturepass123'
|
|
)
|
|
|
|
|
|
@pytest.fixture
|
|
def admin_user():
|
|
"""Fixture for creating an admin user."""
|
|
return User.objects.create_user(
|
|
username='admin',
|
|
email='admin@example.com',
|
|
password='adminpass123',
|
|
is_staff=True,
|
|
is_superuser=True
|
|
)
|
|
|
|
|
|
@pytest.fixture
|
|
def test_aviary(test_user):
|
|
"""Fixture for creating a test aviary."""
|
|
return Aviary.objects.create(
|
|
name='Fixture Aviary',
|
|
location='Fixture Location',
|
|
capacity=15,
|
|
current_occupancy=3,
|
|
created_by=test_user
|
|
)
|
|
|
|
|
|
@pytest.fixture
|
|
def bird_status():
|
|
"""Fixture for creating a bird status."""
|
|
return BirdStatus.objects.create(
|
|
name='Fixture Status',
|
|
description='Fixture bird status'
|
|
)
|
|
|
|
|
|
@pytest.fixture
|
|
def circumstance():
|
|
"""Fixture for creating a circumstance."""
|
|
return Circumstance.objects.create(
|
|
name='Fixture Circumstance',
|
|
description='Fixture circumstance'
|
|
)
|
|
|
|
|
|
@pytest.fixture
|
|
def test_bird(test_user, test_aviary, bird_status, circumstance):
|
|
"""Fixture for creating a test bird."""
|
|
return Bird.objects.create(
|
|
name='Fixture Bird',
|
|
species='Fixture Species',
|
|
age_group='adult',
|
|
gender='male',
|
|
weight=Decimal('95.75'),
|
|
aviary=test_aviary,
|
|
status=bird_status,
|
|
circumstance=circumstance,
|
|
created_by=test_user
|
|
)
|
|
|
|
|
|
class TestUtilities:
|
|
"""Utility functions for tests."""
|
|
|
|
@staticmethod
|
|
def assert_model_fields(instance, expected_values):
|
|
"""Assert that model instance has expected field values."""
|
|
for field, expected_value in expected_values.items():
|
|
actual_value = getattr(instance, field)
|
|
assert actual_value == expected_value, f"Field {field}: expected {expected_value}, got {actual_value}"
|
|
|
|
@staticmethod
|
|
def assert_form_errors(form, expected_errors):
|
|
"""Assert that form has expected validation errors."""
|
|
assert not form.is_valid(), "Form should be invalid"
|
|
for field, error_messages in expected_errors.items():
|
|
assert field in form.errors, f"Field {field} should have errors"
|
|
for error_message in error_messages:
|
|
assert any(error_message in str(error) for error in form.errors[field]), \
|
|
f"Error message '{error_message}' not found in {form.errors[field]}"
|
|
|
|
@staticmethod
|
|
def assert_response_contains(response, expected_content):
|
|
"""Assert that response contains expected content."""
|
|
if isinstance(expected_content, list):
|
|
for content in expected_content:
|
|
assert content in response.content.decode(), f"Content '{content}' not found in response"
|
|
else:
|
|
assert expected_content in response.content.decode(), f"Content '{expected_content}' not found in response"
|
|
|
|
@staticmethod
|
|
def create_form_data(**kwargs):
|
|
"""Create form data with default values."""
|
|
defaults = {
|
|
'name': 'Test Name',
|
|
'species': 'Test Species',
|
|
'age_group': 'adult',
|
|
'gender': 'unknown',
|
|
'weight': '100.00'
|
|
}
|
|
defaults.update(kwargs)
|
|
return defaults
|
|
|
|
@staticmethod
|
|
def assert_redirect(response, expected_url=None):
|
|
"""Assert that response is a redirect."""
|
|
assert response.status_code in [301, 302], f"Expected redirect, got {response.status_code}"
|
|
if expected_url:
|
|
assert expected_url in response.url, f"Expected redirect to {expected_url}, got {response.url}"
|
|
|
|
|
|
def sample_bird_data():
|
|
"""Return sample bird data for testing."""
|
|
return [
|
|
{
|
|
'name': 'Robin',
|
|
'species': 'European Robin',
|
|
'age_group': 'adult',
|
|
'gender': 'male',
|
|
'weight': Decimal('18.5')
|
|
},
|
|
{
|
|
'name': 'Sparrow',
|
|
'species': 'House Sparrow',
|
|
'age_group': 'juvenile',
|
|
'gender': 'female',
|
|
'weight': Decimal('22.3')
|
|
},
|
|
{
|
|
'name': 'Falcon',
|
|
'species': 'Peregrine Falcon',
|
|
'age_group': 'adult',
|
|
'gender': 'unknown',
|
|
'weight': Decimal('750.0')
|
|
}
|
|
]
|
|
|
|
|
|
def sample_aviary_data():
|
|
"""Return sample aviary data for testing."""
|
|
return [
|
|
{
|
|
'name': 'Forest Sanctuary',
|
|
'location': 'Black Forest',
|
|
'capacity': 25,
|
|
'current_occupancy': 8
|
|
},
|
|
{
|
|
'name': 'Lake Resort',
|
|
'location': 'Lake Constance',
|
|
'capacity': 30,
|
|
'current_occupancy': 12
|
|
},
|
|
{
|
|
'name': 'Mountain Refuge',
|
|
'location': 'Bavarian Alps',
|
|
'capacity': 15,
|
|
'current_occupancy': 5
|
|
}
|
|
]
|
|
|
|
|
|
def create_test_database_state():
|
|
"""Create a complete test database state with relationships."""
|
|
# Create users
|
|
admin = User.objects.create_user(
|
|
username='testadmin',
|
|
email='admin@testfbf.com',
|
|
password='adminpass123',
|
|
is_staff=True
|
|
)
|
|
|
|
user = User.objects.create_user(
|
|
username='testuser',
|
|
email='user@testfbf.com',
|
|
password='userpass123'
|
|
)
|
|
|
|
# Create aviaries
|
|
aviaries = []
|
|
for aviary_data in sample_aviary_data():
|
|
aviary = Aviary.objects.create(
|
|
**aviary_data,
|
|
created_by=admin
|
|
)
|
|
aviaries.append(aviary)
|
|
|
|
# Create statuses and circumstances
|
|
statuses = [
|
|
BirdStatus.objects.create(name='Gesund', description='Healthy bird'),
|
|
BirdStatus.objects.create(name='Krank', description='Sick bird'),
|
|
BirdStatus.objects.create(name='Verletzt', description='Injured bird'),
|
|
]
|
|
|
|
circumstances = [
|
|
Circumstance.objects.create(name='Gefunden', description='Found bird'),
|
|
Circumstance.objects.create(name='Gebracht', description='Brought bird'),
|
|
Circumstance.objects.create(name='Übertragen', description='Transferred bird'),
|
|
]
|
|
|
|
# Create birds
|
|
birds = []
|
|
for i, bird_data in enumerate(sample_bird_data()):
|
|
bird = Bird.objects.create(
|
|
**bird_data,
|
|
aviary=aviaries[i % len(aviaries)],
|
|
status=statuses[i % len(statuses)],
|
|
circumstance=circumstances[i % len(circumstances)],
|
|
found_date=timezone.now().date(),
|
|
created_by=user
|
|
)
|
|
birds.append(bird)
|
|
|
|
# Create contacts
|
|
contacts = []
|
|
contact_data = [
|
|
('John', 'Doe', 'john.doe@example.com'),
|
|
('Jane', 'Smith', 'jane.smith@example.com'),
|
|
('Bob', 'Johnson', 'bob.johnson@example.com'),
|
|
]
|
|
|
|
for first_name, last_name, email in contact_data:
|
|
contact = Contact.objects.create(
|
|
first_name=first_name,
|
|
last_name=last_name,
|
|
email=email,
|
|
phone='123456789',
|
|
created_by=user
|
|
)
|
|
contacts.append(contact)
|
|
|
|
# Create costs
|
|
costs = []
|
|
for i, bird in enumerate(birds):
|
|
cost = Costs.objects.create(
|
|
bird=bird,
|
|
description=f'Treatment for {bird.name}',
|
|
amount=Decimal(f'{50 + i * 10}.75'),
|
|
cost_date=timezone.now().date(),
|
|
category='medical',
|
|
created_by=user
|
|
)
|
|
costs.append(cost)
|
|
|
|
return {
|
|
'users': [admin, user],
|
|
'aviaries': aviaries,
|
|
'statuses': statuses,
|
|
'circumstances': circumstances,
|
|
'birds': birds,
|
|
'contacts': contacts,
|
|
'costs': costs
|
|
}
|