From 3ce7245c5c81b261659d869b00b6764d421f5c4f Mon Sep 17 00:00:00 2001 From: hjlarry Date: Mon, 22 Dec 2025 14:42:31 +0800 Subject: [PATCH] fix unittests --- .../console/auth/test_password_reset.py | 66 ++++++++----------- .../console/test_workspace_account.py | 33 ++++++---- .../console/test_workspace_members.py | 22 ++++--- ...assword.py => test_web_forgot_password.py} | 0 .../web/{test_login.py => test_web_login.py} | 0 .../services/test_account_service.py | 9 +-- 6 files changed, 67 insertions(+), 63 deletions(-) rename api/tests/unit_tests/controllers/web/{test_forgot_password.py => test_web_forgot_password.py} (100%) rename api/tests/unit_tests/controllers/web/{test_login.py => test_web_login.py} (100%) diff --git a/api/tests/unit_tests/controllers/console/auth/test_password_reset.py b/api/tests/unit_tests/controllers/console/auth/test_password_reset.py index f584952a00..202bc24618 100644 --- a/api/tests/unit_tests/controllers/console/auth/test_password_reset.py +++ b/api/tests/unit_tests/controllers/console/auth/test_password_reset.py @@ -28,6 +28,22 @@ from controllers.console.auth.forgot_password import ( from controllers.console.error import AccountNotFound, EmailSendIpLimitError +@pytest.fixture(autouse=True) +def _mock_forgot_password_session(): + with patch("controllers.console.auth.forgot_password.Session") as mock_session_cls: + mock_session = MagicMock() + mock_session_cls.return_value.__enter__.return_value = mock_session + mock_session_cls.return_value.__exit__.return_value = None + yield mock_session + + +@pytest.fixture(autouse=True) +def _mock_forgot_password_db(): + with patch("controllers.console.auth.forgot_password.db") as mock_db: + mock_db.engine = MagicMock() + yield mock_db + + class TestForgotPasswordSendEmailApi: """Test cases for sending password reset emails.""" @@ -47,20 +63,16 @@ class TestForgotPasswordSendEmailApi: return account @patch("controllers.console.wraps.db") - @patch("controllers.console.auth.forgot_password.db") @patch("controllers.console.auth.forgot_password.AccountService.is_email_send_ip_limit") - @patch("controllers.console.auth.forgot_password.Session") - @patch("controllers.console.auth.forgot_password.select") + @patch("controllers.console.auth.forgot_password.AccountService.get_account_by_email_with_case_fallback") @patch("controllers.console.auth.forgot_password.AccountService.send_reset_password_email") @patch("controllers.console.auth.forgot_password.FeatureService.get_system_features") def test_send_reset_email_success( self, mock_get_features, mock_send_email, - mock_select, - mock_session, + mock_get_account, mock_is_ip_limit, - mock_forgot_db, mock_wraps_db, app, mock_account, @@ -75,11 +87,8 @@ class TestForgotPasswordSendEmailApi: """ # Arrange mock_wraps_db.session.query.return_value.first.return_value = MagicMock() - mock_forgot_db.engine = MagicMock() mock_is_ip_limit.return_value = False - mock_session_instance = MagicMock() - mock_session_instance.execute.return_value.scalar_one_or_none.return_value = mock_account - mock_session.return_value.__enter__.return_value = mock_session_instance + mock_get_account.return_value = mock_account mock_send_email.return_value = "reset_token_123" mock_get_features.return_value.is_allow_register = True @@ -125,20 +134,16 @@ class TestForgotPasswordSendEmailApi: ], ) @patch("controllers.console.wraps.db") - @patch("controllers.console.auth.forgot_password.db") @patch("controllers.console.auth.forgot_password.AccountService.is_email_send_ip_limit") - @patch("controllers.console.auth.forgot_password.Session") - @patch("controllers.console.auth.forgot_password.select") + @patch("controllers.console.auth.forgot_password.AccountService.get_account_by_email_with_case_fallback") @patch("controllers.console.auth.forgot_password.AccountService.send_reset_password_email") @patch("controllers.console.auth.forgot_password.FeatureService.get_system_features") def test_send_reset_email_language_handling( self, mock_get_features, mock_send_email, - mock_select, - mock_session, + mock_get_account, mock_is_ip_limit, - mock_forgot_db, mock_wraps_db, app, mock_account, @@ -154,11 +159,8 @@ class TestForgotPasswordSendEmailApi: """ # Arrange mock_wraps_db.session.query.return_value.first.return_value = MagicMock() - mock_forgot_db.engine = MagicMock() mock_is_ip_limit.return_value = False - mock_session_instance = MagicMock() - mock_session_instance.execute.return_value.scalar_one_or_none.return_value = mock_account - mock_session.return_value.__enter__.return_value = mock_session_instance + mock_get_account.return_value = mock_account mock_send_email.return_value = "token" mock_get_features.return_value.is_allow_register = True @@ -355,20 +357,16 @@ class TestForgotPasswordResetApi: return account @patch("controllers.console.wraps.db") - @patch("controllers.console.auth.forgot_password.db") @patch("controllers.console.auth.forgot_password.AccountService.get_reset_password_data") @patch("controllers.console.auth.forgot_password.AccountService.revoke_reset_password_token") - @patch("controllers.console.auth.forgot_password.Session") - @patch("controllers.console.auth.forgot_password.select") + @patch("controllers.console.auth.forgot_password.AccountService.get_account_by_email_with_case_fallback") @patch("controllers.console.auth.forgot_password.TenantService.get_join_tenants") def test_reset_password_success( self, mock_get_tenants, - mock_select, - mock_session, + mock_get_account, mock_revoke_token, mock_get_data, - mock_forgot_db, mock_wraps_db, app, mock_account, @@ -383,11 +381,8 @@ class TestForgotPasswordResetApi: """ # Arrange mock_wraps_db.session.query.return_value.first.return_value = MagicMock() - mock_forgot_db.engine = MagicMock() mock_get_data.return_value = {"email": "test@example.com", "phase": "reset"} - mock_session_instance = MagicMock() - mock_session_instance.execute.return_value.scalar_one_or_none.return_value = mock_account - mock_session.return_value.__enter__.return_value = mock_session_instance + mock_get_account.return_value = mock_account mock_get_tenants.return_value = [MagicMock()] # Act @@ -475,13 +470,11 @@ class TestForgotPasswordResetApi: api.post() @patch("controllers.console.wraps.db") - @patch("controllers.console.auth.forgot_password.db") @patch("controllers.console.auth.forgot_password.AccountService.get_reset_password_data") @patch("controllers.console.auth.forgot_password.AccountService.revoke_reset_password_token") - @patch("controllers.console.auth.forgot_password.Session") - @patch("controllers.console.auth.forgot_password.select") + @patch("controllers.console.auth.forgot_password.AccountService.get_account_by_email_with_case_fallback") def test_reset_password_account_not_found( - self, mock_select, mock_session, mock_revoke_token, mock_get_data, mock_forgot_db, mock_wraps_db, app + self, mock_get_account, mock_revoke_token, mock_get_data, mock_wraps_db, app ): """ Test password reset for non-existent account. @@ -491,11 +484,8 @@ class TestForgotPasswordResetApi: """ # Arrange mock_wraps_db.session.query.return_value.first.return_value = MagicMock() - mock_forgot_db.engine = MagicMock() mock_get_data.return_value = {"email": "nonexistent@example.com", "phase": "reset"} - mock_session_instance = MagicMock() - mock_session_instance.execute.return_value.scalar_one_or_none.return_value = None - mock_session.return_value.__enter__.return_value = mock_session_instance + mock_get_account.return_value = None # Act & Assert with app.test_request_context( diff --git a/api/tests/unit_tests/controllers/console/test_workspace_account.py b/api/tests/unit_tests/controllers/console/test_workspace_account.py index 3cae3be52f..9afc1c4166 100644 --- a/api/tests/unit_tests/controllers/console/test_workspace_account.py +++ b/api/tests/unit_tests/controllers/console/test_workspace_account.py @@ -19,6 +19,7 @@ from services.account_service import AccountService def app(): app = Flask(__name__) app.config["TESTING"] = True + app.config["RESTX_MASK_HEADER"] = "X-Fields" app.login_manager = SimpleNamespace(_load_user=lambda: None) return app @@ -27,14 +28,21 @@ def _mock_wraps_db(mock_db): mock_db.session.query.return_value.first.return_value = MagicMock() -def _build_account(email: str, account_id: str = "acc") -> Account: +def _build_account(email: str, account_id: str = "acc", tenant: object | None = None) -> Account: + tenant_obj = tenant if tenant is not None else SimpleNamespace(id="tenant-id") account = Account(name=account_id, email=email) account.email = email account.id = account_id account.status = "active" + account._current_tenant = tenant_obj return account +def _set_logged_in_user(account: Account): + g._login_user = account + g._current_tenant = account.current_tenant + + class TestChangeEmailSend: @patch("controllers.console.wraps.db") @patch("controllers.console.workspace.account.current_account_with_tenant") @@ -68,7 +76,7 @@ class TestChangeEmailSend: method="POST", json={"email": "New@Example.com", "language": "en-US", "phase": "new_email", "token": "token-123"}, ): - g._login_user = SimpleNamespace(is_authenticated=True, id="tester") + _set_logged_in_user(_build_account("tester@example.com", "tester")) response = ChangeEmailSendEmailApi().post() assert response == {"result": "success", "data": "token-abc"} @@ -122,7 +130,7 @@ class TestChangeEmailValidity: method="POST", json={"email": "User@Example.com", "code": "1234", "token": "token-123"}, ): - g._login_user = SimpleNamespace(is_authenticated=True, id="tester") + _set_logged_in_user(_build_account("tester@example.com", "tester")) response = ChangeEmailCheckApi().post() assert response == {"is_valid": True, "email": "user@example.com", "token": "new-token"} @@ -168,22 +176,23 @@ class TestChangeEmailReset: mock_is_freeze.return_value = False mock_check_unique.return_value = True mock_get_data.return_value = {"old_email": "OLD@example.com"} - mock_update_account.return_value = MagicMock() + mock_account_after_update = _build_account("new@example.com", "acc3-updated") + mock_update_account.return_value = mock_account_after_update with app.test_request_context( "/account/change-email/reset", method="POST", json={"new_email": "New@Example.com", "token": "token-123"}, ): - g._login_user = SimpleNamespace(is_authenticated=True, id="tester") + _set_logged_in_user(_build_account("tester@example.com", "tester")) ChangeEmailResetApi().post() - mock_is_freeze.assert_called_once_with("new@example.com") - mock_check_unique.assert_called_once_with("new@example.com") - mock_revoke_token.assert_called_once_with("token-123") - mock_update_account.assert_called_once_with(current_user, email="new@example.com") - mock_send_notify.assert_called_once_with(email="new@example.com") - mock_csrf.assert_called_once() + mock_is_freeze.assert_called_once_with("new@example.com") + mock_check_unique.assert_called_once_with("new@example.com") + mock_revoke_token.assert_called_once_with("token-123") + mock_update_account.assert_called_once_with(current_user, email="new@example.com") + mock_send_notify.assert_called_once_with(email="new@example.com") + mock_csrf.assert_called_once() class TestAccountDeletionFeedback: @@ -199,7 +208,7 @@ class TestAccountDeletionFeedback: response = AccountDeleteUpdateFeedbackApi().post() assert response == {"result": "success"} - mock_update.assert_called_once_with("user@example.com", "test") + mock_update.assert_called_once_with("User@Example.com", "test") class TestCheckEmailUnique: diff --git a/api/tests/unit_tests/controllers/console/test_workspace_members.py b/api/tests/unit_tests/controllers/console/test_workspace_members.py index 58b0f92fb3..368892b922 100644 --- a/api/tests/unit_tests/controllers/console/test_workspace_members.py +++ b/api/tests/unit_tests/controllers/console/test_workspace_members.py @@ -5,7 +5,7 @@ import pytest from flask import Flask, g from controllers.console.workspace.members import MemberInviteEmailApi -from models.account import TenantAccountRole +from models.account import Account, TenantAccountRole @pytest.fixture @@ -63,16 +63,20 @@ class TestMemberInviteEmailApi: method="POST", json={"emails": ["User@Example.com"], "role": TenantAccountRole.EDITOR.value, "language": "en-US"}, ): - g._login_user = SimpleNamespace(is_authenticated=True, id="tester") + account = Account(name="tester", email="tester@example.com") + account._current_tenant = tenant + g._login_user = account + g._current_tenant = tenant response, status_code = MemberInviteEmailApi().post() assert status_code == 201 assert response["invitation_results"][0]["email"] == "user@example.com" - mock_invite_member.assert_called_once_with( - tenant, - "User@Example.com", - "en-US", - role=TenantAccountRole.EDITOR, - inviter=inviter, - ) + + assert mock_invite_member.call_count == 1 + call_args = mock_invite_member.call_args + assert call_args.kwargs["tenant"] == tenant + assert call_args.kwargs["email"] == "User@Example.com" + assert call_args.kwargs["language"] == "en-US" + assert call_args.kwargs["role"] == TenantAccountRole.EDITOR + assert call_args.kwargs["inviter"] == inviter mock_csrf.assert_called_once() diff --git a/api/tests/unit_tests/controllers/web/test_forgot_password.py b/api/tests/unit_tests/controllers/web/test_web_forgot_password.py similarity index 100% rename from api/tests/unit_tests/controllers/web/test_forgot_password.py rename to api/tests/unit_tests/controllers/web/test_web_forgot_password.py diff --git a/api/tests/unit_tests/controllers/web/test_login.py b/api/tests/unit_tests/controllers/web/test_web_login.py similarity index 100% rename from api/tests/unit_tests/controllers/web/test_login.py rename to api/tests/unit_tests/controllers/web/test_web_login.py diff --git a/api/tests/unit_tests/services/test_account_service.py b/api/tests/unit_tests/services/test_account_service.py index 011fc02669..f85bd0d985 100644 --- a/api/tests/unit_tests/services/test_account_service.py +++ b/api/tests/unit_tests/services/test_account_service.py @@ -5,7 +5,7 @@ from unittest.mock import MagicMock, patch import pytest from configs import dify_config -from models.account import Account +from models.account import Account, AccountStatus from services.account_service import AccountService, RegisterService, TenantService from services.errors.account import ( AccountAlreadyInTenantError, @@ -1210,9 +1210,9 @@ class TestRegisterService: with patch("services.account_service.RegisterService.register") as mock_register: mock_register.return_value = mock_new_account with ( - patch("services.account_service.TenantService.check_member_permission"), - patch("services.account_service.TenantService.create_tenant_member"), - patch("services.account_service.TenantService.switch_tenant"), + patch("services.account_service.TenantService.check_member_permission") as mock_check_permission, + patch("services.account_service.TenantService.create_tenant_member") as mock_create_member, + patch("services.account_service.TenantService.switch_tenant") as mock_switch_tenant, patch("services.account_service.RegisterService.generate_invite_token") as mock_generate_token, ): mock_generate_token.return_value = "invite-token-abc" @@ -1233,6 +1233,7 @@ class TestRegisterService: is_setup=True, ) mock_lookup.assert_called_once_with(mixed_email, session=mock_session) + mock_check_permission.assert_called_once_with(mock_tenant, mock_inviter, None, "add") mock_create_member.assert_called_once_with(mock_tenant, mock_new_account, "normal") mock_switch_tenant.assert_called_once_with(mock_new_account, mock_tenant.id) mock_generate_token.assert_called_once_with(mock_tenant, mock_new_account)