mirror of
https://github.com/langgenius/dify.git
synced 2026-05-03 08:58:09 +08:00
Merge branch 'release/e-1.12.1' into 1.12.1-otel-ee
This commit is contained in:
@ -0,0 +1,141 @@
|
||||
"""Unit tests for enterprise service integrations.
|
||||
|
||||
This module covers the enterprise-only default workspace auto-join behavior:
|
||||
- Enterprise mode disabled: no external calls
|
||||
- Successful join / skipped join: no errors
|
||||
- Failures (network/invalid response/invalid UUID): soft-fail wrapper must not raise
|
||||
"""
|
||||
|
||||
from unittest.mock import patch
|
||||
|
||||
import pytest
|
||||
|
||||
from services.enterprise.enterprise_service import (
|
||||
DefaultWorkspaceJoinResult,
|
||||
EnterpriseService,
|
||||
try_join_default_workspace,
|
||||
)
|
||||
|
||||
|
||||
class TestJoinDefaultWorkspace:
|
||||
def test_join_default_workspace_success(self):
|
||||
account_id = "11111111-1111-1111-1111-111111111111"
|
||||
response = {"workspace_id": "22222222-2222-2222-2222-222222222222", "joined": True, "message": "ok"}
|
||||
|
||||
with patch("services.enterprise.enterprise_service.EnterpriseRequest.send_request") as mock_send_request:
|
||||
mock_send_request.return_value = response
|
||||
|
||||
result = EnterpriseService.join_default_workspace(account_id=account_id)
|
||||
|
||||
assert isinstance(result, DefaultWorkspaceJoinResult)
|
||||
assert result.workspace_id == response["workspace_id"]
|
||||
assert result.joined is True
|
||||
assert result.message == "ok"
|
||||
|
||||
mock_send_request.assert_called_once_with(
|
||||
"POST",
|
||||
"/default-workspace/members",
|
||||
json={"account_id": account_id},
|
||||
timeout=1.0,
|
||||
raise_for_status=True,
|
||||
)
|
||||
|
||||
def test_join_default_workspace_invalid_response_format_raises(self):
|
||||
account_id = "11111111-1111-1111-1111-111111111111"
|
||||
|
||||
with patch("services.enterprise.enterprise_service.EnterpriseRequest.send_request") as mock_send_request:
|
||||
mock_send_request.return_value = "not-a-dict"
|
||||
|
||||
with pytest.raises(ValueError, match="Invalid response format"):
|
||||
EnterpriseService.join_default_workspace(account_id=account_id)
|
||||
|
||||
def test_join_default_workspace_invalid_account_id_raises(self):
|
||||
with pytest.raises(ValueError):
|
||||
EnterpriseService.join_default_workspace(account_id="not-a-uuid")
|
||||
|
||||
def test_join_default_workspace_missing_required_fields_raises(self):
|
||||
account_id = "11111111-1111-1111-1111-111111111111"
|
||||
response = {"workspace_id": "", "message": "ok"} # missing "joined"
|
||||
|
||||
with patch("services.enterprise.enterprise_service.EnterpriseRequest.send_request") as mock_send_request:
|
||||
mock_send_request.return_value = response
|
||||
|
||||
with pytest.raises(ValueError, match="Invalid response payload"):
|
||||
EnterpriseService.join_default_workspace(account_id=account_id)
|
||||
|
||||
def test_join_default_workspace_joined_without_workspace_id_raises(self):
|
||||
with pytest.raises(ValueError, match="workspace_id must be non-empty when joined is True"):
|
||||
DefaultWorkspaceJoinResult(workspace_id="", joined=True, message="ok")
|
||||
|
||||
|
||||
class TestTryJoinDefaultWorkspace:
|
||||
def test_try_join_default_workspace_enterprise_disabled_noop(self):
|
||||
with (
|
||||
patch("services.enterprise.enterprise_service.dify_config") as mock_config,
|
||||
patch("services.enterprise.enterprise_service.EnterpriseService.join_default_workspace") as mock_join,
|
||||
):
|
||||
mock_config.ENTERPRISE_ENABLED = False
|
||||
|
||||
try_join_default_workspace("11111111-1111-1111-1111-111111111111")
|
||||
|
||||
mock_join.assert_not_called()
|
||||
|
||||
def test_try_join_default_workspace_successful_join_does_not_raise(self):
|
||||
account_id = "11111111-1111-1111-1111-111111111111"
|
||||
|
||||
with (
|
||||
patch("services.enterprise.enterprise_service.dify_config") as mock_config,
|
||||
patch("services.enterprise.enterprise_service.EnterpriseService.join_default_workspace") as mock_join,
|
||||
):
|
||||
mock_config.ENTERPRISE_ENABLED = True
|
||||
mock_join.return_value = DefaultWorkspaceJoinResult(
|
||||
workspace_id="22222222-2222-2222-2222-222222222222",
|
||||
joined=True,
|
||||
message="ok",
|
||||
)
|
||||
|
||||
# Should not raise
|
||||
try_join_default_workspace(account_id)
|
||||
|
||||
mock_join.assert_called_once_with(account_id=account_id)
|
||||
|
||||
def test_try_join_default_workspace_skipped_join_does_not_raise(self):
|
||||
account_id = "11111111-1111-1111-1111-111111111111"
|
||||
|
||||
with (
|
||||
patch("services.enterprise.enterprise_service.dify_config") as mock_config,
|
||||
patch("services.enterprise.enterprise_service.EnterpriseService.join_default_workspace") as mock_join,
|
||||
):
|
||||
mock_config.ENTERPRISE_ENABLED = True
|
||||
mock_join.return_value = DefaultWorkspaceJoinResult(
|
||||
workspace_id="",
|
||||
joined=False,
|
||||
message="no default workspace configured",
|
||||
)
|
||||
|
||||
# Should not raise
|
||||
try_join_default_workspace(account_id)
|
||||
|
||||
mock_join.assert_called_once_with(account_id=account_id)
|
||||
|
||||
def test_try_join_default_workspace_api_failure_soft_fails(self):
|
||||
account_id = "11111111-1111-1111-1111-111111111111"
|
||||
|
||||
with (
|
||||
patch("services.enterprise.enterprise_service.dify_config") as mock_config,
|
||||
patch("services.enterprise.enterprise_service.EnterpriseService.join_default_workspace") as mock_join,
|
||||
):
|
||||
mock_config.ENTERPRISE_ENABLED = True
|
||||
mock_join.side_effect = Exception("network failure")
|
||||
|
||||
# Should not raise
|
||||
try_join_default_workspace(account_id)
|
||||
|
||||
mock_join.assert_called_once_with(account_id=account_id)
|
||||
|
||||
def test_try_join_default_workspace_invalid_account_id_soft_fails(self):
|
||||
with patch("services.enterprise.enterprise_service.dify_config") as mock_config:
|
||||
mock_config.ENTERPRISE_ENABLED = True
|
||||
|
||||
# Should not raise even though UUID parsing fails inside join_default_workspace
|
||||
try_join_default_workspace("not-a-uuid")
|
||||
@ -938,6 +938,99 @@ class TestRegisterService:
|
||||
|
||||
# ==================== Registration Tests ====================
|
||||
|
||||
def test_create_account_and_tenant_calls_default_workspace_join_when_enterprise_enabled(
|
||||
self, mock_db_dependencies, mock_external_service_dependencies, monkeypatch
|
||||
):
|
||||
"""Enterprise-only side effect should be invoked when ENTERPRISE_ENABLED is True."""
|
||||
monkeypatch.setattr(dify_config, "ENTERPRISE_ENABLED", True, raising=False)
|
||||
|
||||
mock_external_service_dependencies["feature_service"].get_system_features.return_value.is_allow_register = True
|
||||
mock_external_service_dependencies["billing_service"].is_email_in_freeze.return_value = False
|
||||
|
||||
mock_account = TestAccountAssociatedDataFactory.create_account_mock(
|
||||
account_id="11111111-1111-1111-1111-111111111111"
|
||||
)
|
||||
|
||||
with (
|
||||
patch("services.account_service.AccountService.create_account") as mock_create_account,
|
||||
patch("services.account_service.TenantService.create_owner_tenant_if_not_exist") as mock_create_workspace,
|
||||
patch("services.enterprise.enterprise_service.try_join_default_workspace") as mock_join_default_workspace,
|
||||
):
|
||||
mock_create_account.return_value = mock_account
|
||||
|
||||
result = AccountService.create_account_and_tenant(
|
||||
email="test@example.com",
|
||||
name="Test User",
|
||||
interface_language="en-US",
|
||||
password=None,
|
||||
)
|
||||
|
||||
assert result == mock_account
|
||||
mock_create_workspace.assert_called_once_with(account=mock_account)
|
||||
mock_join_default_workspace.assert_called_once_with(str(mock_account.id))
|
||||
|
||||
def test_create_account_and_tenant_does_not_call_default_workspace_join_when_enterprise_disabled(
|
||||
self, mock_db_dependencies, mock_external_service_dependencies, monkeypatch
|
||||
):
|
||||
"""Enterprise-only side effect should not be invoked when ENTERPRISE_ENABLED is False."""
|
||||
monkeypatch.setattr(dify_config, "ENTERPRISE_ENABLED", False, raising=False)
|
||||
|
||||
mock_external_service_dependencies["feature_service"].get_system_features.return_value.is_allow_register = True
|
||||
mock_external_service_dependencies["billing_service"].is_email_in_freeze.return_value = False
|
||||
|
||||
mock_account = TestAccountAssociatedDataFactory.create_account_mock(
|
||||
account_id="11111111-1111-1111-1111-111111111111"
|
||||
)
|
||||
|
||||
with (
|
||||
patch("services.account_service.AccountService.create_account") as mock_create_account,
|
||||
patch("services.account_service.TenantService.create_owner_tenant_if_not_exist") as mock_create_workspace,
|
||||
patch("services.enterprise.enterprise_service.try_join_default_workspace") as mock_join_default_workspace,
|
||||
):
|
||||
mock_create_account.return_value = mock_account
|
||||
|
||||
AccountService.create_account_and_tenant(
|
||||
email="test@example.com",
|
||||
name="Test User",
|
||||
interface_language="en-US",
|
||||
password=None,
|
||||
)
|
||||
|
||||
mock_create_workspace.assert_called_once_with(account=mock_account)
|
||||
mock_join_default_workspace.assert_not_called()
|
||||
|
||||
def test_create_account_and_tenant_still_calls_default_workspace_join_when_workspace_creation_fails(
|
||||
self, mock_db_dependencies, mock_external_service_dependencies, monkeypatch
|
||||
):
|
||||
"""Default workspace join should still be attempted when personal workspace creation fails."""
|
||||
from services.errors.workspace import WorkSpaceNotAllowedCreateError
|
||||
|
||||
monkeypatch.setattr(dify_config, "ENTERPRISE_ENABLED", True, raising=False)
|
||||
mock_external_service_dependencies["feature_service"].get_system_features.return_value.is_allow_register = True
|
||||
mock_external_service_dependencies["billing_service"].is_email_in_freeze.return_value = False
|
||||
|
||||
mock_account = TestAccountAssociatedDataFactory.create_account_mock(
|
||||
account_id="11111111-1111-1111-1111-111111111111"
|
||||
)
|
||||
|
||||
with (
|
||||
patch("services.account_service.AccountService.create_account") as mock_create_account,
|
||||
patch("services.account_service.TenantService.create_owner_tenant_if_not_exist") as mock_create_workspace,
|
||||
patch("services.enterprise.enterprise_service.try_join_default_workspace") as mock_join_default_workspace,
|
||||
):
|
||||
mock_create_account.return_value = mock_account
|
||||
mock_create_workspace.side_effect = WorkSpaceNotAllowedCreateError()
|
||||
|
||||
with pytest.raises(WorkSpaceNotAllowedCreateError):
|
||||
AccountService.create_account_and_tenant(
|
||||
email="test@example.com",
|
||||
name="Test User",
|
||||
interface_language="en-US",
|
||||
password=None,
|
||||
)
|
||||
|
||||
mock_join_default_workspace.assert_called_once_with(str(mock_account.id))
|
||||
|
||||
def test_register_success(self, mock_db_dependencies, mock_external_service_dependencies):
|
||||
"""Test successful account registration."""
|
||||
# Setup mocks
|
||||
@ -989,6 +1082,143 @@ class TestRegisterService:
|
||||
mock_event.send.assert_called_once_with(mock_tenant)
|
||||
self._assert_database_operations_called(mock_db_dependencies["db"])
|
||||
|
||||
def test_register_calls_default_workspace_join_when_enterprise_enabled(
|
||||
self, mock_db_dependencies, mock_external_service_dependencies, monkeypatch
|
||||
):
|
||||
"""Enterprise-only side effect should be invoked after successful register commit."""
|
||||
monkeypatch.setattr(dify_config, "ENTERPRISE_ENABLED", True, raising=False)
|
||||
|
||||
mock_external_service_dependencies["feature_service"].get_system_features.return_value.is_allow_register = True
|
||||
mock_external_service_dependencies["billing_service"].is_email_in_freeze.return_value = False
|
||||
|
||||
mock_account = TestAccountAssociatedDataFactory.create_account_mock(
|
||||
account_id="11111111-1111-1111-1111-111111111111"
|
||||
)
|
||||
|
||||
with (
|
||||
patch("services.account_service.AccountService.create_account") as mock_create_account,
|
||||
patch("services.enterprise.enterprise_service.try_join_default_workspace") as mock_join_default_workspace,
|
||||
):
|
||||
mock_create_account.return_value = mock_account
|
||||
|
||||
result = RegisterService.register(
|
||||
email="test@example.com",
|
||||
name="Test User",
|
||||
password="password123",
|
||||
language="en-US",
|
||||
create_workspace_required=False,
|
||||
)
|
||||
|
||||
assert result == mock_account
|
||||
mock_join_default_workspace.assert_called_once_with(str(mock_account.id))
|
||||
|
||||
def test_register_does_not_call_default_workspace_join_when_enterprise_disabled(
|
||||
self, mock_db_dependencies, mock_external_service_dependencies, monkeypatch
|
||||
):
|
||||
"""Enterprise-only side effect should not be invoked when ENTERPRISE_ENABLED is False."""
|
||||
monkeypatch.setattr(dify_config, "ENTERPRISE_ENABLED", False, raising=False)
|
||||
|
||||
mock_external_service_dependencies["feature_service"].get_system_features.return_value.is_allow_register = True
|
||||
mock_external_service_dependencies["billing_service"].is_email_in_freeze.return_value = False
|
||||
|
||||
mock_account = TestAccountAssociatedDataFactory.create_account_mock(
|
||||
account_id="11111111-1111-1111-1111-111111111111"
|
||||
)
|
||||
|
||||
with (
|
||||
patch("services.account_service.AccountService.create_account") as mock_create_account,
|
||||
patch("services.enterprise.enterprise_service.try_join_default_workspace") as mock_join_default_workspace,
|
||||
):
|
||||
mock_create_account.return_value = mock_account
|
||||
|
||||
RegisterService.register(
|
||||
email="test@example.com",
|
||||
name="Test User",
|
||||
password="password123",
|
||||
language="en-US",
|
||||
create_workspace_required=False,
|
||||
)
|
||||
|
||||
mock_join_default_workspace.assert_not_called()
|
||||
|
||||
def test_register_still_calls_default_workspace_join_when_personal_workspace_creation_fails(
|
||||
self, mock_db_dependencies, mock_external_service_dependencies, monkeypatch
|
||||
):
|
||||
"""Default workspace join should run even when personal workspace creation raises."""
|
||||
from services.errors.workspace import WorkSpaceNotAllowedCreateError
|
||||
|
||||
monkeypatch.setattr(dify_config, "ENTERPRISE_ENABLED", True, raising=False)
|
||||
mock_external_service_dependencies["feature_service"].get_system_features.return_value.is_allow_register = True
|
||||
mock_external_service_dependencies[
|
||||
"feature_service"
|
||||
].get_system_features.return_value.is_allow_create_workspace = True
|
||||
mock_external_service_dependencies[
|
||||
"feature_service"
|
||||
].get_system_features.return_value.license.workspaces.is_available.return_value = True
|
||||
mock_external_service_dependencies["billing_service"].is_email_in_freeze.return_value = False
|
||||
|
||||
mock_account = TestAccountAssociatedDataFactory.create_account_mock(
|
||||
account_id="11111111-1111-1111-1111-111111111111"
|
||||
)
|
||||
|
||||
with (
|
||||
patch("services.account_service.AccountService.create_account") as mock_create_account,
|
||||
patch("services.account_service.TenantService.create_tenant") as mock_create_tenant,
|
||||
patch("services.enterprise.enterprise_service.try_join_default_workspace") as mock_join_default_workspace,
|
||||
):
|
||||
mock_create_account.return_value = mock_account
|
||||
mock_create_tenant.side_effect = WorkSpaceNotAllowedCreateError()
|
||||
|
||||
with pytest.raises(AccountRegisterError, match="Workspace is not allowed to create."):
|
||||
RegisterService.register(
|
||||
email="test@example.com",
|
||||
name="Test User",
|
||||
password="password123",
|
||||
language="en-US",
|
||||
)
|
||||
|
||||
mock_join_default_workspace.assert_called_once_with(str(mock_account.id))
|
||||
mock_db_dependencies["db"].session.commit.assert_not_called()
|
||||
|
||||
def test_register_still_calls_default_workspace_join_when_workspace_limit_exceeded(
|
||||
self, mock_db_dependencies, mock_external_service_dependencies, monkeypatch
|
||||
):
|
||||
"""Default workspace join should run before propagating workspace-limit registration failure."""
|
||||
from services.errors.workspace import WorkspacesLimitExceededError
|
||||
|
||||
monkeypatch.setattr(dify_config, "ENTERPRISE_ENABLED", True, raising=False)
|
||||
mock_external_service_dependencies["feature_service"].get_system_features.return_value.is_allow_register = True
|
||||
mock_external_service_dependencies[
|
||||
"feature_service"
|
||||
].get_system_features.return_value.is_allow_create_workspace = True
|
||||
mock_external_service_dependencies[
|
||||
"feature_service"
|
||||
].get_system_features.return_value.license.workspaces.is_available.return_value = True
|
||||
mock_external_service_dependencies["billing_service"].is_email_in_freeze.return_value = False
|
||||
|
||||
mock_account = TestAccountAssociatedDataFactory.create_account_mock(
|
||||
account_id="11111111-1111-1111-1111-111111111111"
|
||||
)
|
||||
|
||||
with (
|
||||
patch("services.account_service.AccountService.create_account") as mock_create_account,
|
||||
patch("services.account_service.TenantService.create_tenant") as mock_create_tenant,
|
||||
patch("services.enterprise.enterprise_service.try_join_default_workspace") as mock_join_default_workspace,
|
||||
):
|
||||
mock_create_account.return_value = mock_account
|
||||
mock_create_tenant.side_effect = WorkspacesLimitExceededError()
|
||||
|
||||
with pytest.raises(AccountRegisterError, match="Registration failed:"):
|
||||
RegisterService.register(
|
||||
email="test@example.com",
|
||||
name="Test User",
|
||||
password="password123",
|
||||
language="en-US",
|
||||
)
|
||||
|
||||
mock_join_default_workspace.assert_called_once_with(str(mock_account.id))
|
||||
mock_db_dependencies["db"].session.commit.assert_not_called()
|
||||
|
||||
def test_register_with_oauth(self, mock_db_dependencies, mock_external_service_dependencies):
|
||||
"""Test account registration with OAuth integration."""
|
||||
# Setup mocks
|
||||
|
||||
Reference in New Issue
Block a user