1
1
from fastapi .testclient import TestClient
2
2
from httpx import Response
3
- from sqlmodel import Session
4
- from unittest .mock import patch
5
-
3
+ from sqlmodel import Session , select
4
+ from unittest .mock import patch , MagicMock
5
+ from tests . conftest import SetupError
6
6
from main import app
7
- from utils .models import User , Role
7
+ from utils .models import User , Role , Organization
8
8
from utils .images import InvalidImageError
9
+ import re
10
+ import pytest
9
11
10
12
# Mock data for consistent testing
11
13
MOCK_IMAGE_DATA = b"processed fake image data"
@@ -57,7 +59,9 @@ def test_update_profile_unauthorized(unauth_client: TestClient):
57
59
58
60
59
61
@patch ('routers.user.validate_and_process_image' )
60
- def test_update_profile_authorized (mock_validate , auth_client : TestClient , test_user : User , session : Session ):
62
+ def test_update_profile_authorized (
63
+ mock_validate : MagicMock , auth_client : TestClient , test_user : User , session : Session
64
+ ):
61
65
"""Test that authorized users can edit their profile"""
62
66
63
67
# Configure mock to return processed image data
@@ -157,7 +161,9 @@ def test_delete_account_success(auth_client: TestClient, test_user: User, sessio
157
161
158
162
159
163
@patch ('routers.user.validate_and_process_image' )
160
- def test_get_avatar_authorized (mock_validate , auth_client : TestClient , test_user : User ):
164
+ def test_get_avatar_authorized (
165
+ mock_validate : MagicMock , auth_client : TestClient , test_user : User
166
+ ):
161
167
"""Test getting user avatar"""
162
168
# Configure mock to return processed image data
163
169
mock_validate .return_value = (MOCK_IMAGE_DATA , MOCK_CONTENT_TYPE )
@@ -194,7 +200,9 @@ def test_get_avatar_unauthorized(unauth_client: TestClient):
194
200
195
201
# Add new test for invalid image
196
202
@patch ('routers.user.validate_and_process_image' )
197
- def test_update_profile_invalid_image (mock_validate , auth_client : TestClient ):
203
+ def test_update_profile_invalid_image (
204
+ mock_validate : MagicMock , auth_client : TestClient
205
+ ):
198
206
"""Test that invalid images are rejected"""
199
207
# Configure mock to raise InvalidImageError
200
208
mock_validate .side_effect = InvalidImageError ("Invalid test image" )
@@ -214,8 +222,13 @@ def test_update_profile_invalid_image(mock_validate, auth_client: TestClient):
214
222
215
223
# --- Multi-Organization Profile Tests ---
216
224
217
- def test_profile_displays_multiple_organizations (auth_client , test_user , session , test_organization , second_test_organization ):
225
+ def test_profile_displays_multiple_organizations (
226
+ auth_client : TestClient , test_user : User , session : Session , test_organization : Organization , second_test_organization : Organization
227
+ ):
218
228
"""Test that a user's profile page displays all organizations they belong to"""
229
+ if second_test_organization .id is None :
230
+ raise SetupError ("Second test organization ID is None" )
231
+
219
232
# Ensure test_user is part of both organizations
220
233
# First org should already be set up through the org_owner fixture
221
234
# Now add to second org
@@ -226,50 +239,47 @@ def test_profile_displays_multiple_organizations(auth_client, test_user, session
226
239
test_user .roles .append (member_role )
227
240
session .add (member_role )
228
241
session .commit ()
229
-
242
+
230
243
# Visit profile page
231
244
response = auth_client .get ("/user/profile" )
232
245
assert response .status_code == 200
233
-
246
+
234
247
# Check that both organizations are displayed
235
248
assert test_organization .name in response .text
236
249
assert second_test_organization .name in response .text
237
250
238
251
239
- def test_profile_displays_organization_list (auth_client , test_user , session , test_organization ):
252
+ def test_profile_displays_organization_list (
253
+ auth_client_owner : TestClient , session : Session , test_organization : Organization
254
+ ):
240
255
"""Test that the profile page shows organizations in a macro-rendered list"""
241
- response = auth_client .get ("/user/profile" )
256
+
257
+ response = auth_client_owner .get ("/user/profile" )
242
258
assert response .status_code == 200
243
259
244
- # Check that organizations are rendered in a list
245
- assert "Organizations" in response .text
246
- assert test_organization .name in response .text
247
- assert f"/organizations/{ test_organization .id } " in response .text
248
-
249
-
250
- def test_profile_organization_roles (auth_client , test_user , session , test_organization , second_test_organization ):
251
- """Test that the profile shows the user's roles in each organization"""
252
- # Add user to a second organization with a different role
253
- admin_role = Role (
254
- name = "Administrator" ,
255
- organization_id = second_test_organization .id
260
+ # Find the entire Organizations card section using regex
261
+ # This regex looks for the card div, the header with "Organizations" and the button,
262
+ # and captures everything until the next card's div or the end of the container
263
+ org_section_match = re .search (
264
+ r'(<div class="card mb-4">\s*<div class="card-header.*?">\s*Organizations\s*<button.*?</div>.*?<div class="card-body">.*?</div>\s*</div>)' ,
265
+ response .text ,
266
+ re .DOTALL # Allow . to match newline characters
256
267
)
257
- test_user .roles .append (admin_role )
258
- session .add (admin_role )
259
- session .commit ()
260
268
261
- # Visit profile page
262
- response = auth_client .get ("/user/profile" )
263
- assert response .status_code == 200
269
+ # Check that the organizations section was found
270
+ assert org_section_match , "Organizations card section not found in profile HTML"
264
271
265
- # Check that both organizations and roles are displayed
266
- assert test_organization .name in response .text
267
- assert second_test_organization .name in response .text
268
- assert "Owner" in response .text # From the first org
269
- assert "Administrator" in response .text # From the second org
272
+ # Extract the matched HTML for the organizations section
273
+ org_section_html = org_section_match .group (1 )
274
+
275
+ # Check that the organization name and link are rendered within this specific section
276
+ assert test_organization .name in org_section_html , f"Organization name '{ test_organization .name } ' not found within the organizations card section"
277
+ assert f"/organizations/{ test_organization .id } " in org_section_html , f"Organization link '/organizations/{ test_organization .id } ' not found within the organizations card section"
270
278
271
279
272
- def test_profile_no_organizations (auth_client , session , test_user ):
280
+ def test_profile_no_organizations (
281
+ auth_client : TestClient , test_user : User , session : Session
282
+ ):
273
283
"""Test profile display when user has no organizations"""
274
284
# Remove user from all orgs by clearing roles
275
285
test_user .roles = []
0 commit comments