hotfix: add test containers based tests for webapp auth service (#24397)
This commit is contained in:
@@ -0,0 +1,877 @@
|
|||||||
|
from unittest.mock import patch
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
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.model import App, Site
|
||||||
|
from services.errors.account import AccountLoginError, AccountNotFoundError, AccountPasswordError
|
||||||
|
from services.webapp_auth_service import WebAppAuthService, WebAppAuthType
|
||||||
|
|
||||||
|
|
||||||
|
class TestWebAppAuthService:
|
||||||
|
"""Integration tests for WebAppAuthService using testcontainers."""
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def mock_external_service_dependencies(self):
|
||||||
|
"""Mock setup for external service dependencies."""
|
||||||
|
with (
|
||||||
|
patch("services.webapp_auth_service.PassportService") as mock_passport_service,
|
||||||
|
patch("services.webapp_auth_service.TokenManager") as mock_token_manager,
|
||||||
|
patch("services.webapp_auth_service.send_email_code_login_mail_task") as mock_mail_task,
|
||||||
|
patch("services.webapp_auth_service.AppService") as mock_app_service,
|
||||||
|
patch("services.webapp_auth_service.EnterpriseService") as mock_enterprise_service,
|
||||||
|
):
|
||||||
|
# Setup default mock returns
|
||||||
|
mock_passport_service.return_value.issue.return_value = "mock_jwt_token"
|
||||||
|
mock_token_manager.generate_token.return_value = "mock_token"
|
||||||
|
mock_token_manager.get_token_data.return_value = {"code": "123456"}
|
||||||
|
mock_mail_task.delay.return_value = None
|
||||||
|
mock_app_service.get_app_id_by_code.return_value = "mock_app_id"
|
||||||
|
mock_enterprise_service.WebAppAuth.get_app_access_mode_by_id.return_value = type(
|
||||||
|
"MockWebAppAuth", (), {"access_mode": "private"}
|
||||||
|
)()
|
||||||
|
mock_enterprise_service.WebAppAuth.get_app_access_mode_by_code.return_value = type(
|
||||||
|
"MockWebAppAuth", (), {"access_mode": "private"}
|
||||||
|
)()
|
||||||
|
|
||||||
|
yield {
|
||||||
|
"passport_service": mock_passport_service,
|
||||||
|
"token_manager": mock_token_manager,
|
||||||
|
"mail_task": mock_mail_task,
|
||||||
|
"app_service": mock_app_service,
|
||||||
|
"enterprise_service": mock_enterprise_service,
|
||||||
|
}
|
||||||
|
|
||||||
|
def _create_test_account_and_tenant(self, db_session_with_containers, mock_external_service_dependencies):
|
||||||
|
"""
|
||||||
|
Helper method to create a test account and tenant for testing.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
db_session_with_containers: Database session from testcontainers infrastructure
|
||||||
|
mock_external_service_dependencies: Mock dependencies
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
tuple: (account, tenant) - Created account and tenant instances
|
||||||
|
"""
|
||||||
|
fake = Faker()
|
||||||
|
|
||||||
|
# Create account
|
||||||
|
account = Account(
|
||||||
|
email=fake.email(),
|
||||||
|
name=fake.name(),
|
||||||
|
interface_language="en-US",
|
||||||
|
status="active",
|
||||||
|
)
|
||||||
|
|
||||||
|
from extensions.ext_database import db
|
||||||
|
|
||||||
|
db.session.add(account)
|
||||||
|
db.session.commit()
|
||||||
|
|
||||||
|
# Create tenant for the account
|
||||||
|
tenant = Tenant(
|
||||||
|
name=fake.company(),
|
||||||
|
status="normal",
|
||||||
|
)
|
||||||
|
db.session.add(tenant)
|
||||||
|
db.session.commit()
|
||||||
|
|
||||||
|
# Create tenant-account join
|
||||||
|
join = TenantAccountJoin(
|
||||||
|
tenant_id=tenant.id,
|
||||||
|
account_id=account.id,
|
||||||
|
role=TenantAccountRole.OWNER.value,
|
||||||
|
current=True,
|
||||||
|
)
|
||||||
|
db.session.add(join)
|
||||||
|
db.session.commit()
|
||||||
|
|
||||||
|
# Set current tenant for account
|
||||||
|
account.current_tenant = tenant
|
||||||
|
|
||||||
|
return account, tenant
|
||||||
|
|
||||||
|
def _create_test_account_with_password(self, db_session_with_containers, mock_external_service_dependencies):
|
||||||
|
"""
|
||||||
|
Helper method to create a test account with password for testing.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
db_session_with_containers: Database session from testcontainers infrastructure
|
||||||
|
mock_external_service_dependencies: Mock dependencies
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
tuple: (account, tenant, password) - Created account, tenant and password
|
||||||
|
"""
|
||||||
|
fake = Faker()
|
||||||
|
password = fake.password(length=12)
|
||||||
|
|
||||||
|
# Create account with password
|
||||||
|
account = Account(
|
||||||
|
email=fake.email(),
|
||||||
|
name=fake.name(),
|
||||||
|
interface_language="en-US",
|
||||||
|
status="active",
|
||||||
|
)
|
||||||
|
|
||||||
|
# Hash password
|
||||||
|
salt = b"test_salt_16_bytes"
|
||||||
|
password_hash = hash_password(password, salt)
|
||||||
|
|
||||||
|
# Convert to base64 for storage
|
||||||
|
import base64
|
||||||
|
|
||||||
|
account.password = base64.b64encode(password_hash).decode()
|
||||||
|
account.password_salt = base64.b64encode(salt).decode()
|
||||||
|
|
||||||
|
from extensions.ext_database import db
|
||||||
|
|
||||||
|
db.session.add(account)
|
||||||
|
db.session.commit()
|
||||||
|
|
||||||
|
# Create tenant for the account
|
||||||
|
tenant = Tenant(
|
||||||
|
name=fake.company(),
|
||||||
|
status="normal",
|
||||||
|
)
|
||||||
|
db.session.add(tenant)
|
||||||
|
db.session.commit()
|
||||||
|
|
||||||
|
# Create tenant-account join
|
||||||
|
join = TenantAccountJoin(
|
||||||
|
tenant_id=tenant.id,
|
||||||
|
account_id=account.id,
|
||||||
|
role=TenantAccountRole.OWNER.value,
|
||||||
|
current=True,
|
||||||
|
)
|
||||||
|
db.session.add(join)
|
||||||
|
db.session.commit()
|
||||||
|
|
||||||
|
# Set current tenant for account
|
||||||
|
account.current_tenant = tenant
|
||||||
|
|
||||||
|
return account, tenant, password
|
||||||
|
|
||||||
|
def _create_test_app_and_site(self, db_session_with_containers, mock_external_service_dependencies, tenant):
|
||||||
|
"""
|
||||||
|
Helper method to create a test app and site for testing.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
db_session_with_containers: Database session from testcontainers infrastructure
|
||||||
|
mock_external_service_dependencies: Mock dependencies
|
||||||
|
tenant: Tenant instance to associate with
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
tuple: (app, site) - Created app and site instances
|
||||||
|
"""
|
||||||
|
fake = Faker()
|
||||||
|
|
||||||
|
# Create app
|
||||||
|
app = App(
|
||||||
|
tenant_id=tenant.id,
|
||||||
|
name=fake.company(),
|
||||||
|
description=fake.text(max_nb_chars=100),
|
||||||
|
mode="chat",
|
||||||
|
icon_type="emoji",
|
||||||
|
icon="🤖",
|
||||||
|
icon_background="#FF6B6B",
|
||||||
|
api_rph=100,
|
||||||
|
api_rpm=10,
|
||||||
|
enable_site=True,
|
||||||
|
enable_api=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
from extensions.ext_database import db
|
||||||
|
|
||||||
|
db.session.add(app)
|
||||||
|
db.session.commit()
|
||||||
|
|
||||||
|
# Create site
|
||||||
|
site = Site(
|
||||||
|
app_id=app.id,
|
||||||
|
title=fake.company(),
|
||||||
|
code=fake.unique.lexify(text="??????"),
|
||||||
|
description=fake.text(max_nb_chars=100),
|
||||||
|
default_language="en-US",
|
||||||
|
status="normal",
|
||||||
|
customize_token_strategy="not_allow",
|
||||||
|
)
|
||||||
|
db.session.add(site)
|
||||||
|
db.session.commit()
|
||||||
|
|
||||||
|
return app, site
|
||||||
|
|
||||||
|
def test_authenticate_success(self, db_session_with_containers, mock_external_service_dependencies):
|
||||||
|
"""
|
||||||
|
Test successful authentication with valid email and password.
|
||||||
|
|
||||||
|
This test verifies:
|
||||||
|
- Proper authentication with valid credentials
|
||||||
|
- Correct account return
|
||||||
|
- Database state consistency
|
||||||
|
"""
|
||||||
|
# Arrange: Create test data
|
||||||
|
account, tenant, password = self._create_test_account_with_password(
|
||||||
|
db_session_with_containers, mock_external_service_dependencies
|
||||||
|
)
|
||||||
|
|
||||||
|
# Act: Execute authentication
|
||||||
|
result = WebAppAuthService.authenticate(account.email, password)
|
||||||
|
|
||||||
|
# Assert: Verify successful authentication
|
||||||
|
assert result is not None
|
||||||
|
assert result.id == account.id
|
||||||
|
assert result.email == account.email
|
||||||
|
assert result.name == account.name
|
||||||
|
assert result.status == AccountStatus.ACTIVE.value
|
||||||
|
|
||||||
|
# Verify database state
|
||||||
|
from extensions.ext_database import db
|
||||||
|
|
||||||
|
db.session.refresh(result)
|
||||||
|
assert result.id is not None
|
||||||
|
assert result.password is not None
|
||||||
|
assert result.password_salt is not None
|
||||||
|
|
||||||
|
def test_authenticate_account_not_found(self, db_session_with_containers, mock_external_service_dependencies):
|
||||||
|
"""
|
||||||
|
Test authentication with non-existent email.
|
||||||
|
|
||||||
|
This test verifies:
|
||||||
|
- Proper error handling for non-existent accounts
|
||||||
|
- Correct exception type and message
|
||||||
|
"""
|
||||||
|
# Arrange: Use non-existent email
|
||||||
|
fake = Faker()
|
||||||
|
non_existent_email = fake.email()
|
||||||
|
|
||||||
|
# Act & Assert: Verify proper error handling
|
||||||
|
with pytest.raises(AccountNotFoundError):
|
||||||
|
WebAppAuthService.authenticate(non_existent_email, "any_password")
|
||||||
|
|
||||||
|
def test_authenticate_account_banned(self, db_session_with_containers, mock_external_service_dependencies):
|
||||||
|
"""
|
||||||
|
Test authentication with banned account.
|
||||||
|
|
||||||
|
This test verifies:
|
||||||
|
- Proper error handling for banned accounts
|
||||||
|
- Correct exception type and message
|
||||||
|
"""
|
||||||
|
# Arrange: Create banned account
|
||||||
|
fake = Faker()
|
||||||
|
password = fake.password(length=12)
|
||||||
|
|
||||||
|
account = Account(
|
||||||
|
email=fake.email(),
|
||||||
|
name=fake.name(),
|
||||||
|
interface_language="en-US",
|
||||||
|
status=AccountStatus.BANNED.value,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Hash password
|
||||||
|
salt = b"test_salt_16_bytes"
|
||||||
|
password_hash = hash_password(password, salt)
|
||||||
|
|
||||||
|
# Convert to base64 for storage
|
||||||
|
import base64
|
||||||
|
|
||||||
|
account.password = base64.b64encode(password_hash).decode()
|
||||||
|
account.password_salt = base64.b64encode(salt).decode()
|
||||||
|
|
||||||
|
from extensions.ext_database import db
|
||||||
|
|
||||||
|
db.session.add(account)
|
||||||
|
db.session.commit()
|
||||||
|
|
||||||
|
# Act & Assert: Verify proper error handling
|
||||||
|
with pytest.raises(AccountLoginError) as exc_info:
|
||||||
|
WebAppAuthService.authenticate(account.email, password)
|
||||||
|
|
||||||
|
assert "Account is banned." in str(exc_info.value)
|
||||||
|
|
||||||
|
def test_authenticate_invalid_password(self, db_session_with_containers, mock_external_service_dependencies):
|
||||||
|
"""
|
||||||
|
Test authentication with invalid password.
|
||||||
|
|
||||||
|
This test verifies:
|
||||||
|
- Proper error handling for invalid passwords
|
||||||
|
- Correct exception type and message
|
||||||
|
"""
|
||||||
|
# Arrange: Create account with password
|
||||||
|
account, tenant, correct_password = self._create_test_account_with_password(
|
||||||
|
db_session_with_containers, mock_external_service_dependencies
|
||||||
|
)
|
||||||
|
|
||||||
|
# Act & Assert: Verify proper error handling with wrong password
|
||||||
|
with pytest.raises(AccountPasswordError) as exc_info:
|
||||||
|
WebAppAuthService.authenticate(account.email, "wrong_password")
|
||||||
|
|
||||||
|
assert "Invalid email or password." in str(exc_info.value)
|
||||||
|
|
||||||
|
def test_authenticate_account_without_password(
|
||||||
|
self, db_session_with_containers, mock_external_service_dependencies
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
Test authentication for account without password.
|
||||||
|
|
||||||
|
This test verifies:
|
||||||
|
- Proper error handling for accounts without password
|
||||||
|
- Correct exception type and message
|
||||||
|
"""
|
||||||
|
# Arrange: Create account without password
|
||||||
|
fake = Faker()
|
||||||
|
|
||||||
|
account = Account(
|
||||||
|
email=fake.email(),
|
||||||
|
name=fake.name(),
|
||||||
|
interface_language="en-US",
|
||||||
|
status="active",
|
||||||
|
)
|
||||||
|
|
||||||
|
from extensions.ext_database import db
|
||||||
|
|
||||||
|
db.session.add(account)
|
||||||
|
db.session.commit()
|
||||||
|
|
||||||
|
# Act & Assert: Verify proper error handling
|
||||||
|
with pytest.raises(AccountPasswordError) as exc_info:
|
||||||
|
WebAppAuthService.authenticate(account.email, "any_password")
|
||||||
|
|
||||||
|
assert "Invalid email or password." in str(exc_info.value)
|
||||||
|
|
||||||
|
def test_login_success(self, db_session_with_containers, mock_external_service_dependencies):
|
||||||
|
"""
|
||||||
|
Test successful login and JWT token generation.
|
||||||
|
|
||||||
|
This test verifies:
|
||||||
|
- Proper JWT token generation
|
||||||
|
- Correct token format and content
|
||||||
|
- Mock service integration
|
||||||
|
"""
|
||||||
|
# Arrange: Create test account
|
||||||
|
account, tenant = self._create_test_account_and_tenant(
|
||||||
|
db_session_with_containers, mock_external_service_dependencies
|
||||||
|
)
|
||||||
|
|
||||||
|
# Act: Execute login
|
||||||
|
result = WebAppAuthService.login(account)
|
||||||
|
|
||||||
|
# Assert: Verify successful login
|
||||||
|
assert result is not None
|
||||||
|
assert result == "mock_jwt_token"
|
||||||
|
|
||||||
|
# Verify mock service was called correctly
|
||||||
|
mock_external_service_dependencies["passport_service"].return_value.issue.assert_called_once()
|
||||||
|
call_args = mock_external_service_dependencies["passport_service"].return_value.issue.call_args[0][0]
|
||||||
|
|
||||||
|
assert call_args["sub"] == "Web API Passport"
|
||||||
|
assert call_args["user_id"] == account.id
|
||||||
|
assert call_args["session_id"] == account.email
|
||||||
|
assert call_args["token_source"] == "webapp_login_token"
|
||||||
|
assert call_args["auth_type"] == "internal"
|
||||||
|
assert "exp" in call_args
|
||||||
|
|
||||||
|
def test_get_user_through_email_success(self, db_session_with_containers, mock_external_service_dependencies):
|
||||||
|
"""
|
||||||
|
Test successful user retrieval through email.
|
||||||
|
|
||||||
|
This test verifies:
|
||||||
|
- Proper user retrieval by email
|
||||||
|
- Correct account return
|
||||||
|
- Database state consistency
|
||||||
|
"""
|
||||||
|
# Arrange: Create test data
|
||||||
|
account, tenant = self._create_test_account_and_tenant(
|
||||||
|
db_session_with_containers, mock_external_service_dependencies
|
||||||
|
)
|
||||||
|
|
||||||
|
# Act: Execute user retrieval
|
||||||
|
result = WebAppAuthService.get_user_through_email(account.email)
|
||||||
|
|
||||||
|
# Assert: Verify successful retrieval
|
||||||
|
assert result is not None
|
||||||
|
assert result.id == account.id
|
||||||
|
assert result.email == account.email
|
||||||
|
assert result.name == account.name
|
||||||
|
assert result.status == AccountStatus.ACTIVE.value
|
||||||
|
|
||||||
|
# Verify database state
|
||||||
|
from extensions.ext_database import db
|
||||||
|
|
||||||
|
db.session.refresh(result)
|
||||||
|
assert result.id is not None
|
||||||
|
|
||||||
|
def test_get_user_through_email_not_found(self, db_session_with_containers, mock_external_service_dependencies):
|
||||||
|
"""
|
||||||
|
Test user retrieval with non-existent email.
|
||||||
|
|
||||||
|
This test verifies:
|
||||||
|
- Proper handling for non-existent users
|
||||||
|
- Correct return value (None)
|
||||||
|
"""
|
||||||
|
# Arrange: Use non-existent email
|
||||||
|
fake = Faker()
|
||||||
|
non_existent_email = fake.email()
|
||||||
|
|
||||||
|
# Act: Execute user retrieval
|
||||||
|
result = WebAppAuthService.get_user_through_email(non_existent_email)
|
||||||
|
|
||||||
|
# Assert: Verify proper handling
|
||||||
|
assert result is None
|
||||||
|
|
||||||
|
def test_get_user_through_email_banned(self, db_session_with_containers, mock_external_service_dependencies):
|
||||||
|
"""
|
||||||
|
Test user retrieval with banned account.
|
||||||
|
|
||||||
|
This test verifies:
|
||||||
|
- Proper error handling for banned accounts
|
||||||
|
- Correct exception type and message
|
||||||
|
"""
|
||||||
|
# Arrange: Create banned account
|
||||||
|
fake = Faker()
|
||||||
|
|
||||||
|
account = Account(
|
||||||
|
email=fake.email(),
|
||||||
|
name=fake.name(),
|
||||||
|
interface_language="en-US",
|
||||||
|
status=AccountStatus.BANNED.value,
|
||||||
|
)
|
||||||
|
|
||||||
|
from extensions.ext_database import db
|
||||||
|
|
||||||
|
db.session.add(account)
|
||||||
|
db.session.commit()
|
||||||
|
|
||||||
|
# Act & Assert: Verify proper error handling
|
||||||
|
with pytest.raises(Unauthorized) as exc_info:
|
||||||
|
WebAppAuthService.get_user_through_email(account.email)
|
||||||
|
|
||||||
|
assert "Account is banned." in str(exc_info.value)
|
||||||
|
|
||||||
|
def test_send_email_code_login_email_with_account(
|
||||||
|
self, db_session_with_containers, mock_external_service_dependencies
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
Test sending email code login email with account.
|
||||||
|
|
||||||
|
This test verifies:
|
||||||
|
- Proper email code generation
|
||||||
|
- Token generation with correct data
|
||||||
|
- Mail task scheduling
|
||||||
|
- Mock service integration
|
||||||
|
"""
|
||||||
|
# Arrange: Create test account
|
||||||
|
account, tenant = self._create_test_account_and_tenant(
|
||||||
|
db_session_with_containers, mock_external_service_dependencies
|
||||||
|
)
|
||||||
|
|
||||||
|
# Act: Execute email code login email sending
|
||||||
|
result = WebAppAuthService.send_email_code_login_email(account=account, language="en-US")
|
||||||
|
|
||||||
|
# Assert: Verify successful email sending
|
||||||
|
assert result is not None
|
||||||
|
assert result == "mock_token"
|
||||||
|
|
||||||
|
# Verify mock services were called correctly
|
||||||
|
mock_external_service_dependencies["token_manager"].generate_token.assert_called_once()
|
||||||
|
mock_external_service_dependencies["mail_task"].delay.assert_called_once()
|
||||||
|
|
||||||
|
# Verify token generation parameters
|
||||||
|
token_call_args = mock_external_service_dependencies["token_manager"].generate_token.call_args
|
||||||
|
assert token_call_args[1]["account"] == account
|
||||||
|
assert token_call_args[1]["email"] == account.email
|
||||||
|
assert token_call_args[1]["token_type"] == "email_code_login"
|
||||||
|
assert "code" in token_call_args[1]["additional_data"]
|
||||||
|
|
||||||
|
# Verify mail task parameters
|
||||||
|
mail_call_args = mock_external_service_dependencies["mail_task"].delay.call_args
|
||||||
|
assert mail_call_args[1]["language"] == "en-US"
|
||||||
|
assert mail_call_args[1]["to"] == account.email
|
||||||
|
assert "code" in mail_call_args[1]
|
||||||
|
|
||||||
|
def test_send_email_code_login_email_with_email_only(
|
||||||
|
self, db_session_with_containers, mock_external_service_dependencies
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
Test sending email code login email with email only.
|
||||||
|
|
||||||
|
This test verifies:
|
||||||
|
- Proper email code generation without account
|
||||||
|
- Token generation with email only
|
||||||
|
- Mail task scheduling
|
||||||
|
- Mock service integration
|
||||||
|
"""
|
||||||
|
# Arrange: Use test email
|
||||||
|
fake = Faker()
|
||||||
|
test_email = fake.email()
|
||||||
|
|
||||||
|
# Act: Execute email code login email sending
|
||||||
|
result = WebAppAuthService.send_email_code_login_email(email=test_email, language="zh-Hans")
|
||||||
|
|
||||||
|
# Assert: Verify successful email sending
|
||||||
|
assert result is not None
|
||||||
|
assert result == "mock_token"
|
||||||
|
|
||||||
|
# Verify mock services were called correctly
|
||||||
|
mock_external_service_dependencies["token_manager"].generate_token.assert_called_once()
|
||||||
|
mock_external_service_dependencies["mail_task"].delay.assert_called_once()
|
||||||
|
|
||||||
|
# Verify token generation parameters
|
||||||
|
token_call_args = mock_external_service_dependencies["token_manager"].generate_token.call_args
|
||||||
|
assert token_call_args[1]["account"] is None
|
||||||
|
assert token_call_args[1]["email"] == test_email
|
||||||
|
assert token_call_args[1]["token_type"] == "email_code_login"
|
||||||
|
assert "code" in token_call_args[1]["additional_data"]
|
||||||
|
|
||||||
|
# Verify mail task parameters
|
||||||
|
mail_call_args = mock_external_service_dependencies["mail_task"].delay.call_args
|
||||||
|
assert mail_call_args[1]["language"] == "zh-Hans"
|
||||||
|
assert mail_call_args[1]["to"] == test_email
|
||||||
|
assert "code" in mail_call_args[1]
|
||||||
|
|
||||||
|
def test_send_email_code_login_email_no_email_provided(
|
||||||
|
self, db_session_with_containers, mock_external_service_dependencies
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
Test sending email code login email without providing email.
|
||||||
|
|
||||||
|
This test verifies:
|
||||||
|
- Proper error handling when no email is provided
|
||||||
|
- Correct exception type and message
|
||||||
|
"""
|
||||||
|
# Arrange: No email provided
|
||||||
|
|
||||||
|
# Act & Assert: Verify proper error handling
|
||||||
|
with pytest.raises(ValueError) as exc_info:
|
||||||
|
WebAppAuthService.send_email_code_login_email()
|
||||||
|
|
||||||
|
assert "Email must be provided." in str(exc_info.value)
|
||||||
|
|
||||||
|
def test_get_email_code_login_data_success(self, db_session_with_containers, mock_external_service_dependencies):
|
||||||
|
"""
|
||||||
|
Test successful retrieval of email code login data.
|
||||||
|
|
||||||
|
This test verifies:
|
||||||
|
- Proper token data retrieval
|
||||||
|
- Correct data format
|
||||||
|
- Mock service integration
|
||||||
|
"""
|
||||||
|
# Arrange: Setup mock return
|
||||||
|
expected_data = {"code": "123456", "email": "test@example.com"}
|
||||||
|
mock_external_service_dependencies["token_manager"].get_token_data.return_value = expected_data
|
||||||
|
|
||||||
|
# Act: Execute data retrieval
|
||||||
|
result = WebAppAuthService.get_email_code_login_data("mock_token")
|
||||||
|
|
||||||
|
# Assert: Verify successful retrieval
|
||||||
|
assert result is not None
|
||||||
|
assert result == expected_data
|
||||||
|
assert result["code"] == "123456"
|
||||||
|
assert result["email"] == "test@example.com"
|
||||||
|
|
||||||
|
# Verify mock service was called correctly
|
||||||
|
mock_external_service_dependencies["token_manager"].get_token_data.assert_called_once_with(
|
||||||
|
"mock_token", "email_code_login"
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_get_email_code_login_data_no_data(self, db_session_with_containers, mock_external_service_dependencies):
|
||||||
|
"""
|
||||||
|
Test email code login data retrieval when no data exists.
|
||||||
|
|
||||||
|
This test verifies:
|
||||||
|
- Proper handling when no token data exists
|
||||||
|
- Correct return value (None)
|
||||||
|
- Mock service integration
|
||||||
|
"""
|
||||||
|
# Arrange: Setup mock return for no data
|
||||||
|
mock_external_service_dependencies["token_manager"].get_token_data.return_value = None
|
||||||
|
|
||||||
|
# Act: Execute data retrieval
|
||||||
|
result = WebAppAuthService.get_email_code_login_data("invalid_token")
|
||||||
|
|
||||||
|
# Assert: Verify proper handling
|
||||||
|
assert result is None
|
||||||
|
|
||||||
|
# Verify mock service was called correctly
|
||||||
|
mock_external_service_dependencies["token_manager"].get_token_data.assert_called_once_with(
|
||||||
|
"invalid_token", "email_code_login"
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_revoke_email_code_login_token_success(
|
||||||
|
self, db_session_with_containers, mock_external_service_dependencies
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
Test successful revocation of email code login token.
|
||||||
|
|
||||||
|
This test verifies:
|
||||||
|
- Proper token revocation
|
||||||
|
- Mock service integration
|
||||||
|
"""
|
||||||
|
# Arrange: Setup mock
|
||||||
|
|
||||||
|
# Act: Execute token revocation
|
||||||
|
WebAppAuthService.revoke_email_code_login_token("mock_token")
|
||||||
|
|
||||||
|
# Assert: Verify mock service was called correctly
|
||||||
|
mock_external_service_dependencies["token_manager"].revoke_token.assert_called_once_with(
|
||||||
|
"mock_token", "email_code_login"
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_create_end_user_success(self, db_session_with_containers, mock_external_service_dependencies):
|
||||||
|
"""
|
||||||
|
Test successful end user creation.
|
||||||
|
|
||||||
|
This test verifies:
|
||||||
|
- Proper end user creation with valid app code
|
||||||
|
- Correct database state after creation
|
||||||
|
- Proper relationship establishment
|
||||||
|
- Mock service integration
|
||||||
|
"""
|
||||||
|
# Arrange: Create test data
|
||||||
|
account, tenant = self._create_test_account_and_tenant(
|
||||||
|
db_session_with_containers, mock_external_service_dependencies
|
||||||
|
)
|
||||||
|
app, site = self._create_test_app_and_site(
|
||||||
|
db_session_with_containers, mock_external_service_dependencies, tenant
|
||||||
|
)
|
||||||
|
|
||||||
|
# Act: Execute end user creation
|
||||||
|
result = WebAppAuthService.create_end_user(site.code, "test@example.com")
|
||||||
|
|
||||||
|
# Assert: Verify successful creation
|
||||||
|
assert result is not None
|
||||||
|
assert result.tenant_id == app.tenant_id
|
||||||
|
assert result.app_id == app.id
|
||||||
|
assert result.type == "browser"
|
||||||
|
assert result.is_anonymous is False
|
||||||
|
assert result.session_id == "test@example.com"
|
||||||
|
assert result.name == "enterpriseuser"
|
||||||
|
assert result.external_user_id == "enterpriseuser"
|
||||||
|
|
||||||
|
# Verify database state
|
||||||
|
from extensions.ext_database import db
|
||||||
|
|
||||||
|
db.session.refresh(result)
|
||||||
|
assert result.id is not None
|
||||||
|
assert result.created_at is not None
|
||||||
|
assert result.updated_at is not None
|
||||||
|
|
||||||
|
def test_create_end_user_site_not_found(self, db_session_with_containers, mock_external_service_dependencies):
|
||||||
|
"""
|
||||||
|
Test end user creation with non-existent site code.
|
||||||
|
|
||||||
|
This test verifies:
|
||||||
|
- Proper error handling for non-existent sites
|
||||||
|
- Correct exception type and message
|
||||||
|
"""
|
||||||
|
# Arrange: Use non-existent site code
|
||||||
|
fake = Faker()
|
||||||
|
non_existent_code = fake.unique.lexify(text="??????")
|
||||||
|
|
||||||
|
# Act & Assert: Verify proper error handling
|
||||||
|
with pytest.raises(NotFound) as exc_info:
|
||||||
|
WebAppAuthService.create_end_user(non_existent_code, "test@example.com")
|
||||||
|
|
||||||
|
assert "Site not found." in str(exc_info.value)
|
||||||
|
|
||||||
|
def test_create_end_user_app_not_found(self, db_session_with_containers, mock_external_service_dependencies):
|
||||||
|
"""
|
||||||
|
Test end user creation when app is not found.
|
||||||
|
|
||||||
|
This test verifies:
|
||||||
|
- Proper error handling when app is missing
|
||||||
|
- Correct exception type and message
|
||||||
|
"""
|
||||||
|
# Arrange: Create site without app
|
||||||
|
fake = Faker()
|
||||||
|
tenant = Tenant(
|
||||||
|
name=fake.company(),
|
||||||
|
status="normal",
|
||||||
|
)
|
||||||
|
|
||||||
|
from extensions.ext_database import db
|
||||||
|
|
||||||
|
db.session.add(tenant)
|
||||||
|
db.session.commit()
|
||||||
|
|
||||||
|
site = Site(
|
||||||
|
app_id="00000000-0000-0000-0000-000000000000",
|
||||||
|
title=fake.company(),
|
||||||
|
code=fake.unique.lexify(text="??????"),
|
||||||
|
description=fake.text(max_nb_chars=100),
|
||||||
|
default_language="en-US",
|
||||||
|
status="normal",
|
||||||
|
customize_token_strategy="not_allow",
|
||||||
|
)
|
||||||
|
db.session.add(site)
|
||||||
|
db.session.commit()
|
||||||
|
|
||||||
|
# Act & Assert: Verify proper error handling
|
||||||
|
with pytest.raises(NotFound) as exc_info:
|
||||||
|
WebAppAuthService.create_end_user(site.code, "test@example.com")
|
||||||
|
|
||||||
|
assert "App not found." in str(exc_info.value)
|
||||||
|
|
||||||
|
def test_is_app_require_permission_check_with_access_mode_private(
|
||||||
|
self, db_session_with_containers, mock_external_service_dependencies
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
Test permission check requirement for private access mode.
|
||||||
|
|
||||||
|
This test verifies:
|
||||||
|
- Proper permission check requirement for private mode
|
||||||
|
- Correct return value
|
||||||
|
- Mock service integration
|
||||||
|
"""
|
||||||
|
# Arrange: Setup test with private access mode
|
||||||
|
|
||||||
|
# Act: Execute permission check requirement test
|
||||||
|
result = WebAppAuthService.is_app_require_permission_check(access_mode="private")
|
||||||
|
|
||||||
|
# Assert: Verify correct result
|
||||||
|
assert result is True
|
||||||
|
|
||||||
|
def test_is_app_require_permission_check_with_access_mode_public(
|
||||||
|
self, db_session_with_containers, mock_external_service_dependencies
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
Test permission check requirement for public access mode.
|
||||||
|
|
||||||
|
This test verifies:
|
||||||
|
- Proper permission check requirement for public mode
|
||||||
|
- Correct return value
|
||||||
|
- Mock service integration
|
||||||
|
"""
|
||||||
|
# Arrange: Setup test with public access mode
|
||||||
|
|
||||||
|
# Act: Execute permission check requirement test
|
||||||
|
result = WebAppAuthService.is_app_require_permission_check(access_mode="public")
|
||||||
|
|
||||||
|
# Assert: Verify correct result
|
||||||
|
assert result is False
|
||||||
|
|
||||||
|
def test_is_app_require_permission_check_with_app_code(
|
||||||
|
self, db_session_with_containers, mock_external_service_dependencies
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
Test permission check requirement using app code.
|
||||||
|
|
||||||
|
This test verifies:
|
||||||
|
- Proper permission check requirement using app code
|
||||||
|
- Correct return value
|
||||||
|
- Mock service integration
|
||||||
|
"""
|
||||||
|
# Arrange: Setup mock for app service
|
||||||
|
mock_external_service_dependencies["app_service"].get_app_id_by_code.return_value = "mock_app_id"
|
||||||
|
|
||||||
|
# Act: Execute permission check requirement test
|
||||||
|
result = WebAppAuthService.is_app_require_permission_check(app_code="mock_app_code")
|
||||||
|
|
||||||
|
# Assert: Verify correct result
|
||||||
|
assert result is True
|
||||||
|
|
||||||
|
# Verify mock service was called correctly
|
||||||
|
mock_external_service_dependencies["app_service"].get_app_id_by_code.assert_called_once_with("mock_app_code")
|
||||||
|
mock_external_service_dependencies[
|
||||||
|
"enterprise_service"
|
||||||
|
].WebAppAuth.get_app_access_mode_by_id.assert_called_once_with("mock_app_id")
|
||||||
|
|
||||||
|
def test_is_app_require_permission_check_no_parameters(
|
||||||
|
self, db_session_with_containers, mock_external_service_dependencies
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
Test permission check requirement with no parameters.
|
||||||
|
|
||||||
|
This test verifies:
|
||||||
|
- Proper error handling when no parameters provided
|
||||||
|
- Correct exception type and message
|
||||||
|
"""
|
||||||
|
# Arrange: No parameters provided
|
||||||
|
|
||||||
|
# Act & Assert: Verify proper error handling
|
||||||
|
with pytest.raises(ValueError) as exc_info:
|
||||||
|
WebAppAuthService.is_app_require_permission_check()
|
||||||
|
|
||||||
|
assert "Either app_code or app_id must be provided." in str(exc_info.value)
|
||||||
|
|
||||||
|
def test_get_app_auth_type_with_access_mode_public(
|
||||||
|
self, db_session_with_containers, mock_external_service_dependencies
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
Test app authentication type for public access mode.
|
||||||
|
|
||||||
|
This test verifies:
|
||||||
|
- Proper authentication type determination for public mode
|
||||||
|
- Correct return value
|
||||||
|
- Mock service integration
|
||||||
|
"""
|
||||||
|
# Arrange: Setup test with public access mode
|
||||||
|
|
||||||
|
# Act: Execute authentication type determination
|
||||||
|
result = WebAppAuthService.get_app_auth_type(access_mode="public")
|
||||||
|
|
||||||
|
# Assert: Verify correct result
|
||||||
|
assert result == WebAppAuthType.PUBLIC
|
||||||
|
|
||||||
|
def test_get_app_auth_type_with_access_mode_private(
|
||||||
|
self, db_session_with_containers, mock_external_service_dependencies
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
Test app authentication type for private access mode.
|
||||||
|
|
||||||
|
This test verifies:
|
||||||
|
- Proper authentication type determination for private mode
|
||||||
|
- Correct return value
|
||||||
|
- Mock service integration
|
||||||
|
"""
|
||||||
|
# Arrange: Setup test with private access mode
|
||||||
|
|
||||||
|
# Act: Execute authentication type determination
|
||||||
|
result = WebAppAuthService.get_app_auth_type(access_mode="private")
|
||||||
|
|
||||||
|
# Assert: Verify correct result
|
||||||
|
assert result == WebAppAuthType.INTERNAL
|
||||||
|
|
||||||
|
def test_get_app_auth_type_with_app_code(self, db_session_with_containers, mock_external_service_dependencies):
|
||||||
|
"""
|
||||||
|
Test app authentication type using app code.
|
||||||
|
|
||||||
|
This test verifies:
|
||||||
|
- Proper authentication type determination using app code
|
||||||
|
- Correct return value
|
||||||
|
- Mock service integration
|
||||||
|
"""
|
||||||
|
# Arrange: Setup mock for enterprise service
|
||||||
|
mock_webapp_auth = type("MockWebAppAuth", (), {"access_mode": "sso_verified"})()
|
||||||
|
mock_external_service_dependencies[
|
||||||
|
"enterprise_service"
|
||||||
|
].WebAppAuth.get_app_access_mode_by_code.return_value = mock_webapp_auth
|
||||||
|
|
||||||
|
# Act: Execute authentication type determination
|
||||||
|
result = WebAppAuthService.get_app_auth_type(app_code="mock_app_code")
|
||||||
|
|
||||||
|
# Assert: Verify correct result
|
||||||
|
assert result == WebAppAuthType.EXTERNAL
|
||||||
|
|
||||||
|
# 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")
|
||||||
|
|
||||||
|
def test_get_app_auth_type_no_parameters(self, db_session_with_containers, mock_external_service_dependencies):
|
||||||
|
"""
|
||||||
|
Test app authentication type with no parameters.
|
||||||
|
|
||||||
|
This test verifies:
|
||||||
|
- Proper error handling when no parameters provided
|
||||||
|
- Correct exception type and message
|
||||||
|
"""
|
||||||
|
# Arrange: No parameters provided
|
||||||
|
|
||||||
|
# Act & Assert: Verify proper error handling
|
||||||
|
with pytest.raises(ValueError) as exc_info:
|
||||||
|
WebAppAuthService.get_app_auth_type()
|
||||||
|
|
||||||
|
assert "Either app_code or access_mode must be provided." in str(exc_info.value)
|
Reference in New Issue
Block a user