chore: initial snapshot of tubco people portal
This commit is contained in:
0
backend/workflows/tests/__init__.py
Normal file
0
backend/workflows/tests/__init__.py
Normal file
27
backend/workflows/tests/test_email_mode_override.py
Normal file
27
backend/workflows/tests/test_email_mode_override.py
Normal file
@@ -0,0 +1,27 @@
|
||||
from django.test import TestCase, override_settings
|
||||
|
||||
from workflows.models import WorkflowConfig
|
||||
from workflows.services import is_email_test_mode
|
||||
|
||||
|
||||
class EmailModeOverrideTests(TestCase):
|
||||
@override_settings(EMAIL_TEST_MODE=False)
|
||||
def test_uses_env_when_no_override(self):
|
||||
config, _ = WorkflowConfig.objects.get_or_create(name='Default')
|
||||
config.email_test_mode_override = None
|
||||
config.save(update_fields=['email_test_mode_override'])
|
||||
self.assertEqual(is_email_test_mode(), False)
|
||||
|
||||
@override_settings(EMAIL_TEST_MODE=False)
|
||||
def test_true_override_enables_test_mode(self):
|
||||
config, _ = WorkflowConfig.objects.get_or_create(name='Default')
|
||||
config.email_test_mode_override = True
|
||||
config.save(update_fields=['email_test_mode_override'])
|
||||
self.assertEqual(is_email_test_mode(), True)
|
||||
|
||||
@override_settings(EMAIL_TEST_MODE=True)
|
||||
def test_false_override_disables_test_mode(self):
|
||||
config, _ = WorkflowConfig.objects.get_or_create(name='Default')
|
||||
config.email_test_mode_override = False
|
||||
config.save(update_fields=['email_test_mode_override'])
|
||||
self.assertEqual(is_email_test_mode(), False)
|
||||
43
backend/workflows/tests/test_emailing_fallback.py
Normal file
43
backend/workflows/tests/test_emailing_fallback.py
Normal file
@@ -0,0 +1,43 @@
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
from django.test import TestCase
|
||||
|
||||
from workflows.models import WorkflowConfig
|
||||
from workflows.emailing import send_system_email
|
||||
|
||||
|
||||
class EmailingFallbackTests(TestCase):
|
||||
@patch('workflows.emailing.EmailMessage')
|
||||
@patch('workflows.emailing.get_connection')
|
||||
def test_uses_workflowconfig_smtp_when_no_active_system_config(self, mock_get_connection, mock_email_message):
|
||||
WorkflowConfig.objects.update_or_create(
|
||||
name='Default',
|
||||
defaults={
|
||||
'smtp_server': 'mx.tub.co',
|
||||
'smtp_port': 465,
|
||||
'email_account': 'onboarding@tub.co',
|
||||
'email_password': 'secret',
|
||||
'smtp_use_ssl': True,
|
||||
'smtp_use_tls': False,
|
||||
},
|
||||
)
|
||||
|
||||
fake_connection = object()
|
||||
mock_get_connection.return_value = fake_connection
|
||||
msg_instance = MagicMock()
|
||||
mock_email_message.return_value = msg_instance
|
||||
|
||||
send_system_email(
|
||||
subject='x',
|
||||
body='y',
|
||||
to=['target@example.com'],
|
||||
)
|
||||
|
||||
self.assertEqual(mock_get_connection.call_count, 1)
|
||||
kwargs = mock_get_connection.call_args.kwargs
|
||||
self.assertEqual(kwargs['host'], 'mx.tub.co')
|
||||
self.assertEqual(kwargs['port'], 465)
|
||||
self.assertEqual(kwargs['username'], 'onboarding@tub.co')
|
||||
self.assertEqual(kwargs['password'], 'secret')
|
||||
self.assertEqual(kwargs['use_ssl'], True)
|
||||
self.assertEqual(kwargs['use_tls'], False)
|
||||
96
backend/workflows/tests/test_form_builder_admin.py
Normal file
96
backend/workflows/tests/test_form_builder_admin.py
Normal file
@@ -0,0 +1,96 @@
|
||||
import json
|
||||
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.test import TestCase
|
||||
|
||||
from workflows.models import FormFieldConfig, FormOption
|
||||
|
||||
|
||||
class FormBuilderAdminTests(TestCase):
|
||||
def setUp(self):
|
||||
user_model = get_user_model()
|
||||
self.staff = user_model.objects.create_user(
|
||||
username='builder_admin',
|
||||
password='secret123',
|
||||
email='builder_admin@tub.co',
|
||||
is_staff=True,
|
||||
)
|
||||
self.user = user_model.objects.create_user(
|
||||
username='builder_user',
|
||||
password='secret123',
|
||||
email='builder_user@tub.co',
|
||||
)
|
||||
|
||||
def test_staff_can_open_form_builder(self):
|
||||
self.client.force_login(self.staff)
|
||||
response = self.client.get('/admin-tools/form-builder/?form_type=onboarding', HTTP_HOST='localhost')
|
||||
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertContains(response, 'Form Builder')
|
||||
self.assertContains(response, '1. Stammdaten')
|
||||
|
||||
def test_non_staff_cannot_open_form_builder(self):
|
||||
self.client.force_login(self.user)
|
||||
response = self.client.get('/admin-tools/form-builder/?form_type=onboarding', HTTP_HOST='localhost')
|
||||
self.assertEqual(response.status_code, 302)
|
||||
|
||||
def test_save_order_updates_sort_order_and_step(self):
|
||||
self.client.force_login(self.staff)
|
||||
self.client.get('/admin-tools/form-builder/?form_type=onboarding', HTTP_HOST='localhost')
|
||||
|
||||
payload = {
|
||||
'form_type': 'onboarding',
|
||||
'columns': {
|
||||
'stammdaten': ['department', 'full_name'],
|
||||
'vertrag': ['contract_start'],
|
||||
'itsetup': [],
|
||||
'abschluss': [],
|
||||
},
|
||||
}
|
||||
|
||||
response = self.client.post(
|
||||
'/admin-tools/form-builder/save-order/',
|
||||
data=json.dumps(payload),
|
||||
content_type='application/json',
|
||||
HTTP_HOST='localhost',
|
||||
)
|
||||
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertEqual(response.json()['ok'], True)
|
||||
|
||||
department = FormFieldConfig.objects.get(form_type='onboarding', field_name='department')
|
||||
full_name = FormFieldConfig.objects.get(form_type='onboarding', field_name='full_name')
|
||||
contract_start = FormFieldConfig.objects.get(form_type='onboarding', field_name='contract_start')
|
||||
|
||||
self.assertEqual(department.sort_order, 0)
|
||||
self.assertEqual(department.page_key, 'stammdaten')
|
||||
self.assertEqual(full_name.sort_order, 1)
|
||||
self.assertEqual(full_name.page_key, 'stammdaten')
|
||||
self.assertEqual(contract_start.sort_order, 2)
|
||||
self.assertEqual(contract_start.page_key, 'vertrag')
|
||||
|
||||
def test_save_order_requires_staff(self):
|
||||
self.client.force_login(self.user)
|
||||
response = self.client.post(
|
||||
'/admin-tools/form-builder/save-order/',
|
||||
data=json.dumps({'form_type': 'onboarding', 'columns': {}}),
|
||||
content_type='application/json',
|
||||
HTTP_HOST='localhost',
|
||||
)
|
||||
self.assertEqual(response.status_code, 302)
|
||||
|
||||
def test_staff_can_add_option_item_without_django_admin(self):
|
||||
self.client.force_login(self.staff)
|
||||
response = self.client.post(
|
||||
'/admin-tools/form-builder/?form_type=onboarding&option_category=device',
|
||||
data={
|
||||
'builder_action': 'add_option',
|
||||
'category': 'device',
|
||||
'label': 'Tablet',
|
||||
'value': 'Tablet',
|
||||
},
|
||||
HTTP_HOST='localhost',
|
||||
)
|
||||
|
||||
self.assertEqual(response.status_code, 302)
|
||||
self.assertTrue(FormOption.objects.filter(category='device', label='Tablet').exists())
|
||||
73
backend/workflows/tests/test_nextcloud_service.py
Normal file
73
backend/workflows/tests/test_nextcloud_service.py
Normal file
@@ -0,0 +1,73 @@
|
||||
from pathlib import Path
|
||||
from unittest.mock import patch
|
||||
|
||||
from django.test import TestCase, override_settings
|
||||
|
||||
from workflows.models import WorkflowConfig
|
||||
from workflows.services import upload_to_nextcloud
|
||||
|
||||
|
||||
class NextcloudServiceTests(TestCase):
|
||||
@override_settings(NEXTCLOUD_ENABLED=False)
|
||||
@patch('workflows.services.requests.put')
|
||||
def test_upload_returns_false_when_disabled(self, mock_put):
|
||||
result = upload_to_nextcloud(Path('/tmp/nonexistent.txt'), 'x.txt')
|
||||
self.assertFalse(result)
|
||||
mock_put.assert_not_called()
|
||||
|
||||
@override_settings(
|
||||
NEXTCLOUD_ENABLED=True,
|
||||
NEXTCLOUD_BASE_URL='https://cloud.example/remote.php/dav/files/test-user',
|
||||
NEXTCLOUD_DIRECTORY='Onboarding',
|
||||
NEXTCLOUD_USERNAME='u',
|
||||
NEXTCLOUD_PASSWORD='p',
|
||||
)
|
||||
@patch('workflows.services.requests.put')
|
||||
def test_upload_calls_webdav_and_accepts_201(self, mock_put):
|
||||
temp_file = Path('/tmp/nextcloud_mock_upload.txt')
|
||||
temp_file.write_text('hello', encoding='utf-8')
|
||||
mock_put.return_value.status_code = 201
|
||||
|
||||
try:
|
||||
result = upload_to_nextcloud(temp_file, 'target.txt')
|
||||
finally:
|
||||
temp_file.unlink(missing_ok=True)
|
||||
|
||||
self.assertTrue(result)
|
||||
self.assertEqual(mock_put.call_count, 1)
|
||||
called_url = mock_put.call_args.kwargs['url'] if 'url' in mock_put.call_args.kwargs else mock_put.call_args.args[0]
|
||||
self.assertTrue(called_url.endswith('/Onboarding/target.txt'))
|
||||
|
||||
@override_settings(
|
||||
NEXTCLOUD_ENABLED=True,
|
||||
NEXTCLOUD_BASE_URL='https://cloud.example/remote.php/dav/files/env-user',
|
||||
NEXTCLOUD_DIRECTORY='EnvFolder',
|
||||
NEXTCLOUD_USERNAME='env-user',
|
||||
NEXTCLOUD_PASSWORD='env-pass',
|
||||
)
|
||||
@patch('workflows.services.requests.put')
|
||||
def test_upload_prefers_workflowconfig_overrides(self, mock_put):
|
||||
WorkflowConfig.objects.update_or_create(
|
||||
name='Default',
|
||||
defaults={
|
||||
'nextcloud_enabled_override': True,
|
||||
'nextcloud_base_url_override': 'https://cloud.example/remote.php/dav/files/admin-user',
|
||||
'nextcloud_directory_override': 'AdminFolder',
|
||||
'nextcloud_username_override': 'admin-user',
|
||||
'nextcloud_password_override': 'admin-pass',
|
||||
},
|
||||
)
|
||||
temp_file = Path('/tmp/nextcloud_override_upload.txt')
|
||||
temp_file.write_text('hello', encoding='utf-8')
|
||||
mock_put.return_value.status_code = 201
|
||||
|
||||
try:
|
||||
result = upload_to_nextcloud(temp_file, 'override.txt')
|
||||
finally:
|
||||
temp_file.unlink(missing_ok=True)
|
||||
|
||||
self.assertTrue(result)
|
||||
called_url = mock_put.call_args.kwargs['url'] if 'url' in mock_put.call_args.kwargs else mock_put.call_args.args[0]
|
||||
self.assertTrue(called_url.endswith('/AdminFolder/override.txt'))
|
||||
auth = mock_put.call_args.kwargs.get('auth')
|
||||
self.assertEqual(auth, ('admin-user', 'admin-pass'))
|
||||
59
backend/workflows/tests/test_offboarding_flow.py
Normal file
59
backend/workflows/tests/test_offboarding_flow.py
Normal file
@@ -0,0 +1,59 @@
|
||||
from unittest.mock import patch
|
||||
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.test import TestCase
|
||||
|
||||
from workflows.models import EmployeeProfile, OffboardingRequest
|
||||
|
||||
|
||||
class OffboardingFlowTests(TestCase):
|
||||
def setUp(self):
|
||||
user_model = get_user_model()
|
||||
self.user = user_model.objects.create_user(
|
||||
username='offboard_user',
|
||||
password='secret123',
|
||||
email='operator@tub.co',
|
||||
first_name='Nina',
|
||||
last_name='Admin',
|
||||
)
|
||||
self.client.force_login(self.user)
|
||||
self.profile = EmployeeProfile.objects.create(
|
||||
full_name='Lara Beispiel',
|
||||
first_name='Lara',
|
||||
last_name='Beispiel',
|
||||
department='IT-Service',
|
||||
job_title='Engineer',
|
||||
work_email='lara.beispiel@tub.co',
|
||||
)
|
||||
|
||||
def test_offboarding_prefill_from_profile(self):
|
||||
response = self.client.get(f'/offboarding/new/?profile={self.profile.id}', HTTP_HOST='localhost')
|
||||
html = response.content.decode('utf-8')
|
||||
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertIn('value="Lara Beispiel"', html)
|
||||
self.assertIn('value="lara.beispiel@tub.co"', html)
|
||||
self.assertIn('value="Engineer"', html)
|
||||
|
||||
@patch('workflows.views.process_offboarding_request.delay')
|
||||
def test_offboarding_submit_uses_logged_in_user_email(self, mock_delay):
|
||||
payload = {
|
||||
'full_name': self.profile.full_name,
|
||||
'work_email': self.profile.work_email,
|
||||
'department': self.profile.department,
|
||||
'job_title': self.profile.job_title,
|
||||
'last_working_day': '2026-12-31',
|
||||
'notes': 'Bitte Accounts sperren.',
|
||||
}
|
||||
|
||||
response = self.client.post(
|
||||
f'/offboarding/new/?profile={self.profile.id}',
|
||||
payload,
|
||||
HTTP_HOST='localhost',
|
||||
)
|
||||
|
||||
self.assertEqual(response.status_code, 302)
|
||||
obj = OffboardingRequest.objects.get(work_email=self.profile.work_email)
|
||||
self.assertEqual(obj.requested_by_email, 'operator@tub.co')
|
||||
self.assertEqual(obj.requested_by_name, 'Nina Admin')
|
||||
mock_delay.assert_called_once_with(obj.id)
|
||||
50
backend/workflows/tests/test_onboarding_flow.py
Normal file
50
backend/workflows/tests/test_onboarding_flow.py
Normal file
@@ -0,0 +1,50 @@
|
||||
from unittest.mock import patch
|
||||
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.test import TestCase
|
||||
|
||||
from workflows.models import OnboardingRequest
|
||||
|
||||
|
||||
class OnboardingFlowTests(TestCase):
|
||||
def setUp(self):
|
||||
user_model = get_user_model()
|
||||
self.user = user_model.objects.create_user(
|
||||
username='onboard_user',
|
||||
password='secret123',
|
||||
email='requester@tub.co',
|
||||
first_name='Mia',
|
||||
last_name='Beispiel',
|
||||
)
|
||||
self.client.force_login(self.user)
|
||||
|
||||
@patch('workflows.views.process_onboarding_request.delay')
|
||||
def test_onboarding_submit_persists_and_enqueues_task(self, mock_delay):
|
||||
payload = {
|
||||
'first_name': 'Max',
|
||||
'last_name': 'Mustermann',
|
||||
'gender': 'herr',
|
||||
'job_title': 'Consultant',
|
||||
'department': 'IT-Service',
|
||||
'work_email': 'max.mustermann@tub.co',
|
||||
'contract_start': '2026-11-01',
|
||||
'employment_type': 'unbefristet',
|
||||
'group_mailboxes_required_choice': 'nein',
|
||||
'additional_hardware_needed_choice': 'nein',
|
||||
'additional_software_needed_choice': 'nein',
|
||||
'additional_access_needed_choice': 'nein',
|
||||
'successor_required_choice': 'nein',
|
||||
'inherit_phone_number_choice': 'nein',
|
||||
'agreement_confirm': 'on',
|
||||
}
|
||||
|
||||
response = self.client.post('/onboarding/new/', payload, HTTP_HOST='localhost')
|
||||
|
||||
self.assertEqual(response.status_code, 302)
|
||||
self.assertIn('/onboarding/new/?saved=1&id=', response['Location'])
|
||||
|
||||
obj = OnboardingRequest.objects.get(work_email='max.mustermann@tub.co')
|
||||
self.assertEqual(obj.full_name, 'Max Mustermann')
|
||||
self.assertEqual(obj.onboarded_by_email, 'requester@tub.co')
|
||||
self.assertEqual(obj.onboarded_by_name, 'Mia Beispiel')
|
||||
mock_delay.assert_called_once_with(obj.id)
|
||||
18
backend/workflows/tests/test_pdf_smoke.py
Normal file
18
backend/workflows/tests/test_pdf_smoke.py
Normal file
@@ -0,0 +1,18 @@
|
||||
from pathlib import Path
|
||||
|
||||
from django.test import SimpleTestCase
|
||||
|
||||
from workflows.tasks import _generate_content_pdf
|
||||
|
||||
|
||||
class PdfSmokeTests(SimpleTestCase):
|
||||
def test_generate_content_pdf_creates_nonempty_file(self):
|
||||
output_pdf = Path('/tmp/pdf_smoke_output.pdf')
|
||||
html = '<html><body><h1>PDF Smoke</h1><p>This is a smoke test.</p></body></html>'
|
||||
|
||||
try:
|
||||
_generate_content_pdf(html, output_pdf)
|
||||
self.assertTrue(output_pdf.exists())
|
||||
self.assertGreater(output_pdf.stat().st_size, 100)
|
||||
finally:
|
||||
output_pdf.unlink(missing_ok=True)
|
||||
107
backend/workflows/tests/test_tasks_email_routing.py
Normal file
107
backend/workflows/tests/test_tasks_email_routing.py
Normal file
@@ -0,0 +1,107 @@
|
||||
from pathlib import Path
|
||||
from datetime import date
|
||||
from unittest.mock import patch
|
||||
|
||||
from django.test import TestCase, override_settings
|
||||
|
||||
from workflows.models import NotificationRule, OnboardingRequest, WorkflowConfig
|
||||
from workflows.tasks import process_onboarding_request
|
||||
|
||||
|
||||
@override_settings(PDF_OUTPUT_DIR=Path('/tmp/onoff_test_pdfs'))
|
||||
class TaskEmailRoutingTests(TestCase):
|
||||
def setUp(self):
|
||||
WorkflowConfig.objects.update_or_create(
|
||||
name='Default',
|
||||
defaults={
|
||||
'it_onboarding_email': 'it@tub.co',
|
||||
'general_info_email': 'ingo@tub.co',
|
||||
'business_card_email': 'kommunikation@tub.co',
|
||||
'hr_works_email': 'dittrich@tub.co',
|
||||
'key_notification_email': 'minuth@tub.co',
|
||||
},
|
||||
)
|
||||
self.request_obj = OnboardingRequest.objects.create(
|
||||
full_name='Nina Routing',
|
||||
gender='frau',
|
||||
job_title='Manager',
|
||||
department='IT-Service',
|
||||
work_email='nina.routing@tub.co',
|
||||
contract_start=date(2026, 11, 1),
|
||||
employment_type='unbefristet',
|
||||
order_business_cards=True,
|
||||
business_card_name='Nina Routing',
|
||||
business_card_title='Manager',
|
||||
business_card_email='nina.routing@tub.co',
|
||||
business_card_phone='030 123456',
|
||||
needed_devices='Laptop\nSchlüssel',
|
||||
needed_accesses='HR Works',
|
||||
onboarded_by_email='requester@tub.co',
|
||||
agreement='accepted',
|
||||
)
|
||||
|
||||
@patch('workflows.tasks.upload_to_nextcloud')
|
||||
@patch('workflows.tasks._send_templated_email')
|
||||
@patch('workflows.tasks._generate_onboarding_pdf')
|
||||
def test_onboarding_email_routing_conditions(
|
||||
self,
|
||||
mock_generate_pdf,
|
||||
mock_send_templated_email,
|
||||
mock_upload,
|
||||
):
|
||||
pdf_path = Path('/tmp/onoff_test_pdfs/onboarding_letter_Nina_Routing.pdf')
|
||||
pdf_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
pdf_path.write_bytes(b'%PDF-1.4\n%test\n')
|
||||
mock_generate_pdf.return_value = pdf_path
|
||||
|
||||
process_onboarding_request(self.request_obj.id)
|
||||
|
||||
template_keys = [call.kwargs['template_key'] for call in mock_send_templated_email.call_args_list]
|
||||
self.assertIn('onboarding_it', template_keys)
|
||||
self.assertIn('onboarding_general_info', template_keys)
|
||||
self.assertIn('onboarding_business_card', template_keys)
|
||||
self.assertIn('onboarding_hr_works', template_keys)
|
||||
self.assertIn('onboarding_key', template_keys)
|
||||
self.assertIn('onboarding_reference', template_keys)
|
||||
self.assertEqual(len(template_keys), 6)
|
||||
|
||||
mock_upload.assert_called_once_with(pdf_path, pdf_path.name)
|
||||
|
||||
@patch('workflows.tasks.upload_to_nextcloud')
|
||||
@patch('workflows.tasks._send_templated_email')
|
||||
@patch('workflows.tasks._generate_onboarding_pdf')
|
||||
def test_onboarding_additional_notification_rule(
|
||||
self,
|
||||
mock_generate_pdf,
|
||||
mock_send_templated_email,
|
||||
mock_upload,
|
||||
):
|
||||
NotificationRule.objects.create(
|
||||
name='Extra Schluessel Regel',
|
||||
event_type='onboarding',
|
||||
field_name='needed_devices',
|
||||
operator='contains',
|
||||
expected_value='Schlüssel',
|
||||
recipients='extra.recipient@tub.co',
|
||||
template_key='onboarding_key',
|
||||
include_pdf_attachment=True,
|
||||
sort_order=1,
|
||||
is_active=True,
|
||||
)
|
||||
|
||||
pdf_path = Path('/tmp/onoff_test_pdfs/onboarding_letter_Nina_Routing.pdf')
|
||||
pdf_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
pdf_path.write_bytes(b'%PDF-1.4\n%test\n')
|
||||
mock_generate_pdf.return_value = pdf_path
|
||||
|
||||
process_onboarding_request(self.request_obj.id)
|
||||
|
||||
matching_calls = [
|
||||
call for call in mock_send_templated_email.call_args_list
|
||||
if call.kwargs.get('template_key') == 'onboarding_key'
|
||||
and call.kwargs.get('to') == ['extra.recipient@tub.co']
|
||||
]
|
||||
self.assertEqual(len(matching_calls), 1)
|
||||
self.assertEqual(matching_calls[0].kwargs.get('attachments'), [pdf_path])
|
||||
|
||||
mock_upload.assert_called_once_with(pdf_path, pdf_path.name)
|
||||
162
backend/workflows/tests/test_welcome_email_schedule.py
Normal file
162
backend/workflows/tests/test_welcome_email_schedule.py
Normal file
@@ -0,0 +1,162 @@
|
||||
from datetime import date
|
||||
from pathlib import Path
|
||||
from unittest.mock import patch
|
||||
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.test import TestCase, override_settings
|
||||
from django.utils import timezone
|
||||
|
||||
from workflows.models import NotificationTemplate, OnboardingRequest, ScheduledWelcomeEmail, WorkflowConfig
|
||||
from workflows.tasks import process_onboarding_request, send_scheduled_welcome_email
|
||||
|
||||
|
||||
@override_settings(PDF_OUTPUT_DIR=Path('/tmp/onoff_test_pdfs'))
|
||||
class WelcomeEmailScheduleTests(TestCase):
|
||||
def setUp(self):
|
||||
WorkflowConfig.objects.update_or_create(
|
||||
name='Default',
|
||||
defaults={
|
||||
'welcome_email_delay_days': 9,
|
||||
'welcome_sender_email': 'admin.sender@tub.co',
|
||||
'welcome_include_pdf': False,
|
||||
},
|
||||
)
|
||||
self.onboarding = OnboardingRequest.objects.create(
|
||||
full_name='Welcome Person',
|
||||
gender='frau',
|
||||
job_title='Manager',
|
||||
department='IT-Service',
|
||||
work_email='welcome.person@tub.co',
|
||||
contract_start=date(2026, 11, 1),
|
||||
employment_type='unbefristet',
|
||||
onboarded_by_email='requester@tub.co',
|
||||
agreement='accepted',
|
||||
)
|
||||
|
||||
@patch('workflows.tasks.upload_to_nextcloud')
|
||||
@patch('workflows.tasks.send_scheduled_welcome_email.apply_async')
|
||||
@patch('workflows.tasks._send_templated_email')
|
||||
@patch('workflows.tasks._generate_onboarding_pdf')
|
||||
def test_onboarding_creates_scheduled_welcome_email(
|
||||
self,
|
||||
mock_generate_pdf,
|
||||
mock_send_templated_email,
|
||||
mock_welcome_apply_async,
|
||||
mock_upload,
|
||||
):
|
||||
pdf_path = Path('/tmp/onoff_test_pdfs/onboarding_letter_Welcome_Person.pdf')
|
||||
pdf_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
pdf_path.write_bytes(b'%PDF-1.4\n%test\n')
|
||||
mock_generate_pdf.return_value = pdf_path
|
||||
mock_welcome_apply_async.return_value.id = 'celery-welcome-1'
|
||||
|
||||
process_onboarding_request(self.onboarding.id)
|
||||
|
||||
scheduled = ScheduledWelcomeEmail.objects.get(onboarding_request_id=self.onboarding.id)
|
||||
self.assertEqual(scheduled.recipient_email, 'welcome.person@tub.co')
|
||||
self.assertEqual(scheduled.status, 'scheduled')
|
||||
self.assertEqual(scheduled.celery_task_id, 'celery-welcome-1')
|
||||
delay = scheduled.send_at - timezone.now()
|
||||
self.assertGreaterEqual(delay.days, 8)
|
||||
|
||||
@patch('workflows.views.send_scheduled_welcome_email.delay')
|
||||
def test_admin_can_trigger_welcome_email_now(self, mock_delay):
|
||||
scheduled = ScheduledWelcomeEmail.objects.create(
|
||||
onboarding_request=self.onboarding,
|
||||
recipient_email='welcome.person@tub.co',
|
||||
send_at=timezone.now(),
|
||||
status='scheduled',
|
||||
)
|
||||
|
||||
user_model = get_user_model()
|
||||
admin = user_model.objects.create_user(
|
||||
username='welcome_admin',
|
||||
password='secret123',
|
||||
email='welcome_admin@tub.co',
|
||||
is_staff=True,
|
||||
is_superuser=True,
|
||||
)
|
||||
self.client.force_login(admin)
|
||||
mock_delay.return_value.id = 'forced-welcome-1'
|
||||
|
||||
response = self.client.post(
|
||||
f'/admin-tools/welcome-emails/{scheduled.id}/trigger-now/',
|
||||
HTTP_HOST='localhost',
|
||||
)
|
||||
|
||||
self.assertEqual(response.status_code, 302)
|
||||
scheduled.refresh_from_db()
|
||||
self.assertEqual(scheduled.celery_task_id, 'forced-welcome-1')
|
||||
mock_delay.assert_called_once_with(scheduled.id, True)
|
||||
|
||||
@patch('workflows.views.send_scheduled_welcome_email.apply_async')
|
||||
def test_admin_can_pause_resume_and_cancel(self, mock_apply_async):
|
||||
scheduled = ScheduledWelcomeEmail.objects.create(
|
||||
onboarding_request=self.onboarding,
|
||||
recipient_email='welcome.person@tub.co',
|
||||
send_at=timezone.now(),
|
||||
status='scheduled',
|
||||
celery_task_id='task-abc',
|
||||
)
|
||||
mock_apply_async.return_value.id = 'resumed-task-1'
|
||||
|
||||
user_model = get_user_model()
|
||||
admin = user_model.objects.create_user(
|
||||
username='welcome_admin_2',
|
||||
password='secret123',
|
||||
email='welcome_admin_2@tub.co',
|
||||
is_staff=True,
|
||||
is_superuser=True,
|
||||
)
|
||||
self.client.force_login(admin)
|
||||
|
||||
pause_response = self.client.post(
|
||||
f'/admin-tools/welcome-emails/{scheduled.id}/pause/',
|
||||
HTTP_HOST='localhost',
|
||||
)
|
||||
self.assertEqual(pause_response.status_code, 302)
|
||||
scheduled.refresh_from_db()
|
||||
self.assertEqual(scheduled.status, 'paused')
|
||||
|
||||
resume_response = self.client.post(
|
||||
f'/admin-tools/welcome-emails/{scheduled.id}/resume/',
|
||||
HTTP_HOST='localhost',
|
||||
)
|
||||
self.assertEqual(resume_response.status_code, 302)
|
||||
scheduled.refresh_from_db()
|
||||
self.assertEqual(scheduled.status, 'scheduled')
|
||||
self.assertEqual(scheduled.celery_task_id, 'resumed-task-1')
|
||||
|
||||
cancel_response = self.client.post(
|
||||
f'/admin-tools/welcome-emails/{scheduled.id}/cancel/',
|
||||
HTTP_HOST='localhost',
|
||||
)
|
||||
self.assertEqual(cancel_response.status_code, 302)
|
||||
scheduled.refresh_from_db()
|
||||
self.assertEqual(scheduled.status, 'cancelled')
|
||||
|
||||
@patch('workflows.tasks._send_templated_email')
|
||||
def test_scheduled_welcome_uses_sender_override_without_pdf_if_disabled(self, mock_send_templated):
|
||||
NotificationTemplate.objects.update_or_create(
|
||||
key='onboarding_welcome',
|
||||
defaults={
|
||||
'subject_template': 'Welcome {{ FULL_NAME }}',
|
||||
'body_template': 'Body {{ EMAIL }}',
|
||||
'is_active': True,
|
||||
},
|
||||
)
|
||||
scheduled = ScheduledWelcomeEmail.objects.create(
|
||||
onboarding_request=self.onboarding,
|
||||
recipient_email='welcome.person@tub.co',
|
||||
send_at=timezone.now(),
|
||||
status='scheduled',
|
||||
)
|
||||
self.onboarding.generated_pdf_path = '/tmp/onoff_test_pdfs/should_not_attach.pdf'
|
||||
self.onboarding.save(update_fields=['generated_pdf_path'])
|
||||
|
||||
send_scheduled_welcome_email(scheduled.id, True)
|
||||
|
||||
mock_send_templated.assert_called_once()
|
||||
kwargs = mock_send_templated.call_args.kwargs
|
||||
self.assertEqual(kwargs.get('attachments'), [])
|
||||
self.assertEqual(kwargs.get('from_email'), 'admin.sender@tub.co')
|
||||
Reference in New Issue
Block a user