Merge branch 'main' into feat/mcp-06-18

This commit is contained in:
Novice
2025-10-20 10:29:09 +08:00
542 changed files with 11548 additions and 7438 deletions

View File

@ -8,7 +8,7 @@ from werkzeug.exceptions import Unauthorized
from configs import dify_config
from controllers.console.error import AccountNotFound, NotAllowedCreateWorkspace
from models.account import AccountStatus, TenantAccountJoin
from models import AccountStatus, TenantAccountJoin
from services.account_service import AccountService, RegisterService, TenantService, TokenPair
from services.errors.account import (
AccountAlreadyInTenantError,
@ -470,7 +470,7 @@ class TestAccountService:
# Verify integration was created
from extensions.ext_database import db
from models.account import AccountIntegrate
from models import AccountIntegrate
integration = db.session.query(AccountIntegrate).filter_by(account_id=account.id, provider="new-google").first()
assert integration is not None
@ -505,7 +505,7 @@ class TestAccountService:
# Verify integration was updated
from extensions.ext_database import db
from models.account import AccountIntegrate
from models import AccountIntegrate
integration = (
db.session.query(AccountIntegrate).filter_by(account_id=account.id, provider="exists-google").first()
@ -2303,7 +2303,7 @@ class TestRegisterService:
# Verify account was created
from extensions.ext_database import db
from models.account import Account
from models import Account
from models.model import DifySetup
account = db.session.query(Account).filter_by(email=admin_email).first()
@ -2352,7 +2352,7 @@ class TestRegisterService:
# Verify no entities were created (rollback worked)
from extensions.ext_database import db
from models.account import Account, Tenant, TenantAccountJoin
from models import Account, Tenant, TenantAccountJoin
from models.model import DifySetup
account = db.session.query(Account).filter_by(email=admin_email).first()
@ -2446,7 +2446,7 @@ class TestRegisterService:
# Verify OAuth integration was created
from extensions.ext_database import db
from models.account import AccountIntegrate
from models import AccountIntegrate
integration = db.session.query(AccountIntegrate).filter_by(account_id=account.id, provider=provider).first()
assert integration is not None
@ -2472,7 +2472,7 @@ class TestRegisterService:
mock_external_service_dependencies["billing_service"].is_email_in_freeze.return_value = False
# Execute registration with pending status
from models.account import AccountStatus
from models import AccountStatus
account = RegisterService.register(
email=email,
@ -2661,7 +2661,7 @@ class TestRegisterService:
# Verify new account was created with pending status
from extensions.ext_database import db
from models.account import Account, TenantAccountJoin
from models import Account, TenantAccountJoin
new_account = db.session.query(Account).filter_by(email=new_member_email).first()
assert new_account is not None

View File

@ -5,7 +5,7 @@ import pytest
from faker import Faker
from core.plugin.impl.exc import PluginDaemonClientSideError
from models.account import Account
from models import Account
from models.model import AppModelConfig, Conversation, EndUser, Message, MessageAgentThought
from services.account_service import AccountService, TenantService
from services.agent_service import AgentService

View File

@ -4,7 +4,7 @@ import pytest
from faker import Faker
from werkzeug.exceptions import NotFound
from models.account import Account
from models import Account
from models.model import MessageAnnotation
from services.annotation_service import AppAnnotationService
from services.app_service import AppService

View File

@ -4,7 +4,7 @@ import pytest
from faker import Faker
from constants.model_template import default_app_templates
from models.account import Account
from models import Account
from models.model import App, Site
from services.account_service import AccountService, TenantService
from services.app_service import AppService

View File

@ -8,7 +8,7 @@ from sqlalchemy import Engine
from werkzeug.exceptions import NotFound
from configs import dify_config
from models.account import Account, Tenant
from models import Account, Tenant
from models.enums import CreatorUserRole
from models.model import EndUser, UploadFile
from services.errors.file import FileTooLargeError, UnsupportedFileTypeError

View File

@ -4,7 +4,7 @@ import pytest
from faker import Faker
from core.rag.index_processor.constant.built_in_field import BuiltInField
from models.account import Account, Tenant, TenantAccountJoin, TenantAccountRole
from models import Account, Tenant, TenantAccountJoin, TenantAccountRole
from models.dataset import Dataset, DatasetMetadata, DatasetMetadataBinding, Document
from services.entities.knowledge_entities.knowledge_entities import MetadataArgs
from services.metadata_service import MetadataService
@ -17,9 +17,7 @@ class TestMetadataService:
def mock_external_service_dependencies(self):
"""Mock setup for external service dependencies."""
with (
patch(
"services.metadata_service.current_user", create_autospec(Account, instance=True)
) as mock_current_user,
patch("libs.login.current_user", create_autospec(Account, instance=True)) as mock_current_user,
patch("services.metadata_service.redis_client") as mock_redis_client,
patch("services.dataset_service.DocumentService") as mock_document_service,
):

View File

@ -5,7 +5,7 @@ from faker import Faker
from core.entities.model_entities import ModelStatus
from core.model_runtime.entities.model_entities import FetchFrom, ModelType
from models.account import Account, Tenant, TenantAccountJoin, TenantAccountRole
from models import Account, Tenant, TenantAccountJoin, TenantAccountRole
from models.provider import Provider, ProviderModel, ProviderModelSetting, ProviderType
from services.model_provider_service import ModelProviderService

View File

@ -5,7 +5,7 @@ from faker import Faker
from sqlalchemy import select
from werkzeug.exceptions import NotFound
from models.account import Account, Tenant, TenantAccountJoin, TenantAccountRole
from models import Account, Tenant, TenantAccountJoin, TenantAccountRole
from models.dataset import Dataset
from models.model import App, Tag, TagBinding
from services.tag_service import TagService

View File

@ -5,7 +5,7 @@ from faker import Faker
from sqlalchemy import select
from core.app.entities.app_invoke_entities import InvokeFrom
from models.account import Account
from models import Account
from models.model import Conversation, EndUser
from models.web import PinnedConversation
from services.account_service import AccountService, TenantService

View File

@ -7,7 +7,7 @@ from faker import Faker
from werkzeug.exceptions import NotFound, Unauthorized
from libs.password import hash_password
from models.account import Account, AccountStatus, Tenant, TenantAccountJoin, TenantAccountRole
from models import Account, AccountStatus, Tenant, TenantAccountJoin, TenantAccountRole
from models.model import App, Site
from services.errors.account import AccountLoginError, AccountNotFoundError, AccountPasswordError
from services.webapp_auth_service import WebAppAuthService, WebAppAuthType
@ -863,13 +863,14 @@ class TestWebAppAuthService:
- Mock service integration
"""
# Arrange: Setup mock for enterprise service
mock_webapp_auth = type("MockWebAppAuth", (), {"access_mode": "sso_verified"})()
mock_external_service_dependencies["app_service"].get_app_id_by_code.return_value = "mock_app_id"
setting = type("MockWebAppAuth", (), {"access_mode": "sso_verified"})()
mock_external_service_dependencies[
"enterprise_service"
].WebAppAuth.get_app_access_mode_by_code.return_value = mock_webapp_auth
].WebAppAuth.get_app_access_mode_by_id.return_value = setting
# Act: Execute authentication type determination
result = WebAppAuthService.get_app_auth_type(app_code="mock_app_code")
result: WebAppAuthType = WebAppAuthService.get_app_auth_type(app_code="mock_app_code")
# Assert: Verify correct result
assert result == WebAppAuthType.EXTERNAL
@ -877,7 +878,7 @@ class TestWebAppAuthService:
# Verify mock service was called correctly
mock_external_service_dependencies[
"enterprise_service"
].WebAppAuth.get_app_access_mode_by_code.assert_called_once_with("mock_app_code")
].WebAppAuth.get_app_access_mode_by_id.assert_called_once_with(app_id="mock_app_id")
def test_get_app_auth_type_no_parameters(self, db_session_with_containers, mock_external_service_dependencies):
"""

View File

@ -3,7 +3,7 @@ from unittest.mock import patch
import pytest
from faker import Faker
from models.account import Account, Tenant, TenantAccountJoin, TenantAccountRole
from models import Account, Tenant, TenantAccountJoin, TenantAccountRole
from services.workspace_service import WorkspaceService

View File

@ -3,7 +3,7 @@ from unittest.mock import patch
import pytest
from faker import Faker
from models.account import Account, Tenant
from models import Account, Tenant
from models.tools import ApiToolProvider
from services.tools.api_tools_manage_service import ApiToolManageService

View File

@ -4,7 +4,7 @@ import pytest
from faker import Faker
from core.tools.entities.tool_entities import ToolProviderType
from models.account import Account, Tenant
from models import Account, Tenant
from models.tools import MCPToolProvider
from services.tools.mcp_tools_manage_service import UNCHANGED_SERVER_URL_PLACEHOLDER, MCPToolManageService

View File

@ -15,7 +15,7 @@ from core.app.app_config.entities import (
)
from core.model_runtime.entities.llm_entities import LLMMode
from core.prompt.utils.prompt_template_parser import PromptTemplateParser
from models.account import Account, Tenant
from models import Account, Tenant
from models.api_based_extension import APIBasedExtension
from models.model import App, AppMode, AppModelConfig
from models.workflow import Workflow

View File

@ -6,7 +6,7 @@ from faker import Faker
from core.rag.index_processor.constant.index_type import IndexType
from extensions.ext_database import db
from extensions.ext_redis import redis_client
from models.account import Account, Tenant, TenantAccountJoin, TenantAccountRole
from models import Account, Tenant, TenantAccountJoin, TenantAccountRole
from models.dataset import Dataset, DatasetAutoDisableLog, Document, DocumentSegment
from tasks.add_document_to_index_task import add_document_to_index_task

View File

@ -14,7 +14,7 @@ from faker import Faker
from extensions.ext_database import db
from libs.datetime_utils import naive_utc_now
from models.account import Account, Tenant, TenantAccountJoin, TenantAccountRole
from models import Account, Tenant, TenantAccountJoin, TenantAccountRole
from models.dataset import Dataset, Document, DocumentSegment
from models.model import UploadFile
from tasks.batch_clean_document_task import batch_clean_document_task

View File

@ -18,7 +18,7 @@ from unittest.mock import MagicMock, patch
import pytest
from faker import Faker
from models.account import Account, Tenant, TenantAccountJoin, TenantAccountRole
from models import Account, Tenant, TenantAccountJoin, TenantAccountRole
from models.dataset import Dataset, Document, DocumentSegment
from models.enums import CreatorUserRole
from models.model import UploadFile

View File

@ -17,7 +17,7 @@ from unittest.mock import MagicMock, patch
import pytest
from faker import Faker
from models.account import Account, Tenant, TenantAccountJoin, TenantAccountRole
from models import Account, Tenant, TenantAccountJoin, TenantAccountRole
from models.dataset import (
AppDatasetJoin,
Dataset,

View File

@ -13,7 +13,7 @@ import pytest
from faker import Faker
from extensions.ext_redis import redis_client
from models.account import Account, Tenant, TenantAccountJoin, TenantAccountRole
from models import Account, Tenant, TenantAccountJoin, TenantAccountRole
from models.dataset import Dataset, Document, DocumentSegment
from tasks.create_segment_to_index_task import create_segment_to_index_task

View File

@ -16,7 +16,7 @@ from faker import Faker
from extensions.ext_database import db
from extensions.ext_redis import redis_client
from models.account import Account, Tenant, TenantAccountJoin, TenantAccountRole
from models import Account, Tenant, TenantAccountJoin, TenantAccountRole
from models.dataset import Dataset, Document, DocumentSegment
from tasks.disable_segment_from_index_task import disable_segment_from_index_task

View File

@ -4,7 +4,7 @@ import pytest
from faker import Faker
from extensions.ext_database import db
from models.account import Account, Tenant, TenantAccountJoin, TenantAccountRole
from models import Account, Tenant, TenantAccountJoin, TenantAccountRole
from models.dataset import Dataset, Document
from tasks.document_indexing_task import document_indexing_task

View File

@ -0,0 +1,134 @@
"""
TestContainers-based integration tests for mail_register_task.py
This module provides integration tests for email registration tasks
using TestContainers to ensure real database and service interactions.
"""
from unittest.mock import MagicMock, patch
import pytest
from faker import Faker
from libs.email_i18n import EmailType
from tasks.mail_register_task import send_email_register_mail_task, send_email_register_mail_task_when_account_exist
class TestMailRegisterTask:
"""Integration tests for mail_register_task using testcontainers."""
@pytest.fixture
def mock_mail_dependencies(self):
"""Mock setup for mail service dependencies."""
with (
patch("tasks.mail_register_task.mail") as mock_mail,
patch("tasks.mail_register_task.get_email_i18n_service") as mock_get_email_service,
):
# Setup mock mail service
mock_mail.is_inited.return_value = True
# Setup mock email i18n service
mock_email_service = MagicMock()
mock_get_email_service.return_value = mock_email_service
yield {
"mail": mock_mail,
"email_service": mock_email_service,
"get_email_service": mock_get_email_service,
}
def test_send_email_register_mail_task_success(self, db_session_with_containers, mock_mail_dependencies):
"""Test successful email registration mail sending."""
fake = Faker()
language = "en-US"
to_email = fake.email()
code = fake.numerify("######")
send_email_register_mail_task(language=language, to=to_email, code=code)
mock_mail_dependencies["mail"].is_inited.assert_called_once()
mock_mail_dependencies["email_service"].send_email.assert_called_once_with(
email_type=EmailType.EMAIL_REGISTER,
language_code=language,
to=to_email,
template_context={
"to": to_email,
"code": code,
},
)
def test_send_email_register_mail_task_mail_not_initialized(
self, db_session_with_containers, mock_mail_dependencies
):
"""Test email registration task when mail service is not initialized."""
mock_mail_dependencies["mail"].is_inited.return_value = False
send_email_register_mail_task(language="en-US", to="test@example.com", code="123456")
mock_mail_dependencies["get_email_service"].assert_not_called()
mock_mail_dependencies["email_service"].send_email.assert_not_called()
def test_send_email_register_mail_task_exception_handling(self, db_session_with_containers, mock_mail_dependencies):
"""Test email registration task exception handling."""
mock_mail_dependencies["email_service"].send_email.side_effect = Exception("Email service error")
fake = Faker()
to_email = fake.email()
code = fake.numerify("######")
with patch("tasks.mail_register_task.logger") as mock_logger:
send_email_register_mail_task(language="en-US", to=to_email, code=code)
mock_logger.exception.assert_called_once_with("Send email register mail to %s failed", to_email)
def test_send_email_register_mail_task_when_account_exist_success(
self, db_session_with_containers, mock_mail_dependencies
):
"""Test successful email registration mail sending when account exists."""
fake = Faker()
language = "en-US"
to_email = fake.email()
account_name = fake.name()
with patch("tasks.mail_register_task.dify_config") as mock_config:
mock_config.CONSOLE_WEB_URL = "https://console.dify.ai"
send_email_register_mail_task_when_account_exist(language=language, to=to_email, account_name=account_name)
mock_mail_dependencies["email_service"].send_email.assert_called_once_with(
email_type=EmailType.EMAIL_REGISTER_WHEN_ACCOUNT_EXIST,
language_code=language,
to=to_email,
template_context={
"to": to_email,
"login_url": "https://console.dify.ai/signin",
"reset_password_url": "https://console.dify.ai/reset-password",
"account_name": account_name,
},
)
def test_send_email_register_mail_task_when_account_exist_mail_not_initialized(
self, db_session_with_containers, mock_mail_dependencies
):
"""Test account exist email task when mail service is not initialized."""
mock_mail_dependencies["mail"].is_inited.return_value = False
send_email_register_mail_task_when_account_exist(
language="en-US", to="test@example.com", account_name="Test User"
)
mock_mail_dependencies["get_email_service"].assert_not_called()
mock_mail_dependencies["email_service"].send_email.assert_not_called()
def test_send_email_register_mail_task_when_account_exist_exception_handling(
self, db_session_with_containers, mock_mail_dependencies
):
"""Test account exist email task exception handling."""
mock_mail_dependencies["email_service"].send_email.side_effect = Exception("Email service error")
fake = Faker()
to_email = fake.email()
account_name = fake.name()
with patch("tasks.mail_register_task.logger") as mock_logger:
send_email_register_mail_task_when_account_exist(language="en-US", to=to_email, account_name=account_name)
mock_logger.exception.assert_called_once_with("Send email register mail to %s failed", to_email)