Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 0 additions & 8 deletions src/aap_eda/settings/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,6 @@
# See https://cfpb.github.io/django-flags/
ANALYTICS_FEATURE_FLAG_NAME = "FEATURE_EDA_ANALYTICS_ENABLED"

FLAGS = {
ANALYTICS_FEATURE_FLAG_NAME: [
{
"condition": "boolean",
"value": False,
},
],
}

INSTALLED_APPS = [
"daphne",
Expand Down
8 changes: 8 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@
import logging

import pytest
from ansible_base.feature_flags.utils import (
create_initial_data as seed_feature_flags,
)
from django.conf import settings

from aap_eda.core import enums, models
Expand Down Expand Up @@ -88,3 +91,8 @@ def aap_credential_type(preseed_credential_types):
return models.CredentialType.objects.get(
name=enums.DefaultCredentialType.AAP
)


@pytest.fixture
def preseed_feature_flags():
seed_feature_flags()
4 changes: 2 additions & 2 deletions tests/integration/api/test_feature_flags.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@


@pytest.mark.django_db
def test_feature_flags_list_endpoint(admin_client):
def test_feature_flags_list_endpoint(admin_client, preseed_feature_flags):
response = admin_client.get(f"{api_url_v1}/feature_flags_state/")
assert response.status_code == status.HTTP_200_OK, response.data
# Validates expected default feature flags
Expand All @@ -25,6 +25,6 @@ def test_feature_flags_list_endpoint(admin_client):
def test_feature_flags_toggle(flag_value):
flag_name = "FEATURE_EDA_ANALYTICS_ENABLED"
setattr(settings, flag_name, flag_value)
AAPFlag.objects.all().delete()
AAPFlag.objects.filter(name=flag_name).delete()
seed_feature_flags()
assert flag_state(flag_name) is flag_value
22 changes: 19 additions & 3 deletions tests/integration/test_advisory_lock.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import time
from concurrent.futures import ThreadPoolExecutor, wait
from contextlib import ExitStack
from threading import Lock
from unittest import mock

Expand Down Expand Up @@ -64,9 +65,24 @@ def _wrapper_call(shared_list):

importlib.reload(module)

with mock.patch(
f"{module_data['module_path']}.{module_data['fn_mock']}",
) as fn_mock:
patches = [
mock.patch(
f"{module_data['module_path']}.{module_data['fn_mock']}",
),
]

# Add mock for get_insights_tracking_state for analytics tests
if module_data["fn_call"] == "gather_analytics":
patches.append(
mock.patch(
"aap_eda.analytics.utils.get_insights_tracking_state",
return_value=True,
)
)

with ExitStack() as stack:
mocks = [stack.enter_context(patch) for patch in patches]
fn_mock = mocks[0]

def record_call(void=None):
time.sleep(1)
Expand Down
61 changes: 28 additions & 33 deletions tests/unit/test_features.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@
"""Unit tests for feature flags functionality."""

import pytest
from ansible_base.feature_flags.models import AAPFlag
from ansible_base.feature_flags.utils import (
create_initial_data as seed_feature_flags,
)

from aap_eda.settings import features
from aap_eda.settings.features import _get_feature
Expand All @@ -29,60 +33,51 @@ def clear_feature_cache():
@pytest.mark.django_db
def test_get_feature_flag(settings):
"""Test getting feature flag values."""
settings.FLAGS = {
settings.ANALYTICS_FEATURE_FLAG_NAME: [
{"condition": "boolean", "value": False}
],
}
AAPFlag.objects.filter(name=settings.ANALYTICS_FEATURE_FLAG_NAME).delete()
setattr(settings, settings.ANALYTICS_FEATURE_FLAG_NAME, False)
seed_feature_flags()

assert features.ANALYTICS is False


@pytest.mark.django_db
def test_feature_flag_caching(settings):
"""Test that feature flag values are properly cached."""
settings.FLAGS = {
settings.ANALYTICS_FEATURE_FLAG_NAME: [
{"condition": "boolean", "value": True}
]
}
AAPFlag.objects.filter(name=settings.ANALYTICS_FEATURE_FLAG_NAME).delete()
setattr(settings, settings.ANALYTICS_FEATURE_FLAG_NAME, True)
seed_feature_flags()
# Clear cache to ensure settings are picked up
_get_feature.cache_clear()

# First access - should cache the value
first_result = features.ANALYTICS
features.ANALYTICS

# Second access should return the same cached value
second_result = features.ANALYTICS
assert first_result == second_result

# The exact value may depend on flag implementation
assert isinstance(first_result, bool)
# Change the underlying flag value
setattr(settings, settings.ANALYTICS_FEATURE_FLAG_NAME, False)
seed_feature_flags()
# Should still get the cached value
assert features.ANALYTICS is True


@pytest.mark.django_db
def test_cache_invalidation(settings):
"""Test that cache invalidation works as expected."""
# Set initial flag value
settings.FLAGS = {
settings.ANALYTICS_FEATURE_FLAG_NAME: [
{"condition": "boolean", "value": True}
]
}
_get_feature.cache_clear()
AAPFlag.objects.filter(name=settings.ANALYTICS_FEATURE_FLAG_NAME).delete()
setattr(settings, settings.ANALYTICS_FEATURE_FLAG_NAME, True)
seed_feature_flags()

# Get initial value
initial_value = features.ANALYTICS
# Populate cache
assert features.ANALYTICS is True

# Clear cache manually
# Change the flag value and clear cache
setattr(settings, settings.ANALYTICS_FEATURE_FLAG_NAME, False)
seed_feature_flags()
_get_feature.cache_clear()

# Get value again - should be consistent
after_cache_clear = features.ANALYTICS

# The cache clearing should work without errors
assert isinstance(initial_value, bool)
assert isinstance(after_cache_clear, bool)
# Feature should remain true.
# If runtime toggle, we should only be able to
# update the value after toggling it via the platform gateway
assert features.ANALYTICS is True


@pytest.mark.django_db
Expand Down