From 141257aefb277140556dc7e88d891a52baa94898 Mon Sep 17 00:00:00 2001 From: Asuka Minato Date: Tue, 14 Jul 2026 23:09:28 +0900 Subject: [PATCH] test: move web forgot-password coverage to unit tests (#38943) --- .../web/test_web_forgot_password.py | 67 ++++++++++--------- 1 file changed, 35 insertions(+), 32 deletions(-) rename api/tests/{test_containers_integration_tests => unit_tests}/controllers/web/test_web_forgot_password.py (85%) diff --git a/api/tests/test_containers_integration_tests/controllers/web/test_web_forgot_password.py b/api/tests/unit_tests/controllers/web/test_web_forgot_password.py similarity index 85% rename from api/tests/test_containers_integration_tests/controllers/web/test_web_forgot_password.py rename to api/tests/unit_tests/controllers/web/test_web_forgot_password.py index cd754782df4..ffd51f81b0d 100644 --- a/api/tests/test_containers_integration_tests/controllers/web/test_web_forgot_password.py +++ b/api/tests/unit_tests/controllers/web/test_web_forgot_password.py @@ -1,9 +1,9 @@ -"""Testcontainers integration tests for controllers.web.forgot_password endpoints.""" +"""Unit tests for controllers.web.forgot_password endpoints.""" from __future__ import annotations import base64 -from types import SimpleNamespace +from collections.abc import Iterator from unittest.mock import ANY, MagicMock, patch import pytest @@ -14,25 +14,35 @@ from controllers.web.forgot_password import ( ForgotPasswordResetApi, ForgotPasswordSendEmailApi, ) +from models.account import Account +from models.engine import db +from services.feature_service import SystemFeatureModel + + +@pytest.fixture +def database_app() -> Iterator[Flask]: + app = Flask(__name__) + app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///:memory:" + db.init_app(app) + + with app.app_context(): + Account.__table__.create(db.engine) + yield app @pytest.fixture(autouse=True) def _patch_wraps(): - wraps_features = SimpleNamespace(enable_email_password_login=True) - dify_settings = SimpleNamespace(ENTERPRISE_ENABLED=True, EDITION="CLOUD") + wraps_features = SystemFeatureModel(enable_email_password_login=True) with ( patch("controllers.console.wraps.db") as mock_db, - patch("controllers.console.wraps.dify_config", dify_settings), + patch("controllers.console.wraps.dify_config.ENTERPRISE_ENABLED", True), + patch("controllers.console.wraps.dify_config.EDITION", "CLOUD"), patch("controllers.console.wraps.FeatureService.get_system_features", return_value=wraps_features), ): yield class TestForgotPasswordSendEmailApi: - @pytest.fixture - def app(self, flask_app_with_containers: Flask): - return flask_app_with_containers - @patch("controllers.web.forgot_password.AccountService.send_reset_password_email") @patch("controllers.web.forgot_password.AccountService.get_account_by_email_with_case_fallback") @patch("controllers.web.forgot_password.AccountService.is_email_send_ip_limit", return_value=False) @@ -64,10 +74,6 @@ class TestForgotPasswordSendEmailApi: class TestForgotPasswordCheckApi: - @pytest.fixture - def app(self, flask_app_with_containers: Flask): - return flask_app_with_containers - @patch("controllers.web.forgot_password.AccountService.reset_forgot_password_error_rate_limit") @patch("controllers.web.forgot_password.AccountService.generate_reset_password_token") @patch("controllers.web.forgot_password.AccountService.revoke_reset_password_token") @@ -142,30 +148,25 @@ class TestForgotPasswordCheckApi: class TestForgotPasswordResetApi: - @pytest.fixture - def app(self, flask_app_with_containers: Flask): - return flask_app_with_containers - @patch("controllers.web.forgot_password.ForgotPasswordResetApi._update_existing_account") @patch("controllers.web.forgot_password.AccountService.get_account_by_email_with_case_fallback") - @patch("controllers.web.forgot_password.db") @patch("controllers.web.forgot_password.AccountService.revoke_reset_password_token") @patch("controllers.web.forgot_password.AccountService.get_reset_password_data") def test_should_fetch_account_with_fallback( self, mock_get_reset_data, mock_revoke_token, - mock_db, mock_get_account, mock_update_account, - app: Flask, + database_app: Flask, ): mock_get_reset_data.return_value = {"phase": "reset", "email": "User@Example.com", "code": "1234"} - mock_account = MagicMock() - mock_get_account.return_value = mock_account - mock_db.session.merge.return_value = mock_account + account = Account(name="User", email="user@example.com") + db.session.add(account) + db.session.commit() + mock_get_account.return_value = account - with app.test_request_context( + with database_app.test_request_context( "/web/forgot-password/resets", method="POST", json={ @@ -183,7 +184,6 @@ class TestForgotPasswordResetApi: @patch("controllers.web.forgot_password.hash_password", return_value=b"hashed-value") @patch("controllers.web.forgot_password.secrets.token_bytes", return_value=b"0123456789abcdef") - @patch("controllers.web.forgot_password.db") @patch("controllers.web.forgot_password.AccountService.revoke_reset_password_token") @patch("controllers.web.forgot_password.AccountService.get_reset_password_data") @patch("controllers.web.forgot_password.AccountService.get_account_by_email_with_case_fallback") @@ -192,17 +192,17 @@ class TestForgotPasswordResetApi: mock_get_account, mock_get_reset_data, mock_revoke_token, - mock_db, mock_token_bytes, mock_hash_password, - app: Flask, + database_app: Flask, ): mock_get_reset_data.return_value = {"phase": "reset", "email": "user@example.com"} - account = MagicMock() + account = Account(name="User", email="user@example.com") + db.session.add(account) + db.session.commit() mock_get_account.return_value = account - mock_db.session.merge.return_value = account - with app.test_request_context( + with database_app.test_request_context( "/web/forgot-password/resets", method="POST", json={ @@ -218,7 +218,10 @@ class TestForgotPasswordResetApi: mock_revoke_token.assert_called_once_with("reset-token") mock_token_bytes.assert_called_once_with(16) mock_hash_password.assert_called_once_with("StrongPass123!", b"0123456789abcdef") + db.session.expire_all() + stored_account = db.session.get(Account, account.id) + assert stored_account is not None expected_password = base64.b64encode(b"hashed-value").decode() - assert account.password == expected_password + assert stored_account.password == expected_password expected_salt = base64.b64encode(b"0123456789abcdef").decode() - assert account.password_salt == expected_salt + assert stored_account.password_salt == expected_salt