"""Unit tests for features.py.""" from unittest.mock import MagicMock import pytest from features import exceptions from features.constants import context as context_constants from features.models import feature from features.models import feature_variant from features.models import feature_variant_force_rule as force_rule @pytest.fixture def fixture_feature_variants(): """Return a list of basic feature variants.""" return [ feature_variant.FeatureVariant('control'), feature_variant.FeatureVariant('catchy'), ] def test_create_no_name_fails(): """Test a feature cannot be created without a name.""" with pytest.raises(Exception) as excinfo: feature.Feature(None, []) assert 'Name of the feature is mandatory' in str(excinfo.value) def test_create_no_variants_fails(): """Test a feature cannot be created with duplicate feature variants.""" with pytest.raises(exceptions.MissingFeatureVariant): feature.Feature('name', []) def test_create_duplicate_variant_fails(): """Test a feature cannot be created with duplicate feature variants.""" with pytest.raises(exceptions.DuplicateFeatureVariantException): feature.Feature( 'name', [ feature_variant.FeatureVariant('control'), feature_variant.FeatureVariant('control', active=True), ], ) def test_create_missing_force_rule_variant_fails(fixture_feature_variants): """Test a feature cannot be created with a malformed force rule.""" with pytest.raises(exceptions.MissingFeatureVariant): feature.Feature( 'name', fixture_feature_variants, [force_rule.FeatureVariantForceRule('missing', lambda x: x, [])], ) def test_feature_creation(fixture_feature_variants): """Test the creation of a feature.""" test_feature = feature.Feature( 'name', fixture_feature_variants, [force_rule.FeatureVariantForceRule('catchy', lambda x: x, [])], ) assert test_feature.name == 'name' assert len(test_feature.variants) == 2 assert len(test_feature.force_rules) == 1 feature_dict = test_feature.to_dict() assert isinstance(feature_dict, dict) assert 'name' in feature_dict assert len(feature_dict.get('variants')) == 2 def test_feature_creation_no_force_rules(fixture_feature_variants): """Test the creation of a feature without force rules.""" test_feature = feature.Feature('name', fixture_feature_variants, []) assert test_feature.name == 'name' assert len(test_feature.variants) == 2 assert len(test_feature.force_rules) == 0 feature_dict = test_feature.to_dict() assert isinstance(feature_dict, dict) assert 'name' in feature_dict assert len(feature_dict.get('variants')) == 2 def test_is_variant_active(): """Test if a feature variant is active.""" test_feature = feature.Feature( 'name', [ feature_variant.FeatureVariant('control', active=True), feature_variant.FeatureVariant('catchy'), ], [ force_rule.FeatureVariantForceRule( 'catchy', lambda values, context: ( context.get(context_constants.USER_ID) in values ), ['id1'], ) ], ) # The user with id1 should be in control but catchy has an override, so the # override takes over the default (user should be in catchy but not in # control) id1_context = {context_constants.USER_ID: 'id1'} assert test_feature.is_variant_active('catchy', id1_context) assert not test_feature.is_variant_active('control', id1_context) # Any other user should be in one or the other id2_context = {context_constants.USER_ID: 'id2'} assert test_feature.is_variant_active('control', id2_context) assert not test_feature.is_variant_active('catchy', id2_context) def test_get_variants(monkeypatch): """Test getting all variants.""" test_feature = feature.Feature( 'feature_a', [ feature_variant.FeatureVariant('control'), feature_variant.FeatureVariant('catchy', active=True), ], ) mock = MagicMock(return_value='catchy') monkeypatch.setattr(test_feature, 'get_active_variant', mock) context = {context_constants.USER_ID: 'userid'} variants = test_feature.get_variants(context) mock.assert_called_with(context) assert variants == {'control': False, 'catchy': True} def test_get_all_features_and_variants(): """Test getting all features and their variants.""" feature_set = { 'feature_a': feature.Feature( 'feature_a', [ feature_variant.FeatureVariant('control'), feature_variant.FeatureVariant('catchy', active=True), ], ), 'feature_b': feature.Feature( 'feature_b', [ feature_variant.FeatureVariant('control', active=True), feature_variant.FeatureVariant('spooky'), ], ), } expected = { 'feature_a': { 'control': False, 'catchy': True, }, 'feature_b': { 'control': True, 'spooky': False, }, } features_with_variants = feature.get_all_features_and_variants(feature_set, {}) assert features_with_variants == expected