Skip to content
Draft
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
1 change: 1 addition & 0 deletions manifests/cpp.yml
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ tests/:
Test_TracerSCITagging: missing_feature
test_tracer_flare.py: missing_feature
test_feature_flag_exposures.py:
Test_FFE_Config_Update: missing_feature
Test_FFE_Exposure_Events: missing_feature
Test_FFE_Exposure_Events_Empty: missing_feature
Test_FFE_Exposure_Events_Errors: missing_feature
Expand Down
1 change: 1 addition & 0 deletions manifests/cpp_httpd.yml
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ tests/:
Test_Span_Links_From_Conflicting_Contexts: missing_feature (baggage should be implemented and conflicting trace contexts should generate span link)
Test_Span_Links_Omit_Tracestate_From_Conflicting_Contexts: missing_feature (implementation specs have not been determined)
test_feature_flag_exposures.py:
Test_FFE_Config_Update: missing_feature
Test_FFE_Exposure_Events: missing_feature
Test_FFE_Exposure_Events_Empty: missing_feature
Test_FFE_Exposure_Events_Errors: missing_feature
Expand Down
1 change: 1 addition & 0 deletions manifests/cpp_nginx.yml
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,7 @@ tests/:
Test_Span_Links_From_Conflicting_Contexts: missing_feature (baggage should be implemented and conflicting trace contexts should generate span link)
Test_Span_Links_Omit_Tracestate_From_Conflicting_Contexts: missing_feature (implementation specs have not been determined)
test_feature_flag_exposures.py:
Test_FFE_Config_Update: missing_feature
Test_FFE_Exposure_Events: missing_feature
Test_FFE_Exposure_Events_Empty: missing_feature
Test_FFE_Exposure_Events_Errors: missing_feature
Expand Down
1 change: 1 addition & 0 deletions manifests/dotnet.yml
Original file line number Diff line number Diff line change
Expand Up @@ -692,6 +692,7 @@ tests/:
Test_Span_Links_From_Conflicting_Contexts: v3.26.3
Test_Span_Links_Omit_Tracestate_From_Conflicting_Contexts: missing_feature (implementation specs have not been determined)
test_feature_flag_exposures.py:
Test_FFE_Config_Update: missing_feature
Test_FFE_Exposure_Events: missing_feature
Test_FFE_Exposure_Events_Empty: missing_feature
Test_FFE_Exposure_Events_Errors: missing_feature
Expand Down
1 change: 1 addition & 0 deletions manifests/golang.yml
Original file line number Diff line number Diff line change
Expand Up @@ -882,6 +882,7 @@ tests/:
Test_Span_Links_Omit_Tracestate_From_Conflicting_Contexts: missing_feature (implementation specs have not been determined)
Test_Synthetics_APM_Datadog: bug (APMAPI-901) # the incoming headers are considered invalid
test_feature_flag_exposures.py:
Test_FFE_Config_Update: v2.5.0-dev
Test_FFE_Exposure_Events: v2.5.0-dev
Test_FFE_Exposure_Events_Empty: v2.5.0-dev
Test_FFE_Exposure_Events_Errors: v2.5.0-dev
Expand Down
3 changes: 3 additions & 0 deletions manifests/java.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2331,6 +2331,9 @@ tests/:
Test_Span_Links_From_Conflicting_Contexts: v1.43.0
Test_Span_Links_Omit_Tracestate_From_Conflicting_Contexts: missing_feature (implementation specs have not been determined)
test_feature_flag_exposures.py:
Test_FFE_Config_Update:
'*': irrelevant
spring-boot: v1.56.0
Test_FFE_Exposure_Events:
'*': irrelevant
spring-boot: v1.56.0
Expand Down
3 changes: 3 additions & 0 deletions manifests/nodejs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1482,6 +1482,9 @@ tests/:
'*': *ref_5_25_0
nextjs: bug (APMAPI-939) # the nextjs weblog application changes the sampling priority from 1.0 to 2.0
test_feature_flag_exposures.py:
Test_FFE_Config_Update:
'*': incomplete_test_app
express4: *ref_5_77_0 # only target express 4
Test_FFE_Exposure_Events:
'*': incomplete_test_app
express4: *ref_5_77_0 # only target express 4
Expand Down
1 change: 1 addition & 0 deletions manifests/php.yml
Original file line number Diff line number Diff line change
Expand Up @@ -685,6 +685,7 @@ tests/:
Test_Span_Links_From_Conflicting_Contexts: missing_feature
Test_Span_Links_Omit_Tracestate_From_Conflicting_Contexts: missing_feature (implementation specs have not been determined)
test_feature_flag_exposures.py:
Test_FFE_Config_Update: missing_feature
Test_FFE_Exposure_Events: missing_feature
Test_FFE_Exposure_Events_Empty: missing_feature
Test_FFE_Exposure_Events_Errors: missing_feature
Expand Down
1 change: 1 addition & 0 deletions manifests/python.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1292,6 +1292,7 @@ tests/:
Test_Span_Links_From_Conflicting_Contexts: v2.17.0
Test_Span_Links_Omit_Tracestate_From_Conflicting_Contexts: missing_feature (implementation specs have not been determined)
test_feature_flag_exposures.py:
Test_FFE_Config_Update: v4.0.0
Test_FFE_Exposure_Events: v4.0.0
Test_FFE_Exposure_Events_Empty: v4.0.0
Test_FFE_Exposure_Events_Errors: v4.0.0
Expand Down
11 changes: 6 additions & 5 deletions manifests/ruby.yml
Original file line number Diff line number Diff line change
Expand Up @@ -879,11 +879,12 @@ tests/:
Test_Span_Links_From_Conflicting_Contexts: missing_feature
Test_Span_Links_Omit_Tracestate_From_Conflicting_Contexts: missing_feature (implementation specs have not been determined)
test_feature_flag_exposures.py:
Test_FFE_Exposure_Events: missing_feature
Test_FFE_Exposure_Events_Empty: missing_feature
Test_FFE_Exposure_Events_Errors: missing_feature
Test_FFE_RC_Down_From_Start: missing_feature
Test_FFE_RC_Unavailable: missing_feature
Test_FFE_Config_Update: v2.23.0+6cb632a8089d21b2b5a7ca3ca5a198dcaa566bef
Test_FFE_Exposure_Events: v2.23.0+6cb632a8089d21b2b5a7ca3ca5a198dcaa566bef
Test_FFE_Exposure_Events_Empty: v2.23.0+6cb632a8089d21b2b5a7ca3ca5a198dcaa566bef
Test_FFE_Exposure_Events_Errors: v2.23.0+6cb632a8089d21b2b5a7ca3ca5a198dcaa566bef
Test_FFE_RC_Down_From_Start: v2.23.0+6cb632a8089d21b2b5a7ca3ca5a198dcaa566bef
Test_FFE_RC_Unavailable: v2.23.0+6cb632a8089d21b2b5a7ca3ca5a198dcaa566bef
test_graphql.py:
Test_GraphQLOperationErrorReporting:
"*": missing_feature
Expand Down
1 change: 1 addition & 0 deletions manifests/rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ tests/:
test_tracer_flare.py:
TestTracerFlareV1: missing_feature
test_feature_flag_exposures.py:
Test_FFE_Config_Update: missing_feature
Test_FFE_Exposure_Events: missing_feature
Test_FFE_Exposure_Events_Empty: missing_feature
Test_FFE_Exposure_Events_Errors: missing_feature
Expand Down
116 changes: 116 additions & 0 deletions tests/test_feature_flag_exposures.py
Original file line number Diff line number Diff line change
Expand Up @@ -590,3 +590,119 @@ def test_ffe_rc_down_from_start(self):
assert result["value"] == self.default_value, (
f"Expected default '{self.default_value}', got '{result['value']}'"
)


@scenarios.feature_flag_exposure
@features.feature_flag_exposure
class Test_FFE_Config_Update:
"""Test that FFE correctly updates flag values when remote config is updated."""

def setup_ffe_config_update_changes_flag_value(self):
"""Set up FFE with initial config, evaluate, update config, and evaluate again."""
config_id = "ffe-update-test-config"
self.flag_key = "test-flag-updatable"
self.targeting_key = "test-user-update"
self.initial_value = "initial-value"
self.updated_value = "updated-value"
self.default_value = "default"

# Initial configuration with first value
initial_config = {
"id": "1",
"createdAt": "2024-04-17T19:40:53.716Z",
"format": "SERVER",
"environment": {"name": "Test"},
"flags": {
self.flag_key: {
"key": self.flag_key,
"enabled": True,
"variationType": "STRING",
"variations": {
"initial": {"key": "initial", "value": self.initial_value},
"updated": {"key": "updated", "value": self.updated_value},
},
"allocations": [
{
"key": "default-allocation",
"rules": [],
"splits": [{"variationKey": "initial", "shards": []}],
"doLog": True,
}
],
}
},
}

# Reset and apply initial config
rc.rc_state.reset().set_config(f"{RC_PATH}/{config_id}/config", initial_config).apply()

# Evaluate flag with initial config
self.r1 = weblog.post(
"/ffe",
json={
"flag": self.flag_key,
"variationType": "STRING",
"defaultValue": self.default_value,
"targetingKey": self.targeting_key,
"attributes": {},
},
)

# Updated configuration with second value
updated_config = {
"id": "2",
"createdAt": "2024-04-17T19:41:00.000Z",
"format": "SERVER",
"environment": {"name": "Test"},
"flags": {
self.flag_key: {
"key": self.flag_key,
"enabled": True,
"variationType": "STRING",
"variations": {
"initial": {"key": "initial", "value": self.initial_value},
"updated": {"key": "updated", "value": self.updated_value},
},
"allocations": [
{
"key": "default-allocation",
"rules": [],
"splits": [{"variationKey": "updated", "shards": []}],
"doLog": True,
}
],
}
},
}

# Apply updated config
rc.rc_state.set_config(f"{RC_PATH}/{config_id}/config", updated_config).apply()

# Evaluate flag with updated config
self.r2 = weblog.post(
"/ffe",
json={
"flag": self.flag_key,
"variationType": "STRING",
"defaultValue": self.default_value,
"targetingKey": self.targeting_key,
"attributes": {},
},
)

def test_ffe_config_update_changes_flag_value(self):
"""Test that flag evaluation returns updated value after config update."""
assert self.r1.status_code == 200, f"First flag evaluation failed: {self.r1.text}"
assert self.r2.status_code == 200, f"Second flag evaluation failed: {self.r2.text}"

# Verify first evaluation returned initial value
result1 = json.loads(self.r1.text)
assert result1["value"] == self.initial_value, (
f"First evaluation: expected '{self.initial_value}', got '{result1['value']}'"
)

# Verify second evaluation returned updated value
result2 = json.loads(self.r2.text)
assert result2["value"] == self.updated_value, (
f"Second evaluation: expected '{self.updated_value}', got '{result2['value']}'"
)
Loading