| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785 |
- from unittest.mock import patch
- import pytest
- from faker import Faker
- from services.feature_service import FeatureModel, FeatureService, KnowledgeRateLimitModel, SystemFeatureModel
- class TestFeatureService:
- """Integration tests for FeatureService using testcontainers."""
- @pytest.fixture
- def mock_external_service_dependencies(self):
- """Mock setup for external service dependencies."""
- with (
- patch("services.feature_service.BillingService") as mock_billing_service,
- patch("services.feature_service.EnterpriseService") as mock_enterprise_service,
- ):
- # Setup default mock returns for BillingService
- mock_billing_service.get_info.return_value = {
- "enabled": True,
- "subscription": {"plan": "pro", "interval": "monthly", "education": True},
- "members": {"size": 5, "limit": 10},
- "apps": {"size": 3, "limit": 20},
- "vector_space": {"size": 2, "limit": 10},
- "documents_upload_quota": {"size": 15, "limit": 100},
- "annotation_quota_limit": {"size": 8, "limit": 50},
- "docs_processing": "enhanced",
- "can_replace_logo": True,
- "model_load_balancing_enabled": True,
- "knowledge_rate_limit": {"limit": 100},
- }
- mock_billing_service.get_knowledge_rate_limit.return_value = {"limit": 100, "subscription_plan": "pro"}
- # Setup default mock returns for EnterpriseService
- mock_enterprise_service.get_workspace_info.return_value = {
- "WorkspaceMembers": {"used": 5, "limit": 10, "enabled": True}
- }
- mock_enterprise_service.get_info.return_value = {
- "SSOEnforcedForSignin": True,
- "SSOEnforcedForSigninProtocol": "saml",
- "EnableEmailCodeLogin": True,
- "EnableEmailPasswordLogin": False,
- "IsAllowRegister": False,
- "IsAllowCreateWorkspace": False,
- "Branding": {
- "applicationTitle": "Test Enterprise",
- "loginPageLogo": "https://example.com/logo.png",
- "workspaceLogo": "https://example.com/workspace.png",
- "favicon": "https://example.com/favicon.ico",
- },
- "WebAppAuth": {"allowSso": True, "allowEmailCodeLogin": True, "allowEmailPasswordLogin": False},
- "SSOEnforcedForWebProtocol": "oidc",
- "License": {
- "status": "active",
- "expiredAt": "2025-12-31",
- "workspaces": {"enabled": True, "limit": 5, "used": 2},
- },
- "PluginInstallationPermission": {
- "pluginInstallationScope": "official_only",
- "restrictToMarketplaceOnly": True,
- },
- }
- yield {
- "billing_service": mock_billing_service,
- "enterprise_service": mock_enterprise_service,
- }
- def _create_test_tenant_id(self):
- """Helper method to create a test tenant ID."""
- fake = Faker()
- return fake.uuid4()
- def test_get_features_success(self, db_session_with_containers, mock_external_service_dependencies):
- """
- Test successful feature retrieval with billing and enterprise enabled.
- This test verifies:
- - Proper feature model creation with all required fields
- - Correct integration with billing service
- - Proper enterprise workspace information handling
- - Return value correctness and structure
- """
- # Arrange: Setup test data with proper config mocking
- tenant_id = self._create_test_tenant_id()
- with patch("services.feature_service.dify_config") as mock_config:
- mock_config.BILLING_ENABLED = True
- mock_config.ENTERPRISE_ENABLED = True
- mock_config.CAN_REPLACE_LOGO = True
- mock_config.MODEL_LB_ENABLED = True
- mock_config.DATASET_OPERATOR_ENABLED = True
- mock_config.EDUCATION_ENABLED = True
- # Act: Execute the method under test
- result = FeatureService.get_features(tenant_id)
- # Assert: Verify the expected outcomes
- assert result is not None
- assert isinstance(result, FeatureModel)
- # Verify billing features
- assert result.billing.enabled is True
- assert result.billing.subscription.plan == "pro"
- assert result.billing.subscription.interval == "monthly"
- assert result.education.activated is True
- # Verify member limitations
- assert result.members.size == 5
- assert result.members.limit == 10
- # Verify app limitations
- assert result.apps.size == 3
- assert result.apps.limit == 20
- # Verify vector space limitations
- assert result.vector_space.size == 2
- assert result.vector_space.limit == 10
- # Verify document upload quota
- assert result.documents_upload_quota.size == 15
- assert result.documents_upload_quota.limit == 100
- # Verify annotation quota
- assert result.annotation_quota_limit.size == 8
- assert result.annotation_quota_limit.limit == 50
- # Verify other features
- assert result.docs_processing == "enhanced"
- assert result.can_replace_logo is True
- assert result.model_load_balancing_enabled is True
- assert result.knowledge_rate_limit == 100
- # Verify enterprise features
- assert result.workspace_members.enabled is True
- assert result.workspace_members.size == 5
- assert result.workspace_members.limit == 10
- # Verify webapp copyright is enabled for non-sandbox plans
- assert result.webapp_copyright_enabled is True
- assert result.is_allow_transfer_workspace is True
- # Verify mock interactions
- mock_external_service_dependencies["billing_service"].get_info.assert_called_once_with(tenant_id)
- mock_external_service_dependencies["enterprise_service"].get_workspace_info.assert_called_once_with(
- tenant_id
- )
- def test_get_features_sandbox_plan(self, db_session_with_containers, mock_external_service_dependencies):
- """
- Test feature retrieval for sandbox plan with specific limitations.
- This test verifies:
- - Proper handling of sandbox plan limitations
- - Correct webapp copyright settings for sandbox
- - Transfer workspace restrictions for sandbox plans
- - Proper billing service integration
- """
- # Arrange: Setup sandbox plan mock with proper config
- tenant_id = self._create_test_tenant_id()
- with patch("services.feature_service.dify_config") as mock_config:
- mock_config.BILLING_ENABLED = True
- mock_config.ENTERPRISE_ENABLED = False
- mock_config.CAN_REPLACE_LOGO = False
- mock_config.MODEL_LB_ENABLED = False
- mock_config.DATASET_OPERATOR_ENABLED = False
- mock_config.EDUCATION_ENABLED = False
- # Set mock return value inside the patch context
- mock_external_service_dependencies["billing_service"].get_info.return_value = {
- "enabled": True,
- "subscription": {"plan": "sandbox", "interval": "monthly", "education": False},
- "members": {"size": 1, "limit": 3},
- "apps": {"size": 1, "limit": 5},
- "vector_space": {"size": 1, "limit": 2},
- "documents_upload_quota": {"size": 5, "limit": 20},
- "annotation_quota_limit": {"size": 2, "limit": 10},
- "docs_processing": "standard",
- "can_replace_logo": False,
- "model_load_balancing_enabled": False,
- "knowledge_rate_limit": {"limit": 10},
- }
- # Act: Execute the method under test
- result = FeatureService.get_features(tenant_id)
- # Assert: Verify sandbox-specific limitations
- assert result.billing.subscription.plan == "sandbox"
- assert result.education.activated is False
- # Verify sandbox limitations
- assert result.members.size == 1
- assert result.members.limit == 3
- assert result.apps.size == 1
- assert result.apps.limit == 5
- assert result.vector_space.size == 1
- assert result.vector_space.limit == 2
- assert result.documents_upload_quota.size == 5
- assert result.documents_upload_quota.limit == 20
- assert result.annotation_quota_limit.size == 2
- assert result.annotation_quota_limit.limit == 10
- # Verify sandbox-specific restrictions
- assert result.webapp_copyright_enabled is False
- assert result.is_allow_transfer_workspace is False
- assert result.can_replace_logo is False
- assert result.model_load_balancing_enabled is False
- assert result.docs_processing == "standard"
- assert result.knowledge_rate_limit == 10
- # Verify mock interactions
- mock_external_service_dependencies["billing_service"].get_info.assert_called_once_with(tenant_id)
- def test_get_knowledge_rate_limit_success(self, db_session_with_containers, mock_external_service_dependencies):
- """
- Test successful knowledge rate limit retrieval with billing enabled.
- This test verifies:
- - Proper knowledge rate limit model creation
- - Correct integration with billing service
- - Proper rate limit configuration
- - Return value correctness and structure
- """
- # Arrange: Setup test data with proper config
- tenant_id = self._create_test_tenant_id()
- with patch("services.feature_service.dify_config") as mock_config:
- mock_config.BILLING_ENABLED = True
- # Act: Execute the method under test
- result = FeatureService.get_knowledge_rate_limit(tenant_id)
- # Assert: Verify the expected outcomes
- assert result is not None
- assert isinstance(result, KnowledgeRateLimitModel)
- # Verify rate limit configuration
- assert result.enabled is True
- assert result.limit == 100
- assert result.subscription_plan == "pro"
- # Verify mock interactions
- mock_external_service_dependencies["billing_service"].get_knowledge_rate_limit.assert_called_once_with(
- tenant_id
- )
- def test_get_system_features_success(self, db_session_with_containers, mock_external_service_dependencies):
- """
- Test successful system features retrieval with enterprise and marketplace enabled.
- This test verifies:
- - Proper system feature model creation
- - Correct integration with enterprise service
- - Proper marketplace configuration
- - Return value correctness and structure
- """
- # Arrange: Setup test data with proper config
- tenant_id = self._create_test_tenant_id()
- with patch("services.feature_service.dify_config") as mock_config:
- mock_config.ENTERPRISE_ENABLED = True
- mock_config.MARKETPLACE_ENABLED = True
- mock_config.ENABLE_EMAIL_CODE_LOGIN = True
- mock_config.ENABLE_EMAIL_PASSWORD_LOGIN = True
- mock_config.ENABLE_SOCIAL_OAUTH_LOGIN = False
- mock_config.ALLOW_REGISTER = False
- mock_config.ALLOW_CREATE_WORKSPACE = False
- mock_config.MAIL_TYPE = "smtp"
- mock_config.PLUGIN_MAX_PACKAGE_SIZE = 100
- # Act: Execute the method under test
- result = FeatureService.get_system_features()
- # Assert: Verify the expected outcomes
- assert result is not None
- assert isinstance(result, SystemFeatureModel)
- # Verify enterprise features
- assert result.branding.enabled is True
- assert result.webapp_auth.enabled is True
- assert result.enable_change_email is False
- # Verify SSO configuration
- assert result.sso_enforced_for_signin is True
- assert result.sso_enforced_for_signin_protocol == "saml"
- # Verify authentication settings
- assert result.enable_email_code_login is True
- assert result.enable_email_password_login is False
- assert result.is_allow_register is False
- assert result.is_allow_create_workspace is False
- # Verify branding configuration
- assert result.branding.application_title == "Test Enterprise"
- assert result.branding.login_page_logo == "https://example.com/logo.png"
- assert result.branding.workspace_logo == "https://example.com/workspace.png"
- assert result.branding.favicon == "https://example.com/favicon.ico"
- # Verify webapp auth configuration
- assert result.webapp_auth.allow_sso is True
- assert result.webapp_auth.allow_email_code_login is True
- assert result.webapp_auth.allow_email_password_login is False
- assert result.webapp_auth.sso_config.protocol == "oidc"
- # Verify license configuration
- assert result.license.status.value == "active"
- assert result.license.expired_at == "2025-12-31"
- assert result.license.workspaces.enabled is True
- assert result.license.workspaces.limit == 5
- assert result.license.workspaces.size == 2
- # Verify plugin installation permission
- assert result.plugin_installation_permission.plugin_installation_scope == "official_only"
- assert result.plugin_installation_permission.restrict_to_marketplace_only is True
- # Verify marketplace configuration
- assert result.enable_marketplace is True
- # Verify mock interactions
- mock_external_service_dependencies["enterprise_service"].get_info.assert_called_once()
- def test_get_system_features_basic_config(self, db_session_with_containers, mock_external_service_dependencies):
- """
- Test system features retrieval with basic configuration (no enterprise).
- This test verifies:
- - Proper system feature model creation without enterprise
- - Correct environment variable handling
- - Default configuration values
- - Return value correctness and structure
- """
- # Arrange: Setup basic config mock (no enterprise)
- with patch("services.feature_service.dify_config") as mock_config:
- mock_config.ENTERPRISE_ENABLED = False
- mock_config.MARKETPLACE_ENABLED = False
- mock_config.ENABLE_EMAIL_CODE_LOGIN = True
- mock_config.ENABLE_EMAIL_PASSWORD_LOGIN = True
- mock_config.ENABLE_SOCIAL_OAUTH_LOGIN = False
- mock_config.ALLOW_REGISTER = True
- mock_config.ALLOW_CREATE_WORKSPACE = True
- mock_config.MAIL_TYPE = "smtp"
- mock_config.PLUGIN_MAX_PACKAGE_SIZE = 100
- # Act: Execute the method under test
- result = FeatureService.get_system_features()
- # Assert: Verify the expected outcomes
- assert result is not None
- assert isinstance(result, SystemFeatureModel)
- # Verify basic configuration
- assert result.branding.enabled is False
- assert result.webapp_auth.enabled is False
- assert result.enable_change_email is True
- # Verify authentication settings from config
- assert result.enable_email_code_login is True
- assert result.enable_email_password_login is True
- assert result.enable_social_oauth_login is False
- assert result.is_allow_register is True
- assert result.is_allow_create_workspace is True
- assert result.is_email_setup is True
- # Verify marketplace configuration
- assert result.enable_marketplace is False
- # Verify plugin package size (uses default value from dify_config)
- assert result.max_plugin_package_size == 15728640
- def test_get_features_billing_disabled(self, db_session_with_containers, mock_external_service_dependencies):
- """
- Test feature retrieval when billing is disabled.
- This test verifies:
- - Proper feature model creation without billing
- - Correct environment variable handling
- - Default configuration values
- - Return value correctness and structure
- """
- # Arrange: Setup billing disabled mock
- with patch("services.feature_service.dify_config") as mock_config:
- mock_config.BILLING_ENABLED = False
- mock_config.ENTERPRISE_ENABLED = False
- mock_config.CAN_REPLACE_LOGO = True
- mock_config.MODEL_LB_ENABLED = True
- mock_config.DATASET_OPERATOR_ENABLED = True
- mock_config.EDUCATION_ENABLED = True
- tenant_id = self._create_test_tenant_id()
- # Act: Execute the method under test
- result = FeatureService.get_features(tenant_id)
- # Assert: Verify the expected outcomes
- assert result is not None
- assert isinstance(result, FeatureModel)
- # Verify billing is disabled
- assert result.billing.enabled is False
- # Verify environment-based features
- assert result.can_replace_logo is True
- assert result.model_load_balancing_enabled is True
- assert result.dataset_operator_enabled is True
- assert result.education.enabled is True
- # Verify default limitations
- assert result.members.size == 0
- assert result.members.limit == 1
- assert result.apps.size == 0
- assert result.apps.limit == 10
- assert result.vector_space.size == 0
- assert result.vector_space.limit == 5
- assert result.documents_upload_quota.size == 0
- assert result.documents_upload_quota.limit == 50
- assert result.annotation_quota_limit.size == 0
- assert result.annotation_quota_limit.limit == 10
- assert result.knowledge_rate_limit == 10
- assert result.docs_processing == "standard"
- # Verify no enterprise features
- assert result.workspace_members.enabled is False
- assert result.webapp_copyright_enabled is False
- def test_get_knowledge_rate_limit_billing_disabled(
- self, db_session_with_containers, mock_external_service_dependencies
- ):
- """
- Test knowledge rate limit retrieval when billing is disabled.
- This test verifies:
- - Proper knowledge rate limit model creation without billing
- - Default rate limit configuration
- - Return value correctness and structure
- """
- # Arrange: Setup billing disabled mock
- with patch("services.feature_service.dify_config") as mock_config:
- mock_config.BILLING_ENABLED = False
- tenant_id = self._create_test_tenant_id()
- # Act: Execute the method under test
- result = FeatureService.get_knowledge_rate_limit(tenant_id)
- # Assert: Verify the expected outcomes
- assert result is not None
- assert isinstance(result, KnowledgeRateLimitModel)
- # Verify default configuration
- assert result.enabled is False
- assert result.limit == 10
- assert result.subscription_plan == "" # Empty string when billing is disabled
- # Verify no billing service calls
- mock_external_service_dependencies["billing_service"].get_knowledge_rate_limit.assert_not_called()
- def test_get_features_enterprise_only(self, db_session_with_containers, mock_external_service_dependencies):
- """
- Test feature retrieval with enterprise enabled but billing disabled.
- This test verifies:
- - Proper feature model creation with enterprise only
- - Correct enterprise service integration
- - Proper workspace member handling
- - Return value correctness and structure
- """
- # Arrange: Setup enterprise only mock
- with patch("services.feature_service.dify_config") as mock_config:
- mock_config.BILLING_ENABLED = False
- mock_config.ENTERPRISE_ENABLED = True
- mock_config.CAN_REPLACE_LOGO = False
- mock_config.MODEL_LB_ENABLED = False
- mock_config.DATASET_OPERATOR_ENABLED = False
- mock_config.EDUCATION_ENABLED = False
- tenant_id = self._create_test_tenant_id()
- # Act: Execute the method under test
- result = FeatureService.get_features(tenant_id)
- # Assert: Verify the expected outcomes
- assert result is not None
- assert isinstance(result, FeatureModel)
- # Verify billing is disabled
- assert result.billing.enabled is False
- # Verify enterprise features
- assert result.webapp_copyright_enabled is True
- # Verify workspace members from enterprise
- assert result.workspace_members.enabled is True
- assert result.workspace_members.size == 5
- assert result.workspace_members.limit == 10
- # Verify environment-based features
- assert result.can_replace_logo is False
- assert result.model_load_balancing_enabled is False
- assert result.dataset_operator_enabled is False
- assert result.education.enabled is False
- # Verify default limitations
- assert result.members.size == 0
- assert result.members.limit == 1
- assert result.apps.size == 0
- assert result.apps.limit == 10
- assert result.vector_space.size == 0
- assert result.vector_space.limit == 5
- # Verify mock interactions
- mock_external_service_dependencies["enterprise_service"].get_workspace_info.assert_called_once_with(
- tenant_id
- )
- mock_external_service_dependencies["billing_service"].get_info.assert_not_called()
- def test_get_system_features_enterprise_disabled(
- self, db_session_with_containers, mock_external_service_dependencies
- ):
- """
- Test system features retrieval when enterprise is disabled.
- This test verifies:
- - Proper system feature model creation without enterprise
- - Correct environment variable handling
- - Default configuration values
- - Return value correctness and structure
- """
- # Arrange: Setup enterprise disabled mock
- with patch("services.feature_service.dify_config") as mock_config:
- mock_config.ENTERPRISE_ENABLED = False
- mock_config.MARKETPLACE_ENABLED = True
- mock_config.ENABLE_EMAIL_CODE_LOGIN = False
- mock_config.ENABLE_EMAIL_PASSWORD_LOGIN = True
- mock_config.ENABLE_SOCIAL_OAUTH_LOGIN = True
- mock_config.ALLOW_REGISTER = False
- mock_config.ALLOW_CREATE_WORKSPACE = False
- mock_config.MAIL_TYPE = None
- mock_config.PLUGIN_MAX_PACKAGE_SIZE = 50
- # Act: Execute the method under test
- result = FeatureService.get_system_features()
- # Assert: Verify the expected outcomes
- assert result is not None
- assert isinstance(result, SystemFeatureModel)
- # Verify enterprise features are disabled
- assert result.branding.enabled is False
- assert result.webapp_auth.enabled is False
- assert result.enable_change_email is True
- # Verify authentication settings from config
- assert result.enable_email_code_login is False
- assert result.enable_email_password_login is True
- assert result.enable_social_oauth_login is True
- assert result.is_allow_register is False
- assert result.is_allow_create_workspace is False
- assert result.is_email_setup is False
- # Verify marketplace configuration
- assert result.enable_marketplace is True
- # Verify plugin package size (uses default value from dify_config)
- assert result.max_plugin_package_size == 15728640
- # Verify default license status
- assert result.license.status.value == "none"
- assert result.license.expired_at == ""
- assert result.license.workspaces.enabled is False
- # Verify no enterprise service calls
- mock_external_service_dependencies["enterprise_service"].get_info.assert_not_called()
- def test_get_features_no_tenant_id(self, db_session_with_containers, mock_external_service_dependencies):
- """
- Test feature retrieval without tenant ID (billing disabled).
- This test verifies:
- - Proper feature model creation without tenant ID
- - Correct handling when billing is disabled
- - Default configuration values
- - Return value correctness and structure
- """
- # Arrange: Setup no tenant ID scenario
- with patch("services.feature_service.dify_config") as mock_config:
- mock_config.BILLING_ENABLED = True
- mock_config.ENTERPRISE_ENABLED = False
- mock_config.CAN_REPLACE_LOGO = True
- mock_config.MODEL_LB_ENABLED = False
- mock_config.DATASET_OPERATOR_ENABLED = True
- mock_config.EDUCATION_ENABLED = False
- # Act: Execute the method under test
- result = FeatureService.get_features("")
- # Assert: Verify the expected outcomes
- assert result is not None
- assert isinstance(result, FeatureModel)
- # Verify billing is disabled due to no tenant ID
- assert result.billing.enabled is False
- # Verify environment-based features
- assert result.can_replace_logo is True
- assert result.model_load_balancing_enabled is False
- assert result.dataset_operator_enabled is True
- assert result.education.enabled is False
- # Verify default limitations
- assert result.members.size == 0
- assert result.members.limit == 1
- assert result.apps.size == 0
- assert result.apps.limit == 10
- assert result.vector_space.size == 0
- assert result.vector_space.limit == 5
- # Verify no billing service calls
- mock_external_service_dependencies["billing_service"].get_info.assert_not_called()
- def test_get_features_partial_billing_info(self, db_session_with_containers, mock_external_service_dependencies):
- """
- Test feature retrieval with partial billing information.
- This test verifies:
- - Proper handling of partial billing data
- - Correct fallback to default values
- - Proper billing service integration
- - Return value correctness and structure
- """
- # Arrange: Setup partial billing info mock with proper config
- tenant_id = self._create_test_tenant_id()
- with patch("services.feature_service.dify_config") as mock_config:
- mock_config.BILLING_ENABLED = True
- mock_config.ENTERPRISE_ENABLED = False
- mock_config.CAN_REPLACE_LOGO = True
- mock_config.MODEL_LB_ENABLED = False
- mock_config.DATASET_OPERATOR_ENABLED = True
- mock_config.EDUCATION_ENABLED = False
- mock_external_service_dependencies["billing_service"].get_info.return_value = {
- "enabled": True,
- "subscription": {"plan": "basic", "interval": "yearly"},
- # Missing members, apps, vector_space, etc.
- }
- # Act: Execute the method under test
- result = FeatureService.get_features(tenant_id)
- # Assert: Verify the expected outcomes
- assert result is not None
- assert isinstance(result, FeatureModel)
- # Verify billing features
- assert result.billing.enabled is True
- assert result.billing.subscription.plan == "basic"
- assert result.billing.subscription.interval == "yearly"
- # Verify default values for missing billing info
- assert result.members.size == 0
- assert result.members.limit == 1
- assert result.apps.size == 0
- assert result.apps.limit == 10
- assert result.vector_space.size == 0
- assert result.vector_space.limit == 5
- assert result.documents_upload_quota.size == 0
- assert result.documents_upload_quota.limit == 50
- assert result.annotation_quota_limit.size == 0
- assert result.annotation_quota_limit.limit == 10
- assert result.knowledge_rate_limit == 10
- assert result.docs_processing == "standard"
- # Verify basic plan restrictions (non-sandbox plans have webapp copyright enabled)
- assert result.webapp_copyright_enabled is True
- assert result.is_allow_transfer_workspace is True
- # Verify mock interactions
- mock_external_service_dependencies["billing_service"].get_info.assert_called_once_with(tenant_id)
- def test_get_features_edge_case_vector_space(self, db_session_with_containers, mock_external_service_dependencies):
- """
- Test feature retrieval with edge case vector space configuration.
- This test verifies:
- - Proper handling of vector space quota limits
- - Correct integration with billing service
- - Proper fallback to default values
- - Return value correctness and structure
- """
- # Arrange: Setup edge case vector space mock with proper config
- tenant_id = self._create_test_tenant_id()
- with patch("services.feature_service.dify_config") as mock_config:
- mock_config.BILLING_ENABLED = True
- mock_config.ENTERPRISE_ENABLED = False
- mock_config.CAN_REPLACE_LOGO = True
- mock_config.MODEL_LB_ENABLED = False
- mock_config.DATASET_OPERATOR_ENABLED = True
- mock_config.EDUCATION_ENABLED = False
- mock_external_service_dependencies["billing_service"].get_info.return_value = {
- "enabled": True,
- "subscription": {"plan": "pro", "interval": "monthly"},
- "vector_space": {"size": 0, "limit": 0},
- "apps": {"size": 5, "limit": 10},
- }
- # Act: Execute the method under test
- result = FeatureService.get_features(tenant_id)
- # Assert: Verify the expected outcomes
- assert result is not None
- assert isinstance(result, FeatureModel)
- # Verify vector space configuration
- assert result.vector_space.size == 0
- assert result.vector_space.limit == 0
- # Verify apps configuration
- assert result.apps.size == 5
- assert result.apps.limit == 10
- # Verify pro plan features
- assert result.webapp_copyright_enabled is True
- assert result.is_allow_transfer_workspace is True
- # Verify default values for missing billing info
- assert result.members.size == 0
- assert result.members.limit == 1
- assert result.documents_upload_quota.size == 0
- assert result.documents_upload_quota.limit == 50
- assert result.annotation_quota_limit.size == 0
- assert result.annotation_quota_limit.limit == 10
- assert result.knowledge_rate_limit == 10
- assert result.docs_processing == "standard"
- # Verify mock interactions
- mock_external_service_dependencies["billing_service"].get_info.assert_called_once_with(tenant_id)
- def test_get_system_features_edge_case_webapp_auth(
- self, db_session_with_containers, mock_external_service_dependencies
- ):
- """
- Test system features retrieval with edge case webapp auth configuration.
- This test verifies:
- - Proper handling of webapp auth configuration
- - Correct enterprise service integration
- - Proper fallback to default values
- - Return value correctness and structure
- """
- # Arrange: Setup edge case webapp auth mock with proper config
- with patch("services.feature_service.dify_config") as mock_config:
- mock_config.ENTERPRISE_ENABLED = True
- mock_config.MARKETPLACE_ENABLED = False
- mock_config.ENABLE_EMAIL_CODE_LOGIN = False
- mock_config.ENABLE_EMAIL_PASSWORD_LOGIN = True
- mock_config.ENABLE_SOCIAL_OAUTH_LOGIN = False
- mock_config.ALLOW_REGISTER = False
- mock_config.ALLOW_CREATE_WORKSPACE = False
- mock_config.MAIL_TYPE = "smtp"
- mock_config.PLUGIN_MAX_PACKAGE_SIZE = 100
- mock_external_service_dependencies["enterprise_service"].get_info.return_value = {
- "WebAppAuth": {"allowSso": False, "allowEmailCodeLogin": True, "allowEmailPasswordLogin": False}
- }
- # Act: Execute the method under test
- result = FeatureService.get_system_features()
- # Assert: Verify the expected outcomes
- assert result is not None
- assert isinstance(result, SystemFeatureModel)
- # Verify webapp auth configuration
- assert result.webapp_auth.allow_sso is False
- assert result.webapp_auth.allow_email_code_login is True
- assert result.webapp_auth.allow_email_password_login is False
- assert result.webapp_auth.sso_config.protocol == ""
- # Verify enterprise features
- assert result.branding.enabled is True
- assert result.webapp_auth.enabled is True
- assert result.enable_change_email is False
- # Verify default values for missing enterprise info
- assert result.sso_enforced_for_signin is False
- assert result.sso_enforced_for_signin_protocol == ""
- assert result.enable_email_code_login is False
- assert result.enable_email_password_login is True
- assert result.is_allow_register is False
- assert result.is_allow_create_workspace is False
- # Verify mock interactions
- mock_external_service_dependencies["enterprise_service"].get_info.assert_called_once()
- def test_get_features_edge_case_members_quota(self, db_session_with_containers, mock_external_service_dependencies):
- """
- Test feature retrieval with edge case members quota configuration.
- This test verifies:
- - Proper handling of members quota limits
- - Correct integration with billing service
- - Proper fallback to default values
- - Return value correctness and structure
- """
- # Arrange: Setup edge case members quota mock with proper config
- tenant_id = self._create_test_tenant_id()
- with patch("services.feature_service.dify_config") as mock_config:
- mock_config.BILLING_ENABLED = True
- mock_config.ENTERPRISE_ENABLED = False
- mock_config.CAN_REPLACE_LOGO = True
- mock_config.MODEL_LB_ENABLED = False
- mock_config.DATASET_OPERATOR_ENABLED = True
- mock_config.EDUCATION_ENABLED = False
- mock_external_service_dependencies["billing_service"].get_info.return_value = {
- "enabled": True,
- "subscription": {"plan": "basic", "interval": "yearly"},
- "members": {"size": 10, "limit": 10},
- "vector_space": {"size": 3, "limit": 5},
- }
- # Act: Execute the method under test
- result = FeatureService.get_features(tenant_id)
- # Assert: Verify the expected outcomes
- assert result is not None
- assert isinstance(result, FeatureModel)
- # Verify members configuration
- assert result.members.size == 10
- assert result.members.limit == 10
- # Verify vector space configuration
- assert result.vector_space.size == 3
- assert result.vector_space.limit == 5
- # Verify basic plan features (non-sandbox plans have webapp copyright enabled)
- assert result.webapp_copyright_enabled is True
- assert result.is_allow_transfer_workspace is True
- # Verify default values for missing billing info
- assert result.apps.size == 0
- assert result.apps.limit == 10
- assert result.documents_upload_quota.size == 0
- assert result.documents_upload_quota.limit == 50
- assert result.annotation_quota_limit.size == 0
- assert result.annotation_quota_limit.limit == 10
- assert result.knowledge_rate_limit == 10
- assert result.docs_processing == "standard"
- # Verify mock interactions
- mock_external_service_dependencies["billing_service"].get_info.assert_called_once_with(tenant_id)
- def test_plugin_installation_permission_scopes(
- self, db_session_with_containers, mock_external_service_dependencies
- ):
- """
- Test system features retrieval with different plugin installation permission scopes.
- This test verifies:
- - Proper handling of different plugin installation scopes
- - Correct enterprise service integration
- - Proper permission configuration
- - Return value correctness and structure
- """
- # Test case 1: Official only scope
- with patch("services.feature_service.dify_config") as mock_config:
- mock_config.ENTERPRISE_ENABLED = True
- mock_config.MARKETPLACE_ENABLED = False
- mock_config.ENABLE_EMAIL_CODE_LOGIN = False
- mock_config.ENABLE_EMAIL_PASSWORD_LOGIN = True
- mock_config.ENABLE_SOCIAL_OAUTH_LOGIN = False
- mock_config.ALLOW_REGISTER = False
- mock_config.ALLOW_CREATE_WORKSPACE = False
- mock_config.MAIL_TYPE = "smtp"
- mock_config.PLUGIN_MAX_PACKAGE_SIZE = 100
- mock_external_service_dependencies["enterprise_service"].get_info.return_value = {
- "PluginInstallationPermission": {
- "pluginInstallationScope": "official_only",
- "restrictToMarketplaceOnly": True,
- }
- }
- result = FeatureService.get_system_features()
- assert result.plugin_installation_permission.plugin_installation_scope == "official_only"
- assert result.plugin_installation_permission.restrict_to_marketplace_only is True
- # Test case 2: All plugins scope
- with patch("services.feature_service.dify_config") as mock_config:
- mock_config.ENTERPRISE_ENABLED = True
- mock_config.MARKETPLACE_ENABLED = False
- mock_config.ENABLE_EMAIL_CODE_LOGIN = False
- mock_config.ENABLE_EMAIL_PASSWORD_LOGIN = True
- mock_config.ENABLE_SOCIAL_OAUTH_LOGIN = False
- mock_config.ALLOW_REGISTER = False
- mock_config.ALLOW_CREATE_WORKSPACE = False
- mock_config.MAIL_TYPE = "smtp"
- mock_config.PLUGIN_MAX_PACKAGE_SIZE = 100
- mock_external_service_dependencies["enterprise_service"].get_info.return_value = {
- "PluginInstallationPermission": {"pluginInstallationScope": "all", "restrictToMarketplaceOnly": False}
- }
- result = FeatureService.get_system_features()
- assert result.plugin_installation_permission.plugin_installation_scope == "all"
- assert result.plugin_installation_permission.restrict_to_marketplace_only is False
- # Test case 3: Specific partners scope
- with patch("services.feature_service.dify_config") as mock_config:
- mock_config.ENTERPRISE_ENABLED = True
- mock_config.MARKETPLACE_ENABLED = False
- mock_config.ENABLE_EMAIL_CODE_LOGIN = False
- mock_config.ENABLE_EMAIL_PASSWORD_LOGIN = True
- mock_config.ENABLE_SOCIAL_OAUTH_LOGIN = False
- mock_config.ALLOW_REGISTER = False
- mock_config.ALLOW_CREATE_WORKSPACE = False
- mock_config.MAIL_TYPE = "smtp"
- mock_config.PLUGIN_MAX_PACKAGE_SIZE = 100
- mock_external_service_dependencies["enterprise_service"].get_info.return_value = {
- "PluginInstallationPermission": {
- "pluginInstallationScope": "official_and_specific_partners",
- "restrictToMarketplaceOnly": False,
- }
- }
- result = FeatureService.get_system_features()
- assert result.plugin_installation_permission.plugin_installation_scope == "official_and_specific_partners"
- assert result.plugin_installation_permission.restrict_to_marketplace_only is False
- # Test case 4: None scope
- with patch("services.feature_service.dify_config") as mock_config:
- mock_config.ENTERPRISE_ENABLED = True
- mock_config.MARKETPLACE_ENABLED = False
- mock_config.ENABLE_EMAIL_CODE_LOGIN = False
- mock_config.ENABLE_EMAIL_PASSWORD_LOGIN = True
- mock_config.ENABLE_SOCIAL_OAUTH_LOGIN = False
- mock_config.ALLOW_REGISTER = False
- mock_config.ALLOW_CREATE_WORKSPACE = False
- mock_config.MAIL_TYPE = "smtp"
- mock_config.PLUGIN_MAX_PACKAGE_SIZE = 100
- mock_external_service_dependencies["enterprise_service"].get_info.return_value = {
- "PluginInstallationPermission": {"pluginInstallationScope": "none", "restrictToMarketplaceOnly": True}
- }
- result = FeatureService.get_system_features()
- assert result.plugin_installation_permission.plugin_installation_scope == "none"
- assert result.plugin_installation_permission.restrict_to_marketplace_only is True
- def test_get_features_workspace_members_missing(
- self, db_session_with_containers, mock_external_service_dependencies
- ):
- """
- Test feature retrieval when workspace members info is missing from enterprise.
- This test verifies:
- - Proper handling of missing workspace members data
- - Correct enterprise service integration
- - Proper fallback to default values
- - Return value correctness and structure
- """
- # Arrange: Setup missing workspace members mock
- tenant_id = self._create_test_tenant_id()
- mock_external_service_dependencies["enterprise_service"].get_workspace_info.return_value = {
- # Missing WorkspaceMembers key
- }
- with patch("services.feature_service.dify_config") as mock_config:
- mock_config.BILLING_ENABLED = False
- mock_config.ENTERPRISE_ENABLED = True
- # Act: Execute the method under test
- result = FeatureService.get_features(tenant_id)
- # Assert: Verify the expected outcomes
- assert result is not None
- assert isinstance(result, FeatureModel)
- # Verify workspace members use default values
- assert result.workspace_members.enabled is False
- assert result.workspace_members.size == 0
- assert result.workspace_members.limit == 0
- # Verify enterprise features
- assert result.webapp_copyright_enabled is True
- # Verify mock interactions
- mock_external_service_dependencies["enterprise_service"].get_workspace_info.assert_called_once_with(
- tenant_id
- )
- def test_get_system_features_license_inactive(self, db_session_with_containers, mock_external_service_dependencies):
- """
- Test system features retrieval with inactive license.
- This test verifies:
- - Proper handling of inactive license status
- - Correct enterprise service integration
- - Proper license status handling
- - Return value correctness and structure
- """
- # Arrange: Setup inactive license mock with proper config
- with patch("services.feature_service.dify_config") as mock_config:
- mock_config.ENTERPRISE_ENABLED = True
- mock_config.MARKETPLACE_ENABLED = False
- mock_config.ENABLE_EMAIL_CODE_LOGIN = False
- mock_config.ENABLE_EMAIL_PASSWORD_LOGIN = True
- mock_config.ENABLE_SOCIAL_OAUTH_LOGIN = False
- mock_config.ALLOW_REGISTER = False
- mock_config.ALLOW_CREATE_WORKSPACE = False
- mock_config.MAIL_TYPE = "smtp"
- mock_config.PLUGIN_MAX_PACKAGE_SIZE = 100
- mock_external_service_dependencies["enterprise_service"].get_info.return_value = {
- "License": {
- "status": "inactive",
- "expiredAt": "",
- "workspaces": {"enabled": False, "limit": 0, "used": 0},
- }
- }
- # Act: Execute the method under test
- result = FeatureService.get_system_features()
- # Assert: Verify the expected outcomes
- assert result is not None
- assert isinstance(result, SystemFeatureModel)
- # Verify license status
- assert result.license.status == "inactive"
- assert result.license.expired_at == ""
- assert result.license.workspaces.enabled is False
- assert result.license.workspaces.size == 0
- assert result.license.workspaces.limit == 0
- # Verify enterprise features
- assert result.branding.enabled is True
- assert result.webapp_auth.enabled is True
- assert result.enable_change_email is False
- # Verify mock interactions
- mock_external_service_dependencies["enterprise_service"].get_info.assert_called_once()
- def test_get_system_features_partial_enterprise_info(
- self, db_session_with_containers, mock_external_service_dependencies
- ):
- """
- Test system features retrieval with partial enterprise information.
- This test verifies:
- - Proper handling of partial enterprise data
- - Correct fallback to default values
- - Proper enterprise service integration
- - Return value correctness and structure
- """
- # Arrange: Setup partial enterprise info mock with proper config
- with patch("services.feature_service.dify_config") as mock_config:
- mock_config.ENTERPRISE_ENABLED = True
- mock_config.MARKETPLACE_ENABLED = False
- mock_config.ENABLE_EMAIL_CODE_LOGIN = False
- mock_config.ENABLE_EMAIL_PASSWORD_LOGIN = True
- mock_config.ENABLE_SOCIAL_OAUTH_LOGIN = False
- mock_config.ALLOW_REGISTER = False
- mock_config.ALLOW_CREATE_WORKSPACE = False
- mock_config.MAIL_TYPE = "smtp"
- mock_config.PLUGIN_MAX_PACKAGE_SIZE = 100
- mock_external_service_dependencies["enterprise_service"].get_info.return_value = {
- "SSOEnforcedForSignin": True,
- "Branding": {"applicationTitle": "Partial Enterprise"},
- # Missing WebAppAuth, License, PluginInstallationPermission, etc.
- }
- # Act: Execute the method under test
- result = FeatureService.get_system_features()
- # Assert: Verify the expected outcomes
- assert result is not None
- assert isinstance(result, SystemFeatureModel)
- # Verify enterprise features
- assert result.branding.enabled is True
- assert result.webapp_auth.enabled is True
- assert result.enable_change_email is False
- # Verify SSO configuration
- assert result.sso_enforced_for_signin is True
- assert result.sso_enforced_for_signin_protocol == ""
- # Verify branding configuration (partial)
- assert result.branding.application_title == "Partial Enterprise"
- assert result.branding.login_page_logo == ""
- assert result.branding.workspace_logo == ""
- assert result.branding.favicon == ""
- # Verify default values for missing enterprise info
- assert result.webapp_auth.allow_sso is False
- assert result.webapp_auth.allow_email_code_login is False
- assert result.webapp_auth.allow_email_password_login is False
- assert result.webapp_auth.sso_config.protocol == ""
- # Verify default license status
- assert result.license.status == "none"
- assert result.license.expired_at == ""
- assert result.license.workspaces.enabled is False
- # Verify default plugin installation permission
- assert result.plugin_installation_permission.plugin_installation_scope == "all"
- assert result.plugin_installation_permission.restrict_to_marketplace_only is False
- # Verify mock interactions
- mock_external_service_dependencies["enterprise_service"].get_info.assert_called_once()
- def test_get_features_edge_case_limits(self, db_session_with_containers, mock_external_service_dependencies):
- """
- Test feature retrieval with edge case limit values.
- This test verifies:
- - Proper handling of zero and negative limits
- - Correct handling of very large limits
- - Proper fallback to default values
- - Return value correctness and structure
- """
- # Arrange: Setup edge case limits mock with proper config
- tenant_id = self._create_test_tenant_id()
- with patch("services.feature_service.dify_config") as mock_config:
- mock_config.BILLING_ENABLED = True
- mock_config.ENTERPRISE_ENABLED = False
- mock_config.CAN_REPLACE_LOGO = True
- mock_config.MODEL_LB_ENABLED = False
- mock_config.DATASET_OPERATOR_ENABLED = True
- mock_config.EDUCATION_ENABLED = False
- mock_external_service_dependencies["billing_service"].get_info.return_value = {
- "enabled": True,
- "subscription": {"plan": "enterprise", "interval": "yearly"},
- "members": {"size": 0, "limit": 0},
- "apps": {"size": 0, "limit": -1},
- "vector_space": {"size": 0, "limit": 999999},
- "documents_upload_quota": {"size": 0, "limit": 0},
- "annotation_quota_limit": {"size": 0, "limit": 1},
- }
- # Act: Execute the method under test
- result = FeatureService.get_features(tenant_id)
- # Assert: Verify the expected outcomes
- assert result is not None
- assert isinstance(result, FeatureModel)
- # Verify edge case limits
- assert result.members.size == 0
- assert result.members.limit == 0
- assert result.apps.size == 0
- assert result.apps.limit == -1
- assert result.vector_space.size == 0
- assert result.vector_space.limit == 999999
- assert result.documents_upload_quota.size == 0
- assert result.documents_upload_quota.limit == 0
- assert result.annotation_quota_limit.size == 0
- assert result.annotation_quota_limit.limit == 1
- # Verify enterprise plan features
- assert result.webapp_copyright_enabled is True
- assert result.is_allow_transfer_workspace is True
- # Verify mock interactions
- mock_external_service_dependencies["billing_service"].get_info.assert_called_once_with(tenant_id)
- def test_get_system_features_edge_case_protocols(
- self, db_session_with_containers, mock_external_service_dependencies
- ):
- """
- Test system features retrieval with edge case protocol values.
- This test verifies:
- - Proper handling of empty protocol strings
- - Correct handling of special protocol values
- - Proper fallback to default values
- - Return value correctness and structure
- """
- # Arrange: Setup edge case protocols mock with proper config
- with patch("services.feature_service.dify_config") as mock_config:
- mock_config.ENTERPRISE_ENABLED = True
- mock_config.MARKETPLACE_ENABLED = False
- mock_config.ENABLE_EMAIL_CODE_LOGIN = False
- mock_config.ENABLE_EMAIL_PASSWORD_LOGIN = True
- mock_config.ENABLE_SOCIAL_OAUTH_LOGIN = False
- mock_config.ALLOW_REGISTER = False
- mock_config.ALLOW_CREATE_WORKSPACE = False
- mock_config.MAIL_TYPE = "smtp"
- mock_config.PLUGIN_MAX_PACKAGE_SIZE = 100
- mock_external_service_dependencies["enterprise_service"].get_info.return_value = {
- "SSOEnforcedForSigninProtocol": "",
- "SSOEnforcedForWebProtocol": " ",
- "WebAppAuth": {"allowSso": True, "allowEmailCodeLogin": False, "allowEmailPasswordLogin": True},
- }
- # Act: Execute the method under test
- result = FeatureService.get_system_features()
- # Assert: Verify the expected outcomes
- assert result is not None
- assert isinstance(result, SystemFeatureModel)
- # Verify edge case protocols
- assert result.sso_enforced_for_signin_protocol == ""
- assert result.webapp_auth.sso_config.protocol == " "
- # Verify webapp auth configuration
- assert result.webapp_auth.allow_sso is True
- assert result.webapp_auth.allow_email_code_login is False
- assert result.webapp_auth.allow_email_password_login is True
- # Verify enterprise features
- assert result.branding.enabled is True
- assert result.webapp_auth.enabled is True
- assert result.enable_change_email is False
- # Verify mock interactions
- mock_external_service_dependencies["enterprise_service"].get_info.assert_called_once()
- def test_get_features_edge_case_education(self, db_session_with_containers, mock_external_service_dependencies):
- """
- Test feature retrieval with edge case education configuration.
- This test verifies:
- - Proper handling of education feature flags
- - Correct integration with billing service
- - Proper fallback to default values
- - Return value correctness and structure
- """
- # Arrange: Setup edge case education mock
- tenant_id = self._create_test_tenant_id()
- mock_external_service_dependencies["billing_service"].get_info.return_value = {
- "enabled": True,
- "subscription": {"plan": "education", "interval": "semester", "education": True},
- "members": {"size": 100, "limit": 200},
- "apps": {"size": 50, "limit": 100},
- "vector_space": {"size": 20, "limit": 50},
- "documents_upload_quota": {"size": 500, "limit": 1000},
- "annotation_quota_limit": {"size": 200, "limit": 500},
- }
- with patch("services.feature_service.dify_config") as mock_config:
- mock_config.EDUCATION_ENABLED = True
- # Act: Execute the method under test
- result = FeatureService.get_features(tenant_id)
- # Assert: Verify the expected outcomes
- assert result is not None
- assert isinstance(result, FeatureModel)
- # Verify education features
- assert result.education.enabled is True
- assert result.education.activated is True
- # Verify education plan limits
- assert result.members.size == 100
- assert result.members.limit == 200
- assert result.apps.size == 50
- assert result.apps.limit == 100
- assert result.vector_space.size == 20
- assert result.vector_space.limit == 50
- assert result.documents_upload_quota.size == 500
- assert result.documents_upload_quota.limit == 1000
- assert result.annotation_quota_limit.size == 200
- assert result.annotation_quota_limit.limit == 500
- # Verify education plan features
- assert result.webapp_copyright_enabled is True
- assert result.is_allow_transfer_workspace is True
- # Verify mock interactions
- mock_external_service_dependencies["billing_service"].get_info.assert_called_once_with(tenant_id)
- def test_license_limitation_model_is_available(
- self, db_session_with_containers, mock_external_service_dependencies
- ):
- """
- Test LicenseLimitationModel.is_available method with various scenarios.
- This test verifies:
- - Proper quota availability calculation
- - Correct handling of unlimited limits
- - Proper handling of disabled limits
- - Return value correctness for different scenarios
- """
- from services.feature_service import LicenseLimitationModel
- # Test case 1: Limit disabled
- disabled_limit = LicenseLimitationModel(enabled=False, size=5, limit=10)
- assert disabled_limit.is_available(3) is True
- assert disabled_limit.is_available(10) is True
- # Test case 2: Unlimited limit
- unlimited_limit = LicenseLimitationModel(enabled=True, size=5, limit=0)
- assert unlimited_limit.is_available(3) is True
- assert unlimited_limit.is_available(100) is True
- # Test case 3: Available quota
- available_limit = LicenseLimitationModel(enabled=True, size=5, limit=10)
- assert available_limit.is_available(3) is True
- assert available_limit.is_available(5) is True
- assert available_limit.is_available(1) is True
- # Test case 4: Insufficient quota
- insufficient_limit = LicenseLimitationModel(enabled=True, size=8, limit=10)
- assert insufficient_limit.is_available(3) is False
- assert insufficient_limit.is_available(2) is True
- assert insufficient_limit.is_available(1) is True
- # Test case 5: Exact quota usage
- exact_limit = LicenseLimitationModel(enabled=True, size=7, limit=10)
- assert exact_limit.is_available(3) is True
- assert exact_limit.is_available(3) is True
- def test_get_features_workspace_members_disabled(
- self, db_session_with_containers, mock_external_service_dependencies
- ):
- """
- Test feature retrieval when workspace members are disabled in enterprise.
- This test verifies:
- - Proper handling of disabled workspace members
- - Correct enterprise service integration
- - Proper fallback to default values
- - Return value correctness and structure
- """
- # Arrange: Setup workspace members disabled mock
- tenant_id = self._create_test_tenant_id()
- mock_external_service_dependencies["enterprise_service"].get_workspace_info.return_value = {
- "WorkspaceMembers": {"used": 0, "limit": 0, "enabled": False}
- }
- with patch("services.feature_service.dify_config") as mock_config:
- mock_config.BILLING_ENABLED = False
- mock_config.ENTERPRISE_ENABLED = True
- # Act: Execute the method under test
- result = FeatureService.get_features(tenant_id)
- # Assert: Verify the expected outcomes
- assert result is not None
- assert isinstance(result, FeatureModel)
- # Verify workspace members are disabled
- assert result.workspace_members.enabled is False
- assert result.workspace_members.size == 0
- assert result.workspace_members.limit == 0
- # Verify enterprise features
- assert result.webapp_copyright_enabled is True
- # Verify mock interactions
- mock_external_service_dependencies["enterprise_service"].get_workspace_info.assert_called_once_with(tenant_id)
- def test_get_system_features_license_expired(self, db_session_with_containers, mock_external_service_dependencies):
- """
- Test system features retrieval with expired license.
- This test verifies:
- - Proper handling of expired license status
- - Correct enterprise service integration
- - Proper license status handling
- - Return value correctness and structure
- """
- # Arrange: Setup expired license mock with proper config
- with patch("services.feature_service.dify_config") as mock_config:
- mock_config.ENTERPRISE_ENABLED = True
- mock_config.MARKETPLACE_ENABLED = False
- mock_config.ENABLE_EMAIL_CODE_LOGIN = False
- mock_config.ENABLE_EMAIL_PASSWORD_LOGIN = True
- mock_config.ENABLE_SOCIAL_OAUTH_LOGIN = False
- mock_config.ALLOW_REGISTER = False
- mock_config.ALLOW_CREATE_WORKSPACE = False
- mock_config.MAIL_TYPE = "smtp"
- mock_config.PLUGIN_MAX_PACKAGE_SIZE = 100
- mock_external_service_dependencies["enterprise_service"].get_info.return_value = {
- "License": {
- "status": "expired",
- "expiredAt": "2023-12-31",
- "workspaces": {"enabled": False, "limit": 0, "used": 0},
- }
- }
- # Act: Execute the method under test
- result = FeatureService.get_system_features()
- # Assert: Verify the expected outcomes
- assert result is not None
- assert isinstance(result, SystemFeatureModel)
- # Verify license status
- assert result.license.status == "expired"
- assert result.license.expired_at == "2023-12-31"
- assert result.license.workspaces.enabled is False
- assert result.license.workspaces.size == 0
- assert result.license.workspaces.limit == 0
- # Verify enterprise features
- assert result.branding.enabled is True
- assert result.webapp_auth.enabled is True
- assert result.enable_change_email is False
- # Verify mock interactions
- mock_external_service_dependencies["enterprise_service"].get_info.assert_called_once()
- def test_get_features_edge_case_docs_processing(
- self, db_session_with_containers, mock_external_service_dependencies
- ):
- """
- Test feature retrieval with edge case document processing configuration.
- This test verifies:
- - Proper handling of different document processing modes
- - Correct integration with billing service
- - Proper fallback to default values
- - Return value correctness and structure
- """
- # Arrange: Setup edge case docs processing mock with proper config
- tenant_id = self._create_test_tenant_id()
- with patch("services.feature_service.dify_config") as mock_config:
- mock_config.BILLING_ENABLED = True
- mock_config.ENTERPRISE_ENABLED = False
- mock_config.CAN_REPLACE_LOGO = True
- mock_config.MODEL_LB_ENABLED = True
- mock_config.DATASET_OPERATOR_ENABLED = True
- mock_config.EDUCATION_ENABLED = False
- mock_external_service_dependencies["billing_service"].get_info.return_value = {
- "enabled": True,
- "subscription": {"plan": "premium", "interval": "monthly"},
- "docs_processing": "advanced",
- "can_replace_logo": True,
- "model_load_balancing_enabled": True,
- }
- # Act: Execute the method under test
- result = FeatureService.get_features(tenant_id)
- # Assert: Verify the expected outcomes
- assert result is not None
- assert isinstance(result, FeatureModel)
- # Verify docs processing configuration
- assert result.docs_processing == "advanced"
- assert result.can_replace_logo is True
- assert result.model_load_balancing_enabled is True
- # Verify premium plan features
- assert result.webapp_copyright_enabled is True
- assert result.is_allow_transfer_workspace is True
- # Verify default limitations (no specific billing info)
- assert result.members.size == 0
- assert result.members.limit == 1
- assert result.apps.size == 0
- assert result.apps.limit == 10
- assert result.vector_space.size == 0
- assert result.vector_space.limit == 5
- # Verify mock interactions
- mock_external_service_dependencies["billing_service"].get_info.assert_called_once_with(tenant_id)
- def test_get_system_features_edge_case_branding(
- self, db_session_with_containers, mock_external_service_dependencies
- ):
- """
- Test system features retrieval with edge case branding configuration.
- This test verifies:
- - Proper handling of partial branding information
- - Correct enterprise service integration
- - Proper fallback to default values
- - Return value correctness and structure
- """
- # Arrange: Setup edge case branding mock with proper config
- with patch("services.feature_service.dify_config") as mock_config:
- mock_config.ENTERPRISE_ENABLED = True
- mock_config.MARKETPLACE_ENABLED = False
- mock_config.ENABLE_EMAIL_CODE_LOGIN = False
- mock_config.ENABLE_EMAIL_PASSWORD_LOGIN = True
- mock_config.ENABLE_SOCIAL_OAUTH_LOGIN = False
- mock_config.ALLOW_REGISTER = False
- mock_config.ALLOW_CREATE_WORKSPACE = False
- mock_config.MAIL_TYPE = "smtp"
- mock_config.PLUGIN_MAX_PACKAGE_SIZE = 100
- mock_external_service_dependencies["enterprise_service"].get_info.return_value = {
- "Branding": {
- "applicationTitle": "Edge Case App",
- "loginPageLogo": None,
- "workspaceLogo": "",
- "favicon": "https://example.com/favicon.ico",
- }
- }
- # Act: Execute the method under test
- result = FeatureService.get_system_features()
- # Assert: Verify the expected outcomes
- assert result is not None
- assert isinstance(result, SystemFeatureModel)
- # Verify branding configuration (edge cases)
- assert result.branding.application_title == "Edge Case App"
- assert result.branding.login_page_logo is None # None value from mock
- assert result.branding.workspace_logo == ""
- assert result.branding.favicon == "https://example.com/favicon.ico"
- # Verify enterprise features
- assert result.branding.enabled is True
- assert result.webapp_auth.enabled is True
- assert result.enable_change_email is False
- # Verify default values for missing enterprise info
- assert result.sso_enforced_for_signin is False
- assert result.sso_enforced_for_signin_protocol == ""
- assert result.enable_email_code_login is False
- assert result.enable_email_password_login is True
- assert result.is_allow_register is False
- assert result.is_allow_create_workspace is False
- # Verify mock interactions
- mock_external_service_dependencies["enterprise_service"].get_info.assert_called_once()
- def test_get_features_edge_case_annotation_quota(
- self, db_session_with_containers, mock_external_service_dependencies
- ):
- """
- Test feature retrieval with edge case annotation quota configuration.
- This test verifies:
- - Proper handling of annotation quota limits
- - Correct integration with billing service
- - Proper fallback to default values
- - Return value correctness and structure
- """
- # Arrange: Setup edge case annotation quota mock with proper config
- tenant_id = self._create_test_tenant_id()
- with patch("services.feature_service.dify_config") as mock_config:
- mock_config.BILLING_ENABLED = True
- mock_config.ENTERPRISE_ENABLED = False
- mock_config.CAN_REPLACE_LOGO = True
- mock_config.MODEL_LB_ENABLED = False
- mock_config.DATASET_OPERATOR_ENABLED = True
- mock_config.EDUCATION_ENABLED = False
- mock_external_service_dependencies["billing_service"].get_info.return_value = {
- "enabled": True,
- "subscription": {"plan": "enterprise", "interval": "yearly"},
- "annotation_quota_limit": {"size": 999, "limit": 1000},
- "knowledge_rate_limit": {"limit": 500},
- }
- # Act: Execute the method under test
- result = FeatureService.get_features(tenant_id)
- # Assert: Verify the expected outcomes
- assert result is not None
- assert isinstance(result, FeatureModel)
- # Verify annotation quota configuration
- assert result.annotation_quota_limit.size == 999
- assert result.annotation_quota_limit.limit == 1000
- # Verify knowledge rate limit
- assert result.knowledge_rate_limit == 500
- # Verify enterprise plan features
- assert result.webapp_copyright_enabled is True
- assert result.is_allow_transfer_workspace is True
- # Verify default values for missing billing info
- assert result.members.size == 0
- assert result.members.limit == 1
- assert result.apps.size == 0
- assert result.apps.limit == 10
- assert result.vector_space.size == 0
- assert result.vector_space.limit == 5
- assert result.documents_upload_quota.size == 0
- assert result.documents_upload_quota.limit == 50
- assert result.docs_processing == "standard"
- # Verify mock interactions
- mock_external_service_dependencies["billing_service"].get_info.assert_called_once_with(tenant_id)
- def test_get_features_edge_case_documents_upload(
- self, db_session_with_containers, mock_external_service_dependencies
- ):
- """
- Test feature retrieval with edge case documents upload settings.
- This test verifies:
- - Proper handling of edge case documents upload configuration
- - Correct integration with billing service
- - Proper fallback to default values
- - Return value correctness and structure
- """
- # Arrange: Setup edge case documents upload mock with proper config
- tenant_id = self._create_test_tenant_id()
- with patch("services.feature_service.dify_config") as mock_config:
- mock_config.BILLING_ENABLED = True
- mock_config.ENTERPRISE_ENABLED = False
- mock_config.CAN_REPLACE_LOGO = True
- mock_config.MODEL_LB_ENABLED = False
- mock_config.DATASET_OPERATOR_ENABLED = True
- mock_config.EDUCATION_ENABLED = False
- mock_external_service_dependencies["billing_service"].get_info.return_value = {
- "enabled": True,
- "subscription": {"plan": "pro", "interval": "monthly"},
- "documents_upload_quota": {
- "size": 0, # Edge case: zero current size
- "limit": 0, # Edge case: zero limit
- },
- "knowledge_rate_limit": {"limit": 100},
- }
- # Act: Execute the method under test
- result = FeatureService.get_features(tenant_id)
- # Assert: Verify the expected outcomes
- assert result is not None
- assert isinstance(result, FeatureModel)
- # Verify documents upload quota configuration (edge cases)
- assert result.documents_upload_quota.size == 0
- assert result.documents_upload_quota.limit == 0
- # Verify knowledge rate limit
- assert result.knowledge_rate_limit == 100
- # Verify pro plan features
- assert result.webapp_copyright_enabled is True
- assert result.is_allow_transfer_workspace is True
- # Verify default values for missing billing info
- assert result.members.size == 0
- assert result.members.limit == 1
- assert result.apps.size == 0
- assert result.apps.limit == 10
- assert result.vector_space.size == 0
- assert result.vector_space.limit == 5
- assert result.annotation_quota_limit.size == 0
- assert result.annotation_quota_limit.limit == 10 # Default value when not provided
- assert result.docs_processing == "standard"
- # Verify mock interactions
- mock_external_service_dependencies["billing_service"].get_info.assert_called_once_with(tenant_id)
- def test_get_system_features_edge_case_license_lost(
- self, db_session_with_containers, mock_external_service_dependencies
- ):
- """
- Test system features with lost license status.
- This test verifies:
- - Proper handling of lost license status
- - Correct enterprise service integration
- - Proper fallback to default values
- - Return value correctness and structure
- """
- # Arrange: Setup lost license mock with proper config
- with patch("services.feature_service.dify_config") as mock_config:
- mock_config.ENTERPRISE_ENABLED = True
- mock_config.MARKETPLACE_ENABLED = False
- mock_config.ENABLE_EMAIL_CODE_LOGIN = False
- mock_config.ENABLE_EMAIL_PASSWORD_LOGIN = True
- mock_config.ENABLE_SOCIAL_OAUTH_LOGIN = False
- mock_config.ALLOW_REGISTER = False
- mock_config.ALLOW_CREATE_WORKSPACE = False
- mock_config.MAIL_TYPE = "smtp"
- mock_config.PLUGIN_MAX_PACKAGE_SIZE = 100
- mock_external_service_dependencies["enterprise_service"].get_info.return_value = {
- "license": {"status": "lost", "expired_at": None, "plan": None}
- }
- # Act: Execute the method under test
- result = FeatureService.get_system_features()
- # Assert: Verify the expected outcomes
- assert result is not None
- assert isinstance(result, SystemFeatureModel)
- # Verify enterprise features
- assert result.branding.enabled is True
- assert result.webapp_auth.enabled is True
- assert result.enable_change_email is False
- # Verify default values for missing enterprise info
- assert result.sso_enforced_for_signin is False
- assert result.sso_enforced_for_signin_protocol == ""
- assert result.enable_email_code_login is False
- assert result.enable_email_password_login is True
- assert result.is_allow_register is False
- assert result.is_allow_create_workspace is False
- # Verify mock interactions
- mock_external_service_dependencies["enterprise_service"].get_info.assert_called_once()
- def test_get_features_edge_case_education_disabled(
- self, db_session_with_containers, mock_external_service_dependencies
- ):
- """
- Test feature retrieval with education feature disabled.
- This test verifies:
- - Proper handling of disabled education features
- - Correct integration with billing service
- - Proper fallback to default values
- - Return value correctness and structure
- """
- # Arrange: Setup education disabled mock with proper config
- tenant_id = self._create_test_tenant_id()
- with patch("services.feature_service.dify_config") as mock_config:
- mock_config.BILLING_ENABLED = True
- mock_config.ENTERPRISE_ENABLED = False
- mock_config.CAN_REPLACE_LOGO = True
- mock_config.MODEL_LB_ENABLED = False
- mock_config.DATASET_OPERATOR_ENABLED = True
- mock_config.EDUCATION_ENABLED = False
- mock_external_service_dependencies["billing_service"].get_info.return_value = {
- "enabled": True,
- "subscription": {
- "plan": "pro",
- "interval": "monthly",
- "education": False, # Education explicitly disabled
- },
- "knowledge_rate_limit": {"limit": 100},
- }
- # Act: Execute the method under test
- result = FeatureService.get_features(tenant_id)
- # Assert: Verify the expected outcomes
- assert result is not None
- assert isinstance(result, FeatureModel)
- # Verify education configuration
- assert result.education.activated is False
- # Verify knowledge rate limit
- assert result.knowledge_rate_limit == 100
- # Verify pro plan features
- assert result.webapp_copyright_enabled is True
- assert result.is_allow_transfer_workspace is True
- # Verify default values for missing billing info
- assert result.members.size == 0
- assert result.members.limit == 1
- assert result.apps.size == 0
- assert result.apps.limit == 10
- assert result.vector_space.size == 0
- assert result.vector_space.limit == 5
- assert result.documents_upload_quota.size == 0
- assert result.documents_upload_quota.limit == 50
- assert result.annotation_quota_limit.size == 0
- assert result.annotation_quota_limit.limit == 10 # Default value when not provided
- assert result.docs_processing == "standard"
- # Verify mock interactions
- mock_external_service_dependencies["billing_service"].get_info.assert_called_once_with(tenant_id)
|