test_feature.py 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137
  1. from datetime import datetime, timezone
  2. import pytest
  3. from flagpole import ContextBuilder, EvaluationContext, Feature, InvalidFeatureFlagConfiguration
  4. from flagpole.operators import OperatorKind
  5. class TestParseFeatureConfig:
  6. def get_is_true_context_builder(self, is_true_value: bool):
  7. return ContextBuilder().add_context_transformer(lambda _data: dict(is_true=is_true_value))
  8. def test_valid_without_created_at(self):
  9. feature = Feature.from_feature_config_json("foo", '{"owner": "test", "segments":[]}')
  10. assert feature.name == "foo"
  11. assert isinstance(feature.created_at, datetime)
  12. assert feature.segments == []
  13. def test_feature_with_empty_segments(self):
  14. feature = Feature.from_feature_config_json(
  15. "foobar",
  16. """
  17. {
  18. "created_at": "2023-10-12T00:00:00.000Z",
  19. "owner": "test-owner",
  20. "segments": []
  21. }
  22. """,
  23. )
  24. assert feature.name == "foobar"
  25. assert feature.created_at == datetime(2023, 10, 12, tzinfo=timezone.utc)
  26. assert feature.owner == "test-owner"
  27. assert feature.segments == []
  28. assert not feature.match(EvaluationContext(dict()))
  29. def test_valid_with_all_nesting(self):
  30. feature = Feature.from_feature_config_json(
  31. "foobar",
  32. """
  33. {
  34. "created_at": "2023-10-12T00:00:00.000Z",
  35. "owner": "test-owner",
  36. "segments": [{
  37. "name": "segment1",
  38. "rollout": 100,
  39. "conditions": [{
  40. "property": "test_property",
  41. "operator": {
  42. "kind": "in",
  43. "value": ["foobar"]
  44. }
  45. }]
  46. }]
  47. }
  48. """,
  49. )
  50. assert feature.name == "foobar"
  51. assert len(feature.segments) == 1
  52. assert feature.segments[0].name == "segment1"
  53. assert feature.segments[0].rollout == 100
  54. assert len(feature.segments[0].conditions) == 1
  55. condition = feature.segments[0].conditions[0]
  56. assert condition.property == "test_property"
  57. assert condition.operator
  58. assert condition.operator.kind == OperatorKind.IN
  59. assert condition.operator.value == ["foobar"]
  60. assert feature.match(EvaluationContext(dict(test_property="foobar")))
  61. assert not feature.match(EvaluationContext(dict(test_property="barfoo")))
  62. def test_invalid_json(self):
  63. with pytest.raises(InvalidFeatureFlagConfiguration):
  64. Feature.from_feature_config_json("foobar", "{")
  65. def test_empty_string_name(self):
  66. with pytest.raises(InvalidFeatureFlagConfiguration) as exception:
  67. Feature.from_feature_config_json("", '{"segments":[]}')
  68. assert "Provided JSON is not a valid feature" in str(exception)
  69. def test_missing_segments(self):
  70. with pytest.raises(InvalidFeatureFlagConfiguration) as exception:
  71. Feature.from_feature_config_json("foo", "{}")
  72. assert "Provided JSON is not a valid feature" in str(exception)
  73. def test_enabled_feature(self):
  74. feature = Feature.from_feature_config_json(
  75. "foo",
  76. """
  77. {
  78. "owner": "test-user",
  79. "segments": [{
  80. "name": "always_pass_segment",
  81. "rollout": 100,
  82. "conditions": [{
  83. "name": "Always true",
  84. "property": "is_true",
  85. "operator": {
  86. "kind": "equals",
  87. "value": true
  88. }
  89. }]
  90. }]
  91. }
  92. """,
  93. )
  94. context_builder = self.get_is_true_context_builder(is_true_value=True)
  95. assert feature.match(context_builder.build())
  96. def test_disabled_feature(self):
  97. feature = Feature.from_feature_config_json(
  98. "foo",
  99. """
  100. {
  101. "owner": "test-user",
  102. "enabled": false,
  103. "segments": [{
  104. "name": "always_pass_segment",
  105. "rollout": 100,
  106. "conditions": [{
  107. "name": "Always true",
  108. "property": "is_true",
  109. "operator": {
  110. "kind": "equals",
  111. "value": true
  112. }
  113. }]
  114. }]
  115. }
  116. """,
  117. )
  118. context_builder = self.get_is_true_context_builder(is_true_value=True)
  119. assert not feature.match(context_builder.build())