Exclude tests directory from pyright type checking (#26496)
Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com> Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
This commit is contained in:
@@ -19,6 +19,7 @@ from core.ops.ops_trace_manager import OpsTraceManager
|
|||||||
from extensions.ext_database import db
|
from extensions.ext_database import db
|
||||||
from fields.app_fields import app_detail_fields, app_detail_fields_with_site, app_pagination_fields
|
from fields.app_fields import app_detail_fields, app_detail_fields_with_site, app_pagination_fields
|
||||||
from libs.login import login_required
|
from libs.login import login_required
|
||||||
|
from libs.validators import validate_description_length
|
||||||
from models import Account, App
|
from models import Account, App
|
||||||
from services.app_dsl_service import AppDslService, ImportMode
|
from services.app_dsl_service import AppDslService, ImportMode
|
||||||
from services.app_service import AppService
|
from services.app_service import AppService
|
||||||
@@ -28,12 +29,6 @@ from services.feature_service import FeatureService
|
|||||||
ALLOW_CREATE_APP_MODES = ["chat", "agent-chat", "advanced-chat", "workflow", "completion"]
|
ALLOW_CREATE_APP_MODES = ["chat", "agent-chat", "advanced-chat", "workflow", "completion"]
|
||||||
|
|
||||||
|
|
||||||
def _validate_description_length(description):
|
|
||||||
if description and len(description) > 400:
|
|
||||||
raise ValueError("Description cannot exceed 400 characters.")
|
|
||||||
return description
|
|
||||||
|
|
||||||
|
|
||||||
@console_ns.route("/apps")
|
@console_ns.route("/apps")
|
||||||
class AppListApi(Resource):
|
class AppListApi(Resource):
|
||||||
@api.doc("list_apps")
|
@api.doc("list_apps")
|
||||||
@@ -138,7 +133,7 @@ class AppListApi(Resource):
|
|||||||
"""Create app"""
|
"""Create app"""
|
||||||
parser = reqparse.RequestParser()
|
parser = reqparse.RequestParser()
|
||||||
parser.add_argument("name", type=str, required=True, location="json")
|
parser.add_argument("name", type=str, required=True, location="json")
|
||||||
parser.add_argument("description", type=_validate_description_length, location="json")
|
parser.add_argument("description", type=validate_description_length, location="json")
|
||||||
parser.add_argument("mode", type=str, choices=ALLOW_CREATE_APP_MODES, location="json")
|
parser.add_argument("mode", type=str, choices=ALLOW_CREATE_APP_MODES, location="json")
|
||||||
parser.add_argument("icon_type", type=str, location="json")
|
parser.add_argument("icon_type", type=str, location="json")
|
||||||
parser.add_argument("icon", type=str, location="json")
|
parser.add_argument("icon", type=str, location="json")
|
||||||
@@ -219,7 +214,7 @@ class AppApi(Resource):
|
|||||||
|
|
||||||
parser = reqparse.RequestParser()
|
parser = reqparse.RequestParser()
|
||||||
parser.add_argument("name", type=str, required=True, nullable=False, location="json")
|
parser.add_argument("name", type=str, required=True, nullable=False, location="json")
|
||||||
parser.add_argument("description", type=_validate_description_length, location="json")
|
parser.add_argument("description", type=validate_description_length, location="json")
|
||||||
parser.add_argument("icon_type", type=str, location="json")
|
parser.add_argument("icon_type", type=str, location="json")
|
||||||
parser.add_argument("icon", type=str, location="json")
|
parser.add_argument("icon", type=str, location="json")
|
||||||
parser.add_argument("icon_background", type=str, location="json")
|
parser.add_argument("icon_background", type=str, location="json")
|
||||||
@@ -297,7 +292,7 @@ class AppCopyApi(Resource):
|
|||||||
|
|
||||||
parser = reqparse.RequestParser()
|
parser = reqparse.RequestParser()
|
||||||
parser.add_argument("name", type=str, location="json")
|
parser.add_argument("name", type=str, location="json")
|
||||||
parser.add_argument("description", type=_validate_description_length, location="json")
|
parser.add_argument("description", type=validate_description_length, location="json")
|
||||||
parser.add_argument("icon_type", type=str, location="json")
|
parser.add_argument("icon_type", type=str, location="json")
|
||||||
parser.add_argument("icon", type=str, location="json")
|
parser.add_argument("icon", type=str, location="json")
|
||||||
parser.add_argument("icon_background", type=str, location="json")
|
parser.add_argument("icon_background", type=str, location="json")
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ from fields.app_fields import related_app_list
|
|||||||
from fields.dataset_fields import dataset_detail_fields, dataset_query_detail_fields
|
from fields.dataset_fields import dataset_detail_fields, dataset_query_detail_fields
|
||||||
from fields.document_fields import document_status_fields
|
from fields.document_fields import document_status_fields
|
||||||
from libs.login import login_required
|
from libs.login import login_required
|
||||||
|
from libs.validators import validate_description_length
|
||||||
from models import ApiToken, Dataset, Document, DocumentSegment, UploadFile
|
from models import ApiToken, Dataset, Document, DocumentSegment, UploadFile
|
||||||
from models.account import Account
|
from models.account import Account
|
||||||
from models.dataset import DatasetPermissionEnum
|
from models.dataset import DatasetPermissionEnum
|
||||||
@@ -44,12 +45,6 @@ def _validate_name(name: str) -> str:
|
|||||||
return name
|
return name
|
||||||
|
|
||||||
|
|
||||||
def _validate_description_length(description):
|
|
||||||
if description and len(description) > 400:
|
|
||||||
raise ValueError("Description cannot exceed 400 characters.")
|
|
||||||
return description
|
|
||||||
|
|
||||||
|
|
||||||
@console_ns.route("/datasets")
|
@console_ns.route("/datasets")
|
||||||
class DatasetListApi(Resource):
|
class DatasetListApi(Resource):
|
||||||
@api.doc("get_datasets")
|
@api.doc("get_datasets")
|
||||||
@@ -149,7 +144,7 @@ class DatasetListApi(Resource):
|
|||||||
)
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"description",
|
"description",
|
||||||
type=_validate_description_length,
|
type=validate_description_length,
|
||||||
nullable=True,
|
nullable=True,
|
||||||
required=False,
|
required=False,
|
||||||
default="",
|
default="",
|
||||||
@@ -290,7 +285,7 @@ class DatasetApi(Resource):
|
|||||||
help="type is required. Name must be between 1 to 40 characters.",
|
help="type is required. Name must be between 1 to 40 characters.",
|
||||||
type=_validate_name,
|
type=_validate_name,
|
||||||
)
|
)
|
||||||
parser.add_argument("description", location="json", store_missing=False, type=_validate_description_length)
|
parser.add_argument("description", location="json", store_missing=False, type=validate_description_length)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"indexing_technique",
|
"indexing_technique",
|
||||||
type=str,
|
type=str,
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ from core.provider_manager import ProviderManager
|
|||||||
from fields.dataset_fields import dataset_detail_fields
|
from fields.dataset_fields import dataset_detail_fields
|
||||||
from fields.tag_fields import build_dataset_tag_fields
|
from fields.tag_fields import build_dataset_tag_fields
|
||||||
from libs.login import current_user
|
from libs.login import current_user
|
||||||
|
from libs.validators import validate_description_length
|
||||||
from models.account import Account
|
from models.account import Account
|
||||||
from models.dataset import Dataset, DatasetPermissionEnum
|
from models.dataset import Dataset, DatasetPermissionEnum
|
||||||
from models.provider_ids import ModelProviderID
|
from models.provider_ids import ModelProviderID
|
||||||
@@ -31,12 +32,6 @@ def _validate_name(name):
|
|||||||
return name
|
return name
|
||||||
|
|
||||||
|
|
||||||
def _validate_description_length(description):
|
|
||||||
if description and len(description) > 400:
|
|
||||||
raise ValueError("Description cannot exceed 400 characters.")
|
|
||||||
return description
|
|
||||||
|
|
||||||
|
|
||||||
# Define parsers for dataset operations
|
# Define parsers for dataset operations
|
||||||
dataset_create_parser = reqparse.RequestParser()
|
dataset_create_parser = reqparse.RequestParser()
|
||||||
dataset_create_parser.add_argument(
|
dataset_create_parser.add_argument(
|
||||||
@@ -48,7 +43,7 @@ dataset_create_parser.add_argument(
|
|||||||
)
|
)
|
||||||
dataset_create_parser.add_argument(
|
dataset_create_parser.add_argument(
|
||||||
"description",
|
"description",
|
||||||
type=_validate_description_length,
|
type=validate_description_length,
|
||||||
nullable=True,
|
nullable=True,
|
||||||
required=False,
|
required=False,
|
||||||
default="",
|
default="",
|
||||||
@@ -101,7 +96,7 @@ dataset_update_parser.add_argument(
|
|||||||
type=_validate_name,
|
type=_validate_name,
|
||||||
)
|
)
|
||||||
dataset_update_parser.add_argument(
|
dataset_update_parser.add_argument(
|
||||||
"description", location="json", store_missing=False, type=_validate_description_length
|
"description", location="json", store_missing=False, type=validate_description_length
|
||||||
)
|
)
|
||||||
dataset_update_parser.add_argument(
|
dataset_update_parser.add_argument(
|
||||||
"indexing_technique",
|
"indexing_technique",
|
||||||
|
|||||||
5
api/libs/validators.py
Normal file
5
api/libs/validators.py
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
def validate_description_length(description: str | None) -> str | None:
|
||||||
|
"""Validate description length."""
|
||||||
|
if description and len(description) > 400:
|
||||||
|
raise ValueError("Description cannot exceed 400 characters.")
|
||||||
|
return description
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
{
|
{
|
||||||
"include": ["."],
|
"include": ["."],
|
||||||
"exclude": [
|
"exclude": [
|
||||||
".venv",
|
|
||||||
"tests/",
|
"tests/",
|
||||||
|
".venv",
|
||||||
"migrations/",
|
"migrations/",
|
||||||
"core/rag",
|
"core/rag",
|
||||||
"extensions",
|
"extensions",
|
||||||
|
|||||||
@@ -11,8 +11,8 @@ from controllers.console.app import completion as completion_api
|
|||||||
from controllers.console.app import message as message_api
|
from controllers.console.app import message as message_api
|
||||||
from controllers.console.app import wraps
|
from controllers.console.app import wraps
|
||||||
from libs.datetime_utils import naive_utc_now
|
from libs.datetime_utils import naive_utc_now
|
||||||
from models import Account, App, Tenant
|
from models import App, Tenant
|
||||||
from models.account import TenantAccountRole
|
from models.account import Account, TenantAccountJoin, TenantAccountRole
|
||||||
from models.model import AppMode
|
from models.model import AppMode
|
||||||
from services.app_generate_service import AppGenerateService
|
from services.app_generate_service import AppGenerateService
|
||||||
|
|
||||||
@@ -31,9 +31,8 @@ class TestChatMessageApiPermissions:
|
|||||||
return app
|
return app
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def mock_account(self):
|
def mock_account(self, monkeypatch: pytest.MonkeyPatch):
|
||||||
"""Create a mock Account for testing."""
|
"""Create a mock Account for testing."""
|
||||||
|
|
||||||
account = Account()
|
account = Account()
|
||||||
account.id = str(uuid.uuid4())
|
account.id = str(uuid.uuid4())
|
||||||
account.name = "Test User"
|
account.name = "Test User"
|
||||||
@@ -42,12 +41,24 @@ class TestChatMessageApiPermissions:
|
|||||||
account.created_at = naive_utc_now()
|
account.created_at = naive_utc_now()
|
||||||
account.updated_at = naive_utc_now()
|
account.updated_at = naive_utc_now()
|
||||||
|
|
||||||
# Create mock tenant
|
|
||||||
tenant = Tenant()
|
tenant = Tenant()
|
||||||
tenant.id = str(uuid.uuid4())
|
tenant.id = str(uuid.uuid4())
|
||||||
tenant.name = "Test Tenant"
|
tenant.name = "Test Tenant"
|
||||||
|
|
||||||
account._current_tenant = tenant
|
mock_session_instance = mock.Mock()
|
||||||
|
|
||||||
|
mock_tenant_join = TenantAccountJoin(role=TenantAccountRole.OWNER)
|
||||||
|
monkeypatch.setattr(mock_session_instance, "scalar", mock.Mock(return_value=mock_tenant_join))
|
||||||
|
|
||||||
|
mock_scalars_result = mock.Mock()
|
||||||
|
mock_scalars_result.one.return_value = tenant
|
||||||
|
monkeypatch.setattr(mock_session_instance, "scalars", mock.Mock(return_value=mock_scalars_result))
|
||||||
|
|
||||||
|
mock_session_context = mock.Mock()
|
||||||
|
mock_session_context.__enter__.return_value = mock_session_instance
|
||||||
|
monkeypatch.setattr("models.account.Session", lambda _, expire_on_commit: mock_session_context)
|
||||||
|
|
||||||
|
account.current_tenant = tenant
|
||||||
return account
|
return account
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
|
|||||||
@@ -18,124 +18,87 @@ class TestAppDescriptionValidationUnit:
|
|||||||
"""Unit tests for description validation function"""
|
"""Unit tests for description validation function"""
|
||||||
|
|
||||||
def test_validate_description_length_function(self):
|
def test_validate_description_length_function(self):
|
||||||
"""Test the _validate_description_length function directly"""
|
"""Test the validate_description_length function directly"""
|
||||||
from controllers.console.app.app import _validate_description_length
|
from libs.validators import validate_description_length
|
||||||
|
|
||||||
# Test valid descriptions
|
# Test valid descriptions
|
||||||
assert _validate_description_length("") == ""
|
assert validate_description_length("") == ""
|
||||||
assert _validate_description_length("x" * 400) == "x" * 400
|
assert validate_description_length("x" * 400) == "x" * 400
|
||||||
assert _validate_description_length(None) is None
|
assert validate_description_length(None) is None
|
||||||
|
|
||||||
# Test invalid descriptions
|
# Test invalid descriptions
|
||||||
with pytest.raises(ValueError) as exc_info:
|
with pytest.raises(ValueError) as exc_info:
|
||||||
_validate_description_length("x" * 401)
|
validate_description_length("x" * 401)
|
||||||
assert "Description cannot exceed 400 characters." in str(exc_info.value)
|
assert "Description cannot exceed 400 characters." in str(exc_info.value)
|
||||||
|
|
||||||
with pytest.raises(ValueError) as exc_info:
|
with pytest.raises(ValueError) as exc_info:
|
||||||
_validate_description_length("x" * 500)
|
validate_description_length("x" * 500)
|
||||||
assert "Description cannot exceed 400 characters." in str(exc_info.value)
|
assert "Description cannot exceed 400 characters." in str(exc_info.value)
|
||||||
|
|
||||||
with pytest.raises(ValueError) as exc_info:
|
with pytest.raises(ValueError) as exc_info:
|
||||||
_validate_description_length("x" * 1000)
|
validate_description_length("x" * 1000)
|
||||||
assert "Description cannot exceed 400 characters." in str(exc_info.value)
|
assert "Description cannot exceed 400 characters." in str(exc_info.value)
|
||||||
|
|
||||||
def test_validation_consistency_with_dataset(self):
|
|
||||||
"""Test that App and Dataset validation functions are consistent"""
|
|
||||||
from controllers.console.app.app import _validate_description_length as app_validate
|
|
||||||
from controllers.console.datasets.datasets import _validate_description_length as dataset_validate
|
|
||||||
from controllers.service_api.dataset.dataset import _validate_description_length as service_dataset_validate
|
|
||||||
|
|
||||||
# Test same valid inputs
|
|
||||||
valid_desc = "x" * 400
|
|
||||||
assert app_validate(valid_desc) == dataset_validate(valid_desc) == service_dataset_validate(valid_desc)
|
|
||||||
assert app_validate("") == dataset_validate("") == service_dataset_validate("")
|
|
||||||
assert app_validate(None) == dataset_validate(None) == service_dataset_validate(None)
|
|
||||||
|
|
||||||
# Test same invalid inputs produce same error
|
|
||||||
invalid_desc = "x" * 401
|
|
||||||
|
|
||||||
app_error = None
|
|
||||||
dataset_error = None
|
|
||||||
service_dataset_error = None
|
|
||||||
|
|
||||||
try:
|
|
||||||
app_validate(invalid_desc)
|
|
||||||
except ValueError as e:
|
|
||||||
app_error = str(e)
|
|
||||||
|
|
||||||
try:
|
|
||||||
dataset_validate(invalid_desc)
|
|
||||||
except ValueError as e:
|
|
||||||
dataset_error = str(e)
|
|
||||||
|
|
||||||
try:
|
|
||||||
service_dataset_validate(invalid_desc)
|
|
||||||
except ValueError as e:
|
|
||||||
service_dataset_error = str(e)
|
|
||||||
|
|
||||||
assert app_error == dataset_error == service_dataset_error
|
|
||||||
assert app_error == "Description cannot exceed 400 characters."
|
|
||||||
|
|
||||||
def test_boundary_values(self):
|
def test_boundary_values(self):
|
||||||
"""Test boundary values for description validation"""
|
"""Test boundary values for description validation"""
|
||||||
from controllers.console.app.app import _validate_description_length
|
from libs.validators import validate_description_length
|
||||||
|
|
||||||
# Test exact boundary
|
# Test exact boundary
|
||||||
exactly_400 = "x" * 400
|
exactly_400 = "x" * 400
|
||||||
assert _validate_description_length(exactly_400) == exactly_400
|
assert validate_description_length(exactly_400) == exactly_400
|
||||||
|
|
||||||
# Test just over boundary
|
# Test just over boundary
|
||||||
just_over_400 = "x" * 401
|
just_over_400 = "x" * 401
|
||||||
with pytest.raises(ValueError):
|
with pytest.raises(ValueError):
|
||||||
_validate_description_length(just_over_400)
|
validate_description_length(just_over_400)
|
||||||
|
|
||||||
# Test just under boundary
|
# Test just under boundary
|
||||||
just_under_400 = "x" * 399
|
just_under_400 = "x" * 399
|
||||||
assert _validate_description_length(just_under_400) == just_under_400
|
assert validate_description_length(just_under_400) == just_under_400
|
||||||
|
|
||||||
def test_edge_cases(self):
|
def test_edge_cases(self):
|
||||||
"""Test edge cases for description validation"""
|
"""Test edge cases for description validation"""
|
||||||
from controllers.console.app.app import _validate_description_length
|
from libs.validators import validate_description_length
|
||||||
|
|
||||||
# Test None input
|
# Test None input
|
||||||
assert _validate_description_length(None) is None
|
assert validate_description_length(None) is None
|
||||||
|
|
||||||
# Test empty string
|
# Test empty string
|
||||||
assert _validate_description_length("") == ""
|
assert validate_description_length("") == ""
|
||||||
|
|
||||||
# Test single character
|
# Test single character
|
||||||
assert _validate_description_length("a") == "a"
|
assert validate_description_length("a") == "a"
|
||||||
|
|
||||||
# Test unicode characters
|
# Test unicode characters
|
||||||
unicode_desc = "测试" * 200 # 400 characters in Chinese
|
unicode_desc = "测试" * 200 # 400 characters in Chinese
|
||||||
assert _validate_description_length(unicode_desc) == unicode_desc
|
assert validate_description_length(unicode_desc) == unicode_desc
|
||||||
|
|
||||||
# Test unicode over limit
|
# Test unicode over limit
|
||||||
unicode_over = "测试" * 201 # 402 characters
|
unicode_over = "测试" * 201 # 402 characters
|
||||||
with pytest.raises(ValueError):
|
with pytest.raises(ValueError):
|
||||||
_validate_description_length(unicode_over)
|
validate_description_length(unicode_over)
|
||||||
|
|
||||||
def test_whitespace_handling(self):
|
def test_whitespace_handling(self):
|
||||||
"""Test how validation handles whitespace"""
|
"""Test how validation handles whitespace"""
|
||||||
from controllers.console.app.app import _validate_description_length
|
from libs.validators import validate_description_length
|
||||||
|
|
||||||
# Test description with spaces
|
# Test description with spaces
|
||||||
spaces_400 = " " * 400
|
spaces_400 = " " * 400
|
||||||
assert _validate_description_length(spaces_400) == spaces_400
|
assert validate_description_length(spaces_400) == spaces_400
|
||||||
|
|
||||||
# Test description with spaces over limit
|
# Test description with spaces over limit
|
||||||
spaces_401 = " " * 401
|
spaces_401 = " " * 401
|
||||||
with pytest.raises(ValueError):
|
with pytest.raises(ValueError):
|
||||||
_validate_description_length(spaces_401)
|
validate_description_length(spaces_401)
|
||||||
|
|
||||||
# Test mixed content
|
# Test mixed content
|
||||||
mixed_400 = "a" * 200 + " " * 200
|
mixed_400 = "a" * 200 + " " * 200
|
||||||
assert _validate_description_length(mixed_400) == mixed_400
|
assert validate_description_length(mixed_400) == mixed_400
|
||||||
|
|
||||||
# Test mixed over limit
|
# Test mixed over limit
|
||||||
mixed_401 = "a" * 200 + " " * 201
|
mixed_401 = "a" * 200 + " " * 201
|
||||||
with pytest.raises(ValueError):
|
with pytest.raises(ValueError):
|
||||||
_validate_description_length(mixed_401)
|
validate_description_length(mixed_401)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|||||||
@@ -9,8 +9,8 @@ from flask.testing import FlaskClient
|
|||||||
from controllers.console.app import model_config as model_config_api
|
from controllers.console.app import model_config as model_config_api
|
||||||
from controllers.console.app import wraps
|
from controllers.console.app import wraps
|
||||||
from libs.datetime_utils import naive_utc_now
|
from libs.datetime_utils import naive_utc_now
|
||||||
from models import Account, App, Tenant
|
from models import App, Tenant
|
||||||
from models.account import TenantAccountRole
|
from models.account import Account, TenantAccountJoin, TenantAccountRole
|
||||||
from models.model import AppMode
|
from models.model import AppMode
|
||||||
from services.app_model_config_service import AppModelConfigService
|
from services.app_model_config_service import AppModelConfigService
|
||||||
|
|
||||||
@@ -30,9 +30,8 @@ class TestModelConfigResourcePermissions:
|
|||||||
return app
|
return app
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def mock_account(self):
|
def mock_account(self, monkeypatch: pytest.MonkeyPatch):
|
||||||
"""Create a mock Account for testing."""
|
"""Create a mock Account for testing."""
|
||||||
|
|
||||||
account = Account()
|
account = Account()
|
||||||
account.id = str(uuid.uuid4())
|
account.id = str(uuid.uuid4())
|
||||||
account.name = "Test User"
|
account.name = "Test User"
|
||||||
@@ -41,12 +40,24 @@ class TestModelConfigResourcePermissions:
|
|||||||
account.created_at = naive_utc_now()
|
account.created_at = naive_utc_now()
|
||||||
account.updated_at = naive_utc_now()
|
account.updated_at = naive_utc_now()
|
||||||
|
|
||||||
# Create mock tenant
|
|
||||||
tenant = Tenant()
|
tenant = Tenant()
|
||||||
tenant.id = str(uuid.uuid4())
|
tenant.id = str(uuid.uuid4())
|
||||||
tenant.name = "Test Tenant"
|
tenant.name = "Test Tenant"
|
||||||
|
|
||||||
account._current_tenant = tenant
|
mock_session_instance = mock.Mock()
|
||||||
|
|
||||||
|
mock_tenant_join = TenantAccountJoin(role=TenantAccountRole.OWNER)
|
||||||
|
monkeypatch.setattr(mock_session_instance, "scalar", mock.Mock(return_value=mock_tenant_join))
|
||||||
|
|
||||||
|
mock_scalars_result = mock.Mock()
|
||||||
|
mock_scalars_result.one.return_value = tenant
|
||||||
|
monkeypatch.setattr(mock_session_instance, "scalars", mock.Mock(return_value=mock_scalars_result))
|
||||||
|
|
||||||
|
mock_session_context = mock.Mock()
|
||||||
|
mock_session_context.__enter__.return_value = mock_session_instance
|
||||||
|
monkeypatch.setattr("models.account.Session", lambda _, expire_on_commit: mock_session_context)
|
||||||
|
|
||||||
|
account.current_tenant = tenant
|
||||||
return account
|
return account
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
|
|||||||
@@ -1,174 +1,53 @@
|
|||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from controllers.console.app.app import _validate_description_length as app_validate
|
from libs.validators import validate_description_length
|
||||||
from controllers.console.datasets.datasets import _validate_description_length as dataset_validate
|
|
||||||
from controllers.service_api.dataset.dataset import _validate_description_length as service_dataset_validate
|
|
||||||
|
|
||||||
|
|
||||||
class TestDescriptionValidationUnit:
|
class TestDescriptionValidationUnit:
|
||||||
"""Unit tests for description validation functions in App and Dataset APIs"""
|
"""Unit tests for the centralized description validation function."""
|
||||||
|
|
||||||
def test_app_validate_description_length_valid(self):
|
def test_validate_description_length_valid(self):
|
||||||
"""Test App validation function with valid descriptions"""
|
"""Test validation function with valid descriptions."""
|
||||||
# Empty string should be valid
|
# Empty string should be valid
|
||||||
assert app_validate("") == ""
|
assert validate_description_length("") == ""
|
||||||
|
|
||||||
# None should be valid
|
# None should be valid
|
||||||
assert app_validate(None) is None
|
assert validate_description_length(None) is None
|
||||||
|
|
||||||
# Short description should be valid
|
# Short description should be valid
|
||||||
short_desc = "Short description"
|
short_desc = "Short description"
|
||||||
assert app_validate(short_desc) == short_desc
|
assert validate_description_length(short_desc) == short_desc
|
||||||
|
|
||||||
# Exactly 400 characters should be valid
|
# Exactly 400 characters should be valid
|
||||||
exactly_400 = "x" * 400
|
exactly_400 = "x" * 400
|
||||||
assert app_validate(exactly_400) == exactly_400
|
assert validate_description_length(exactly_400) == exactly_400
|
||||||
|
|
||||||
# Just under limit should be valid
|
# Just under limit should be valid
|
||||||
just_under = "x" * 399
|
just_under = "x" * 399
|
||||||
assert app_validate(just_under) == just_under
|
assert validate_description_length(just_under) == just_under
|
||||||
|
|
||||||
def test_app_validate_description_length_invalid(self):
|
def test_validate_description_length_invalid(self):
|
||||||
"""Test App validation function with invalid descriptions"""
|
"""Test validation function with invalid descriptions."""
|
||||||
# 401 characters should fail
|
# 401 characters should fail
|
||||||
just_over = "x" * 401
|
just_over = "x" * 401
|
||||||
with pytest.raises(ValueError) as exc_info:
|
with pytest.raises(ValueError) as exc_info:
|
||||||
app_validate(just_over)
|
validate_description_length(just_over)
|
||||||
assert "Description cannot exceed 400 characters." in str(exc_info.value)
|
assert "Description cannot exceed 400 characters." in str(exc_info.value)
|
||||||
|
|
||||||
# 500 characters should fail
|
# 500 characters should fail
|
||||||
way_over = "x" * 500
|
way_over = "x" * 500
|
||||||
with pytest.raises(ValueError) as exc_info:
|
with pytest.raises(ValueError) as exc_info:
|
||||||
app_validate(way_over)
|
validate_description_length(way_over)
|
||||||
assert "Description cannot exceed 400 characters." in str(exc_info.value)
|
assert "Description cannot exceed 400 characters." in str(exc_info.value)
|
||||||
|
|
||||||
# 1000 characters should fail
|
# 1000 characters should fail
|
||||||
very_long = "x" * 1000
|
very_long = "x" * 1000
|
||||||
with pytest.raises(ValueError) as exc_info:
|
with pytest.raises(ValueError) as exc_info:
|
||||||
app_validate(very_long)
|
validate_description_length(very_long)
|
||||||
assert "Description cannot exceed 400 characters." in str(exc_info.value)
|
assert "Description cannot exceed 400 characters." in str(exc_info.value)
|
||||||
|
|
||||||
def test_dataset_validate_description_length_valid(self):
|
|
||||||
"""Test Dataset validation function with valid descriptions"""
|
|
||||||
# Empty string should be valid
|
|
||||||
assert dataset_validate("") == ""
|
|
||||||
|
|
||||||
# Short description should be valid
|
|
||||||
short_desc = "Short description"
|
|
||||||
assert dataset_validate(short_desc) == short_desc
|
|
||||||
|
|
||||||
# Exactly 400 characters should be valid
|
|
||||||
exactly_400 = "x" * 400
|
|
||||||
assert dataset_validate(exactly_400) == exactly_400
|
|
||||||
|
|
||||||
# Just under limit should be valid
|
|
||||||
just_under = "x" * 399
|
|
||||||
assert dataset_validate(just_under) == just_under
|
|
||||||
|
|
||||||
def test_dataset_validate_description_length_invalid(self):
|
|
||||||
"""Test Dataset validation function with invalid descriptions"""
|
|
||||||
# 401 characters should fail
|
|
||||||
just_over = "x" * 401
|
|
||||||
with pytest.raises(ValueError) as exc_info:
|
|
||||||
dataset_validate(just_over)
|
|
||||||
assert "Description cannot exceed 400 characters." in str(exc_info.value)
|
|
||||||
|
|
||||||
# 500 characters should fail
|
|
||||||
way_over = "x" * 500
|
|
||||||
with pytest.raises(ValueError) as exc_info:
|
|
||||||
dataset_validate(way_over)
|
|
||||||
assert "Description cannot exceed 400 characters." in str(exc_info.value)
|
|
||||||
|
|
||||||
def test_service_dataset_validate_description_length_valid(self):
|
|
||||||
"""Test Service Dataset validation function with valid descriptions"""
|
|
||||||
# Empty string should be valid
|
|
||||||
assert service_dataset_validate("") == ""
|
|
||||||
|
|
||||||
# None should be valid
|
|
||||||
assert service_dataset_validate(None) is None
|
|
||||||
|
|
||||||
# Short description should be valid
|
|
||||||
short_desc = "Short description"
|
|
||||||
assert service_dataset_validate(short_desc) == short_desc
|
|
||||||
|
|
||||||
# Exactly 400 characters should be valid
|
|
||||||
exactly_400 = "x" * 400
|
|
||||||
assert service_dataset_validate(exactly_400) == exactly_400
|
|
||||||
|
|
||||||
# Just under limit should be valid
|
|
||||||
just_under = "x" * 399
|
|
||||||
assert service_dataset_validate(just_under) == just_under
|
|
||||||
|
|
||||||
def test_service_dataset_validate_description_length_invalid(self):
|
|
||||||
"""Test Service Dataset validation function with invalid descriptions"""
|
|
||||||
# 401 characters should fail
|
|
||||||
just_over = "x" * 401
|
|
||||||
with pytest.raises(ValueError) as exc_info:
|
|
||||||
service_dataset_validate(just_over)
|
|
||||||
assert "Description cannot exceed 400 characters." in str(exc_info.value)
|
|
||||||
|
|
||||||
# 500 characters should fail
|
|
||||||
way_over = "x" * 500
|
|
||||||
with pytest.raises(ValueError) as exc_info:
|
|
||||||
service_dataset_validate(way_over)
|
|
||||||
assert "Description cannot exceed 400 characters." in str(exc_info.value)
|
|
||||||
|
|
||||||
def test_app_dataset_validation_consistency(self):
|
|
||||||
"""Test that App and Dataset validation functions behave identically"""
|
|
||||||
test_cases = [
|
|
||||||
"", # Empty string
|
|
||||||
"Short description", # Normal description
|
|
||||||
"x" * 100, # Medium description
|
|
||||||
"x" * 400, # Exactly at limit
|
|
||||||
]
|
|
||||||
|
|
||||||
# Test valid cases produce same results
|
|
||||||
for test_desc in test_cases:
|
|
||||||
assert app_validate(test_desc) == dataset_validate(test_desc) == service_dataset_validate(test_desc)
|
|
||||||
|
|
||||||
# Test invalid cases produce same errors
|
|
||||||
invalid_cases = [
|
|
||||||
"x" * 401, # Just over limit
|
|
||||||
"x" * 500, # Way over limit
|
|
||||||
"x" * 1000, # Very long
|
|
||||||
]
|
|
||||||
|
|
||||||
for invalid_desc in invalid_cases:
|
|
||||||
app_error = None
|
|
||||||
dataset_error = None
|
|
||||||
service_dataset_error = None
|
|
||||||
|
|
||||||
# Capture App validation error
|
|
||||||
try:
|
|
||||||
app_validate(invalid_desc)
|
|
||||||
except ValueError as e:
|
|
||||||
app_error = str(e)
|
|
||||||
|
|
||||||
# Capture Dataset validation error
|
|
||||||
try:
|
|
||||||
dataset_validate(invalid_desc)
|
|
||||||
except ValueError as e:
|
|
||||||
dataset_error = str(e)
|
|
||||||
|
|
||||||
# Capture Service Dataset validation error
|
|
||||||
try:
|
|
||||||
service_dataset_validate(invalid_desc)
|
|
||||||
except ValueError as e:
|
|
||||||
service_dataset_error = str(e)
|
|
||||||
|
|
||||||
# All should produce errors
|
|
||||||
assert app_error is not None, f"App validation should fail for {len(invalid_desc)} characters"
|
|
||||||
assert dataset_error is not None, f"Dataset validation should fail for {len(invalid_desc)} characters"
|
|
||||||
error_msg = f"Service Dataset validation should fail for {len(invalid_desc)} characters"
|
|
||||||
assert service_dataset_error is not None, error_msg
|
|
||||||
|
|
||||||
# Errors should be identical
|
|
||||||
error_msg = f"Error messages should be identical for {len(invalid_desc)} characters"
|
|
||||||
assert app_error == dataset_error == service_dataset_error, error_msg
|
|
||||||
assert app_error == "Description cannot exceed 400 characters."
|
|
||||||
|
|
||||||
def test_boundary_values(self):
|
def test_boundary_values(self):
|
||||||
"""Test boundary values around the 400 character limit"""
|
"""Test boundary values around the 400 character limit."""
|
||||||
boundary_tests = [
|
boundary_tests = [
|
||||||
(0, True), # Empty
|
(0, True), # Empty
|
||||||
(1, True), # Minimum
|
(1, True), # Minimum
|
||||||
@@ -184,69 +63,45 @@ class TestDescriptionValidationUnit:
|
|||||||
|
|
||||||
if should_pass:
|
if should_pass:
|
||||||
# Should not raise exception
|
# Should not raise exception
|
||||||
assert app_validate(test_desc) == test_desc
|
assert validate_description_length(test_desc) == test_desc
|
||||||
assert dataset_validate(test_desc) == test_desc
|
|
||||||
assert service_dataset_validate(test_desc) == test_desc
|
|
||||||
else:
|
else:
|
||||||
# Should raise ValueError
|
# Should raise ValueError
|
||||||
with pytest.raises(ValueError):
|
with pytest.raises(ValueError):
|
||||||
app_validate(test_desc)
|
validate_description_length(test_desc)
|
||||||
with pytest.raises(ValueError):
|
|
||||||
dataset_validate(test_desc)
|
|
||||||
with pytest.raises(ValueError):
|
|
||||||
service_dataset_validate(test_desc)
|
|
||||||
|
|
||||||
def test_special_characters(self):
|
def test_special_characters(self):
|
||||||
"""Test validation with special characters, Unicode, etc."""
|
"""Test validation with special characters, Unicode, etc."""
|
||||||
# Unicode characters
|
# Unicode characters
|
||||||
unicode_desc = "测试描述" * 100 # Chinese characters
|
unicode_desc = "测试描述" * 100 # Chinese characters
|
||||||
if len(unicode_desc) <= 400:
|
if len(unicode_desc) <= 400:
|
||||||
assert app_validate(unicode_desc) == unicode_desc
|
assert validate_description_length(unicode_desc) == unicode_desc
|
||||||
assert dataset_validate(unicode_desc) == unicode_desc
|
|
||||||
assert service_dataset_validate(unicode_desc) == unicode_desc
|
|
||||||
|
|
||||||
# Special characters
|
# Special characters
|
||||||
special_desc = "Special chars: !@#$%^&*()_+-=[]{}|;':\",./<>?" * 10
|
special_desc = "Special chars: !@#$%^&*()_+-=[]{}|;':\",./<>?" * 10
|
||||||
if len(special_desc) <= 400:
|
if len(special_desc) <= 400:
|
||||||
assert app_validate(special_desc) == special_desc
|
assert validate_description_length(special_desc) == special_desc
|
||||||
assert dataset_validate(special_desc) == special_desc
|
|
||||||
assert service_dataset_validate(special_desc) == special_desc
|
|
||||||
|
|
||||||
# Mixed content
|
# Mixed content
|
||||||
mixed_desc = "Mixed content: 测试 123 !@# " * 15
|
mixed_desc = "Mixed content: 测试 123 !@# " * 15
|
||||||
if len(mixed_desc) <= 400:
|
if len(mixed_desc) <= 400:
|
||||||
assert app_validate(mixed_desc) == mixed_desc
|
assert validate_description_length(mixed_desc) == mixed_desc
|
||||||
assert dataset_validate(mixed_desc) == mixed_desc
|
|
||||||
assert service_dataset_validate(mixed_desc) == mixed_desc
|
|
||||||
elif len(mixed_desc) > 400:
|
elif len(mixed_desc) > 400:
|
||||||
with pytest.raises(ValueError):
|
with pytest.raises(ValueError):
|
||||||
app_validate(mixed_desc)
|
validate_description_length(mixed_desc)
|
||||||
with pytest.raises(ValueError):
|
|
||||||
dataset_validate(mixed_desc)
|
|
||||||
with pytest.raises(ValueError):
|
|
||||||
service_dataset_validate(mixed_desc)
|
|
||||||
|
|
||||||
def test_whitespace_handling(self):
|
def test_whitespace_handling(self):
|
||||||
"""Test validation with various whitespace scenarios"""
|
"""Test validation with various whitespace scenarios."""
|
||||||
# Leading/trailing whitespace
|
# Leading/trailing whitespace
|
||||||
whitespace_desc = " Description with whitespace "
|
whitespace_desc = " Description with whitespace "
|
||||||
if len(whitespace_desc) <= 400:
|
if len(whitespace_desc) <= 400:
|
||||||
assert app_validate(whitespace_desc) == whitespace_desc
|
assert validate_description_length(whitespace_desc) == whitespace_desc
|
||||||
assert dataset_validate(whitespace_desc) == whitespace_desc
|
|
||||||
assert service_dataset_validate(whitespace_desc) == whitespace_desc
|
|
||||||
|
|
||||||
# Newlines and tabs
|
# Newlines and tabs
|
||||||
multiline_desc = "Line 1\nLine 2\tTabbed content"
|
multiline_desc = "Line 1\nLine 2\tTabbed content"
|
||||||
if len(multiline_desc) <= 400:
|
if len(multiline_desc) <= 400:
|
||||||
assert app_validate(multiline_desc) == multiline_desc
|
assert validate_description_length(multiline_desc) == multiline_desc
|
||||||
assert dataset_validate(multiline_desc) == multiline_desc
|
|
||||||
assert service_dataset_validate(multiline_desc) == multiline_desc
|
|
||||||
|
|
||||||
# Only whitespace over limit
|
# Only whitespace over limit
|
||||||
only_spaces = " " * 401
|
only_spaces = " " * 401
|
||||||
with pytest.raises(ValueError):
|
with pytest.raises(ValueError):
|
||||||
app_validate(only_spaces)
|
validate_description_length(only_spaces)
|
||||||
with pytest.raises(ValueError):
|
|
||||||
dataset_validate(only_spaces)
|
|
||||||
with pytest.raises(ValueError):
|
|
||||||
service_dataset_validate(only_spaces)
|
|
||||||
|
|||||||
Reference in New Issue
Block a user