diff --git a/api/controllers/console/app/agent_app_feature.py b/api/controllers/console/app/agent_app_feature.py index 5d2b77c97f1..d155dae6ac3 100644 --- a/api/controllers/console/app/agent_app_feature.py +++ b/api/controllers/console/app/agent_app_feature.py @@ -29,6 +29,7 @@ from controllers.console.wraps import ( with_current_user, ) from events.app_event import app_model_config_was_updated +from extensions.ext_database import db from libs.helper import dump_response from libs.login import login_required from models import Account @@ -90,9 +91,7 @@ class AgentAppFeatureConfigResource(Resource): args = AgentAppFeaturesPayload.model_validate(console_ns.payload or {}) new_app_model_config = AgentAppFeatureConfigService.update_features( - app_model=app_model, - account=current_user, - config=args.model_dump(exclude_none=True), + app_model=app_model, account=current_user, config=args.model_dump(exclude_none=True), session=db.session ) app_model_config_was_updated.send(app_model, app_model_config=new_app_model_config) diff --git a/api/controllers/console/auth/activate.py b/api/controllers/console/auth/activate.py index f61bb8f6802..c9142d85ede 100644 --- a/api/controllers/console/auth/activate.py +++ b/api/controllers/console/auth/activate.py @@ -174,7 +174,7 @@ class ActivateApi(Resource): RegisterService.revoke_token(args.workspace_id, normalized_request_email, args.token) if membership_id is None: - TenantService.create_tenant_member(tenant, account, str(role)) + TenantService.create_tenant_member(tenant, account, db.session, role=role) if setup_fields: account.name = setup_fields[0] diff --git a/api/controllers/console/auth/forgot_password.py b/api/controllers/console/auth/forgot_password.py index c34dd1ac859..d82f63c11db 100644 --- a/api/controllers/console/auth/forgot_password.py +++ b/api/controllers/console/auth/forgot_password.py @@ -202,6 +202,6 @@ class ForgotPasswordResetApi(Resource): and FeatureService.get_system_features().is_allow_create_workspace ): tenant = TenantService.create_tenant(f"{account.name}'s Workspace") - TenantService.create_tenant_member(tenant, account, role="owner") + TenantService.create_tenant_member(tenant, account, db.session, role="owner") account.current_tenant = tenant tenant_was_created.send(tenant) diff --git a/api/controllers/console/auth/login.py b/api/controllers/console/auth/login.py index 6a1b4c6769e..053f313ba53 100644 --- a/api/controllers/console/auth/login.py +++ b/api/controllers/console/auth/login.py @@ -35,6 +35,7 @@ from controllers.console.wraps import ( with_current_user, ) from events.tenant_event import tenant_was_created +from extensions.ext_database import db from libs.helper import EmailStr, extract_remote_ip from libs.helper import timezone as validate_timezone_string from libs.token import ( @@ -299,7 +300,7 @@ class EmailCodeLoginApi(Resource): raise NotAllowedCreateWorkspace() else: new_tenant = TenantService.create_tenant(f"{account.name}'s Workspace") - TenantService.create_tenant_member(new_tenant, account, role="owner") + TenantService.create_tenant_member(new_tenant, account, db.session, role="owner") account.current_tenant = new_tenant tenant_was_created.send(new_tenant) diff --git a/api/controllers/console/auth/oauth.py b/api/controllers/console/auth/oauth.py index 31649812fe8..78d1583fde9 100644 --- a/api/controllers/console/auth/oauth.py +++ b/api/controllers/console/auth/oauth.py @@ -246,7 +246,7 @@ def _generate_account( raise WorkSpaceNotAllowedCreateError() else: new_tenant = TenantService.create_tenant(f"{account.name}'s Workspace") - TenantService.create_tenant_member(new_tenant, account, role="owner") + TenantService.create_tenant_member(new_tenant, account, db.session, role="owner") account.current_tenant = new_tenant tenant_was_created.send(new_tenant) diff --git a/api/controllers/inner_api/workspace/workspace.py b/api/controllers/inner_api/workspace/workspace.py index ef0a46db63a..dd93616e6b1 100644 --- a/api/controllers/inner_api/workspace/workspace.py +++ b/api/controllers/inner_api/workspace/workspace.py @@ -48,7 +48,7 @@ class EnterpriseWorkspace(Resource): return {"message": "owner account not found."}, 404 tenant = TenantService.create_tenant(args.name, is_from_dashboard=True) - TenantService.create_tenant_member(tenant, account, role="owner") + TenantService.create_tenant_member(tenant, account, db.session, role="owner") tenant_was_created.send(tenant) diff --git a/api/services/account_service.py b/api/services/account_service.py index a608f544747..21b5f1eedba 100644 --- a/api/services/account_service.py +++ b/api/services/account_service.py @@ -1280,7 +1280,7 @@ class TenantService: tenant = TenantService.create_tenant(name=name, is_setup=is_setup) else: tenant = TenantService.create_tenant(name=f"{account.name}'s Workspace", is_setup=is_setup) - TenantService.create_tenant_member(tenant, account, role="owner") + TenantService.create_tenant_member(tenant, account, db.session, role="owner") if dify_config.RBAC_ENABLED: owner_role_id = AccountService._resolve_legacy_role_id(str(tenant.id), account.id, TenantAccountRole.OWNER) RBACService.MemberRoles.replace( @@ -1294,14 +1294,16 @@ class TenantService: tenant_was_created.send(tenant) @staticmethod - def create_tenant_member(tenant: Tenant, account: Account, role: str = "normal") -> TenantAccountJoin: + def create_tenant_member( + tenant: Tenant, account: Account, session: scoped_session, role: str = "normal" + ) -> TenantAccountJoin: """Create tenant member""" if role == TenantAccountRole.OWNER: if TenantService.has_roles(tenant, [TenantAccountRole.OWNER]): logger.error("Tenant %s has already an owner.", tenant.id) raise Exception("Tenant already has an owner.") - ta = db.session.scalar( + ta = session.scalar( select(TenantAccountJoin) .where(TenantAccountJoin.tenant_id == tenant.id, TenantAccountJoin.account_id == account.id) .limit(1) @@ -1310,9 +1312,9 @@ class TenantService: ta.role = TenantAccountRole(role) else: ta = TenantAccountJoin(tenant_id=tenant.id, account_id=account.id, role=TenantAccountRole(role)) - db.session.add(ta) + session.add(ta) - db.session.commit() + session.commit() if dify_config.BILLING_ENABLED: BillingService.clean_billing_info_cache(tenant.id) return ta @@ -1915,7 +1917,7 @@ class RegisterService: ): try: tenant = TenantService.create_tenant(f"{account.name}'s Workspace") - TenantService.create_tenant_member(tenant, account, role="owner") + TenantService.create_tenant_member(tenant, account, db.session, role="owner") account.current_tenant = tenant tenant_was_created.send(tenant) except Exception: @@ -1970,7 +1972,7 @@ class RegisterService: status=AccountStatus.PENDING, is_setup=True, ) - TenantService.create_tenant_member(tenant, account, tenant_join_role) + TenantService.create_tenant_member(tenant, account, db.session, tenant_join_role) TenantService.switch_tenant(account, tenant.id) requires_setup = True else: @@ -1983,7 +1985,7 @@ class RegisterService: requires_setup = account.status == AccountStatus.PENDING if not ta and (account.status == AccountStatus.PENDING or dify_config.RBAC_ENABLED): - TenantService.create_tenant_member(tenant, account, tenant_join_role) + TenantService.create_tenant_member(tenant, account, db.session, tenant_join_role) # Support resend invitation email when the account is pending status if account.status != AccountStatus.PENDING: diff --git a/api/services/agent_app_feature_service.py b/api/services/agent_app_feature_service.py index cc0fe67802d..b8e98653c8e 100644 --- a/api/services/agent_app_feature_service.py +++ b/api/services/agent_app_feature_service.py @@ -13,6 +13,8 @@ from __future__ import annotations from typing import Any, cast +from sqlalchemy.orm import scoped_session + from core.app.app_config.common.sensitive_word_avoidance.manager import SensitiveWordAvoidanceConfigManager from core.app.app_config.features.opening_statement.manager import OpeningStatementConfigManager from core.app.app_config.features.retrieval_resource.manager import RetrievalResourceConfigManager @@ -21,7 +23,6 @@ from core.app.app_config.features.suggested_questions_after_answer.manager impor SuggestedQuestionsAfterAnswerConfigManager, ) from core.app.app_config.features.text_to_speech.manager import TextToSpeechConfigManager -from extensions.ext_database import db from libs.datetime_utils import naive_utc_now from models.account import Account from models.model import App, AppModelConfig, AppModelConfigDict @@ -67,7 +68,9 @@ class AgentAppFeatureConfigService: return cast(AppModelConfigDict, filtered) @classmethod - def update_features(cls, *, app_model: App, account: Account, config: dict[str, Any]) -> AppModelConfig: + def update_features( + cls, *, app_model: App, account: Account, config: dict[str, Any], session: scoped_session + ) -> AppModelConfig: """Persist the presentation features as a new app_model_config version. Returns the new ``AppModelConfig`` row (now referenced by the app); the @@ -82,13 +85,13 @@ class AgentAppFeatureConfigService: updated_by=account.id, ).from_model_config_dict(validated) - db.session.add(new_config) - db.session.flush() + session.add(new_config) + session.flush() app_model.app_model_config_id = new_config.id app_model.updated_by = account.id app_model.updated_at = naive_utc_now() - db.session.commit() + session.commit() return new_config diff --git a/api/tests/test_containers_integration_tests/controllers/console/auth/test_oauth.py b/api/tests/test_containers_integration_tests/controllers/console/auth/test_oauth.py index 9ef6b903066..d043c0d413a 100644 --- a/api/tests/test_containers_integration_tests/controllers/console/auth/test_oauth.py +++ b/api/tests/test_containers_integration_tests/controllers/console/auth/test_oauth.py @@ -2,7 +2,7 @@ from __future__ import annotations -from unittest.mock import MagicMock, patch +from unittest.mock import ANY, MagicMock, patch import pytest from flask import Flask @@ -655,11 +655,11 @@ class TestAccountGeneration: @patch("controllers.console.auth.oauth.tenant_was_created") def test_should_create_workspace_for_account_without_tenant( self, - mock_event, - mock_account_service, - mock_feature_service, - mock_tenant_service, - mock_get_account, + mock_event: MagicMock, + mock_account_service: MagicMock, + mock_feature_service: MagicMock, + mock_tenant_service: MagicMock, + mock_get_account: MagicMock, app: Flask, user_info: OAuthUserInfo, mock_account, @@ -678,6 +678,6 @@ class TestAccountGeneration: assert oauth_new_user is False mock_tenant_service.create_tenant.assert_called_once_with("Test User's Workspace") mock_tenant_service.create_tenant_member.assert_called_once_with( - mock_new_tenant, mock_account, role="owner" + mock_new_tenant, mock_account, ANY, role="owner" ) mock_event.send.assert_called_once_with(mock_new_tenant) diff --git a/api/tests/test_containers_integration_tests/controllers/openapi/conftest.py b/api/tests/test_containers_integration_tests/controllers/openapi/conftest.py index d961479f55b..5fe0f787524 100644 --- a/api/tests/test_containers_integration_tests/controllers/openapi/conftest.py +++ b/api/tests/test_containers_integration_tests/controllers/openapi/conftest.py @@ -12,6 +12,7 @@ from flask import Flask from sqlalchemy.orm import Session from controllers.openapi.auth.data import AuthData +from extensions.ext_database import db from libs.oauth_bearer import AuthContext, Scope, SubjectType, TokenType, reset_auth_ctx, set_auth_ctx from models import Account, Tenant from services.account_service import AccountService, TenantService @@ -58,7 +59,7 @@ def add_tenant_for_account(account: Account, *, role: str = "normal", name: str with patch("services.account_service.FeatureService") as mock_feature_service: mock_feature_service.get_system_features.return_value.is_allow_create_workspace = True tenant = TenantService.create_tenant(name=name) - TenantService.create_tenant_member(tenant, account, role=role) + TenantService.create_tenant_member(tenant, account, db.session, role=role) return tenant diff --git a/api/tests/test_containers_integration_tests/services/test_account_service.py b/api/tests/test_containers_integration_tests/services/test_account_service.py index 61dca361f3e..a2f5370cb76 100644 --- a/api/tests/test_containers_integration_tests/services/test_account_service.py +++ b/api/tests/test_containers_integration_tests/services/test_account_service.py @@ -1276,7 +1276,7 @@ class TestTenantService: ) # Create tenant member - tenant_member = TenantService.create_tenant_member(tenant, account, role="admin") + tenant_member = TenantService.create_tenant_member(tenant, account, db_session_with_containers, role="admin") assert tenant_member.tenant_id == tenant.id assert tenant_member.account_id == account.id @@ -1317,11 +1317,11 @@ class TestTenantService: ) # Create first owner - TenantService.create_tenant_member(tenant, account1, role="owner") + TenantService.create_tenant_member(tenant, account1, db_session_with_containers, role="owner") # Try to create second owner (should fail) with pytest.raises(Exception, match="Tenant already has an owner"): - TenantService.create_tenant_member(tenant, account2, role="owner") + TenantService.create_tenant_member(tenant, account2, db_session_with_containers, role="owner") def test_create_tenant_member_existing_member( self, db_session_with_containers: Session, mock_external_service_dependencies @@ -1349,11 +1349,11 @@ class TestTenantService: ) # Create member with initial role - tenant_member1 = TenantService.create_tenant_member(tenant, account, role="normal") + tenant_member1 = TenantService.create_tenant_member(tenant, account, db_session_with_containers, role="normal") assert tenant_member1.role == "normal" # Update member role - tenant_member2 = TenantService.create_tenant_member(tenant, account, role="editor") + tenant_member2 = TenantService.create_tenant_member(tenant, account, db_session_with_containers, role="editor") assert tenant_member2.tenant_id == tenant_member1.tenant_id assert tenant_member2.account_id == tenant_member1.account_id assert tenant_member2.role == "editor" @@ -1384,8 +1384,8 @@ class TestTenantService: tenant2 = TenantService.create_tenant(name=tenant2_name) # Add account to both tenants - TenantService.create_tenant_member(tenant1, account, role="normal") - TenantService.create_tenant_member(tenant2, account, role="admin") + TenantService.create_tenant_member(tenant1, account, db_session_with_containers, role="normal") + TenantService.create_tenant_member(tenant2, account, db_session_with_containers, role="admin") # Get join tenants join_tenants = TenantService.get_join_tenants(account) @@ -1421,7 +1421,7 @@ class TestTenantService: tenant = TenantService.create_tenant(name=tenant_name) # Add account to tenant and set as current - TenantService.create_tenant_member(tenant, account, role="owner") + TenantService.create_tenant_member(tenant, account, db_session_with_containers, role="owner") account.current_tenant = tenant db_session_with_containers.commit() @@ -1486,8 +1486,8 @@ class TestTenantService: tenant2 = TenantService.create_tenant(name=tenant2_name) # Add account to both tenants - TenantService.create_tenant_member(tenant1, account, role="owner") - TenantService.create_tenant_member(tenant2, account, role="admin") + TenantService.create_tenant_member(tenant1, account, db_session_with_containers, role="owner") + TenantService.create_tenant_member(tenant2, account, db_session_with_containers, role="admin") # Set initial current tenant account.current_tenant = tenant1 @@ -1588,8 +1588,8 @@ class TestTenantService: ) # Add members with different roles - TenantService.create_tenant_member(tenant, owner_account, role="owner") - TenantService.create_tenant_member(tenant, admin_account, role="admin") + TenantService.create_tenant_member(tenant, owner_account, db_session_with_containers, role="owner") + TenantService.create_tenant_member(tenant, admin_account, db_session_with_containers, role="admin") # Check if tenant has owner role from models.account import TenantAccountRole @@ -1648,7 +1648,7 @@ class TestTenantService: ) # Add account to tenant with specific role - TenantService.create_tenant_member(tenant, account, role="editor") + TenantService.create_tenant_member(tenant, account, db_session_with_containers, role="editor") # Get user role user_role = TenantService.get_user_role(account, tenant) @@ -1690,8 +1690,8 @@ class TestTenantService: ) # Add members with different roles - TenantService.create_tenant_member(tenant, owner_account, role="owner") - TenantService.create_tenant_member(tenant, member_account, role="normal") + TenantService.create_tenant_member(tenant, owner_account, db_session_with_containers, role="owner") + TenantService.create_tenant_member(tenant, member_account, db_session_with_containers, role="normal") # Check owner permission to add member (should succeed) TenantService.check_member_permission(tenant, owner_account, member_account, "add") @@ -1723,7 +1723,7 @@ class TestTenantService: ) # Add account to tenant - TenantService.create_tenant_member(tenant, account, role="owner") + TenantService.create_tenant_member(tenant, account, db_session_with_containers, role="owner") # Try to check permission with invalid action with pytest.raises(Exception, match="Invalid action"): @@ -1755,7 +1755,7 @@ class TestTenantService: ) # Add account to tenant - TenantService.create_tenant_member(tenant, account, role="owner") + TenantService.create_tenant_member(tenant, account, db_session_with_containers, role="owner") # Try to check permission to operate self with pytest.raises(Exception, match="Cannot operate self"): @@ -1796,8 +1796,8 @@ class TestTenantService: ) # Add members with different roles - TenantService.create_tenant_member(tenant, owner_account, role="owner") - TenantService.create_tenant_member(tenant, member_account, role="normal") + TenantService.create_tenant_member(tenant, owner_account, db_session_with_containers, role="owner") + TenantService.create_tenant_member(tenant, member_account, db_session_with_containers, role="normal") app = App( tenant_id=tenant.id, @@ -1876,7 +1876,7 @@ class TestTenantService: ) # Add account to tenant - TenantService.create_tenant_member(tenant, account, role="owner") + TenantService.create_tenant_member(tenant, account, db_session_with_containers, role="owner") # Try to remove self with pytest.raises(Exception, match="Cannot operate self"): @@ -1917,7 +1917,7 @@ class TestTenantService: ) # Add only owner to tenant - TenantService.create_tenant_member(tenant, owner_account, role="owner") + TenantService.create_tenant_member(tenant, owner_account, db_session_with_containers, role="owner") # Try to remove non-member with pytest.raises(Exception, match="Member not in tenant"): @@ -1956,8 +1956,8 @@ class TestTenantService: ) # Add members with different roles - TenantService.create_tenant_member(tenant, owner_account, role="owner") - TenantService.create_tenant_member(tenant, member_account, role="normal") + TenantService.create_tenant_member(tenant, owner_account, db_session_with_containers, role="owner") + TenantService.create_tenant_member(tenant, member_account, db_session_with_containers, role="normal") # Update member role TenantService.update_member_role(tenant, member_account, "admin", owner_account) @@ -2005,8 +2005,8 @@ class TestTenantService: ) # Add members with different roles - TenantService.create_tenant_member(tenant, owner_account, role="owner") - TenantService.create_tenant_member(tenant, member_account, role="admin") + TenantService.create_tenant_member(tenant, owner_account, db_session_with_containers, role="owner") + TenantService.create_tenant_member(tenant, member_account, db_session_with_containers, role="admin") # Update member role to owner TenantService.update_member_role(tenant, member_account, "owner", owner_account) @@ -2062,8 +2062,8 @@ class TestTenantService: ) # Add members with different roles - TenantService.create_tenant_member(tenant, owner_account, role="owner") - TenantService.create_tenant_member(tenant, member_account, role="admin") + TenantService.create_tenant_member(tenant, owner_account, db_session_with_containers, role="owner") + TenantService.create_tenant_member(tenant, member_account, db_session_with_containers, role="admin") # Try to update member role to already assigned role with pytest.raises(Exception, match="The provided role is already assigned to the member"): @@ -2160,7 +2160,7 @@ class TestTenantService: password=password, ) existing_tenant = TenantService.create_tenant(name=existing_tenant_name) - TenantService.create_tenant_member(existing_tenant, account, role="owner") + TenantService.create_tenant_member(existing_tenant, account, db_session_with_containers, role="owner") account.current_tenant = existing_tenant db_session_with_containers.commit() @@ -2243,9 +2243,9 @@ class TestTenantService: ) # Add members with different roles - TenantService.create_tenant_member(tenant, owner_account, role="owner") - TenantService.create_tenant_member(tenant, admin_account, role="admin") - TenantService.create_tenant_member(tenant, normal_account, role="normal") + TenantService.create_tenant_member(tenant, owner_account, db_session_with_containers, role="owner") + TenantService.create_tenant_member(tenant, admin_account, db_session_with_containers, role="admin") + TenantService.create_tenant_member(tenant, normal_account, db_session_with_containers, role="normal") # Get tenant members members = TenantService.get_tenant_members(tenant) @@ -2309,9 +2309,11 @@ class TestTenantService: ) # Add members with different roles - TenantService.create_tenant_member(tenant, owner_account, role="owner") - TenantService.create_tenant_member(tenant, dataset_operator_account, role="dataset_operator") - TenantService.create_tenant_member(tenant, normal_account, role="normal") + TenantService.create_tenant_member(tenant, owner_account, db_session_with_containers, role="owner") + TenantService.create_tenant_member( + tenant, dataset_operator_account, db_session_with_containers, role="dataset_operator" + ) + TenantService.create_tenant_member(tenant, normal_account, db_session_with_containers, role="normal") # Get dataset operator members dataset_operators = TenantService.get_dataset_operator_members(tenant) @@ -2742,7 +2744,7 @@ class TestRegisterService: interface_language="en-US", password=inviter_password, ) - TenantService.create_tenant_member(tenant, inviter, role="owner") + TenantService.create_tenant_member(tenant, inviter, db_session_with_containers, role="owner") # Mock the email task with patch("services.account_service.send_invite_member_mail_task") as mock_send_mail: @@ -2808,7 +2810,7 @@ class TestRegisterService: interface_language="en-US", password=inviter_password, ) - TenantService.create_tenant_member(tenant, inviter, role="owner") + TenantService.create_tenant_member(tenant, inviter, db_session_with_containers, role="owner") # Create existing account existing_account = AccountService.create_account( @@ -2877,7 +2879,7 @@ class TestRegisterService: interface_language="en-US", password=inviter_password, ) - TenantService.create_tenant_member(tenant, inviter, role="owner") + TenantService.create_tenant_member(tenant, inviter, db_session_with_containers, role="owner") # Create existing account with pending status existing_account = AccountService.create_account( @@ -2891,7 +2893,7 @@ class TestRegisterService: db_session_with_containers.commit() # Add existing account to tenant - TenantService.create_tenant_member(tenant, existing_account, role="normal") + TenantService.create_tenant_member(tenant, existing_account, db_session_with_containers, role="normal") # Mock the email task with patch("services.account_service.send_invite_member_mail_task") as mock_send_mail: @@ -2967,7 +2969,7 @@ class TestRegisterService: interface_language="en-US", password=inviter_password, ) - TenantService.create_tenant_member(tenant, inviter, role="owner") + TenantService.create_tenant_member(tenant, inviter, db_session_with_containers, role="owner") # Create existing account with active status existing_account = AccountService.create_account( @@ -2981,7 +2983,7 @@ class TestRegisterService: db_session_with_containers.commit() # Add existing account to tenant - TenantService.create_tenant_member(tenant, existing_account, role="normal") + TenantService.create_tenant_member(tenant, existing_account, db_session_with_containers, role="normal") # Execute invitation (should fail for active member) with pytest.raises(AccountAlreadyInTenantError, match="Account already in tenant."): @@ -3193,7 +3195,7 @@ class TestRegisterService: interface_language="en-US", password=password, ) - TenantService.create_tenant_member(tenant, account, role="normal") + TenantService.create_tenant_member(tenant, account, db_session_with_containers, role="normal") # Generate a real token token = RegisterService.generate_invite_token(tenant, account) @@ -3313,7 +3315,7 @@ class TestRegisterService: interface_language="en-US", password=password, ) - TenantService.create_tenant_member(tenant, account, role="normal") + TenantService.create_tenant_member(tenant, account, db_session_with_containers, role="normal") # Create a real token but with mismatched account ID from extensions.ext_redis import redis_client @@ -3363,7 +3365,7 @@ class TestRegisterService: interface_language="en-US", password=password, ) - TenantService.create_tenant_member(tenant, account, role="normal") + TenantService.create_tenant_member(tenant, account, db_session_with_containers, role="normal") # Change tenant status to non-normal tenant.status = TenantStatus.ARCHIVE diff --git a/api/tests/unit_tests/controllers/console/auth/test_account_activation.py b/api/tests/unit_tests/controllers/console/auth/test_account_activation.py index 1422afd7524..6ef7f442591 100644 --- a/api/tests/unit_tests/controllers/console/auth/test_account_activation.py +++ b/api/tests/unit_tests/controllers/console/auth/test_account_activation.py @@ -15,7 +15,7 @@ from flask import Flask from controllers.console.auth.activate import ActivateApi, ActivateCheckApi from controllers.console.error import AccountInFreezeError, AlreadyActivateError -from models.account import AccountStatus +from models.account import AccountStatus, TenantAccountRole class TestActivateCheckApi: @@ -201,11 +201,11 @@ class TestActivateApi: @patch("controllers.console.auth.activate.db") def test_successful_account_activation( self, - mock_db, - mock_revoke_token, - mock_get_invitation, + mock_db: MagicMock, + mock_revoke_token: MagicMock, + mock_get_invitation: MagicMock, app: Flask, - mock_invitation, + mock_invitation: MagicMock, mock_account, ): """ @@ -448,11 +448,11 @@ class TestActivateApi: @patch("controllers.console.auth.activate.db") def test_activation_returns_success_response( self, - mock_db, - mock_revoke_token, - mock_get_invitation, + mock_db: MagicMock, + mock_revoke_token: MagicMock, + mock_get_invitation: MagicMock, app: Flask, - mock_invitation, + mock_invitation: MagicMock, ): """ Test that activation returns a success response without authentication tokens. @@ -488,11 +488,11 @@ class TestActivateApi: @patch("controllers.console.auth.activate.db") def test_activation_without_workspace_id( self, - mock_db, - mock_revoke_token, - mock_get_invitation, + mock_db: MagicMock, + mock_revoke_token: MagicMock, + mock_get_invitation: MagicMock, app: Flask, - mock_invitation, + mock_invitation: MagicMock, ): """ Test account activation without workspace_id. @@ -528,12 +528,12 @@ class TestActivateApi: @patch("controllers.console.auth.activate.db") def test_activation_normalizes_email_before_lookup( self, - mock_db, - mock_revoke_token, - mock_get_invitation, + mock_db: MagicMock, + mock_revoke_token: MagicMock, + mock_get_invitation: MagicMock, app: Flask, - mock_invitation, - mock_account, + mock_invitation: MagicMock, + mock_account: MagicMock, ): """Ensure uppercase emails are normalized before lookup and revocation.""" mock_get_invitation.return_value = mock_invitation @@ -563,14 +563,14 @@ class TestActivateApi: @patch("controllers.console.auth.activate.db") def test_activation_for_existing_active_account_creates_membership_on_acceptance( self, - mock_db, - mock_revoke_token, - mock_get_invitation, - mock_create_tenant_member, + mock_db: MagicMock, + mock_revoke_token: MagicMock, + mock_get_invitation: MagicMock, + mock_create_tenant_member: MagicMock, app: Flask, - mock_invitation, - mock_account, - mock_switch_tenant, + mock_invitation: MagicMock, + mock_account: MagicMock, + mock_switch_tenant: MagicMock, ): mock_account.status = AccountStatus.ACTIVE mock_invitation["data"]["role"] = "admin" @@ -590,7 +590,9 @@ class TestActivateApi: response = ActivateApi().post() assert response["result"] == "success" - mock_create_tenant_member.assert_called_once_with(mock_invitation["tenant"], mock_account, "admin") + mock_create_tenant_member.assert_called_once_with( + mock_invitation["tenant"], mock_account, mock_db.session, role=TenantAccountRole.ADMIN + ) mock_switch_tenant.assert_called_once_with(mock_account, mock_invitation["tenant"].id) mock_revoke_token.assert_called_once_with("workspace-123", "invitee@example.com", "valid_token") @@ -600,14 +602,14 @@ class TestActivateApi: @patch("controllers.console.auth.activate.db") def test_activation_legacy_active_member_invitation_does_not_require_setup( self, - mock_db, - mock_revoke_token, - mock_get_invitation, - mock_create_tenant_member, + mock_db: MagicMock, + mock_revoke_token: MagicMock, + mock_get_invitation: MagicMock, + mock_create_tenant_member: MagicMock, app: Flask, - mock_invitation, - mock_account, - mock_switch_tenant, + mock_invitation: MagicMock, + mock_account: MagicMock, + mock_switch_tenant: MagicMock, ): mock_account.status = AccountStatus.ACTIVE mock_get_invitation.return_value = mock_invitation diff --git a/api/tests/unit_tests/controllers/inner_api/workspace/test_workspace.py b/api/tests/unit_tests/controllers/inner_api/workspace/test_workspace.py index 7d2193adc69..25ae0778d4b 100644 --- a/api/tests/unit_tests/controllers/inner_api/workspace/test_workspace.py +++ b/api/tests/unit_tests/controllers/inner_api/workspace/test_workspace.py @@ -116,7 +116,9 @@ class TestEnterpriseWorkspace: assert result["tenant"]["id"] == "tenant-id" assert result["tenant"]["name"] == "My Workspace" mock_tenant_svc.create_tenant.assert_called_once_with("My Workspace", is_from_dashboard=True) - mock_tenant_svc.create_tenant_member.assert_called_once_with(mock_tenant, mock_account, role="owner") + mock_tenant_svc.create_tenant_member.assert_called_once_with( + mock_tenant, mock_account, mock_db.session, role="owner" + ) mock_event.send.assert_called_once_with(mock_tenant) @patch("controllers.inner_api.workspace.workspace.db") diff --git a/api/tests/unit_tests/services/test_account_service.py b/api/tests/unit_tests/services/test_account_service.py index 0bfa4afb5ba..3b5c6cc9bd6 100644 --- a/api/tests/unit_tests/services/test_account_service.py +++ b/api/tests/unit_tests/services/test_account_service.py @@ -794,7 +794,9 @@ class TestTenantService: mock_db_dependencies["db"].session.add = MagicMock() # Execute test - result = TenantService.create_tenant_member(mock_tenant, mock_account, "normal") + result = TenantService.create_tenant_member( + mock_tenant, mock_account, mock_db_dependencies["db"].session, "normal" + ) # Verify member was created with correct parameters assert result is not None @@ -1483,7 +1485,9 @@ class TestRegisterService: timezone=None, ) mock_create_tenant.assert_called_once_with("Test User's Workspace") - mock_create_member.assert_called_once_with(mock_tenant, mock_account, role="owner") + mock_create_member.assert_called_once_with( + mock_tenant, mock_account, mock_db_dependencies["db"].session, role="owner" + ) mock_event.send.assert_called_once_with(mock_tenant) self._assert_database_operations_called(mock_db_dependencies["db"]) @@ -1863,7 +1867,9 @@ class TestRegisterService: ) mock_lookup.assert_called_once_with(mixed_email) 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_create_member.assert_called_once_with( + mock_tenant, mock_new_account, mock_db_dependencies["db"].session, "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, "normal", requires_setup=True @@ -1910,7 +1916,9 @@ class TestRegisterService: # Verify results assert result == "invite-token-123" - mock_create_member.assert_called_once_with(mock_tenant, mock_existing_account, "normal") + mock_create_member.assert_called_once_with( + mock_tenant, mock_existing_account, mock_db_dependencies["db"].session, "normal" + ) mock_generate_token.assert_called_once_with( mock_tenant, mock_existing_account, "normal", requires_setup=True ) @@ -2044,7 +2052,7 @@ class TestRegisterService: assert result == "rbac-token" mock_create_member.assert_called_once_with( - mock_tenant, mock_new_account, TenantAccountRole.NORMAL.value + mock_tenant, mock_new_account, mock_db_dependencies["db"].session, TenantAccountRole.NORMAL.value ) mock_rbac_service.MemberRoles.replace.assert_called_once_with( tenant_id=str(mock_tenant.id), @@ -2089,7 +2097,10 @@ class TestRegisterService: assert result == "rbac-token" mock_create_member.assert_called_once_with( - mock_tenant, mock_existing_account, TenantAccountRole.NORMAL.value + mock_tenant, + mock_existing_account, + mock_db_dependencies["db"].session, + TenantAccountRole.NORMAL.value, ) mock_rbac_service.MemberRoles.replace.assert_called_once_with( tenant_id=str(mock_tenant.id), @@ -2133,7 +2144,10 @@ class TestRegisterService: ) mock_create_member.assert_called_once_with( - mock_tenant, mock_existing_account, TenantAccountRole.NORMAL.value + mock_tenant, + mock_existing_account, + mock_db_dependencies["db"].session, + TenantAccountRole.NORMAL.value, ) mock_rbac_service.MemberRoles.replace.assert_called_once_with( tenant_id=str(mock_tenant.id), @@ -2180,7 +2194,9 @@ class TestRegisterService: ) assert result == "legacy-token" - mock_create_member.assert_called_once_with(mock_tenant, mock_new_account, "editor") + mock_create_member.assert_called_once_with( + mock_tenant, mock_new_account, mock_db_dependencies["db"].session, "editor" + ) mock_rbac_service.MemberRoles.replace.assert_not_called() # ==================== Token Management Tests ==================== diff --git a/api/tests/unit_tests/services/test_agent_app_feature_service.py b/api/tests/unit_tests/services/test_agent_app_feature_service.py index 5503540356f..3d9337d79fb 100644 --- a/api/tests/unit_tests/services/test_agent_app_feature_service.py +++ b/api/tests/unit_tests/services/test_agent_app_feature_service.py @@ -12,7 +12,6 @@ from typing import Any import pytest -from services import agent_app_feature_service as svc_mod from services.agent_app_feature_service import AgentAppFeatureConfigService TENANT_ID = "11111111-1111-1111-1111-111111111111" @@ -89,9 +88,8 @@ class _FakeWriteSession: class TestUpdateFeatures: - def test_persists_new_app_model_config_version(self, monkeypatch: pytest.MonkeyPatch): + def test_persists_new_app_model_config_version(self): session = _FakeWriteSession() - monkeypatch.setattr(svc_mod.db, "session", session) app_model = SimpleNamespace( tenant_id=TENANT_ID, id="app-1", app_model_config_id=None, updated_by=None, updated_at=None ) @@ -101,6 +99,7 @@ class TestUpdateFeatures: app_model=app_model, # type: ignore[arg-type] account=account, # type: ignore[arg-type] config={"opening_statement": "Hi!", "suggested_questions_after_answer": {"enabled": True}}, + session=session, ) # New row carries the features but no Soul-owned model/prompt/agent_mode.