33import json
44from base64 import b64encode
55from unittest .mock import MagicMock
6+ from urllib .parse import urljoin
67
78import pytest
8- from django .conf import settings
99from django .test import RequestFactory
1010from django .urls import reverse
1111
@@ -28,10 +28,18 @@ def test_custom_login(mocker, next_url, allowed):
2828 assert get_redirect_url (mock_request ) == (next_url if allowed else "/app" )
2929
3030
31- @pytest .mark .parametrize ("has_apisix_header" , [True , False ])
32- @pytest .mark .parametrize ("next_url" , ["/search" , None ])
33- def test_logout (mocker , next_url , client , user , has_apisix_header ):
31+ @pytest .mark .parametrize (
32+ "test_params" ,
33+ [
34+ (True , "/search" ),
35+ (True , None ),
36+ (False , "/search" ),
37+ (False , None ),
38+ ],
39+ )
40+ def test_logout (mocker , client , user , test_params , settings ):
3441 """User should be properly redirected and logged out"""
42+ has_apisix_header , next_url = test_params
3543 header_str = b64encode (
3644 json .dumps (
3745 {
@@ -55,10 +63,10 @@ def test_logout(mocker, next_url, client, user, has_apisix_header):
5563 mock_logout .assert_called_once ()
5664
5765
58- @pytest .mark .parametrize ("is_authenticated" , [True ])
59- @pytest .mark .parametrize ("has_next" , [False ])
60- def test_next_logout (mocker , client , user , is_authenticated , has_next ):
66+ @pytest .mark .parametrize ("test_params" , [(True , False )])
67+ def test_next_logout (mocker , client , user , test_params , settings ):
6168 """Test logout redirect cache assignment"""
69+ is_authenticated , has_next = test_params
6270 next_url = "https://ocw.mit.edu"
6371 mock_request = mocker .MagicMock (
6472 GET = {"next" : next_url if has_next else None },
@@ -211,3 +219,60 @@ def test_custom_login_view_first_time_login_sets_has_logged_in(mocker):
211219
212220 # Verify redirect was called with the correct URL
213221 mock_redirect .assert_called_once_with ("/dashboard" )
222+
223+
224+ @pytest .mark .parametrize (
225+ "test_case" ,
226+ [
227+ (
228+ (False , False ),
229+ "/dashboard/organization/test-organization" ,
230+ ), # First-time login → org dashboard
231+ (
232+ (False , True ),
233+ "/dashboard/organization/test-organization" ,
234+ ), # First-time login → org dashboard
235+ ((True , False ), "/app" ), # Subsequent login → normal app (not onboarding!)
236+ ((True , True ), "/app" ), # Subsequent login → normal app
237+ ],
238+ )
239+ def test_login_org_user_redirect (mocker , client , user , test_case , settings ):
240+ """Test organization user redirect behavior - org users skip onboarding regardless of onboarding status"""
241+ # Unpack test case
242+ profile_state , expected_url = test_case
243+ has_logged_in , completed_onboarding = profile_state
244+
245+ # Set up user profile based on test scenario
246+ user .profile .has_logged_in = has_logged_in
247+ user .profile .completed_onboarding = completed_onboarding
248+ user .profile .save ()
249+
250+ header_str = b64encode (
251+ json .dumps (
252+ {
253+ "preferred_username" : user .username ,
254+ "email" : user .email ,
255+ "sub" : user .global_id ,
256+ "organization" : {
257+ "Test Organization" : {
258+ "role" : "member" ,
259+ "id" : "org-123" ,
260+ }
261+ },
262+ }
263+ ).encode ()
264+ )
265+ client .force_login (user )
266+ response = client .get (
267+ "/login/" ,
268+ follow = False ,
269+ HTTP_X_USERINFO = header_str ,
270+ )
271+ assert response .status_code == 302
272+ # Handle environment differences - in some envs it returns full URL, in others just path
273+ expected_full_url = urljoin (settings .APP_BASE_URL , expected_url )
274+ assert response .url in [expected_url , expected_full_url ]
275+
276+ # Verify that org users are never sent to onboarding
277+ # (onboarding URL would contain settings.MITOL_NEW_USER_LOGIN_URL)
278+ assert settings .MITOL_NEW_USER_LOGIN_URL not in response .url
0 commit comments