From c277f48084a9d269a1f2c710189b4b1b9a3548a4 Mon Sep 17 00:00:00 2001 From: Asuka Minato Date: Wed, 15 Jul 2026 13:05:59 +0900 Subject: [PATCH] test: move email registration coverage to unit tests (#38928) Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> --- .../console/auth/test_email_register.py | 49 +++++----------- .../services/test_account_service.py | 58 +++++++++++-------- 2 files changed, 48 insertions(+), 59 deletions(-) rename api/tests/{test_containers_integration_tests => unit_tests}/controllers/console/auth/test_email_register.py (84%) diff --git a/api/tests/test_containers_integration_tests/controllers/console/auth/test_email_register.py b/api/tests/unit_tests/controllers/console/auth/test_email_register.py similarity index 84% rename from api/tests/test_containers_integration_tests/controllers/console/auth/test_email_register.py rename to api/tests/unit_tests/controllers/console/auth/test_email_register.py index d893e9e6efb..468d98ab377 100644 --- a/api/tests/test_containers_integration_tests/controllers/console/auth/test_email_register.py +++ b/api/tests/unit_tests/controllers/console/auth/test_email_register.py @@ -1,11 +1,9 @@ -"""Testcontainers integration tests for email register controller endpoints.""" +"""Unit tests for email register controller endpoints.""" from __future__ import annotations -from types import SimpleNamespace from unittest.mock import MagicMock, patch -import pytest from flask import Flask from controllers.console.auth.email_register import ( @@ -13,12 +11,7 @@ from controllers.console.auth.email_register import ( EmailRegisterResetApi, EmailRegisterSendEmailApi, ) -from services.account_service import AccountService - - -@pytest.fixture -def app(flask_app_with_containers: Flask): - return flask_app_with_containers +from services.feature_service import SystemFeatureModel class TestEmailRegisterSendEmailApi: @@ -41,10 +34,10 @@ class TestEmailRegisterSendEmailApi: mock_account = MagicMock() mock_get_account.return_value = mock_account - feature_flags = SimpleNamespace(enable_email_password_login=True, is_allow_register=True) + feature_flags = SystemFeatureModel(enable_email_password_login=True, is_allow_register=True) with ( - patch("controllers.console.auth.email_register.dify_config", SimpleNamespace(BILLING_ENABLED=True)), - patch("controllers.console.wraps.dify_config", SimpleNamespace(EDITION="CLOUD")), + patch("controllers.console.auth.email_register.dify_config.BILLING_ENABLED", True), + patch("controllers.console.wraps.dify_config.EDITION", "CLOUD"), patch("controllers.console.wraps.FeatureService.get_system_features", return_value=feature_flags), ): with app.test_request_context( @@ -82,9 +75,9 @@ class TestEmailRegisterCheckApi: mock_get_data.return_value = {"email": "User@Example.com", "code": "4321"} mock_generate_token.return_value = (None, "new-token") - feature_flags = SimpleNamespace(enable_email_password_login=True, is_allow_register=True) + feature_flags = SystemFeatureModel(enable_email_password_login=True, is_allow_register=True) with ( - patch("controllers.console.wraps.dify_config", SimpleNamespace(EDITION="CLOUD")), + patch("controllers.console.wraps.dify_config.EDITION", "CLOUD"), patch("controllers.console.wraps.FeatureService.get_system_features", return_value=feature_flags), ): with app.test_request_context( @@ -130,9 +123,9 @@ class TestEmailRegisterResetApi: mock_login.return_value = token_pair mock_get_account.return_value = None - feature_flags = SimpleNamespace(enable_email_password_login=True, is_allow_register=True) + feature_flags = SystemFeatureModel(enable_email_password_login=True, is_allow_register=True) with ( - patch("controllers.console.wraps.dify_config", SimpleNamespace(EDITION="CLOUD")), + patch("controllers.console.wraps.dify_config.EDITION", "CLOUD"), patch("controllers.console.wraps.FeatureService.get_system_features", return_value=feature_flags), ): with app.test_request_context( @@ -178,9 +171,9 @@ class TestEmailRegisterResetApi: mock_login.return_value = token_pair mock_get_account.return_value = None - feature_flags = SimpleNamespace(enable_email_password_login=True, is_allow_register=True) + feature_flags = SystemFeatureModel(enable_email_password_login=True, is_allow_register=True) with ( - patch("controllers.console.wraps.dify_config", SimpleNamespace(EDITION="CLOUD")), + patch("controllers.console.wraps.dify_config.EDITION", "CLOUD"), patch("controllers.console.wraps.FeatureService.get_system_features", return_value=feature_flags), ): with app.test_request_context( @@ -231,9 +224,9 @@ class TestEmailRegisterResetApi: mock_login.return_value = token_pair mock_get_account.return_value = None - feature_flags = SimpleNamespace(enable_email_password_login=True, is_allow_register=True) + feature_flags = SystemFeatureModel(enable_email_password_login=True, is_allow_register=True) with ( - patch("controllers.console.wraps.dify_config", SimpleNamespace(EDITION="CLOUD")), + patch("controllers.console.wraps.dify_config.EDITION", "CLOUD"), patch("controllers.console.wraps.FeatureService.get_system_features", return_value=feature_flags), ): with app.test_request_context( @@ -258,19 +251,3 @@ class TestEmailRegisterResetApi: mock_reset_login_rate.assert_called_once_with("invitee@example.com") mock_revoke_token.assert_called_once_with("token-123") mock_extract_ip.assert_called_once() - - -def test_get_account_by_email_with_case_fallback_falls_back_to_lowercase(): - """Test that case fallback tries lowercase when exact match fails.""" - mock_session = MagicMock() - first_result = MagicMock() - first_result.scalar_one_or_none.return_value = None - expected_account = MagicMock() - second_result = MagicMock() - second_result.scalar_one_or_none.return_value = expected_account - mock_session.execute.side_effect = [first_result, second_result] - - result = AccountService.get_account_by_email_with_case_fallback("Case@Test.com", session=mock_session) - - assert result is expected_account - assert mock_session.execute.call_count == 2 diff --git a/api/tests/unit_tests/services/test_account_service.py b/api/tests/unit_tests/services/test_account_service.py index a39bc138598..dcb11a4f83e 100644 --- a/api/tests/unit_tests/services/test_account_service.py +++ b/api/tests/unit_tests/services/test_account_service.py @@ -1,7 +1,6 @@ import json from collections.abc import Iterator from datetime import datetime, timedelta -from types import SimpleNamespace from unittest.mock import MagicMock, patch import pytest @@ -22,6 +21,7 @@ from models.account import ( from models.dataset import Dataset from models.model import App, DifySetup from services.account_service import AccountService, RegisterService, TenantService +from services.enterprise.rbac_service import MembersInRole, Paginated from services.errors.account import ( AccountAlreadyInTenantError, AccountLoginError, @@ -666,30 +666,32 @@ class TestTenantService: sqlite_session.add(tenant_account_join) return tenant_account_join - def test_iter_member_account_id_batches_uses_offset_limit(self): - class FakeScalarResult: - def __init__(self, items: list[str]) -> None: - self.items = items - - def all(self) -> list[str]: - return self.items - - offsets: list[int] = [] - - def scalars(stmt): - offsets.append(stmt._offset_clause.value) - if len(offsets) == 1: - return FakeScalarResult(["acct-1", "acct-2"]) - if len(offsets) == 2: - return FakeScalarResult(["acct-3"]) - return FakeScalarResult([]) + @pytest.mark.parametrize("sqlite_session", [(TenantAccountJoin,)], indirect=True) + def test_iter_member_account_id_batches_uses_offset_limit(self, sqlite_session: Session): + account_ids = [ + "00000000-0000-0000-0000-000000000011", + "00000000-0000-0000-0000-000000000012", + "00000000-0000-0000-0000-000000000013", + ] + for index, account_id in enumerate(account_ids, start=1): + membership = TenantAccountJoin( + tenant_id="00000000-0000-0000-0000-000000000001", + account_id=account_id, + role=TenantAccountRole.NORMAL, + ) + membership.id = f"00000000-0000-0000-0000-{index:012d}" + sqlite_session.add(membership) + sqlite_session.commit() batches = list( - TenantService.iter_member_account_id_batches("tenant-1", 2, session=SimpleNamespace(scalars=scalars)) + TenantService.iter_member_account_id_batches( + "00000000-0000-0000-0000-000000000001", + 2, + session=sqlite_session, + ) ) - assert batches == [["acct-1", "acct-2"], ["acct-3"]] - assert offsets == [0, 2, 4] + assert batches == [account_ids[:2], account_ids[2:]] # ==================== get_account_role_in_tenant Tests ==================== # Backs the auth pipeline's `load_workspace_role`: None => non-member @@ -1179,8 +1181,7 @@ class TestTenantService: ) def test_get_rbac_workspace_owner_account_id(self): - mock_roles = MagicMock() - mock_roles.data = [SimpleNamespace(account_id="owner-account")] + mock_roles = Paginated[MembersInRole](data=[MembersInRole(account_id="owner-account")]) mock_rbac_roles = MagicMock() mock_rbac_roles.members.return_value = mock_roles @@ -2645,3 +2646,14 @@ class TestSessionInjectedGetters: assert row[0] is tenant assert row[1] is join assert TenantService.find_workspace_for_account("user-123", other_tenant.id, session=sqlite_session) is None + + +@pytest.mark.parametrize("sqlite_session", [(Account,)], indirect=True) +def test_get_account_by_email_with_case_fallback_uses_lowercase(sqlite_session: Session) -> None: + account = Account(name="Case User", email="case@test.com") + sqlite_session.add(account) + sqlite_session.commit() + + result = AccountService.get_account_by_email_with_case_fallback("Case@Test.com", session=sqlite_session) + + assert result is account