@@ -22,6 +22,9 @@ class SupportAPIRequestMixin:
2222 UPDATE_USERNAME_URL = (
2323 f"{ settings ['EOX_CORE_API_BASE' ]} { reverse ('eox-support-api:eox-support-api:edxapp-replace-username' )} "
2424 )
25+ UPDATE_EMAIL_URL = (
26+ f"{ settings ['EOX_CORE_API_BASE' ]} { reverse ('eox-support-api:eox-support-api:edxapp-replace-email' )} "
27+ )
2528 OAUTH_APP_URL = (
2629 f"{ settings ['EOX_CORE_API_BASE' ]} { reverse ('eox-support-api:eox-support-api:edxapp-oauth-application' )} "
2730 )
@@ -53,6 +56,20 @@ def update_username(self, tenant: dict, params: dict | None = None, data: dict |
5356 """
5457 return make_request (tenant , "PATCH" , url = self .UPDATE_USERNAME_URL , params = params , data = data )
5558
59+ def update_email (self , tenant : dict , params : dict | None = None , data : dict | None = None ) -> requests .Response :
60+ """
61+ Update an edxapp user's email in a tenant.
62+
63+ Args:
64+ tenant (dict): The tenant data.
65+ params (dict, optional): The query parameters for the request.
66+ data (dict, optional): The body data for the request.
67+
68+ Returns:
69+ requests.Response: The response object.
70+ """
71+ return make_request (tenant , "PATCH" , url = self .UPDATE_EMAIL_URL , params = params , data = data )
72+
5673 def create_oauth_application (self , tenant : dict , data : dict | None = None ) -> requests .Response :
5774 """
5875 Create an oauth application in a tenant.
@@ -284,6 +301,153 @@ def test_update_username_in_tenant_missing_body(self) -> None:
284301 self .assertEqual (response .status_code , status .HTTP_400_BAD_REQUEST )
285302 self .assertEqual (response_data , {"new_username" : ["This field is required." ]})
286303
304+ @ddt .data ("username" , "email" )
305+ def test_update_email_in_tenant_success (self , query_param : str ) -> None :
306+ """
307+ Test update an edxapp user's email in a tenant.
308+
309+ Open edX definitions tested:
310+ - `get_edxapp_user`
311+ - `check_edxapp_account_conflicts`
312+ - `get_user_read_only_serializer`
313+
314+ Expected result:
315+ - The status code is 200.
316+ - The response indicates the email was updated successfully.
317+ - The user is found in the tenant with the new email.
318+ - The user cannot be found with the old email.
319+ """
320+ data = next (FAKE_USER_DATA )
321+ self .create_user (self .tenant_x , data )
322+ old_email = data ["email" ]
323+ new_email = f"new-email-{ query_param } @example.com"
324+
325+ response = self .update_email (self .tenant_x , {query_param : data [query_param ]}, {"new_email" : new_email })
326+ response_data = response .json ()
327+ get_response = self .get_user (self .tenant_x , {"email" : new_email })
328+ get_response_data = get_response .json ()
329+ old_email_response = self .get_user (self .tenant_x , {"email" : old_email })
330+
331+ self .assertEqual (response .status_code , status .HTTP_200_OK )
332+ self .assertEqual (response_data ["email" ], new_email )
333+ self .assertEqual (get_response .status_code , status .HTTP_200_OK )
334+ self .assertEqual (get_response_data ["email" ], new_email )
335+ self .assertEqual (old_email_response .status_code , status .HTTP_404_NOT_FOUND )
336+
337+ def test_update_email_in_tenant_conflict (self ) -> None :
338+ """
339+ Test update an edxapp user's email to an email that already exists.
340+
341+ Open edX definitions tested:
342+ - `get_edxapp_user`
343+ - `check_edxapp_account_conflicts`
344+
345+ Expected result:
346+ - The status code is 400.
347+ - The response indicates the email already exists.
348+ """
349+ data1 = next (FAKE_USER_DATA )
350+ data2 = next (FAKE_USER_DATA )
351+ self .create_user (self .tenant_x , data1 )
352+ self .create_user (self .tenant_x , data2 )
353+
354+ response = self .update_email (
355+ self .tenant_x ,
356+ {"username" : data1 ["username" ]},
357+ {"new_email" : data2 ["email" ]},
358+ )
359+ response_data = response .json ()
360+
361+ self .assertEqual (response .status_code , status .HTTP_400_BAD_REQUEST )
362+ self .assertEqual (
363+ response_data ["detail" ],
364+ ["An account already exists with the provided username or email." ],
365+ )
366+
367+ def test_update_email_in_tenant_not_found (self ) -> None :
368+ """
369+ Test update an edxapp user's email in a tenant that does not exist.
370+
371+ Open edX definitions tested:
372+ - `get_edxapp_user`
373+
374+ Expected result:
375+ - The status code is 404.
376+ - The response indicates the user was not found in the tenant.
377+ """
378+ response = self .update_email (
379+ self .tenant_x ,
380+ {"username" : "user-not-found" },
381+ {
"new_email" :
"[email protected] " },
382+ )
383+ response_data = response .json ()
384+
385+ self .assertEqual (response .status_code , status .HTTP_404_NOT_FOUND )
386+ self .assertEqual (
387+ response_data ["detail" ],
388+ f"No user found by {{'username': 'user-not-found'}} on site { self .tenant_x ['domain' ]} ." ,
389+ )
390+
391+ @ddt .data ("username" , "email" )
392+ def test_update_user_email_of_another_tenant (self , query_param : str ) -> None :
393+ """
394+ Test update an edxapp user's email of another tenant.
395+
396+ Open edX definitions tested:
397+ - `get_edxapp_user`
398+
399+ Expected result:
400+ - The status code is 404.
401+ - The response indicates the user was not found in the tenant.
402+ """
403+ data = next (FAKE_USER_DATA )
404+ self .create_user (self .tenant_x , data )
405+ new_email = f"new-email-{ query_param } @example.com"
406+
407+ response = self .update_email (
408+ self .tenant_y ,
409+ {query_param : data [query_param ]},
410+ {"new_email" : new_email },
411+ )
412+ response_data = response .json ()
413+
414+ self .assertEqual (response .status_code , status .HTTP_404_NOT_FOUND )
415+ self .assertEqual (
416+ response_data ["detail" ],
417+ f"No user found by {{'{ query_param } ': '{ data [query_param ]} '}} on site { self .tenant_y ['domain' ]} ." ,
418+ )
419+
420+ def test_update_email_in_tenant_missing_params (self ) -> None :
421+ """
422+ Test update an edxapp user's email in a tenant without providing the username or email.
423+
424+ Expected result:
425+ - The status code is 400.
426+ - The response indicates the username or email is required.
427+ """
428+ response = self .update_email (self .tenant_x )
429+ response_data = response .json ()
430+
431+ self .assertEqual (response .status_code , status .HTTP_400_BAD_REQUEST )
432+ self .assertEqual (response_data , ["Email or username needed" ])
433+
434+ def test_update_email_in_tenant_missing_body (self ) -> None :
435+ """
436+ Test update an edxapp user's email in a tenant without providing the new email.
437+
438+ Expected result:
439+ - The status code is 400.
440+ - The response indicates the new email is required.
441+ """
442+ data = next (FAKE_USER_DATA )
443+ self .create_user (self .tenant_x , data )
444+
445+ response = self .update_email (self .tenant_x , params = {"username" : data ["username" ]})
446+ response_data = response .json ()
447+
448+ self .assertEqual (response .status_code , status .HTTP_400_BAD_REQUEST )
449+ self .assertEqual (response_data , {"new_email" : ["This field is required." ]})
450+
287451
288452@ddt .ddt
289453class TestOauthApplicationAPIIntegration (SupportAPIRequestMixin , BaseIntegrationTest , UsersAPIRequestMixin ):
0 commit comments