backend.tests.unit.components.workers.test_worker_service_manual
1import pytest 2import os 3import io 4import numpy as np 5import face_recognition 6from unittest.mock import patch, MagicMock 7from datetime import datetime 8from backend.app import create_app, db 9from backend.database.models import Worker 10from backend.components.workers import workerService 11 12 13# ============================================================================ 14# Test Setup & Fixtures 15# ============================================================================ 16 17@pytest.fixture 18def app_context(): 19 """ 20 Creates an isolated application context with an in-memory SQLite database. 21 Ensures a clean state for every test function using this fixture. 22 """ 23 app = create_app() 24 app.config.update({ 25 "TESTING": True, 26 "SQLALCHEMY_DATABASE_URI": "sqlite:///:memory:" 27 }) 28 29 with app.app_context(): 30 db.create_all() 31 yield app 32 db.session.remove() 33 db.drop_all() 34 35 36# ============================================================================ 37# Test: Embedding Generation (Integration) 38# ============================================================================ 39 40def test_create_worker_embedding_from_file(): 41 """ 42 Integration test for face embedding generation using a real image file. 43 44 Verifies that: 45 1. The test image file exists. 46 2. The service converts the image to a binary BLOB (bytes). 47 3. The BLOB can be deserialized back into a valid NumPy array of shape (128,). 48 """ 49 current_dir = os.path.dirname(os.path.abspath(__file__)) 50 # Calculate path: backend/tests/unit/components/workers -> backend/tests/assets 51 image_path = os.path.normpath(os.path.join(current_dir, "../../../assets/testimg.jpg")) 52 53 print(f"Looking for test image at: {image_path}") 54 if not os.path.exists(image_path): 55 pytest.skip(f"Skipped: Test image not found at {image_path}") 56 57 image_input = face_recognition.load_image_file(image_path) 58 59 # Execute service function 60 result_blob = workerService.create_worker_embedding(image_input) 61 62 # Basic assertions 63 assert isinstance(result_blob, bytes) 64 assert len(result_blob) > 0 65 66 # Verify that the blob is a valid serialized NumPy array 67 buffer = io.BytesIO(result_blob) 68 embeddings = np.load(buffer, allow_pickle=True) 69 70 assert len(embeddings) > 0, "No faces detected in the test image." 71 # face_recognition library generates 128-dimensional vectors 72 assert len(embeddings[0]) == 128 73 74 75# ============================================================================ 76# Test: Worker CRUD Operations 77# ============================================================================ 78 79@patch('backend.components.workers.workerService.create_worker_embedding') 80def test_create_worker_adds_to_db(mock_create_embedding, app_context): 81 """ 82 Tests if the `create_worker` function correctly persists a new worker to the database. 83 84 We mock the embedding generation to avoid the overhead of the ML library 85 and focus on database interactions. 86 """ 87 mock_create_embedding.return_value = b'test_blob_data' 88 name = "John Doe" 89 fake_image = MagicMock() # Mock the image object 90 expiration = datetime(2025, 12, 31) 91 92 with app_context.app_context(): 93 # Action: Create worker via service 94 worker = workerService.create_worker(name, fake_image, expiration) 95 96 assert worker.id is not None 97 assert worker.name == name 98 99 # Verification: Fetch from DB to ensure persistence 100 fetched_worker = db.session.get(Worker, worker.id) 101 assert fetched_worker is not None 102 assert fetched_worker.name == "John Doe" 103 assert fetched_worker.face_embedding == b'test_blob_data' 104 105 106def test_extend_worker_expiration_success(app_context): 107 """ 108 Tests the `extend_worker_expiration` functionality. 109 110 Verifies that the expiration date is updated in the database object. 111 """ 112 initial_date = datetime(2024, 1, 1) 113 new_date = datetime(2030, 1, 1) 114 115 with app_context.app_context(): 116 # 1. Setup: Create a worker with an initial date 117 worker = Worker( 118 name="Mark ToChange", 119 face_embedding=b'dummy_data', 120 expiration_date=initial_date, 121 secret="dummy_secret" 122 ) 123 db.session.add(worker) 124 db.session.commit() 125 126 # Retrieve the object (service requires an object, not ID) 127 worker_to_update = db.session.get(Worker, worker.id) 128 129 # 2. Action: Extend expiration 130 updated_worker = workerService.extend_worker_expiration(worker_to_update, new_date) 131 132 # 3. Verification 133 assert updated_worker.expiration_date == new_date 134 135 # Force session refresh to check DB state 136 db.session.expire_all() 137 fetched_worker = db.session.get(Worker, worker.id) 138 assert fetched_worker.expiration_date == new_date 139 140 141def test_update_worker_name_success(app_context): 142 """ 143 Tests the `update_worker_name` functionality. 144 145 Verifies that the worker's name is successfully updated in the database. 146 """ 147 initial_name = "Jane Doe" 148 new_name = "Jane Smith" 149 150 with app_context.app_context(): 151 # Setup 152 worker = Worker( 153 name=initial_name, 154 face_embedding=b'dummy_data', 155 expiration_date=datetime(2025, 12, 12), 156 secret="dummy_secret" 157 ) 158 db.session.add(worker) 159 db.session.commit() 160 161 # Action 162 worker_to_update = db.session.get(Worker, worker.id) 163 workerService.update_worker_name(worker_to_update, new_name) 164 165 # Verification 166 db.session.expire_all() 167 fetched_worker = db.session.get(Worker, worker.id) 168 assert fetched_worker.name == new_name 169 170 171def test_update_worker_face_embedding_success(app_context): 172 """ 173 Tests the `update_worker_face_image` functionality. 174 175 Verifies that providing a new image triggers the embedding recalculation 176 and updates the database field. 177 """ 178 initial_name = "Test Face Update" 179 initial_face_data = b'old_dummy_embedding' 180 new_raw_image = b'new_raw_image_data_jpg' 181 mocked_embedding_result = b'new_calculated_embedding_123' 182 183 with app_context.app_context(): 184 # Setup 185 worker = Worker( 186 name=initial_name, 187 face_embedding=initial_face_data, 188 expiration_date=datetime(2025, 12, 12), 189 secret="dummy_secret" 190 ) 191 db.session.add(worker) 192 db.session.commit() 193 194 # Mock the internal embedding function to isolate logic 195 with patch('backend.components.workers.workerService.create_worker_embedding') as mock_embedding: 196 mock_embedding.return_value = mocked_embedding_result 197 198 # Action 199 worker_to_update = db.session.get(Worker, worker.id) 200 workerService.update_worker_face_image(worker_to_update, new_raw_image) 201 202 # Assert internal call 203 mock_embedding.assert_called_once_with(new_raw_image) 204 205 # Verification 206 db.session.expire_all() 207 fetched_worker = db.session.get(Worker, worker.id) 208 assert fetched_worker.face_embedding == mocked_embedding_result
18@pytest.fixture 19def app_context(): 20 """ 21 Creates an isolated application context with an in-memory SQLite database. 22 Ensures a clean state for every test function using this fixture. 23 """ 24 app = create_app() 25 app.config.update({ 26 "TESTING": True, 27 "SQLALCHEMY_DATABASE_URI": "sqlite:///:memory:" 28 }) 29 30 with app.app_context(): 31 db.create_all() 32 yield app 33 db.session.remove() 34 db.drop_all()
Creates an isolated application context with an in-memory SQLite database. Ensures a clean state for every test function using this fixture.
41def test_create_worker_embedding_from_file(): 42 """ 43 Integration test for face embedding generation using a real image file. 44 45 Verifies that: 46 1. The test image file exists. 47 2. The service converts the image to a binary BLOB (bytes). 48 3. The BLOB can be deserialized back into a valid NumPy array of shape (128,). 49 """ 50 current_dir = os.path.dirname(os.path.abspath(__file__)) 51 # Calculate path: backend/tests/unit/components/workers -> backend/tests/assets 52 image_path = os.path.normpath(os.path.join(current_dir, "../../../assets/testimg.jpg")) 53 54 print(f"Looking for test image at: {image_path}") 55 if not os.path.exists(image_path): 56 pytest.skip(f"Skipped: Test image not found at {image_path}") 57 58 image_input = face_recognition.load_image_file(image_path) 59 60 # Execute service function 61 result_blob = workerService.create_worker_embedding(image_input) 62 63 # Basic assertions 64 assert isinstance(result_blob, bytes) 65 assert len(result_blob) > 0 66 67 # Verify that the blob is a valid serialized NumPy array 68 buffer = io.BytesIO(result_blob) 69 embeddings = np.load(buffer, allow_pickle=True) 70 71 assert len(embeddings) > 0, "No faces detected in the test image." 72 # face_recognition library generates 128-dimensional vectors 73 assert len(embeddings[0]) == 128
Integration test for face embedding generation using a real image file.
Verifies that:
- The test image file exists.
- The service converts the image to a binary BLOB (bytes).
- The BLOB can be deserialized back into a valid NumPy array of shape (128,).
80@patch('backend.components.workers.workerService.create_worker_embedding') 81def test_create_worker_adds_to_db(mock_create_embedding, app_context): 82 """ 83 Tests if the `create_worker` function correctly persists a new worker to the database. 84 85 We mock the embedding generation to avoid the overhead of the ML library 86 and focus on database interactions. 87 """ 88 mock_create_embedding.return_value = b'test_blob_data' 89 name = "John Doe" 90 fake_image = MagicMock() # Mock the image object 91 expiration = datetime(2025, 12, 31) 92 93 with app_context.app_context(): 94 # Action: Create worker via service 95 worker = workerService.create_worker(name, fake_image, expiration) 96 97 assert worker.id is not None 98 assert worker.name == name 99 100 # Verification: Fetch from DB to ensure persistence 101 fetched_worker = db.session.get(Worker, worker.id) 102 assert fetched_worker is not None 103 assert fetched_worker.name == "John Doe" 104 assert fetched_worker.face_embedding == b'test_blob_data'
Tests if the create_worker function correctly persists a new worker to the database.
We mock the embedding generation to avoid the overhead of the ML library and focus on database interactions.
107def test_extend_worker_expiration_success(app_context): 108 """ 109 Tests the `extend_worker_expiration` functionality. 110 111 Verifies that the expiration date is updated in the database object. 112 """ 113 initial_date = datetime(2024, 1, 1) 114 new_date = datetime(2030, 1, 1) 115 116 with app_context.app_context(): 117 # 1. Setup: Create a worker with an initial date 118 worker = Worker( 119 name="Mark ToChange", 120 face_embedding=b'dummy_data', 121 expiration_date=initial_date, 122 secret="dummy_secret" 123 ) 124 db.session.add(worker) 125 db.session.commit() 126 127 # Retrieve the object (service requires an object, not ID) 128 worker_to_update = db.session.get(Worker, worker.id) 129 130 # 2. Action: Extend expiration 131 updated_worker = workerService.extend_worker_expiration(worker_to_update, new_date) 132 133 # 3. Verification 134 assert updated_worker.expiration_date == new_date 135 136 # Force session refresh to check DB state 137 db.session.expire_all() 138 fetched_worker = db.session.get(Worker, worker.id) 139 assert fetched_worker.expiration_date == new_date
Tests the extend_worker_expiration functionality.
Verifies that the expiration date is updated in the database object.
142def test_update_worker_name_success(app_context): 143 """ 144 Tests the `update_worker_name` functionality. 145 146 Verifies that the worker's name is successfully updated in the database. 147 """ 148 initial_name = "Jane Doe" 149 new_name = "Jane Smith" 150 151 with app_context.app_context(): 152 # Setup 153 worker = Worker( 154 name=initial_name, 155 face_embedding=b'dummy_data', 156 expiration_date=datetime(2025, 12, 12), 157 secret="dummy_secret" 158 ) 159 db.session.add(worker) 160 db.session.commit() 161 162 # Action 163 worker_to_update = db.session.get(Worker, worker.id) 164 workerService.update_worker_name(worker_to_update, new_name) 165 166 # Verification 167 db.session.expire_all() 168 fetched_worker = db.session.get(Worker, worker.id) 169 assert fetched_worker.name == new_name
Tests the update_worker_name functionality.
Verifies that the worker's name is successfully updated in the database.
172def test_update_worker_face_embedding_success(app_context): 173 """ 174 Tests the `update_worker_face_image` functionality. 175 176 Verifies that providing a new image triggers the embedding recalculation 177 and updates the database field. 178 """ 179 initial_name = "Test Face Update" 180 initial_face_data = b'old_dummy_embedding' 181 new_raw_image = b'new_raw_image_data_jpg' 182 mocked_embedding_result = b'new_calculated_embedding_123' 183 184 with app_context.app_context(): 185 # Setup 186 worker = Worker( 187 name=initial_name, 188 face_embedding=initial_face_data, 189 expiration_date=datetime(2025, 12, 12), 190 secret="dummy_secret" 191 ) 192 db.session.add(worker) 193 db.session.commit() 194 195 # Mock the internal embedding function to isolate logic 196 with patch('backend.components.workers.workerService.create_worker_embedding') as mock_embedding: 197 mock_embedding.return_value = mocked_embedding_result 198 199 # Action 200 worker_to_update = db.session.get(Worker, worker.id) 201 workerService.update_worker_face_image(worker_to_update, new_raw_image) 202 203 # Assert internal call 204 mock_embedding.assert_called_once_with(new_raw_image) 205 206 # Verification 207 db.session.expire_all() 208 fetched_worker = db.session.get(Worker, worker.id) 209 assert fetched_worker.face_embedding == mocked_embedding_result
Tests the update_worker_face_image functionality.
Verifies that providing a new image triggers the embedding recalculation and updates the database field.