Files
xiaoxia-saas/tests/unit/test_feature_flags.py
T
CI Bot b49480359b
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 6s
CI/CD Pipeline / Validate - Type Check (mypy) (pull_request) Successful in 54s
CI/CD Pipeline / Validate - Code Quality (pull_request) Failing after 1m16s
CI/CD Pipeline / Frontend Lint (pull_request) Successful in 36s
CI/CD Pipeline / Validate - Migration (alembic) (pull_request) Successful in 55s
CI/CD Pipeline / Build Staging API Image (pull_request) Has been skipped
CI/CD Pipeline / Build Staging Web Image (pull_request) Has been skipped
CI/CD Pipeline / Build Staging Worker Image (pull_request) Has been skipped
CI/CD Pipeline / PR Build Web Image (pull_request) Successful in 28s
PR Automation / Auto Merge on CI Green + Approved (pull_request) Successful in 51s
Preview Deploy / Deploy Preview Environment (pull_request) Failing after 18s
AI Code Review / AI Code Review (pull_request) Successful in 3m13s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 3m10s
CI/CD Pipeline / PR Build API Image (pull_request) Successful in 4m32s
CI/CD Pipeline / Frontend Unit Tests (pull_request) Has been skipped
CI/CD Pipeline / Build Production API Image (pull_request) Has been skipped
CI/CD Pipeline / Build Production Web Image (pull_request) Has been skipped
CI/CD Pipeline / Build Production Worker Image (pull_request) Has been skipped
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Has been skipped
CI/CD Pipeline / Unit Tests (pull_request) Successful in 2m39s
CI/CD Pipeline / Integration Tests (pull_request) Successful in 2m23s
CI/CD Pipeline / PR Build Worker Image (pull_request) Successful in 10m16s
CI/CD Pipeline / Deploy Production (pull_request) Has been skipped
CI/CD Pipeline / Staging E2E Tests (pull_request) Has been skipped
CI/CD Pipeline / Staging API Integration Tests (pull_request) Has been skipped
CI/CD Pipeline / ACR Image Cleanup (pull_request) Has been skipped
CI/CD Pipeline / Production Browser E2E (pull_request) Has been skipped
Preview Cleanup / Cleanup Preview Environment (pull_request) Successful in 52s
test: P3-1 第42波单元测试(tts_job/voice_clone use_cases + feature_flags)
2026-07-24 12:36:55 +08:00

257 lines
10 KiB
Python
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""Feature Flags 单元测试"""
from __future__ import annotations
import pytest
from packages.infrastructure.feature_flags import (
FeatureFlag,
FeatureFlags,
FeatureScope,
)
class TestFeatureFlag:
"""单个 FeatureFlag 测试"""
def test_default_enabled(self):
"""默认全局启用"""
flag = FeatureFlag(name="test_feature")
assert flag.is_enabled() is True
def test_global_disabled(self):
"""全局禁用"""
flag = FeatureFlag(name="test_feature", global_enabled=False)
assert flag.is_enabled() is False
def test_plan_override_free_disabled(self):
"""free 套餐被覆盖为禁用"""
flag = FeatureFlag(
name="test_feature", global_enabled=True, plan_overrides={"free": False}
)
assert flag.is_enabled(user_plan="free") is False
assert flag.is_enabled(user_plan="basic") is True
assert flag.is_enabled(user_plan="premium") is True
def test_plan_override_premium_only(self):
"""仅 premium 可用"""
flag = FeatureFlag(
name="test_feature",
global_enabled=True,
plan_overrides={"free": False, "basic": False},
)
assert flag.is_enabled(user_plan="free") is False
assert flag.is_enabled(user_plan="basic") is False
assert flag.is_enabled(user_plan="premium") is True
def test_user_override_priority_higher_than_plan(self):
"""用户白名单优先级高于套餐"""
flag = FeatureFlag(
name="test_feature",
global_enabled=False,
plan_overrides={"free": False},
user_overrides={"user_001": True},
)
# 用户在白名单中,即使全局禁用+free套餐也启用
assert flag.is_enabled(user_plan="free", user_id="user_001") is True
def test_user_override_disable(self):
"""用户白名单可单独禁用"""
flag = FeatureFlag(
name="test_feature",
global_enabled=True,
user_overrides={"user_002": False},
)
assert flag.is_enabled(user_id="user_002") is False
assert flag.is_enabled(user_id="user_001") is True
def test_user_override_without_plan(self):
"""用户白名单无需套餐也生效"""
flag = FeatureFlag(
name="test_feature", global_enabled=False, user_overrides={"u1": True}
)
assert flag.is_enabled(user_id="u1") is True
assert flag.is_enabled(user_id="u2") is False
def test_no_plan_uses_global(self):
"""不传 user_plan 时回退到全局开关"""
flag = FeatureFlag(name="test", global_enabled=True, plan_overrides={"free": False})
assert flag.is_enabled() is True
def test_default_values(self):
"""默认值正确"""
flag = FeatureFlag(name="test")
assert flag.name == "test"
assert flag.description == ""
assert flag.global_enabled is True
assert flag.plan_overrides == {}
assert flag.user_overrides == {}
class TestFeatureFlags:
"""FeatureFlags 管理器测试"""
def test_singleton_default_flags(self):
"""默认有 5 个 feature flags"""
ff = FeatureFlags()
flags = ff.list_flags()
assert len(flags) == 5
assert FeatureScope.AI_VOICE_GENERATION in flags
assert FeatureScope.DEDUPLICATION_REPORT in flags
assert FeatureScope.BATCH_EXPORT in flags
assert FeatureScope.MULTI_PLATFORM_OUTPUT in flags
assert FeatureScope.RECIPE_REUSE in flags
def test_is_enabled_existing_flag(self):
"""已存在的 flag 正常判断"""
ff = FeatureFlags()
assert ff.is_enabled(FeatureScope.AI_VOICE_GENERATION) is True
def test_is_enabled_nonexistent_flag(self):
"""不存在的 flag 默认禁用"""
ff = FeatureFlags()
assert ff.is_enabled("nonexistent_flag") is False
def test_is_enabled_with_plan(self):
"""按套餐判断"""
ff = FeatureFlags()
# free 套餐 AI 配音不可用
assert ff.is_enabled(FeatureScope.AI_VOICE_GENERATION, user_plan="free") is False
assert ff.is_enabled(FeatureScope.AI_VOICE_GENERATION, user_plan="basic") is True
assert ff.is_enabled(FeatureScope.AI_VOICE_GENERATION, user_plan="premium") is True
def test_is_enabled_premium_only_features(self):
"""仅 premium 可用的功能"""
ff = FeatureFlags()
for feat in [FeatureScope.DEDUPLICATION_REPORT, FeatureScope.MULTI_PLATFORM_OUTPUT]:
assert ff.is_enabled(feat, user_plan="free") is False
assert ff.is_enabled(feat, user_plan="basic") is False
assert ff.is_enabled(feat, user_plan="premium") is True
def test_register_new_flag(self):
"""注册新 flag"""
ff = FeatureFlags()
new_flag = FeatureFlag(name="new_feature", description="新功能", global_enabled=True)
ff.register(new_flag)
assert ff.is_enabled("new_feature") is True
assert ff.get("new_feature") is not None
assert ff.get("new_feature").description == "新功能"
def test_register_overwrites_existing(self):
"""注册同名 flag 覆盖旧的"""
ff = FeatureFlags()
original = ff.get(FeatureScope.AI_VOICE_GENERATION)
assert original.global_enabled is True
new_flag = FeatureFlag(name=FeatureScope.AI_VOICE_GENERATION, global_enabled=False)
ff.register(new_flag)
assert ff.is_enabled(FeatureScope.AI_VOICE_GENERATION) is False
def test_get_returns_none_for_missing(self):
"""获取不存在的 flag 返回 None"""
ff = FeatureFlags()
assert ff.get("no_such_flag") is None
def test_set_global(self):
"""设置全局开关"""
ff = FeatureFlags()
ff.set_global(FeatureScope.BATCH_EXPORT, False)
assert ff.is_enabled(FeatureScope.BATCH_EXPORT) is False
ff.set_global(FeatureScope.BATCH_EXPORT, True)
assert ff.is_enabled(FeatureScope.BATCH_EXPORT) is True
def test_set_global_missing_raises(self):
"""设置不存在的 flag 抛 KeyError"""
ff = FeatureFlags()
with pytest.raises(KeyError):
ff.set_global("nonexistent", True)
def test_set_plan_override(self):
"""设置套餐级别覆盖"""
ff = FeatureFlags()
ff.set_plan_override(FeatureScope.BATCH_EXPORT, "basic", False)
assert ff.is_enabled(FeatureScope.BATCH_EXPORT, user_plan="basic") is False
assert ff.is_enabled(FeatureScope.BATCH_EXPORT, user_plan="premium") is True
def test_set_plan_override_missing_raises(self):
"""设置不存在的 flag 抛 KeyError"""
ff = FeatureFlags()
with pytest.raises(KeyError):
ff.set_plan_override("nonexistent", "free", False)
def test_set_user_override(self):
"""设置用户白名单"""
ff = FeatureFlags()
ff.set_user_override(FeatureScope.DEDUPLICATION_REPORT, "user_42", True)
assert ff.is_enabled(
FeatureScope.DEDUPLICATION_REPORT, user_plan="free", user_id="user_42"
) is True
def test_set_user_override_disable(self):
"""用户白名单禁用"""
ff = FeatureFlags()
ff.set_user_override(FeatureScope.AI_VOICE_GENERATION, "user_99", False)
assert ff.is_enabled(
FeatureScope.AI_VOICE_GENERATION, user_plan="premium", user_id="user_99"
) is False
def test_set_user_override_missing_raises(self):
"""设置不存在的 flag 抛 KeyError"""
ff = FeatureFlags()
with pytest.raises(KeyError):
ff.set_user_override("nonexistent", "u1", True)
def test_list_flags_returns_copy(self):
"""list_flags 返回副本,修改不影响内部"""
ff = FeatureFlags()
flags = ff.list_flags()
flags["new_one"] = FeatureFlag(name="new_one")
assert ff.get("new_one") is None
def test_get_enabled_for_plan_free(self):
"""获取 free 套餐下启用的功能"""
ff = FeatureFlags()
enabled = ff.get_enabled_for_plan("free")
# free 套餐只有 recipe_reuse 可用?不对,看看默认配置
# AI_VOICE_GENERATION: free=False
# DEDUPLICATION_REPORT: free=False, basic=False
# BATCH_EXPORT: free=False
# MULTI_PLATFORM_OUTPUT: free=False, basic=False
# RECIPE_REUSE: free=False
# 所以 free 套餐全部禁用?不,recipe_reuse free=False
# 等等,RECIPE_REUSE 的 plan_overrides 是 {"free": False}
# 那对于 free 套餐,返回 False;但全局是 True
# 所以 free 套餐没有任何启用的?不对...
# 让我重新看:global_enabled=Trueplan_overrides={"free": False}
# 那么 free 套餐 is_enabled 是 False,其他套餐是 True
# 所以 free 套餐应该 0 个启用?不对,等等...
# 不,我需要重新检查每个 flag 的 plan_overrides
# AI_VOICE_GENERATION: free=False → free: False, basic/premium: True
# DEDUPLICATION_REPORT: free=False, basic=False → free/basic: False, premium: True
# BATCH_EXPORT: free=False → free: False, basic/premium: True
# MULTI_PLATFORM_OUTPUT: free=False, basic=False → free/basic: False, premium: True
# RECIPE_REUSE: free=False → free: False, basic/premium: True
assert len(enabled) == 0
def test_get_enabled_for_plan_basic(self):
"""获取 basic 套餐下启用的功能"""
ff = FeatureFlags()
enabled = ff.get_enabled_for_plan("basic")
# basic 套餐:AI_VOICE、BATCH_EXPORT、RECIPE_REUSE 可用
# DEDUPLICATION_REPORT、MULTI_PLATFORM_OUTPUT 不可用
assert FeatureScope.AI_VOICE_GENERATION in enabled
assert FeatureScope.BATCH_EXPORT in enabled
assert FeatureScope.RECIPE_REUSE in enabled
assert FeatureScope.DEDUPLICATION_REPORT not in enabled
assert FeatureScope.MULTI_PLATFORM_OUTPUT not in enabled
assert len(enabled) == 3
def test_get_enabled_for_plan_premium(self):
"""获取 premium 套餐下所有功能都启用"""
ff = FeatureFlags()
enabled = ff.get_enabled_for_plan("premium")
assert len(enabled) == 5