229 lines
7.6 KiB
Python
229 lines
7.6 KiB
Python
from django.core.exceptions import ValidationError
|
|
from django.test import TestCase
|
|
|
|
from apps.decision.services import decide_for_flag
|
|
from apps.flags.models import (
|
|
FeatureFlagType,
|
|
validate_value_for_type,
|
|
)
|
|
from apps.flags.selectors import feature_flag_get_by_key, feature_flag_list
|
|
from apps.flags.services import (
|
|
feature_flag_create,
|
|
feature_flag_update_default,
|
|
)
|
|
from config.errors import ConflictError
|
|
|
|
|
|
class ValidateValueForTypeTest(TestCase):
|
|
def test_boolean_true_variants(self) -> None:
|
|
for val in ("true", "True", "1", "yes", "YES"):
|
|
self.assertEqual(
|
|
validate_value_for_type(val, FeatureFlagType.BOOLEAN), "true"
|
|
)
|
|
|
|
def test_boolean_false_variants(self) -> None:
|
|
for val in ("false", "False", "0", "no", "NO"):
|
|
self.assertEqual(
|
|
validate_value_for_type(val, FeatureFlagType.BOOLEAN), "false"
|
|
)
|
|
|
|
def test_boolean_invalid(self) -> None:
|
|
with self.assertRaises(ValidationError):
|
|
validate_value_for_type("maybe", FeatureFlagType.BOOLEAN)
|
|
|
|
def test_integer_valid(self) -> None:
|
|
self.assertEqual(
|
|
validate_value_for_type("42", FeatureFlagType.INTEGER), "42"
|
|
)
|
|
|
|
def test_integer_negative(self) -> None:
|
|
self.assertEqual(
|
|
validate_value_for_type("-7", FeatureFlagType.INTEGER), "-7"
|
|
)
|
|
|
|
def test_integer_invalid(self) -> None:
|
|
with self.assertRaises(ValidationError):
|
|
validate_value_for_type("abc", FeatureFlagType.INTEGER)
|
|
|
|
def test_integer_float(self) -> None:
|
|
with self.assertRaises(ValidationError):
|
|
validate_value_for_type("3.14", FeatureFlagType.INTEGER)
|
|
|
|
def test_string_passthrough(self) -> None:
|
|
self.assertEqual(
|
|
validate_value_for_type("anything", FeatureFlagType.STRING),
|
|
"anything",
|
|
)
|
|
|
|
|
|
class FeatureFlagServiceTest(TestCase):
|
|
def test_create_string_flag(self) -> None:
|
|
flag = feature_flag_create(
|
|
key="svc_string",
|
|
name="Service String",
|
|
value_type=FeatureFlagType.STRING,
|
|
default_value="hello",
|
|
)
|
|
self.assertEqual(flag.key, "svc_string")
|
|
self.assertEqual(flag.value_type, FeatureFlagType.STRING)
|
|
self.assertEqual(flag.default_value, "hello")
|
|
|
|
def test_create_boolean_normalises(self) -> None:
|
|
flag = feature_flag_create(
|
|
key="svc_bool",
|
|
name="Service Bool",
|
|
value_type=FeatureFlagType.BOOLEAN,
|
|
default_value="Yes",
|
|
)
|
|
self.assertEqual(flag.default_value, "true")
|
|
|
|
def test_create_integer_normalises(self) -> None:
|
|
flag = feature_flag_create(
|
|
key="svc_int",
|
|
name="Service Int",
|
|
value_type=FeatureFlagType.INTEGER,
|
|
default_value="007",
|
|
)
|
|
self.assertEqual(flag.default_value, "7")
|
|
|
|
def test_create_invalid_type_raises(self) -> None:
|
|
with self.assertRaises(ValidationError):
|
|
feature_flag_create(
|
|
key="svc_bad",
|
|
name="Bad",
|
|
value_type="float",
|
|
default_value="1.0",
|
|
)
|
|
|
|
def test_create_bool_invalid_value_raises(self) -> None:
|
|
with self.assertRaises(ValidationError):
|
|
feature_flag_create(
|
|
key="svc_bad_bool",
|
|
name="Bad Bool",
|
|
value_type=FeatureFlagType.BOOLEAN,
|
|
default_value="maybe",
|
|
)
|
|
|
|
def test_create_int_invalid_value_raises(self) -> None:
|
|
with self.assertRaises(ValidationError):
|
|
feature_flag_create(
|
|
key="svc_bad_int",
|
|
name="Bad Int",
|
|
value_type=FeatureFlagType.INTEGER,
|
|
default_value="abc",
|
|
)
|
|
|
|
def test_create_duplicate_key_raises(self) -> None:
|
|
feature_flag_create(
|
|
key="svc_dup",
|
|
name="First",
|
|
value_type=FeatureFlagType.STRING,
|
|
default_value="a",
|
|
)
|
|
with self.assertRaises(ConflictError):
|
|
feature_flag_create(
|
|
key="svc_dup",
|
|
name="Second",
|
|
value_type=FeatureFlagType.STRING,
|
|
default_value="b",
|
|
)
|
|
|
|
def test_update_default_value(self) -> None:
|
|
flag = feature_flag_create(
|
|
key="svc_upd",
|
|
name="Update",
|
|
value_type=FeatureFlagType.STRING,
|
|
default_value="old",
|
|
)
|
|
updated = feature_flag_update_default(flag=flag, default_value="new")
|
|
self.assertEqual(updated.default_value, "new")
|
|
flag.refresh_from_db()
|
|
self.assertEqual(flag.default_value, "new")
|
|
|
|
def test_update_bool_validates(self) -> None:
|
|
flag = feature_flag_create(
|
|
key="svc_upd_bool",
|
|
name="Update Bool",
|
|
value_type=FeatureFlagType.BOOLEAN,
|
|
default_value="true",
|
|
)
|
|
with self.assertRaises(ValidationError):
|
|
feature_flag_update_default(flag=flag, default_value="maybe")
|
|
|
|
def test_update_int_validates(self) -> None:
|
|
flag = feature_flag_create(
|
|
key="svc_upd_int",
|
|
name="Update Int",
|
|
value_type=FeatureFlagType.INTEGER,
|
|
default_value="5",
|
|
)
|
|
with self.assertRaises(ValidationError):
|
|
feature_flag_update_default(flag=flag, default_value="bad")
|
|
|
|
def test_update_default_invalidates_decide_flag_cache(self) -> None:
|
|
flag = feature_flag_create(
|
|
key="svc_upd_cache",
|
|
name="Update Cache",
|
|
value_type=FeatureFlagType.STRING,
|
|
default_value="old",
|
|
)
|
|
first = decide_for_flag(flag.key, "svc_subj_1", {})
|
|
self.assertEqual(first["value"], "old")
|
|
feature_flag_update_default(flag=flag, default_value="new")
|
|
second = decide_for_flag(flag.key, "svc_subj_2", {})
|
|
self.assertEqual(second["reason"], "no_active_experiment")
|
|
self.assertEqual(second["value"], "new")
|
|
|
|
|
|
class FeatureFlagSelectorTest(TestCase):
|
|
def test_list_all(self) -> None:
|
|
feature_flag_create(
|
|
key="sel_a", name="A", value_type="string", default_value="a"
|
|
)
|
|
feature_flag_create(
|
|
key="sel_b", name="B", value_type="boolean", default_value="true"
|
|
)
|
|
self.assertEqual(feature_flag_list().count(), 2)
|
|
|
|
def test_list_filter_type(self) -> None:
|
|
feature_flag_create(
|
|
key="sel_ft_s",
|
|
name="S",
|
|
value_type="string",
|
|
default_value="s",
|
|
)
|
|
feature_flag_create(
|
|
key="sel_ft_b",
|
|
name="B",
|
|
value_type="boolean",
|
|
default_value="true",
|
|
)
|
|
qs = feature_flag_list(value_type="boolean")
|
|
self.assertEqual(qs.count(), 1)
|
|
self.assertEqual(qs.first().key, "sel_ft_b")
|
|
|
|
def test_list_search(self) -> None:
|
|
feature_flag_create(
|
|
key="sel_search_match",
|
|
name="M",
|
|
value_type="string",
|
|
default_value="m",
|
|
)
|
|
feature_flag_create(
|
|
key="sel_other", name="O", value_type="string", default_value="o"
|
|
)
|
|
qs = feature_flag_list(search="match")
|
|
self.assertEqual(qs.count(), 1)
|
|
|
|
def test_get_by_key(self) -> None:
|
|
feature_flag_create(
|
|
key="sel_get", name="G", value_type="string", default_value="g"
|
|
)
|
|
flag = feature_flag_get_by_key("sel_get")
|
|
self.assertIsNotNone(flag)
|
|
self.assertEqual(flag.key, "sel_get")
|
|
|
|
def test_get_by_key_not_found(self) -> None:
|
|
flag = feature_flag_get_by_key("nonexistent")
|
|
self.assertIsNone(flag)
|