326 lines
12 KiB
Python
326 lines
12 KiB
Python
"""
|
|||
|
|
Integration tests for the complete application workflow.
|
||
|
|
"""
|
||
|
|
import pytest
|
||
|
|
from unittest.mock import patch
|
||
|
|
from app import load_application_data
|
||
|
|
|
||
|
|
|
||
|
|
@pytest.mark.integration
|
||
|
|
class TestCompleteWorkflow:
|
||
|
|
"""Integration tests for the complete application workflow."""
|
||
|
|
|
||
|
|
def test_complete_application_workflow(self, client, app, sample_pdf_file):
|
||
|
|
"""Test the complete workflow from email to confirmation."""
|
||
|
|
# Step 1: Submit email
|
||
|
|
with patch('app.mail.send'):
|
||
|
|
response = client.post('/apply/submit-email', data={
|
||
|
|
'email': 'integration@test.com',
|
||
|
|
'job_name': 'Integration Test Position'
|
||
|
|
}, follow_redirects=True)
|
||
|
|
|
||
|
|
assert response.status_code == 200
|
||
|
|
|
||
|
|
# For testing, create a known session
|
||
|
|
from app import save_application_data
|
||
|
|
session_id = 'integration-test-123'
|
||
|
|
save_application_data(session_id, {
|
||
|
|
'session_id': session_id,
|
||
|
|
'email': 'integration@test.com',
|
||
|
|
'job_name': 'Integration Test Position',
|
||
|
|
'current_page': 2,
|
||
|
|
'personal_info': {},
|
||
|
|
'motivation_answers': {},
|
||
|
|
'uploaded_files': []
|
||
|
|
})
|
||
|
|
|
||
|
|
# Step 2: Submit personal information
|
||
|
|
response = client.post(f'/apply/{session_id}/submit-personal', data={
|
||
|
|
'name': 'Schmidt',
|
||
|
|
'firstname': 'Thomas',
|
||
|
|
'address': 'Bahnhofstrasse 100',
|
||
|
|
'zip_code': '8001',
|
||
|
|
'city': 'Zürich',
|
||
|
|
'phone': '+41 79 999 88 77',
|
||
|
|
'birth_year': '1985',
|
||
|
|
'civil_status': 'verheiratet'
|
||
|
|
}, follow_redirects=True)
|
||
|
|
|
||
|
|
assert response.status_code == 200
|
||
|
|
|
||
|
|
# Verify data was saved
|
||
|
|
data = load_application_data(session_id)
|
||
|
|
assert data['personal_info']['name'] == 'Schmidt'
|
||
|
|
assert data['current_page'] == 3
|
||
|
|
|
||
|
|
# Step 3: Submit motivation answers
|
||
|
|
response = client.post(f'/apply/{session_id}/submit-motivation', data={
|
||
|
|
'current_job': 'Senior Software Entwickler bei Tech Corp',
|
||
|
|
'motivation': 'Ich möchte Teil eines innovativen Teams werden',
|
||
|
|
'qualifications': 'Python, Flask, React, 10 Jahre Erfahrung',
|
||
|
|
'salary': '120000 CHF pro Jahr'
|
||
|
|
}, follow_redirects=True)
|
||
|
|
|
||
|
|
assert response.status_code == 200
|
||
|
|
|
||
|
|
# Verify motivation was saved
|
||
|
|
data = load_application_data(session_id)
|
||
|
|
assert data['motivation_answers']['current_job'] == 'Senior Software Entwickler bei Tech Corp'
|
||
|
|
assert data['current_page'] == 4
|
||
|
|
|
||
|
|
# Step 4: Upload a file
|
||
|
|
sample_pdf_file.seek(0)
|
||
|
|
response = client.post(
|
||
|
|
f'/apply/{session_id}/upload-file',
|
||
|
|
data={'file': (sample_pdf_file, 'lebenslauf.pdf')},
|
||
|
|
content_type='multipart/form-data',
|
||
|
|
follow_redirects=True
|
||
|
|
)
|
||
|
|
|
||
|
|
assert response.status_code == 200
|
||
|
|
|
||
|
|
# Verify file was uploaded
|
||
|
|
data = load_application_data(session_id)
|
||
|
|
assert len(data['uploaded_files']) == 1
|
||
|
|
assert data['uploaded_files'][0]['original_name'] == 'lebenslauf.pdf'
|
||
|
|
|
||
|
|
# Step 5: Submit final application
|
||
|
|
response = client.post(f'/apply/{session_id}/submit-application', follow_redirects=True)
|
||
|
|
|
||
|
|
assert response.status_code == 200
|
||
|
|
assert b'Vielen Dank' in response.data or b'erfolgreich' in response.data
|
||
|
|
|
||
|
|
# Verify application was marked as submitted
|
||
|
|
data = load_application_data(session_id)
|
||
|
|
assert data['current_page'] == 5
|
||
|
|
assert data.get('status') == 'submitted'
|
||
|
|
|
||
|
|
def test_workflow_with_resume(self, client, app, create_test_application):
|
||
|
|
"""Test resuming an application at different stages."""
|
||
|
|
session_id = 'resume-test-456'
|
||
|
|
|
||
|
|
# Create an application at page 2 (personal info)
|
||
|
|
create_test_application(
|
||
|
|
session_id=session_id,
|
||
|
|
current_page=2,
|
||
|
|
email='resume@test.com',
|
||
|
|
job_name='Resume Test Job'
|
||
|
|
)
|
||
|
|
|
||
|
|
# Resume should redirect to personal info page
|
||
|
|
response = client.get(f'/resume/{session_id}', follow_redirects=False)
|
||
|
|
assert response.status_code == 302
|
||
|
|
assert b'/personal' in response.data
|
||
|
|
|
||
|
|
# Fill in personal info
|
||
|
|
client.post(f'/apply/{session_id}/submit-personal', data={
|
||
|
|
'name': 'Weber',
|
||
|
|
'firstname': 'Maria',
|
||
|
|
'address': 'Seestrasse 50',
|
||
|
|
'zip_code': '8002',
|
||
|
|
'city': 'Zürich',
|
||
|
|
'phone': '+41 79 888 77 66',
|
||
|
|
'birth_year': '1992'
|
||
|
|
})
|
||
|
|
|
||
|
|
# Now resume should go to motivation page
|
||
|
|
response = client.get(f'/resume/{session_id}', follow_redirects=False)
|
||
|
|
assert response.status_code == 302
|
||
|
|
assert b'/motivation' in response.data
|
||
|
|
|
||
|
|
def test_multiple_concurrent_applications(self, client, app):
|
||
|
|
"""Test handling multiple concurrent applications with different sessions."""
|
||
|
|
from app import save_application_data
|
||
|
|
|
||
|
|
# Create multiple applications
|
||
|
|
sessions = []
|
||
|
|
for i in range(3):
|
||
|
|
session_id = f'concurrent-{i}'
|
||
|
|
sessions.append(session_id)
|
||
|
|
|
||
|
|
save_application_data(session_id, {
|
||
|
|
'session_id': session_id,
|
||
|
|
'email': f'user{i}@test.com',
|
||
|
|
'job_name': f'Job {i}',
|
||
|
|
'current_page': 2,
|
||
|
|
'personal_info': {},
|
||
|
|
'motivation_answers': {},
|
||
|
|
'uploaded_files': []
|
||
|
|
})
|
||
|
|
|
||
|
|
# Verify all applications are independent
|
||
|
|
for i, session_id in enumerate(sessions):
|
||
|
|
data = load_application_data(session_id)
|
||
|
|
assert data is not None
|
||
|
|
assert data['email'] == f'user{i}@test.com'
|
||
|
|
assert data['job_name'] == f'Job {i}'
|
||
|
|
|
||
|
|
# Update one application
|
||
|
|
client.post(f'/apply/{sessions[1]}/submit-personal', data={
|
||
|
|
'name': 'Test',
|
||
|
|
'firstname': 'User',
|
||
|
|
'address': 'Street 1',
|
||
|
|
'zip_code': '12345',
|
||
|
|
'city': 'City',
|
||
|
|
'phone': '+41 79 123 45 67',
|
||
|
|
'birth_year': '1990'
|
||
|
|
})
|
||
|
|
|
||
|
|
# Verify only that application was updated
|
||
|
|
data0 = load_application_data(sessions[0])
|
||
|
|
data1 = load_application_data(sessions[1])
|
||
|
|
data2 = load_application_data(sessions[2])
|
||
|
|
|
||
|
|
assert data0['current_page'] == 2 # Not updated
|
||
|
|
assert data1['current_page'] == 3 # Updated
|
||
|
|
assert data2['current_page'] == 2 # Not updated
|
||
|
|
|
||
|
|
|
||
|
|
@pytest.mark.integration
|
||
|
|
class TestWorkflowEdgeCases:
|
||
|
|
"""Integration tests for edge cases in the workflow."""
|
||
|
|
|
||
|
|
def test_skip_optional_fields(self, client, app):
|
||
|
|
"""Test completing workflow without filling optional fields."""
|
||
|
|
from app import save_application_data
|
||
|
|
|
||
|
|
session_id = 'optional-test'
|
||
|
|
save_application_data(session_id, {
|
||
|
|
'session_id': session_id,
|
||
|
|
'email': 'optional@test.com',
|
||
|
|
'job_name': 'Test Job',
|
||
|
|
'current_page': 2,
|
||
|
|
'personal_info': {},
|
||
|
|
'motivation_answers': {},
|
||
|
|
'uploaded_files': []
|
||
|
|
})
|
||
|
|
|
||
|
|
# Submit personal info with minimal fields
|
||
|
|
client.post(f'/apply/{session_id}/submit-personal', data={
|
||
|
|
'name': 'Min',
|
||
|
|
'firstname': 'User',
|
||
|
|
'address': 'Addr 1',
|
||
|
|
'zip_code': '1',
|
||
|
|
'city': 'City',
|
||
|
|
'phone': '+1 123',
|
||
|
|
'birth_year': '1990',
|
||
|
|
'civil_status': '' # Optional, empty
|
||
|
|
})
|
||
|
|
|
||
|
|
# Submit empty motivation (all fields optional)
|
||
|
|
client.post(f'/apply/{session_id}/submit-motivation', data={
|
||
|
|
'current_job': '',
|
||
|
|
'motivation': '',
|
||
|
|
'qualifications': '',
|
||
|
|
'salary': ''
|
||
|
|
})
|
||
|
|
|
||
|
|
# Submit without files (optional)
|
||
|
|
response = client.post(f'/apply/{session_id}/submit-application', follow_redirects=True)
|
||
|
|
|
||
|
|
assert response.status_code == 200
|
||
|
|
|
||
|
|
# Verify application was submitted
|
||
|
|
data = load_application_data(session_id)
|
||
|
|
assert data['status'] == 'submitted'
|
||
|
|
|
||
|
|
def test_workflow_with_unicode_data(self, client, app):
|
||
|
|
"""Test workflow with German special characters."""
|
||
|
|
from app import save_application_data
|
||
|
|
|
||
|
|
session_id = 'unicode-test'
|
||
|
|
save_application_data(session_id, {
|
||
|
|
'session_id': session_id,
|
||
|
|
'email': 'ümlaut@test.com',
|
||
|
|
'job_name': 'Geschäftsführer Position',
|
||
|
|
'current_page': 2,
|
||
|
|
'personal_info': {},
|
||
|
|
'motivation_answers': {},
|
||
|
|
'uploaded_files': []
|
||
|
|
})
|
||
|
|
|
||
|
|
# Submit with German characters
|
||
|
|
client.post(f'/apply/{session_id}/submit-personal', data={
|
||
|
|
'name': 'Müller',
|
||
|
|
'firstname': 'Björn',
|
||
|
|
'address': 'Königstrasse 42',
|
||
|
|
'zip_code': '80539',
|
||
|
|
'city': 'München',
|
||
|
|
'phone': '+49 89 123 45 67',
|
||
|
|
'birth_year': '1988'
|
||
|
|
})
|
||
|
|
|
||
|
|
# Verify data persisted correctly
|
||
|
|
data = load_application_data(session_id)
|
||
|
|
assert data['personal_info']['name'] == 'Müller'
|
||
|
|
assert data['personal_info']['firstname'] == 'Björn'
|
||
|
|
assert data['personal_info']['city'] == 'München'
|
||
|
|
assert data['job_name'] == 'Geschäftsführer Position'
|
||
|
|
|
||
|
|
|
||
|
|
@pytest.mark.integration
|
||
|
|
@pytest.mark.slow
|
||
|
|
class TestDataIntegrity:
|
||
|
|
"""Integration tests for data integrity throughout the workflow."""
|
||
|
|
|
||
|
|
def test_data_not_lost_between_pages(self, client, app):
|
||
|
|
"""Test that data persists correctly when navigating between pages."""
|
||
|
|
from app import save_application_data
|
||
|
|
|
||
|
|
session_id = 'persistence-test'
|
||
|
|
save_application_data(session_id, {
|
||
|
|
'session_id': session_id,
|
||
|
|
'email': 'persist@test.com',
|
||
|
|
'job_name': 'Persistence Job',
|
||
|
|
'current_page': 2,
|
||
|
|
'personal_info': {},
|
||
|
|
'motivation_answers': {},
|
||
|
|
'uploaded_files': []
|
||
|
|
})
|
||
|
|
|
||
|
|
# Add personal info
|
||
|
|
personal_data = {
|
||
|
|
'name': 'Persistence',
|
||
|
|
'firstname': 'Test',
|
||
|
|
'address': 'Test Street 1',
|
||
|
|
'zip_code': '12345',
|
||
|
|
'city': 'Test City',
|
||
|
|
'phone': '+1 555 1234',
|
||
|
|
'birth_year': '1990'
|
||
|
|
}
|
||
|
|
|
||
|
|
client.post(f'/apply/{session_id}/submit-personal', data=personal_data)
|
||
|
|
|
||
|
|
# Verify personal data
|
||
|
|
data = load_application_data(session_id)
|
||
|
|
for key, value in personal_data.items():
|
||
|
|
assert data['personal_info'][key] == value
|
||
|
|
|
||
|
|
# Add motivation
|
||
|
|
motivation_data = {
|
||
|
|
'current_job': 'Developer',
|
||
|
|
'motivation': 'Test motivation',
|
||
|
|
'qualifications': 'Test skills',
|
||
|
|
'salary': '100k'
|
||
|
|
}
|
||
|
|
|
||
|
|
client.post(f'/apply/{session_id}/submit-motivation', data=motivation_data)
|
||
|
|
|
||
|
|
# Verify both personal and motivation data still exist
|
||
|
|
data = load_application_data(session_id)
|
||
|
|
for key, value in personal_data.items():
|
||
|
|
assert data['personal_info'][key] == value
|
||
|
|
for key, value in motivation_data.items():
|
||
|
|
assert data['motivation_answers'][key] == value
|
||
|
|
|
||
|
|
# Submit application
|
||
|
|
client.post(f'/apply/{session_id}/submit-application')
|
||
|
|
|
||
|
|
# Verify all data still intact after submission
|
||
|
|
data = load_application_data(session_id)
|
||
|
|
assert data['email'] == 'persist@test.com'
|
||
|
|
for key, value in personal_data.items():
|
||
|
|
assert data['personal_info'][key] == value
|
||
|
|
for key, value in motivation_data.items():
|
||
|
|
assert data['motivation_answers'][key] == value
|