@@ -1372,6 +1372,130 @@ def test_update_account_settings_rollback(self, mock_email_change):
13721372 assert 'm' == data ['gender' ]
13731373
13741374
1375+ @skip_unless_lms
1376+ class TestAccountsAPIExtendedProfile (UserAPITestCase ):
1377+ """
1378+ Tests for extended profile validation in the Accounts API
1379+ """
1380+
1381+ def setUp (self ):
1382+ super ().setUp ()
1383+ self .url = reverse ("accounts_api" , kwargs = {"username" : self .user .username })
1384+ self .client .login (username = self .user .username , password = TEST_PASSWORD )
1385+
1386+ @mock .patch ("openedx.core.djangoapps.user_api.accounts.views.validate_and_get_extended_profile_form" )
1387+ def test_patch_account_with_valid_extended_profile (self , mock_validate_form : mock .Mock ):
1388+ """
1389+ Test that PATCH with valid extended_profile data succeeds
1390+ """
1391+ mock_form = mock .Mock ()
1392+ mock_validate_form .return_value = (mock_form , {})
1393+ extended_profile_data = [
1394+ {"field_name" : "department" , "field_value" : "Engineering" },
1395+ {"field_name" : "title" , "field_value" : "Software Engineer" },
1396+ ]
1397+ json_data = {"extended_profile" : extended_profile_data , "bio" : "Test bio" }
1398+
1399+ response = self .client .patch (self .url , data = json .dumps (json_data ), content_type = "application/merge-patch+json" )
1400+
1401+ self .assertEqual (response .status_code , 200 )
1402+ mock_validate_form .assert_called_once_with (extended_profile_data , self .user )
1403+
1404+ @mock .patch ("openedx.core.djangoapps.user_api.accounts.views.validate_and_get_extended_profile_form" )
1405+ def test_patch_account_with_invalid_extended_profile (self , mock_validate_form : mock .Mock ):
1406+ """
1407+ Test that PATCH with invalid extended_profile data returns 400
1408+ """
1409+ field_errors = {
1410+ "department" : {"developer_message" : "This field is required" , "user_message" : "This field is required" }
1411+ }
1412+ mock_validate_form .return_value = (None , field_errors )
1413+ extended_profile_data = [{"field_name" : "department" , "field_value" : "" }]
1414+ json_data = {"extended_profile" : extended_profile_data }
1415+
1416+ response = self .client .patch (self .url , data = json .dumps (json_data ), content_type = "application/merge-patch+json" )
1417+
1418+ self .assertEqual (response .status_code , 400 )
1419+ self .assertIn ("field_errors" , response .data )
1420+ self .assertIn ("department" , response .data ["field_errors" ])
1421+ mock_validate_form .assert_called_once_with (extended_profile_data , self .user )
1422+
1423+ def test_patch_account_without_extended_profile (self : UserAPITestCase ):
1424+ """
1425+ Test that PATCH without extended_profile data works normally
1426+ """
1427+ json_data = {"bio" : "Test bio without extended profile" }
1428+
1429+ response = self .client .patch (self .url , data = json .dumps (json_data ), content_type = "application/merge-patch+json" )
1430+
1431+ self .assertEqual (response .status_code , 200 )
1432+ self .assertEqual (response .data ["bio" ], "Test bio without extended profile" )
1433+
1434+ @mock .patch ("openedx.core.djangoapps.user_api.accounts.views.validate_and_get_extended_profile_form" )
1435+ def test_patch_account_extended_profile_with_empty_list (self , mock_validate_form : mock .Mock ):
1436+ """
1437+ Test that PATCH with empty extended_profile list works
1438+ """
1439+ mock_validate_form .return_value = (None , {})
1440+ json_data = {"extended_profile" : [], "bio" : "Test bio" }
1441+
1442+ response = self .client .patch (self .url , data = json .dumps (json_data ), content_type = "application/merge-patch+json" )
1443+
1444+ self .assertEqual (response .status_code , 200 )
1445+ mock_validate_form .assert_called_once_with ([], self .user )
1446+
1447+ @mock .patch ("openedx.core.djangoapps.user_api.accounts.views.validate_and_get_extended_profile_form" )
1448+ def test_patch_account_extended_profile_form_exception (self , mock_validate_form : mock .Mock ):
1449+ """
1450+ Test that exceptions in form validation return appropriate errors
1451+ """
1452+ field_errors = {
1453+ "extended_profile" : {"developer_message" : "Unexpected error" , "user_message" : "An error occurred" }
1454+ }
1455+ mock_validate_form .return_value = (None , field_errors )
1456+ extended_profile_data = [{"field_name" : "department" , "field_value" : "Engineering" }]
1457+ json_data = {"extended_profile" : extended_profile_data }
1458+
1459+ response = self .client .patch (self .url , data = json .dumps (json_data ), content_type = "application/merge-patch+json" )
1460+
1461+ self .assertEqual (response .status_code , 400 )
1462+ self .assertIn ("field_errors" , response .data )
1463+
1464+ @mock .patch ("openedx.core.djangoapps.user_api.accounts.views.validate_and_get_extended_profile_form" )
1465+ def test_patch_account_extended_profile_multiple_fields (self , mock_validate_form : mock .Mock ):
1466+ """
1467+ Test PATCH with multiple extended_profile fields
1468+ """
1469+ mock_form = mock .Mock ()
1470+ mock_validate_form .return_value = (mock_form , {})
1471+ extended_profile_data = [
1472+ {"field_name" : "department" , "field_value" : "Engineering" },
1473+ {"field_name" : "title" , "field_value" : "Software Engineer" },
1474+ {"field_name" : "company" , "field_value" : "EdX" },
1475+ {"field_name" : "location" , "field_value" : "Remote" },
1476+ ]
1477+ json_data = {"extended_profile" : extended_profile_data }
1478+
1479+ response = self .client .patch (self .url , data = json .dumps (json_data ), content_type = "application/merge-patch+json" )
1480+
1481+ self .assertEqual (response .status_code , 200 )
1482+ mock_validate_form .assert_called_once_with (extended_profile_data , self .user )
1483+
1484+ def test_patch_account_extended_profile_unauthorized (self ):
1485+ """
1486+ Test that unauthorized users cannot update extended_profile
1487+ """
1488+ self .different_client .login (username = self .different_user .username , password = TEST_PASSWORD )
1489+ extended_profile_data = [{"field_name" : "department" , "field_value" : "Engineering" }]
1490+ json_data = {"extended_profile" : extended_profile_data }
1491+
1492+ response = self .different_client .patch (
1493+ self .url , data = json .dumps (json_data ), content_type = "application/merge-patch+json"
1494+ )
1495+
1496+ self .assertIn (response .status_code , [403 , 404 ])
1497+
1498+
13751499@ddt .ddt
13761500class NameChangeViewTests (UserAPITestCase ):
13771501 """ NameChangeView tests """
0 commit comments