diff --git a/spec/datadog/core/feature_flags/README.md b/spec/datadog/core/feature_flags/README.md new file mode 100644 index 00000000000..9df5c1a9804 --- /dev/null +++ b/spec/datadog/core/feature_flags/README.md @@ -0,0 +1,175 @@ +# Feature Flags Test Suite + +This directory contains comprehensive tests for the Datadog Feature Flags functionality implemented via the `libdatadog_api` C extension. + +## Test Structure + +### Test Files + +- **`feature_flags_spec.rb`** - Core functionality tests and JSON test case execution +- **`flag_types_spec.rb`** - Enhanced flag type support (boolean, numeric, JSON) and backward compatibility +- **`test_case_runner_spec.rb`** - Comprehensive test case runner that validates against all libdatadog test cases +- **`feature_flags_integration_spec.rb`** - Integration tests and smoke tests + +### Support Files + +- **`spec/support/feature_flags_helpers.rb`** - Test helper methods and utilities +- **`fixtures/`** - JSON test data copied from libdatadog repository + +## Test Data + +The `fixtures/` directory contains: + +- **`flags-v1.json`** - Main feature flag configuration file (77KB+ with comprehensive flag definitions) +- **`test-case-*.json`** - Individual test case files covering: + - Boolean attribute matching + - Disabled flags + - Empty flags + - Integer flags + - JSON/Object flags + - Kill switch behavior + - Special character handling + - Edge cases and error conditions + +Total: **20+ test files** with **50+ individual test cases** + +## Running Tests + +### Prerequisites + +1. **libdatadog gem** must be installed and compatible with your platform +2. **libdatadog_api extension** must compile successfully +3. Ruby version compatibility (typically Ruby 2.7+) + +### Individual Test Suites + +```bash +# Core functionality tests +bundle exec rspec spec/datadog/core/feature_flags/feature_flags_spec.rb + +# Flag type tests (enhanced features) +bundle exec rspec spec/datadog/core/feature_flags/flag_types_spec.rb + +# Comprehensive test case validation +bundle exec rspec spec/datadog/core/feature_flags/test_case_runner_spec.rb + +# Integration and smoke tests +bundle exec rspec spec/datadog/core/feature_flags_integration_spec.rb +``` + +### All Feature Flag Tests + +```bash +# Run all feature flag tests +bundle exec rspec spec/datadog/core/feature_flags/ + +# Or with tag filtering +bundle exec rspec --tag feature_flags +``` + +## What The Tests Validate + +### Core Functionality + +- ✅ **Configuration Loading** - JSON parsing and configuration creation +- ✅ **Flag Evaluation** - Basic get_assignment functionality +- ✅ **Context Handling** - User context and attribute processing +- ✅ **Error Handling** - Invalid configurations and missing flags + +### Enhanced Features (if available) + +- ✅ **Multiple Flag Types** - Boolean, String, Number, Object support +- ✅ **Type-Specific Evaluation** - Flag type constants and 3-parameter API +- ✅ **Backward Compatibility** - Original 2-parameter method still works + +### OpenFeature Compliance + +- ✅ **Standard Reasons** - UPPERCASE string constants (STATIC, DEFAULT, etc.) +- ✅ **Standard Error Codes** - UPPERCASE error codes (TYPE_MISMATCH, etc.) +- ✅ **Resolution Details** - Complete ResolutionDetails object structure + +### Performance & Safety + +- ✅ **Memory Safety** - GC safety improvements for string handling +- ✅ **Performance** - Optimized hash iteration +- ✅ **Load Testing** - Multiple rapid evaluations +- ✅ **Large Context** - Handling of large attribute sets + +### Cross-Language Validation + +- ✅ **libdatadog Compatibility** - All test cases from Rust implementation +- ✅ **Expected Results** - Validates against known good outputs +- ✅ **Edge Cases** - Comprehensive edge case coverage + +## Test Output Examples + +### Successful Run + +``` +Feature Flags Integration Tests + Extension availability + ✓ loads the libdatadog_api extension successfully + ✓ has working Configuration class + ✓ has working ResolutionDetails methods + + Test data availability + ✓ has the main configuration file + ✓ has test case files + ✓ can parse all test files as valid JSON + +Finished in 0.12 seconds (files took 0.8 seconds to load) +8 examples, 0 failures +``` + +### Extension Not Available + +``` +Feature Flags Integration Tests + Extension availability (PENDING: libdatadog_api extension not available) + +Finished in 0.01 seconds (files took 0.5 seconds to load) +8 examples, 0 failures, 8 pending +``` + +## Implementation Coverage + +The tests are designed to work with both: + +1. **Original Implementation** - Basic string flag support +2. **Enhanced Implementation** - Multiple flag types, improved performance, OpenFeature compliance + +Tests automatically detect available features and adjust expectations accordingly. + +## Debugging Test Failures + +### Extension Loading Issues + +If tests skip with "libdatadog_api extension not available": + +1. Check if `libdatadog` gem is installed: `bundle list | grep libdatadog` +2. Verify platform compatibility (ARM64 macOS may have issues) +3. Compile extension manually: `cd ext/libdatadog_api && ruby extconf.rb && make` + +### Configuration Errors + +If configuration creation fails: +- Check libdatadog version compatibility +- Verify JSON test data integrity +- Look for missing dependencies + +### Evaluation Failures + +If specific test cases fail: +- Compare with expected results in JSON files +- Check for implementation differences between Ruby and Rust +- Validate context attribute handling + +## Contributing + +When adding new tests: + +1. Use the `FeatureFlagsHelpers` module for common functionality +2. Add `with_feature_flags_extension` to skip when extension unavailable +3. Test both original and enhanced API methods when possible +4. Follow the naming convention: `*_spec.rb` for test files +5. Update this README if adding new test categories \ No newline at end of file diff --git a/spec/datadog/core/feature_flags/feature_flags_spec.rb b/spec/datadog/core/feature_flags/feature_flags_spec.rb new file mode 100644 index 00000000000..ef980d360ec --- /dev/null +++ b/spec/datadog/core/feature_flags/feature_flags_spec.rb @@ -0,0 +1,239 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe 'Datadog::Core::FeatureFlags' do + before do + # Skip tests if libdatadog_api extension is not available + skip 'libdatadog_api extension not available' unless defined?(Datadog::Core::FeatureFlags) + end + + let(:fixtures_path) { File.join(__dir__, 'fixtures') } + let(:main_config_path) { File.join(fixtures_path, 'flags-v1.json') } + let(:main_config_json) { File.read(main_config_path) } + + describe 'Configuration' do + context 'with valid JSON configuration' do + subject(:config) { Datadog::Core::FeatureFlags::Configuration.new(main_config_json) } + + it 'creates a configuration successfully' do + expect(config).to be_a(Datadog::Core::FeatureFlags::Configuration) + end + end + + context 'with invalid JSON configuration' do + it 'raises an error for malformed JSON' do + expect do + Datadog::Core::FeatureFlags::Configuration.new('invalid json') + end.to raise_error(RuntimeError, /Failed to create configuration/) + end + + it 'raises an error for empty string' do + expect do + Datadog::Core::FeatureFlags::Configuration.new('') + end.to raise_error(RuntimeError, /Failed to create configuration/) + end + end + end + + describe 'Flag evaluation with test cases' do + let(:config) { Datadog::Core::FeatureFlags::Configuration.new(main_config_json) } + + # Load and run all JSON test case files + Dir.glob(File.join(__dir__, 'fixtures', 'test-case-*.json')).each do |test_file| + test_name = File.basename(test_file, '.json') + + describe test_name do + let(:test_cases) { JSON.parse(File.read(test_file)) } + + test_cases.each_with_index do |test_case, index| + context "test case #{index + 1}" do + let(:flag_key) { test_case['flag'] } + let(:variation_type) { test_case['variationType'] } + let(:default_value) { test_case['defaultValue'] } + let(:targeting_key) { test_case['targetingKey'] } + let(:attributes) { test_case['attributes'] || {} } + let(:expected_result) { test_case['result'] } + + let(:context) do + ctx = { 'targeting_key' => targeting_key } + ctx.merge!(attributes) + ctx + end + + it "evaluates correctly for #{targeting_key || 'anonymous user'}" do + # Get the assignment + result = config.get_assignment(flag_key, context) + + # Validate based on expected result + if expected_result + # Test has expected result + expect(result.value).to eq(expected_result['value']) + + if expected_result['variant'] + expect(result.variant).to eq(expected_result['variant']) + end + + if expected_result['flagMetadata'] + metadata = expected_result['flagMetadata'] + expect(result.allocation_key).to eq(metadata['allocationKey']) if metadata['allocationKey'] + expect(result.log?).to eq(metadata['doLog']) if metadata.key?('doLog') + end + + # Should not be an error case + expect(result.error?).to be_falsey + expect(result.error_code).to be_nil + else + # Test expects default value behavior (usually nil/default value) + # This typically happens when targeting doesn't match + expect(result.value).to eq(default_value).or be_nil + end + end + end + end + end + end + end + + describe 'Flag types' do + let(:config) { Datadog::Core::FeatureFlags::Configuration.new(main_config_json) } + let(:context) { { 'targeting_key' => 'test-user' } } + + describe 'disabled flags' do + it 'returns appropriate result for disabled flag' do + result = config.get_assignment('disabled_flag', context) + + # Disabled flags typically return nil or default value + expect(result.value).to be_nil + expect(result.reason).to eq('DISABLED').or eq('DEFAULT') + end + end + + describe 'empty flags' do + it 'handles empty flag configuration' do + result = config.get_assignment('empty_flag', context) + + # Empty flags should return some result (possibly default) + expect(result).to be_a(Datadog::Core::FeatureFlags::ResolutionDetails) + expect(result.error?).to be_falsey + end + end + + describe 'non-existent flags' do + it 'handles requests for non-existent flags' do + result = config.get_assignment('non_existent_flag', context) + + # Should return an error or nil + expect(result.error?).to be_truthy.or expect(result.value).to be_nil + end + end + end + + describe 'ResolutionDetails' do + let(:config) { Datadog::Core::FeatureFlags::Configuration.new(main_config_json) } + let(:context) { { 'targeting_key' => 'test-user' } } + let(:result) { config.get_assignment('empty_flag', context) } + + it 'has all required methods' do + expect(result).to respond_to(:value) + expect(result).to respond_to(:reason) + expect(result).to respond_to(:error_code) + expect(result).to respond_to(:error_message) + expect(result).to respond_to(:error?) + expect(result).to respond_to(:variant) + expect(result).to respond_to(:allocation_key) + expect(result).to respond_to(:log?) + end + + it 'returns appropriate types' do + expect(result.reason).to be_a(String).or be_nil + expect(result.error_code).to be_a(String).or be_nil + expect(result.error_message).to be_a(String).or be_nil + expect(result.error?).to be_in([true, false]) + expect(result.variant).to be_a(String).or be_nil + expect(result.allocation_key).to be_a(String).or be_nil + expect(result.log?).to be_in([true, false]) + end + + describe 'error? predicate' do + context 'when there is an error' do + let(:result) { config.get_assignment('non_existent_flag', context) } + + it 'returns true for error?' do + if result.error_code + expect(result.error?).to be_truthy + end + end + end + + context 'when there is no error' do + it 'returns false for error?' do + unless result.error_code + expect(result.error?).to be_falsey + end + end + end + end + + describe 'OpenFeature compliance' do + it 'returns uppercase string constants for reasons' do + unless result.reason.nil? + expect(result.reason).to match(/^[A-Z_]+$/) + end + end + + it 'returns uppercase string constants for error codes' do + unless result.error_code.nil? + expect(result.error_code).to match(/^[A-Z_]+$/) + end + end + end + end + + describe 'Context handling' do + let(:config) { Datadog::Core::FeatureFlags::Configuration.new(main_config_json) } + + it 'handles context with targeting_key' do + context = { 'targeting_key' => 'user-123' } + result = config.get_assignment('empty_flag', context) + expect(result).to be_a(Datadog::Core::FeatureFlags::ResolutionDetails) + end + + it 'handles context with additional attributes' do + context = { + 'targeting_key' => 'user-123', + 'country' => 'US', + 'age' => 25, + 'premium' => true + } + result = config.get_assignment('empty_flag', context) + expect(result).to be_a(Datadog::Core::FeatureFlags::ResolutionDetails) + end + + it 'handles empty context' do + result = config.get_assignment('empty_flag', {}) + expect(result).to be_a(Datadog::Core::FeatureFlags::ResolutionDetails) + end + end + + describe 'Memory safety' do + let(:config) { Datadog::Core::FeatureFlags::Configuration.new(main_config_json) } + + it 'handles many evaluations without memory leaks' do + # This test ensures our GC safety fixes work correctly + 100.times do |i| + context = { 'targeting_key' => "user-#{i}" } + result = config.get_assignment('empty_flag', context) + expect(result).to be_a(Datadog::Core::FeatureFlags::ResolutionDetails) + end + end + + it 'handles large contexts safely' do + large_context = { 'targeting_key' => 'user-123' } + 50.times { |i| large_context["attr_#{i}"] = "value_#{i}" } + + result = config.get_assignment('empty_flag', large_context) + expect(result).to be_a(Datadog::Core::FeatureFlags::ResolutionDetails) + end + end +end \ No newline at end of file diff --git a/spec/datadog/core/feature_flags/fixtures/flags-v1.json b/spec/datadog/core/feature_flags/fixtures/flags-v1.json new file mode 100644 index 00000000000..670715638ec --- /dev/null +++ b/spec/datadog/core/feature_flags/fixtures/flags-v1.json @@ -0,0 +1,3079 @@ +{ + "id": "1", + "createdAt": "2024-04-17T19:40:53.716Z", + "format": "SERVER", + "environment": { + "name": "Test" + }, + "flags": { + "empty_flag": { + "key": "empty_flag", + "enabled": true, + "variationType": "STRING", + "variations": {}, + "allocations": [] + }, + "disabled_flag": { + "key": "disabled_flag", + "enabled": false, + "variationType": "INTEGER", + "variations": {}, + "allocations": [] + }, + "no_allocations_flag": { + "key": "no_allocations_flag", + "enabled": true, + "variationType": "JSON", + "variations": { + "control": { + "key": "control", + "value": { + "variant": "control" + } + }, + "treatment": { + "key": "treatment", + "value": { + "variant": "treatment" + } + } + }, + "allocations": [] + }, + "numeric_flag": { + "key": "numeric_flag", + "enabled": true, + "variationType": "NUMERIC", + "variations": { + "e": { + "key": "e", + "value": 2.7182818 + }, + "pi": { + "key": "pi", + "value": 3.1415926 + } + }, + "allocations": [ + { + "key": "rollout", + "splits": [ + { + "variationKey": "pi", + "shards": [] + } + ], + "doLog": true + } + ] + }, + "regex-flag": { + "key": "regex-flag", + "enabled": true, + "variationType": "STRING", + "variations": { + "partial-example": { + "key": "partial-example", + "value": "partial-example" + }, + "test": { + "key": "test", + "value": "test" + } + }, + "allocations": [ + { + "key": "partial-example", + "rules": [ + { + "conditions": [ + { + "attribute": "email", + "operator": "MATCHES", + "value": "@example\\.com" + } + ] + } + ], + "splits": [ + { + "variationKey": "partial-example", + "shards": [] + } + ], + "doLog": true + }, + { + "key": "test", + "rules": [ + { + "conditions": [ + { + "attribute": "email", + "operator": "MATCHES", + "value": ".*@test\\.com" + } + ] + } + ], + "splits": [ + { + "variationKey": "test", + "shards": [] + } + ], + "doLog": true + } + ] + }, + "numeric-one-of": { + "key": "numeric-one-of", + "enabled": true, + "variationType": "INTEGER", + "variations": { + "1": { + "key": "1", + "value": 1 + }, + "2": { + "key": "2", + "value": 2 + }, + "3": { + "key": "3", + "value": 3 + } + }, + "allocations": [ + { + "key": "1-for-1", + "rules": [ + { + "conditions": [ + { + "attribute": "number", + "operator": "ONE_OF", + "value": [ + "1" + ] + } + ] + } + ], + "splits": [ + { + "variationKey": "1", + "shards": [] + } + ], + "doLog": true + }, + { + "key": "2-for-123456789", + "rules": [ + { + "conditions": [ + { + "attribute": "number", + "operator": "ONE_OF", + "value": [ + "123456789" + ] + } + ] + } + ], + "splits": [ + { + "variationKey": "2", + "shards": [] + } + ], + "doLog": true + }, + { + "key": "3-for-not-2", + "rules": [ + { + "conditions": [ + { + "attribute": "number", + "operator": "NOT_ONE_OF", + "value": [ + "2" + ] + } + ] + } + ], + "splits": [ + { + "variationKey": "3", + "shards": [] + } + ], + "doLog": true + } + ] + }, + "boolean-one-of-matches": { + "key": "boolean-one-of-matches", + "enabled": true, + "variationType": "INTEGER", + "variations": { + "1": { + "key": "1", + "value": 1 + }, + "2": { + "key": "2", + "value": 2 + }, + "3": { + "key": "3", + "value": 3 + }, + "4": { + "key": "4", + "value": 4 + }, + "5": { + "key": "5", + "value": 5 + } + }, + "allocations": [ + { + "key": "1-for-one-of", + "rules": [ + { + "conditions": [ + { + "attribute": "one_of_flag", + "operator": "ONE_OF", + "value": [ + "true" + ] + } + ] + } + ], + "splits": [ + { + "variationKey": "1", + "shards": [] + } + ], + "doLog": true + }, + { + "key": "2-for-matches", + "rules": [ + { + "conditions": [ + { + "attribute": "matches_flag", + "operator": "MATCHES", + "value": "true" + } + ] + } + ], + "splits": [ + { + "variationKey": "2", + "shards": [] + } + ], + "doLog": true + }, + { + "key": "3-for-not-one-of", + "rules": [ + { + "conditions": [ + { + "attribute": "not_one_of_flag", + "operator": "NOT_ONE_OF", + "value": [ + "false" + ] + } + ] + } + ], + "splits": [ + { + "variationKey": "3", + "shards": [] + } + ], + "doLog": true + }, + { + "key": "4-for-not-matches", + "rules": [ + { + "conditions": [ + { + "attribute": "not_matches_flag", + "operator": "NOT_MATCHES", + "value": "false" + } + ] + } + ], + "splits": [ + { + "variationKey": "4", + "shards": [] + } + ], + "doLog": true + }, + { + "key": "5-for-matches-null", + "rules": [ + { + "conditions": [ + { + "attribute": "null_flag", + "operator": "ONE_OF", + "value": [ + "null" + ] + } + ] + } + ], + "splits": [ + { + "variationKey": "5", + "shards": [] + } + ], + "doLog": true + } + ] + }, + "empty_string_flag": { + "key": "empty_string_flag", + "enabled": true, + "comment": "Testing the empty string as a variation value", + "variationType": "STRING", + "variations": { + "empty_string": { + "key": "empty_string", + "value": "" + }, + "non_empty": { + "key": "non_empty", + "value": "non_empty" + } + }, + "allocations": [ + { + "key": "allocation-empty", + "rules": [ + { + "conditions": [ + { + "attribute": "country", + "operator": "MATCHES", + "value": "US" + } + ] + } + ], + "splits": [ + { + "variationKey": "empty_string", + "shards": [ + { + "salt": "allocation-empty-shards", + "totalShards": 10000, + "ranges": [ + { + "start": 0, + "end": 10000 + } + ] + } + ] + } + ], + "doLog": true + }, + { + "key": "allocation-test", + "rules": [], + "splits": [ + { + "variationKey": "non_empty", + "shards": [ + { + "salt": "allocation-empty-shards", + "totalShards": 10000, + "ranges": [ + { + "start": 0, + "end": 10000 + } + ] + } + ] + } + ], + "doLog": true + } + ] + }, + "kill-switch": { + "key": "kill-switch", + "enabled": true, + "variationType": "BOOLEAN", + "variations": { + "on": { + "key": "on", + "value": true + }, + "off": { + "key": "off", + "value": false + } + }, + "allocations": [ + { + "key": "on-for-NA", + "rules": [ + { + "conditions": [ + { + "attribute": "country", + "operator": "ONE_OF", + "value": [ + "US", + "Canada", + "Mexico" + ] + } + ] + } + ], + "splits": [ + { + "variationKey": "on", + "shards": [ + { + "salt": "some-salt", + "totalShards": 10000, + "ranges": [ + { + "start": 0, + "end": 10000 + } + ] + } + ] + } + ], + "doLog": true + }, + { + "key": "on-for-age-50+", + "rules": [ + { + "conditions": [ + { + "attribute": "age", + "operator": "GTE", + "value": 50 + } + ] + } + ], + "splits": [ + { + "variationKey": "on", + "shards": [ + { + "salt": "some-salt", + "totalShards": 10000, + "ranges": [ + { + "start": 0, + "end": 10000 + } + ] + } + ] + } + ], + "doLog": true + }, + { + "key": "off-for-all", + "rules": [], + "splits": [ + { + "variationKey": "off", + "shards": [] + } + ], + "doLog": true + } + ] + }, + "comparator-operator-test": { + "key": "comparator-operator-test", + "enabled": true, + "variationType": "STRING", + "variations": { + "small": { + "key": "small", + "value": "small" + }, + "medium": { + "key": "medium", + "value": "medium" + }, + "large": { + "key": "large", + "value": "large" + } + }, + "allocations": [ + { + "key": "small-size", + "rules": [ + { + "conditions": [ + { + "attribute": "size", + "operator": "LT", + "value": 10 + } + ] + } + ], + "splits": [ + { + "variationKey": "small", + "shards": [] + } + ], + "doLog": true + }, + { + "key": "medum-size", + "rules": [ + { + "conditions": [ + { + "attribute": "size", + "operator": "GTE", + "value": 10 + }, + { + "attribute": "size", + "operator": "LTE", + "value": 20 + } + ] + } + ], + "splits": [ + { + "variationKey": "medium", + "shards": [] + } + ], + "doLog": true + }, + { + "key": "large-size", + "rules": [ + { + "conditions": [ + { + "attribute": "size", + "operator": "GT", + "value": 25 + } + ] + } + ], + "splits": [ + { + "variationKey": "large", + "shards": [] + } + ], + "doLog": true + } + ] + }, + "start-and-end-date-test": { + "key": "start-and-end-date-test", + "enabled": true, + "variationType": "STRING", + "variations": { + "old": { + "key": "old", + "value": "old" + }, + "current": { + "key": "current", + "value": "current" + }, + "new": { + "key": "new", + "value": "new" + } + }, + "allocations": [ + { + "key": "old-versions", + "splits": [ + { + "variationKey": "old", + "shards": [] + } + ], + "endAt": "2002-10-31T09:00:00.594Z", + "doLog": true + }, + { + "key": "future-versions", + "splits": [ + { + "variationKey": "new", + "shards": [] + } + ], + "startAt": "2052-10-31T09:00:00.594Z", + "doLog": true + }, + { + "key": "current-versions", + "splits": [ + { + "variationKey": "current", + "shards": [] + } + ], + "startAt": "2022-10-31T09:00:00.594Z", + "endAt": "2050-10-31T09:00:00.594Z", + "doLog": true + } + ] + }, + "null-operator-test": { + "key": "null-operator-test", + "enabled": true, + "variationType": "STRING", + "variations": { + "old": { + "key": "old", + "value": "old" + }, + "new": { + "key": "new", + "value": "new" + } + }, + "allocations": [ + { + "key": "null-operator", + "rules": [ + { + "conditions": [ + { + "attribute": "size", + "operator": "IS_NULL", + "value": true + } + ] + }, + { + "conditions": [ + { + "attribute": "size", + "operator": "LT", + "value": 10 + } + ] + } + ], + "splits": [ + { + "variationKey": "old", + "shards": [] + } + ], + "doLog": true + }, + { + "key": "not-null-operator", + "rules": [ + { + "conditions": [ + { + "attribute": "size", + "operator": "IS_NULL", + "value": false + } + ] + } + ], + "splits": [ + { + "variationKey": "new", + "shards": [] + } + ], + "doLog": true + } + ] + }, + "new-user-onboarding": { + "key": "new-user-onboarding", + "enabled": true, + "variationType": "STRING", + "variations": { + "control": { + "key": "control", + "value": "control" + }, + "red": { + "key": "red", + "value": "red" + }, + "blue": { + "key": "blue", + "value": "blue" + }, + "green": { + "key": "green", + "value": "green" + }, + "yellow": { + "key": "yellow", + "value": "yellow" + }, + "purple": { + "key": "purple", + "value": "purple" + } + }, + "allocations": [ + { + "key": "id rule", + "rules": [ + { + "conditions": [ + { + "attribute": "id", + "operator": "MATCHES", + "value": "zach" + } + ] + } + ], + "splits": [ + { + "variationKey": "purple", + "shards": [] + } + ], + "doLog": false + }, + { + "key": "internal users", + "rules": [ + { + "conditions": [ + { + "attribute": "email", + "operator": "MATCHES", + "value": "@mycompany.com" + } + ] + } + ], + "splits": [ + { + "variationKey": "green", + "shards": [] + } + ], + "doLog": false + }, + { + "key": "experiment", + "rules": [ + { + "conditions": [ + { + "attribute": "country", + "operator": "NOT_ONE_OF", + "value": [ + "US", + "Canada", + "Mexico" + ] + } + ] + } + ], + "splits": [ + { + "variationKey": "control", + "shards": [ + { + "salt": "traffic-new-user-onboarding-experiment", + "totalShards": 10000, + "ranges": [ + { + "start": 0, + "end": 6000 + } + ] + }, + { + "salt": "split-new-user-onboarding-experiment", + "totalShards": 10000, + "ranges": [ + { + "start": 0, + "end": 5000 + } + ] + } + ] + }, + { + "variationKey": "red", + "shards": [ + { + "salt": "traffic-new-user-onboarding-experiment", + "totalShards": 10000, + "ranges": [ + { + "start": 0, + "end": 6000 + } + ] + }, + { + "salt": "split-new-user-onboarding-experiment", + "totalShards": 10000, + "ranges": [ + { + "start": 5000, + "end": 8000 + } + ] + } + ] + }, + { + "variationKey": "yellow", + "shards": [ + { + "salt": "traffic-new-user-onboarding-experiment", + "totalShards": 10000, + "ranges": [ + { + "start": 0, + "end": 6000 + } + ] + }, + { + "salt": "split-new-user-onboarding-experiment", + "totalShards": 10000, + "ranges": [ + { + "start": 8000, + "end": 10000 + } + ] + } + ] + } + ], + "doLog": true + }, + { + "key": "rollout", + "rules": [ + { + "conditions": [ + { + "attribute": "country", + "operator": "ONE_OF", + "value": [ + "US", + "Canada", + "Mexico" + ] + } + ] + } + ], + "splits": [ + { + "variationKey": "blue", + "shards": [ + { + "salt": "split-new-user-onboarding-rollout", + "totalShards": 10000, + "ranges": [ + { + "start": 0, + "end": 8000 + } + ] + } + ], + "extraLogging": { + "allocationvalue_type": "rollout", + "owner": "hippo" + } + } + ], + "doLog": true + } + ] + }, + "integer-flag": { + "key": "integer-flag", + "enabled": true, + "variationType": "INTEGER", + "variations": { + "one": { + "key": "one", + "value": 1 + }, + "two": { + "key": "two", + "value": 2 + }, + "three": { + "key": "three", + "value": 3 + } + }, + "allocations": [ + { + "key": "targeted allocation", + "rules": [ + { + "conditions": [ + { + "attribute": "country", + "operator": "ONE_OF", + "value": [ + "US", + "Canada", + "Mexico" + ] + } + ] + }, + { + "conditions": [ + { + "attribute": "email", + "operator": "MATCHES", + "value": ".*@example.com" + } + ] + } + ], + "splits": [ + { + "variationKey": "three", + "shards": [ + { + "salt": "full-range-salt", + "totalShards": 10000, + "ranges": [ + { + "start": 0, + "end": 10000 + } + ] + } + ] + } + ], + "doLog": true + }, + { + "key": "50/50 split", + "rules": [], + "splits": [ + { + "variationKey": "one", + "shards": [ + { + "salt": "split-numeric-flag-some-allocation", + "totalShards": 10000, + "ranges": [ + { + "start": 0, + "end": 5000 + } + ] + } + ] + }, + { + "variationKey": "two", + "shards": [ + { + "salt": "split-numeric-flag-some-allocation", + "totalShards": 10000, + "ranges": [ + { + "start": 5000, + "end": 10000 + } + ] + } + ] + } + ], + "doLog": true + } + ] + }, + "json-config-flag": { + "key": "json-config-flag", + "enabled": true, + "variationType": "JSON", + "variations": { + "one": { + "key": "one", + "value": { + "integer": 1, + "string": "one", + "float": 1.0 + } + }, + "two": { + "key": "two", + "value": { + "integer": 2, + "string": "two", + "float": 2.0 + } + }, + "empty": { + "key": "empty", + "value": {} + } + }, + "allocations": [ + { + "key": "Optionally Force Empty", + "rules": [ + { + "conditions": [ + { + "attribute": "Force Empty", + "operator": "ONE_OF", + "value": [ + "true" + ] + } + ] + } + ], + "splits": [ + { + "variationKey": "empty", + "shards": [ + { + "salt": "full-range-salt", + "totalShards": 10000, + "ranges": [ + { + "start": 0, + "end": 10000 + } + ] + } + ] + } + ], + "doLog": true + }, + { + "key": "50/50 split", + "rules": [], + "splits": [ + { + "variationKey": "one", + "shards": [ + { + "salt": "traffic-json-flag", + "totalShards": 10000, + "ranges": [ + { + "start": 0, + "end": 10000 + } + ] + }, + { + "salt": "split-json-flag", + "totalShards": 10000, + "ranges": [ + { + "start": 0, + "end": 5000 + } + ] + } + ] + }, + { + "variationKey": "two", + "shards": [ + { + "salt": "traffic-json-flag", + "totalShards": 10000, + "ranges": [ + { + "start": 0, + "end": 10000 + } + ] + }, + { + "salt": "split-json-flag", + "totalShards": 10000, + "ranges": [ + { + "start": 5000, + "end": 10000 + } + ] + } + ] + } + ], + "doLog": true + } + ] + }, + "special-characters": { + "key": "special-characters", + "enabled": true, + "variationType": "JSON", + "variations": { + "de": { + "key": "de", + "value": { + "a": "kümmert", + "b": "schön" + } + }, + "ua": { + "key": "ua", + "value": { + "a": "піклуватися", + "b": "любов" + } + }, + "zh": { + "key": "zh", + "value": { + "a": "照顾", + "b": "漂亮" + } + }, + "emoji": { + "key": "emoji", + "value": { + "a": "🤗", + "b": "🌸" + } + } + }, + "allocations": [ + { + "key": "allocation-test", + "splits": [ + { + "variationKey": "de", + "shards": [ + { + "salt": "split-json-flag", + "totalShards": 10000, + "ranges": [ + { + "start": 0, + "end": 2500 + } + ] + } + ] + }, + { + "variationKey": "ua", + "shards": [ + { + "salt": "split-json-flag", + "totalShards": 10000, + "ranges": [ + { + "start": 2500, + "end": 5000 + } + ] + } + ] + }, + { + "variationKey": "zh", + "shards": [ + { + "salt": "split-json-flag", + "totalShards": 10000, + "ranges": [ + { + "start": 5000, + "end": 7500 + } + ] + } + ] + }, + { + "variationKey": "emoji", + "shards": [ + { + "salt": "split-json-flag", + "totalShards": 10000, + "ranges": [ + { + "start": 7500, + "end": 10000 + } + ] + } + ] + } + ], + "doLog": true + }, + { + "key": "allocation-default", + "splits": [ + { + "variationKey": "de", + "shards": [] + } + ], + "doLog": false + } + ] + }, + "string_flag_with_special_characters": { + "key": "string_flag_with_special_characters", + "enabled": true, + "comment": "Testing the string with special characters and spaces", + "variationType": "STRING", + "variations": { + "string_with_spaces": { + "key": "string_with_spaces", + "value": " a b c d e f " + }, + "string_with_only_one_space": { + "key": "string_with_only_one_space", + "value": " " + }, + "string_with_only_multiple_spaces": { + "key": "string_with_only_multiple_spaces", + "value": " " + }, + "string_with_dots": { + "key": "string_with_dots", + "value": ".a.b.c.d.e.f." + }, + "string_with_only_one_dot": { + "key": "string_with_only_one_dot", + "value": "." + }, + "string_with_only_multiple_dots": { + "key": "string_with_only_multiple_dots", + "value": "......." + }, + "string_with_comas": { + "key": "string_with_comas", + "value": ",a,b,c,d,e,f," + }, + "string_with_only_one_coma": { + "key": "string_with_only_one_coma", + "value": "," + }, + "string_with_only_multiple_comas": { + "key": "string_with_only_multiple_comas", + "value": ",,,,,,," + }, + "string_with_colons": { + "key": "string_with_colons", + "value": ":a:b:c:d:e:f:" + }, + "string_with_only_one_colon": { + "key": "string_with_only_one_colon", + "value": ":" + }, + "string_with_only_multiple_colons": { + "key": "string_with_only_multiple_colons", + "value": ":::::::" + }, + "string_with_semicolons": { + "key": "string_with_semicolons", + "value": ";a;b;c;d;e;f;" + }, + "string_with_only_one_semicolon": { + "key": "string_with_only_one_semicolon", + "value": ";" + }, + "string_with_only_multiple_semicolons": { + "key": "string_with_only_multiple_semicolons", + "value": ";;;;;;;" + }, + "string_with_slashes": { + "key": "string_with_slashes", + "value": "/a/b/c/d/e/f/" + }, + "string_with_only_one_slash": { + "key": "string_with_only_one_slash", + "value": "/" + }, + "string_with_only_multiple_slashes": { + "key": "string_with_only_multiple_slashes", + "value": "///////" + }, + "string_with_dashes": { + "key": "string_with_dashes", + "value": "-a-b-c-d-e-f-" + }, + "string_with_only_one_dash": { + "key": "string_with_only_one_dash", + "value": "-" + }, + "string_with_only_multiple_dashes": { + "key": "string_with_only_multiple_dashes", + "value": "-------" + }, + "string_with_underscores": { + "key": "string_with_underscores", + "value": "_a_b_c_d_e_f_" + }, + "string_with_only_one_underscore": { + "key": "string_with_only_one_underscore", + "value": "_" + }, + "string_with_only_multiple_underscores": { + "key": "string_with_only_multiple_underscores", + "value": "_______" + }, + "string_with_plus_signs": { + "key": "string_with_plus_signs", + "value": "+a+b+c+d+e+f+" + }, + "string_with_only_one_plus_sign": { + "key": "string_with_only_one_plus_sign", + "value": "+" + }, + "string_with_only_multiple_plus_signs": { + "key": "string_with_only_multiple_plus_signs", + "value": "+++++++" + }, + "string_with_equal_signs": { + "key": "string_with_equal_signs", + "value": "=a=b=c=d=e=f=" + }, + "string_with_only_one_equal_sign": { + "key": "string_with_only_one_equal_sign", + "value": "=" + }, + "string_with_only_multiple_equal_signs": { + "key": "string_with_only_multiple_equal_signs", + "value": "=======" + }, + "string_with_dollar_signs": { + "key": "string_with_dollar_signs", + "value": "$a$b$c$d$e$f$" + }, + "string_with_only_one_dollar_sign": { + "key": "string_with_only_one_dollar_sign", + "value": "$" + }, + "string_with_only_multiple_dollar_signs": { + "key": "string_with_only_multiple_dollar_signs", + "value": "$$$$$$$" + }, + "string_with_at_signs": { + "key": "string_with_at_signs", + "value": "@a@b@c@d@e@f@" + }, + "string_with_only_one_at_sign": { + "key": "string_with_only_one_at_sign", + "value": "@" + }, + "string_with_only_multiple_at_signs": { + "key": "string_with_only_multiple_at_signs", + "value": "@@@@@@@" + }, + "string_with_amp_signs": { + "key": "string_with_amp_signs", + "value": "&a&b&c&d&e&f&" + }, + "string_with_only_one_amp_sign": { + "key": "string_with_only_one_amp_sign", + "value": "&" + }, + "string_with_only_multiple_amp_signs": { + "key": "string_with_only_multiple_amp_signs", + "value": "&&&&&&&" + }, + "string_with_hash_signs": { + "key": "string_with_hash_signs", + "value": "#a#b#c#d#e#f#" + }, + "string_with_only_one_hash_sign": { + "key": "string_with_only_one_hash_sign", + "value": "#" + }, + "string_with_only_multiple_hash_signs": { + "key": "string_with_only_multiple_hash_signs", + "value": "#######" + }, + "string_with_percentage_signs": { + "key": "string_with_percentage_signs", + "value": "%a%b%c%d%e%f%" + }, + "string_with_only_one_percentage_sign": { + "key": "string_with_only_one_percentage_sign", + "value": "%" + }, + "string_with_only_multiple_percentage_signs": { + "key": "string_with_only_multiple_percentage_signs", + "value": "%%%%%%%" + }, + "string_with_tilde_signs": { + "key": "string_with_tilde_signs", + "value": "~a~b~c~d~e~f~" + }, + "string_with_only_one_tilde_sign": { + "key": "string_with_only_one_tilde_sign", + "value": "~" + }, + "string_with_only_multiple_tilde_signs": { + "key": "string_with_only_multiple_tilde_signs", + "value": "~~~~~~~" + }, + "string_with_asterix_signs": { + "key": "string_with_asterix_signs", + "value": "*a*b*c*d*e*f*" + }, + "string_with_only_one_asterix_sign": { + "key": "string_with_only_one_asterix_sign", + "value": "*" + }, + "string_with_only_multiple_asterix_signs": { + "key": "string_with_only_multiple_asterix_signs", + "value": "*******" + }, + "string_with_single_quotes": { + "key": "string_with_single_quotes", + "value": "'a'b'c'd'e'f'" + }, + "string_with_only_one_single_quote": { + "key": "string_with_only_one_single_quote", + "value": "'" + }, + "string_with_only_multiple_single_quotes": { + "key": "string_with_only_multiple_single_quotes", + "value": "'''''''" + }, + "string_with_question_marks": { + "key": "string_with_question_marks", + "value": "?a?b?c?d?e?f?" + }, + "string_with_only_one_question_mark": { + "key": "string_with_only_one_question_mark", + "value": "?" + }, + "string_with_only_multiple_question_marks": { + "key": "string_with_only_multiple_question_marks", + "value": "???????" + }, + "string_with_exclamation_marks": { + "key": "string_with_exclamation_marks", + "value": "!a!b!c!d!e!f!" + }, + "string_with_only_one_exclamation_mark": { + "key": "string_with_only_one_exclamation_mark", + "value": "!" + }, + "string_with_only_multiple_exclamation_marks": { + "key": "string_with_only_multiple_exclamation_marks", + "value": "!!!!!!!" + }, + "string_with_opening_parentheses": { + "key": "string_with_opening_parentheses", + "value": "(a(b(c(d(e(f(" + }, + "string_with_only_one_opening_parenthese": { + "key": "string_with_only_one_opening_parenthese", + "value": "(" + }, + "string_with_only_multiple_opening_parentheses": { + "key": "string_with_only_multiple_opening_parentheses", + "value": "(((((((" + }, + "string_with_closing_parentheses": { + "key": "string_with_closing_parentheses", + "value": ")a)b)c)d)e)f)" + }, + "string_with_only_one_closing_parenthese": { + "key": "string_with_only_one_closing_parenthese", + "value": ")" + }, + "string_with_only_multiple_closing_parentheses": { + "key": "string_with_only_multiple_closing_parentheses", + "value": ")))))))" + } + }, + "allocations": [ + { + "key": "allocation-test-string_with_spaces", + "rules": [ + { + "conditions": [ + { + "attribute": "string_with_spaces", + "operator": "ONE_OF", + "value": [ + "true" + ] + } + ] + } + ], + "splits": [ + { + "variationKey": "string_with_spaces", + "shards": [] + } + ], + "doLog": true + }, + { + "key": "allocation-test-string_with_only_one_space", + "rules": [ + { + "conditions": [ + { + "attribute": "string_with_only_one_space", + "operator": "ONE_OF", + "value": [ + "true" + ] + } + ] + } + ], + "splits": [ + { + "variationKey": "string_with_only_one_space", + "shards": [] + } + ], + "doLog": true + }, + { + "key": "allocation-test-string_with_only_multiple_spaces", + "rules": [ + { + "conditions": [ + { + "attribute": "string_with_only_multiple_spaces", + "operator": "ONE_OF", + "value": [ + "true" + ] + } + ] + } + ], + "splits": [ + { + "variationKey": "string_with_only_multiple_spaces", + "shards": [] + } + ], + "doLog": true + }, + { + "key": "allocation-test-string_with_dots", + "rules": [ + { + "conditions": [ + { + "attribute": "string_with_dots", + "operator": "ONE_OF", + "value": [ + "true" + ] + } + ] + } + ], + "splits": [ + { + "variationKey": "string_with_dots", + "shards": [] + } + ], + "doLog": true + }, + { + "key": "allocation-test-string_with_only_one_dot", + "rules": [ + { + "conditions": [ + { + "attribute": "string_with_only_one_dot", + "operator": "ONE_OF", + "value": [ + "true" + ] + } + ] + } + ], + "splits": [ + { + "variationKey": "string_with_only_one_dot", + "shards": [] + } + ], + "doLog": true + }, + { + "key": "allocation-test-string_with_only_multiple_dots", + "rules": [ + { + "conditions": [ + { + "attribute": "string_with_only_multiple_dots", + "operator": "ONE_OF", + "value": [ + "true" + ] + } + ] + } + ], + "splits": [ + { + "variationKey": "string_with_only_multiple_dots", + "shards": [] + } + ], + "doLog": true + }, + { + "key": "allocation-test-string_with_comas", + "rules": [ + { + "conditions": [ + { + "attribute": "string_with_comas", + "operator": "ONE_OF", + "value": [ + "true" + ] + } + ] + } + ], + "splits": [ + { + "variationKey": "string_with_comas", + "shards": [] + } + ], + "doLog": true + }, + { + "key": "allocation-test-string_with_only_one_coma", + "rules": [ + { + "conditions": [ + { + "attribute": "string_with_only_one_coma", + "operator": "ONE_OF", + "value": [ + "true" + ] + } + ] + } + ], + "splits": [ + { + "variationKey": "string_with_only_one_coma", + "shards": [] + } + ], + "doLog": true + }, + { + "key": "allocation-test-string_with_only_multiple_comas", + "rules": [ + { + "conditions": [ + { + "attribute": "string_with_only_multiple_comas", + "operator": "ONE_OF", + "value": [ + "true" + ] + } + ] + } + ], + "splits": [ + { + "variationKey": "string_with_only_multiple_comas", + "shards": [] + } + ], + "doLog": true + }, + { + "key": "allocation-test-string_with_colons", + "rules": [ + { + "conditions": [ + { + "attribute": "string_with_colons", + "operator": "ONE_OF", + "value": [ + "true" + ] + } + ] + } + ], + "splits": [ + { + "variationKey": "string_with_colons", + "shards": [] + } + ], + "doLog": true + }, + { + "key": "allocation-test-string_with_only_one_colon", + "rules": [ + { + "conditions": [ + { + "attribute": "string_with_only_one_colon", + "operator": "ONE_OF", + "value": [ + "true" + ] + } + ] + } + ], + "splits": [ + { + "variationKey": "string_with_only_one_colon", + "shards": [] + } + ], + "doLog": true + }, + { + "key": "allocation-test-string_with_only_multiple_colons", + "rules": [ + { + "conditions": [ + { + "attribute": "string_with_only_multiple_colons", + "operator": "ONE_OF", + "value": [ + "true" + ] + } + ] + } + ], + "splits": [ + { + "variationKey": "string_with_only_multiple_colons", + "shards": [] + } + ], + "doLog": true + }, + { + "key": "allocation-test-string_with_semicolons", + "rules": [ + { + "conditions": [ + { + "attribute": "string_with_semicolons", + "operator": "ONE_OF", + "value": [ + "true" + ] + } + ] + } + ], + "splits": [ + { + "variationKey": "string_with_semicolons", + "shards": [] + } + ], + "doLog": true + }, + { + "key": "allocation-test-string_with_only_one_semicolon", + "rules": [ + { + "conditions": [ + { + "attribute": "string_with_only_one_semicolon", + "operator": "ONE_OF", + "value": [ + "true" + ] + } + ] + } + ], + "splits": [ + { + "variationKey": "string_with_only_one_semicolon", + "shards": [] + } + ], + "doLog": true + }, + { + "key": "allocation-test-string_with_only_multiple_semicolons", + "rules": [ + { + "conditions": [ + { + "attribute": "string_with_only_multiple_semicolons", + "operator": "ONE_OF", + "value": [ + "true" + ] + } + ] + } + ], + "splits": [ + { + "variationKey": "string_with_only_multiple_semicolons", + "shards": [] + } + ], + "doLog": true + }, + { + "key": "allocation-test-string_with_slashes", + "rules": [ + { + "conditions": [ + { + "attribute": "string_with_slashes", + "operator": "ONE_OF", + "value": [ + "true" + ] + } + ] + } + ], + "splits": [ + { + "variationKey": "string_with_slashes", + "shards": [] + } + ], + "doLog": true + }, + { + "key": "allocation-test-string_with_only_one_slash", + "rules": [ + { + "conditions": [ + { + "attribute": "string_with_only_one_slash", + "operator": "ONE_OF", + "value": [ + "true" + ] + } + ] + } + ], + "splits": [ + { + "variationKey": "string_with_only_one_slash", + "shards": [] + } + ], + "doLog": true + }, + { + "key": "allocation-test-string_with_only_multiple_slashes", + "rules": [ + { + "conditions": [ + { + "attribute": "string_with_only_multiple_slashes", + "operator": "ONE_OF", + "value": [ + "true" + ] + } + ] + } + ], + "splits": [ + { + "variationKey": "string_with_only_multiple_slashes", + "shards": [] + } + ], + "doLog": true + }, + { + "key": "allocation-test-string_with_dashes", + "rules": [ + { + "conditions": [ + { + "attribute": "string_with_dashes", + "operator": "ONE_OF", + "value": [ + "true" + ] + } + ] + } + ], + "splits": [ + { + "variationKey": "string_with_dashes", + "shards": [] + } + ], + "doLog": true + }, + { + "key": "allocation-test-string_with_only_one_dash", + "rules": [ + { + "conditions": [ + { + "attribute": "string_with_only_one_dash", + "operator": "ONE_OF", + "value": [ + "true" + ] + } + ] + } + ], + "splits": [ + { + "variationKey": "string_with_only_one_dash", + "shards": [] + } + ], + "doLog": true + }, + { + "key": "allocation-test-string_with_only_multiple_dashes", + "rules": [ + { + "conditions": [ + { + "attribute": "string_with_only_multiple_dashes", + "operator": "ONE_OF", + "value": [ + "true" + ] + } + ] + } + ], + "splits": [ + { + "variationKey": "string_with_only_multiple_dashes", + "shards": [] + } + ], + "doLog": true + }, + { + "key": "allocation-test-string_with_underscores", + "rules": [ + { + "conditions": [ + { + "attribute": "string_with_underscores", + "operator": "ONE_OF", + "value": [ + "true" + ] + } + ] + } + ], + "splits": [ + { + "variationKey": "string_with_underscores", + "shards": [] + } + ], + "doLog": true + }, + { + "key": "allocation-test-string_with_only_one_underscore", + "rules": [ + { + "conditions": [ + { + "attribute": "string_with_only_one_underscore", + "operator": "ONE_OF", + "value": [ + "true" + ] + } + ] + } + ], + "splits": [ + { + "variationKey": "string_with_only_one_underscore", + "shards": [] + } + ], + "doLog": true + }, + { + "key": "allocation-test-string_with_only_multiple_underscores", + "rules": [ + { + "conditions": [ + { + "attribute": "string_with_only_multiple_underscores", + "operator": "ONE_OF", + "value": [ + "true" + ] + } + ] + } + ], + "splits": [ + { + "variationKey": "string_with_only_multiple_underscores", + "shards": [] + } + ], + "doLog": true + }, + { + "key": "allocation-test-string_with_plus_signs", + "rules": [ + { + "conditions": [ + { + "attribute": "string_with_plus_signs", + "operator": "ONE_OF", + "value": [ + "true" + ] + } + ] + } + ], + "splits": [ + { + "variationKey": "string_with_plus_signs", + "shards": [] + } + ], + "doLog": true + }, + { + "key": "allocation-test-string_with_only_one_plus_sign", + "rules": [ + { + "conditions": [ + { + "attribute": "string_with_only_one_plus_sign", + "operator": "ONE_OF", + "value": [ + "true" + ] + } + ] + } + ], + "splits": [ + { + "variationKey": "string_with_only_one_plus_sign", + "shards": [] + } + ], + "doLog": true + }, + { + "key": "allocation-test-string_with_only_multiple_plus_signs", + "rules": [ + { + "conditions": [ + { + "attribute": "string_with_only_multiple_plus_signs", + "operator": "ONE_OF", + "value": [ + "true" + ] + } + ] + } + ], + "splits": [ + { + "variationKey": "string_with_only_multiple_plus_signs", + "shards": [] + } + ], + "doLog": true + }, + { + "key": "allocation-test-string_with_equal_signs", + "rules": [ + { + "conditions": [ + { + "attribute": "string_with_equal_signs", + "operator": "ONE_OF", + "value": [ + "true" + ] + } + ] + } + ], + "splits": [ + { + "variationKey": "string_with_equal_signs", + "shards": [] + } + ], + "doLog": true + }, + { + "key": "allocation-test-string_with_only_one_equal_sign", + "rules": [ + { + "conditions": [ + { + "attribute": "string_with_only_one_equal_sign", + "operator": "ONE_OF", + "value": [ + "true" + ] + } + ] + } + ], + "splits": [ + { + "variationKey": "string_with_only_one_equal_sign", + "shards": [] + } + ], + "doLog": true + }, + { + "key": "allocation-test-string_with_only_multiple_equal_signs", + "rules": [ + { + "conditions": [ + { + "attribute": "string_with_only_multiple_equal_signs", + "operator": "ONE_OF", + "value": [ + "true" + ] + } + ] + } + ], + "splits": [ + { + "variationKey": "string_with_only_multiple_equal_signs", + "shards": [] + } + ], + "doLog": true + }, + { + "key": "allocation-test-string_with_dollar_signs", + "rules": [ + { + "conditions": [ + { + "attribute": "string_with_dollar_signs", + "operator": "ONE_OF", + "value": [ + "true" + ] + } + ] + } + ], + "splits": [ + { + "variationKey": "string_with_dollar_signs", + "shards": [] + } + ], + "doLog": true + }, + { + "key": "allocation-test-string_with_only_one_dollar_sign", + "rules": [ + { + "conditions": [ + { + "attribute": "string_with_only_one_dollar_sign", + "operator": "ONE_OF", + "value": [ + "true" + ] + } + ] + } + ], + "splits": [ + { + "variationKey": "string_with_only_one_dollar_sign", + "shards": [] + } + ], + "doLog": true + }, + { + "key": "allocation-test-string_with_only_multiple_dollar_signs", + "rules": [ + { + "conditions": [ + { + "attribute": "string_with_only_multiple_dollar_signs", + "operator": "ONE_OF", + "value": [ + "true" + ] + } + ] + } + ], + "splits": [ + { + "variationKey": "string_with_only_multiple_dollar_signs", + "shards": [] + } + ], + "doLog": true + }, + { + "key": "allocation-test-string_with_at_signs", + "rules": [ + { + "conditions": [ + { + "attribute": "string_with_at_signs", + "operator": "ONE_OF", + "value": [ + "true" + ] + } + ] + } + ], + "splits": [ + { + "variationKey": "string_with_at_signs", + "shards": [] + } + ], + "doLog": true + }, + { + "key": "allocation-test-string_with_only_one_at_sign", + "rules": [ + { + "conditions": [ + { + "attribute": "string_with_only_one_at_sign", + "operator": "ONE_OF", + "value": [ + "true" + ] + } + ] + } + ], + "splits": [ + { + "variationKey": "string_with_only_one_at_sign", + "shards": [] + } + ], + "doLog": true + }, + { + "key": "allocation-test-string_with_only_multiple_at_signs", + "rules": [ + { + "conditions": [ + { + "attribute": "string_with_only_multiple_at_signs", + "operator": "ONE_OF", + "value": [ + "true" + ] + } + ] + } + ], + "splits": [ + { + "variationKey": "string_with_only_multiple_at_signs", + "shards": [] + } + ], + "doLog": true + }, + { + "key": "allocation-test-string_with_amp_signs", + "rules": [ + { + "conditions": [ + { + "attribute": "string_with_amp_signs", + "operator": "ONE_OF", + "value": [ + "true" + ] + } + ] + } + ], + "splits": [ + { + "variationKey": "string_with_amp_signs", + "shards": [] + } + ], + "doLog": true + }, + { + "key": "allocation-test-string_with_only_one_amp_sign", + "rules": [ + { + "conditions": [ + { + "attribute": "string_with_only_one_amp_sign", + "operator": "ONE_OF", + "value": [ + "true" + ] + } + ] + } + ], + "splits": [ + { + "variationKey": "string_with_only_one_amp_sign", + "shards": [] + } + ], + "doLog": true + }, + { + "key": "allocation-test-string_with_only_multiple_amp_signs", + "rules": [ + { + "conditions": [ + { + "attribute": "string_with_only_multiple_amp_signs", + "operator": "ONE_OF", + "value": [ + "true" + ] + } + ] + } + ], + "splits": [ + { + "variationKey": "string_with_only_multiple_amp_signs", + "shards": [] + } + ], + "doLog": true + }, + { + "key": "allocation-test-string_with_hash_signs", + "rules": [ + { + "conditions": [ + { + "attribute": "string_with_hash_signs", + "operator": "ONE_OF", + "value": [ + "true" + ] + } + ] + } + ], + "splits": [ + { + "variationKey": "string_with_hash_signs", + "shards": [] + } + ], + "doLog": true + }, + { + "key": "allocation-test-string_with_only_one_hash_sign", + "rules": [ + { + "conditions": [ + { + "attribute": "string_with_only_one_hash_sign", + "operator": "ONE_OF", + "value": [ + "true" + ] + } + ] + } + ], + "splits": [ + { + "variationKey": "string_with_only_one_hash_sign", + "shards": [] + } + ], + "doLog": true + }, + { + "key": "allocation-test-string_with_only_multiple_hash_signs", + "rules": [ + { + "conditions": [ + { + "attribute": "string_with_only_multiple_hash_signs", + "operator": "ONE_OF", + "value": [ + "true" + ] + } + ] + } + ], + "splits": [ + { + "variationKey": "string_with_only_multiple_hash_signs", + "shards": [] + } + ], + "doLog": true + }, + { + "key": "allocation-test-string_with_percentage_signs", + "rules": [ + { + "conditions": [ + { + "attribute": "string_with_percentage_signs", + "operator": "ONE_OF", + "value": [ + "true" + ] + } + ] + } + ], + "splits": [ + { + "variationKey": "string_with_percentage_signs", + "shards": [] + } + ], + "doLog": true + }, + { + "key": "allocation-test-string_with_only_one_percentage_sign", + "rules": [ + { + "conditions": [ + { + "attribute": "string_with_only_one_percentage_sign", + "operator": "ONE_OF", + "value": [ + "true" + ] + } + ] + } + ], + "splits": [ + { + "variationKey": "string_with_only_one_percentage_sign", + "shards": [] + } + ], + "doLog": true + }, + { + "key": "allocation-test-string_with_only_multiple_percentage_signs", + "rules": [ + { + "conditions": [ + { + "attribute": "string_with_only_multiple_percentage_signs", + "operator": "ONE_OF", + "value": [ + "true" + ] + } + ] + } + ], + "splits": [ + { + "variationKey": "string_with_only_multiple_percentage_signs", + "shards": [] + } + ], + "doLog": true + }, + { + "key": "allocation-test-string_with_tilde_signs", + "rules": [ + { + "conditions": [ + { + "attribute": "string_with_tilde_signs", + "operator": "ONE_OF", + "value": [ + "true" + ] + } + ] + } + ], + "splits": [ + { + "variationKey": "string_with_tilde_signs", + "shards": [] + } + ], + "doLog": true + }, + { + "key": "allocation-test-string_with_only_one_tilde_sign", + "rules": [ + { + "conditions": [ + { + "attribute": "string_with_only_one_tilde_sign", + "operator": "ONE_OF", + "value": [ + "true" + ] + } + ] + } + ], + "splits": [ + { + "variationKey": "string_with_only_one_tilde_sign", + "shards": [] + } + ], + "doLog": true + }, + { + "key": "allocation-test-string_with_only_multiple_tilde_signs", + "rules": [ + { + "conditions": [ + { + "attribute": "string_with_only_multiple_tilde_signs", + "operator": "ONE_OF", + "value": [ + "true" + ] + } + ] + } + ], + "splits": [ + { + "variationKey": "string_with_only_multiple_tilde_signs", + "shards": [] + } + ], + "doLog": true + }, + { + "key": "allocation-test-string_with_asterix_signs", + "rules": [ + { + "conditions": [ + { + "attribute": "string_with_asterix_signs", + "operator": "ONE_OF", + "value": [ + "true" + ] + } + ] + } + ], + "splits": [ + { + "variationKey": "string_with_asterix_signs", + "shards": [] + } + ], + "doLog": true + }, + { + "key": "allocation-test-string_with_only_one_asterix_sign", + "rules": [ + { + "conditions": [ + { + "attribute": "string_with_only_one_asterix_sign", + "operator": "ONE_OF", + "value": [ + "true" + ] + } + ] + } + ], + "splits": [ + { + "variationKey": "string_with_only_one_asterix_sign", + "shards": [] + } + ], + "doLog": true + }, + { + "key": "allocation-test-string_with_only_multiple_asterix_signs", + "rules": [ + { + "conditions": [ + { + "attribute": "string_with_only_multiple_asterix_signs", + "operator": "ONE_OF", + "value": [ + "true" + ] + } + ] + } + ], + "splits": [ + { + "variationKey": "string_with_only_multiple_asterix_signs", + "shards": [] + } + ], + "doLog": true + }, + { + "key": "allocation-test-string_with_single_quotes", + "rules": [ + { + "conditions": [ + { + "attribute": "string_with_single_quotes", + "operator": "ONE_OF", + "value": [ + "true" + ] + } + ] + } + ], + "splits": [ + { + "variationKey": "string_with_single_quotes", + "shards": [] + } + ], + "doLog": true + }, + { + "key": "allocation-test-string_with_only_one_single_quote", + "rules": [ + { + "conditions": [ + { + "attribute": "string_with_only_one_single_quote", + "operator": "ONE_OF", + "value": [ + "true" + ] + } + ] + } + ], + "splits": [ + { + "variationKey": "string_with_only_one_single_quote", + "shards": [] + } + ], + "doLog": true + }, + { + "key": "allocation-test-string_with_only_multiple_single_quotes", + "rules": [ + { + "conditions": [ + { + "attribute": "string_with_only_multiple_single_quotes", + "operator": "ONE_OF", + "value": [ + "true" + ] + } + ] + } + ], + "splits": [ + { + "variationKey": "string_with_only_multiple_single_quotes", + "shards": [] + } + ], + "doLog": true + }, + { + "key": "allocation-test-string_with_question_marks", + "rules": [ + { + "conditions": [ + { + "attribute": "string_with_question_marks", + "operator": "ONE_OF", + "value": [ + "true" + ] + } + ] + } + ], + "splits": [ + { + "variationKey": "string_with_question_marks", + "shards": [] + } + ], + "doLog": true + }, + { + "key": "allocation-test-string_with_only_one_question_mark", + "rules": [ + { + "conditions": [ + { + "attribute": "string_with_only_one_question_mark", + "operator": "ONE_OF", + "value": [ + "true" + ] + } + ] + } + ], + "splits": [ + { + "variationKey": "string_with_only_one_question_mark", + "shards": [] + } + ], + "doLog": true + }, + { + "key": "allocation-test-string_with_only_multiple_question_marks", + "rules": [ + { + "conditions": [ + { + "attribute": "string_with_only_multiple_question_marks", + "operator": "ONE_OF", + "value": [ + "true" + ] + } + ] + } + ], + "splits": [ + { + "variationKey": "string_with_only_multiple_question_marks", + "shards": [] + } + ], + "doLog": true + }, + { + "key": "allocation-test-string_with_exclamation_marks", + "rules": [ + { + "conditions": [ + { + "attribute": "string_with_exclamation_marks", + "operator": "ONE_OF", + "value": [ + "true" + ] + } + ] + } + ], + "splits": [ + { + "variationKey": "string_with_exclamation_marks", + "shards": [] + } + ], + "doLog": true + }, + { + "key": "allocation-test-string_with_only_one_exclamation_mark", + "rules": [ + { + "conditions": [ + { + "attribute": "string_with_only_one_exclamation_mark", + "operator": "ONE_OF", + "value": [ + "true" + ] + } + ] + } + ], + "splits": [ + { + "variationKey": "string_with_only_one_exclamation_mark", + "shards": [] + } + ], + "doLog": true + }, + { + "key": "allocation-test-string_with_only_multiple_exclamation_marks", + "rules": [ + { + "conditions": [ + { + "attribute": "string_with_only_multiple_exclamation_marks", + "operator": "ONE_OF", + "value": [ + "true" + ] + } + ] + } + ], + "splits": [ + { + "variationKey": "string_with_only_multiple_exclamation_marks", + "shards": [] + } + ], + "doLog": true + }, + { + "key": "allocation-test-string_with_opening_parentheses", + "rules": [ + { + "conditions": [ + { + "attribute": "string_with_opening_parentheses", + "operator": "ONE_OF", + "value": [ + "true" + ] + } + ] + } + ], + "splits": [ + { + "variationKey": "string_with_opening_parentheses", + "shards": [] + } + ], + "doLog": true + }, + { + "key": "allocation-test-string_with_only_one_opening_parenthese", + "rules": [ + { + "conditions": [ + { + "attribute": "string_with_only_one_opening_parenthese", + "operator": "ONE_OF", + "value": [ + "true" + ] + } + ] + } + ], + "splits": [ + { + "variationKey": "string_with_only_one_opening_parenthese", + "shards": [] + } + ], + "doLog": true + }, + { + "key": "allocation-test-string_with_only_multiple_opening_parentheses", + "rules": [ + { + "conditions": [ + { + "attribute": "string_with_only_multiple_opening_parentheses", + "operator": "ONE_OF", + "value": [ + "true" + ] + } + ] + } + ], + "splits": [ + { + "variationKey": "string_with_only_multiple_opening_parentheses", + "shards": [] + } + ], + "doLog": true + }, + { + "key": "allocation-test-string_with_closing_parentheses", + "rules": [ + { + "conditions": [ + { + "attribute": "string_with_closing_parentheses", + "operator": "ONE_OF", + "value": [ + "true" + ] + } + ] + } + ], + "splits": [ + { + "variationKey": "string_with_closing_parentheses", + "shards": [] + } + ], + "doLog": true + }, + { + "key": "allocation-test-string_with_only_one_closing_parenthese", + "rules": [ + { + "conditions": [ + { + "attribute": "string_with_only_one_closing_parenthese", + "operator": "ONE_OF", + "value": [ + "true" + ] + } + ] + } + ], + "splits": [ + { + "variationKey": "string_with_only_one_closing_parenthese", + "shards": [] + } + ], + "doLog": true + }, + { + "key": "allocation-test-string_with_only_multiple_closing_parentheses", + "rules": [ + { + "conditions": [ + { + "attribute": "string_with_only_multiple_closing_parentheses", + "operator": "ONE_OF", + "value": [ + "true" + ] + } + ] + } + ], + "splits": [ + { + "variationKey": "string_with_only_multiple_closing_parentheses", + "shards": [] + } + ], + "doLog": true + } + ] + } + } +} diff --git a/spec/datadog/core/feature_flags/fixtures/test-case-boolean-one-of-matches.json b/spec/datadog/core/feature_flags/fixtures/test-case-boolean-one-of-matches.json new file mode 100644 index 00000000000..6bfbf0effad --- /dev/null +++ b/spec/datadog/core/feature_flags/fixtures/test-case-boolean-one-of-matches.json @@ -0,0 +1,240 @@ +[ + { + "flag": "boolean-one-of-matches", + "variationType": "INTEGER", + "defaultValue": 0, + "targetingKey": "alice", + "attributes": { + "one_of_flag": true + }, + "result": { + "value": 1, + "variant": "1", + "flagMetadata": { + "allocationKey": "1-for-one-of", + "variationType": "number", + "doLog": true + } + } + }, + { + "flag": "boolean-one-of-matches", + "variationType": "INTEGER", + "defaultValue": 0, + "targetingKey": "bob", + "attributes": { + "one_of_flag": false + }, + "result": { + "value": 0 + } + }, + { + "flag": "boolean-one-of-matches", + "variationType": "INTEGER", + "defaultValue": 0, + "targetingKey": "charlie", + "attributes": { + "one_of_flag": "True" + }, + "result": { + "value": 0 + } + }, + { + "flag": "boolean-one-of-matches", + "variationType": "INTEGER", + "defaultValue": 0, + "targetingKey": "derek", + "attributes": { + "matches_flag": true + }, + "result": { + "value": 2, + "variant": "2", + "flagMetadata": { + "allocationKey": "2-for-matches", + "variationType": "number", + "doLog": true + } + } + }, + { + "flag": "boolean-one-of-matches", + "variationType": "INTEGER", + "defaultValue": 0, + "targetingKey": "erica", + "attributes": { + "matches_flag": false + }, + "result": { + "value": 0 + } + }, + { + "flag": "boolean-one-of-matches", + "variationType": "INTEGER", + "defaultValue": 0, + "targetingKey": "frank", + "attributes": { + "not_matches_flag": false + }, + "result": { + "value": 0 + } + }, + { + "flag": "boolean-one-of-matches", + "variationType": "INTEGER", + "defaultValue": 0, + "targetingKey": "george", + "attributes": { + "not_matches_flag": true + }, + "result": { + "value": 4, + "variant": "4", + "flagMetadata": { + "allocationKey": "4-for-not-matches", + "variationType": "number", + "doLog": true + } + } + }, + { + "flag": "boolean-one-of-matches", + "variationType": "INTEGER", + "defaultValue": 0, + "targetingKey": "haley", + "attributes": { + "not_matches_flag": "False" + }, + "result": { + "value": 4, + "variant": "4", + "flagMetadata": { + "allocationKey": "4-for-not-matches", + "variationType": "number", + "doLog": true + } + } + }, + { + "flag": "boolean-one-of-matches", + "variationType": "INTEGER", + "defaultValue": 0, + "targetingKey": "ivy", + "attributes": { + "not_one_of_flag": true + }, + "result": { + "value": 3, + "variant": "3", + "flagMetadata": { + "allocationKey": "3-for-not-one-of", + "variationType": "number", + "doLog": true + } + } + }, + { + "flag": "boolean-one-of-matches", + "variationType": "INTEGER", + "defaultValue": 0, + "targetingKey": "julia", + "attributes": { + "not_one_of_flag": false + }, + "result": { + "value": 0 + } + }, + { + "flag": "boolean-one-of-matches", + "variationType": "INTEGER", + "defaultValue": 0, + "targetingKey": "kim", + "attributes": { + "not_one_of_flag": "False" + }, + "result": { + "value": 3, + "variant": "3", + "flagMetadata": { + "allocationKey": "3-for-not-one-of", + "variationType": "number", + "doLog": true + } + } + }, + { + "flag": "boolean-one-of-matches", + "variationType": "INTEGER", + "defaultValue": 0, + "targetingKey": "lucas", + "attributes": { + "not_one_of_flag": "true" + }, + "result": { + "value": 3, + "variant": "3", + "flagMetadata": { + "allocationKey": "3-for-not-one-of", + "variationType": "number", + "doLog": true + } + } + }, + { + "flag": "boolean-one-of-matches", + "variationType": "INTEGER", + "defaultValue": 0, + "targetingKey": "mike", + "attributes": { + "not_one_of_flag": "false" + }, + "result": { + "value": 0 + } + }, + { + "flag": "boolean-one-of-matches", + "variationType": "INTEGER", + "defaultValue": 0, + "targetingKey": "nicole", + "attributes": { + "null_flag": "null" + }, + "result": { + "value": 5, + "variant": "5", + "flagMetadata": { + "allocationKey": "5-for-matches-null", + "variationType": "number", + "doLog": true + } + } + }, + { + "flag": "boolean-one-of-matches", + "variationType": "INTEGER", + "defaultValue": 0, + "targetingKey": "owen", + "attributes": { + "null_flag": null + }, + "result": { + "value": 0 + } + }, + { + "flag": "boolean-one-of-matches", + "variationType": "INTEGER", + "defaultValue": 0, + "targetingKey": "pete", + "attributes": {}, + "result": { + "value": 0 + } + } +] diff --git a/spec/datadog/core/feature_flags/fixtures/test-case-comparator-operator-flag.json b/spec/datadog/core/feature_flags/fixtures/test-case-comparator-operator-flag.json new file mode 100644 index 00000000000..a5c8ef07cd4 --- /dev/null +++ b/spec/datadog/core/feature_flags/fixtures/test-case-comparator-operator-flag.json @@ -0,0 +1,82 @@ +[ + { + "flag": "comparator-operator-test", + "variationType": "STRING", + "defaultValue": "unknown", + "targetingKey": "alice", + "attributes": { + "size": 5, + "country": "US" + }, + "result": { + "value": "small", + "variant": "small", + "flagMetadata": { + "allocationKey": "small-size", + "variationType": "string", + "doLog": true + } + } + }, + { + "flag": "comparator-operator-test", + "variationType": "STRING", + "defaultValue": "unknown", + "targetingKey": "bob", + "attributes": { + "size": 10, + "country": "Canada" + }, + "result": { + "value": "medium", + "variant": "medium", + "flagMetadata": { + "allocationKey": "medum-size", + "variationType": "string", + "doLog": true + } + } + }, + { + "flag": "comparator-operator-test", + "variationType": "STRING", + "defaultValue": "unknown", + "targetingKey": "charlie", + "attributes": { + "size": 25 + }, + "result": { + "value": "unknown" + } + }, + { + "flag": "comparator-operator-test", + "variationType": "STRING", + "defaultValue": "unknown", + "targetingKey": "david", + "attributes": { + "size": 26 + }, + "result": { + "value": "large", + "variant": "large", + "flagMetadata": { + "allocationKey": "large-size", + "variationType": "string", + "doLog": true + } + } + }, + { + "flag": "comparator-operator-test", + "variationType": "STRING", + "defaultValue": "unknown", + "targetingKey": "elize", + "attributes": { + "country": "UK" + }, + "result": { + "value": "unknown" + } + } +] diff --git a/spec/datadog/core/feature_flags/fixtures/test-case-disabled-flag.json b/spec/datadog/core/feature_flags/fixtures/test-case-disabled-flag.json new file mode 100644 index 00000000000..0da79189ade --- /dev/null +++ b/spec/datadog/core/feature_flags/fixtures/test-case-disabled-flag.json @@ -0,0 +1,40 @@ +[ + { + "flag": "disabled_flag", + "variationType": "INTEGER", + "defaultValue": 0, + "targetingKey": "alice", + "attributes": { + "email": "alice@mycompany.com", + "country": "US" + }, + "result": { + "value": 0 + } + }, + { + "flag": "disabled_flag", + "variationType": "INTEGER", + "defaultValue": 0, + "targetingKey": "bob", + "attributes": { + "email": "bob@example.com", + "country": "Canada" + }, + "result": { + "value": 0 + } + }, + { + "flag": "disabled_flag", + "variationType": "INTEGER", + "defaultValue": 0, + "targetingKey": "charlie", + "attributes": { + "age": 50 + }, + "result": { + "value": 0 + } + } +] diff --git a/spec/datadog/core/feature_flags/fixtures/test-case-empty-flag.json b/spec/datadog/core/feature_flags/fixtures/test-case-empty-flag.json new file mode 100644 index 00000000000..52100b1fe47 --- /dev/null +++ b/spec/datadog/core/feature_flags/fixtures/test-case-empty-flag.json @@ -0,0 +1,40 @@ +[ + { + "flag": "empty_flag", + "variationType": "STRING", + "defaultValue": "default_value", + "targetingKey": "alice", + "attributes": { + "email": "alice@mycompany.com", + "country": "US" + }, + "result": { + "value": "default_value" + } + }, + { + "flag": "empty_flag", + "variationType": "STRING", + "defaultValue": "default_value", + "targetingKey": "bob", + "attributes": { + "email": "bob@example.com", + "country": "Canada" + }, + "result": { + "value": "default_value" + } + }, + { + "flag": "empty_flag", + "variationType": "STRING", + "defaultValue": "default_value", + "targetingKey": "charlie", + "attributes": { + "age": 50 + }, + "result": { + "value": "default_value" + } + } +] diff --git a/spec/datadog/core/feature_flags/fixtures/test-case-flag-with-empty-string.json b/spec/datadog/core/feature_flags/fixtures/test-case-flag-with-empty-string.json new file mode 100644 index 00000000000..32a1c1febb7 --- /dev/null +++ b/spec/datadog/core/feature_flags/fixtures/test-case-flag-with-empty-string.json @@ -0,0 +1,36 @@ +[ + { + "flag": "empty_string_flag", + "variationType": "STRING", + "defaultValue": "default", + "targetingKey": "alice", + "attributes": { + "country": "US" + }, + "result": { + "value": "", + "variant": "empty_string", + "flagMetadata": { + "allocationKey": "allocation-empty", + "variationType": "string", + "doLog": true + } + } + }, + { + "flag": "empty_string_flag", + "variationType": "STRING", + "defaultValue": "default", + "targetingKey": "bob", + "attributes": {}, + "result": { + "value": "non_empty", + "variant": "non_empty", + "flagMetadata": { + "allocationKey": "allocation-test", + "variationType": "string", + "doLog": true + } + } + } +] diff --git a/spec/datadog/core/feature_flags/fixtures/test-case-integer-flag.json b/spec/datadog/core/feature_flags/fixtures/test-case-integer-flag.json new file mode 100644 index 00000000000..4a41d042f62 --- /dev/null +++ b/spec/datadog/core/feature_flags/fixtures/test-case-integer-flag.json @@ -0,0 +1,382 @@ +[ + { + "flag": "integer-flag", + "variationType": "INTEGER", + "defaultValue": 0, + "targetingKey": "alice", + "attributes": { + "email": "alice@mycompany.com", + "country": "US" + }, + "result": { + "value": 3, + "variant": "three", + "flagMetadata": { + "allocationKey": "targeted allocation", + "variationType": "number", + "doLog": true + } + } + }, + { + "flag": "integer-flag", + "variationType": "INTEGER", + "defaultValue": 0, + "targetingKey": "bob", + "attributes": { + "email": "bob@example.com", + "country": "Canada" + }, + "result": { + "value": 3, + "variant": "three", + "flagMetadata": { + "allocationKey": "targeted allocation", + "variationType": "number", + "doLog": true + } + } + }, + { + "flag": "integer-flag", + "variationType": "INTEGER", + "defaultValue": 0, + "targetingKey": "charlie", + "attributes": { + "age": 50 + }, + "result": { + "value": 2, + "variant": "two", + "flagMetadata": { + "allocationKey": "50/50 split", + "variationType": "number", + "doLog": true + } + } + }, + { + "flag": "integer-flag", + "variationType": "INTEGER", + "defaultValue": 0, + "targetingKey": "debra", + "attributes": { + "email": "test@test.com", + "country": "Mexico", + "age": 25 + }, + "result": { + "value": 3, + "variant": "three", + "flagMetadata": { + "allocationKey": "targeted allocation", + "variationType": "number", + "doLog": true + } + } + }, + { + "flag": "integer-flag", + "variationType": "INTEGER", + "defaultValue": 0, + "targetingKey": "1", + "attributes": {}, + "result": { + "value": 2, + "variant": "two", + "flagMetadata": { + "allocationKey": "50/50 split", + "variationType": "number", + "doLog": true + } + } + }, + { + "flag": "integer-flag", + "variationType": "INTEGER", + "defaultValue": 0, + "targetingKey": "2", + "attributes": {}, + "result": { + "value": 2, + "variant": "two", + "flagMetadata": { + "allocationKey": "50/50 split", + "variationType": "number", + "doLog": true + } + } + }, + { + "flag": "integer-flag", + "variationType": "INTEGER", + "defaultValue": 0, + "targetingKey": "3", + "attributes": {}, + "result": { + "value": 2, + "variant": "two", + "flagMetadata": { + "allocationKey": "50/50 split", + "variationType": "number", + "doLog": true + } + } + }, + { + "flag": "integer-flag", + "variationType": "INTEGER", + "defaultValue": 0, + "targetingKey": "4", + "attributes": {}, + "result": { + "value": 2, + "variant": "two", + "flagMetadata": { + "allocationKey": "50/50 split", + "variationType": "number", + "doLog": true + } + } + }, + { + "flag": "integer-flag", + "variationType": "INTEGER", + "defaultValue": 0, + "targetingKey": "5", + "attributes": {}, + "result": { + "value": 1, + "variant": "one", + "flagMetadata": { + "allocationKey": "50/50 split", + "variationType": "number", + "doLog": true + } + } + }, + { + "flag": "integer-flag", + "variationType": "INTEGER", + "defaultValue": 0, + "targetingKey": "6", + "attributes": {}, + "result": { + "value": 2, + "variant": "two", + "flagMetadata": { + "allocationKey": "50/50 split", + "variationType": "number", + "doLog": true + } + } + }, + { + "flag": "integer-flag", + "variationType": "INTEGER", + "defaultValue": 0, + "targetingKey": "7", + "attributes": {}, + "result": { + "value": 1, + "variant": "one", + "flagMetadata": { + "allocationKey": "50/50 split", + "variationType": "number", + "doLog": true + } + } + }, + { + "flag": "integer-flag", + "variationType": "INTEGER", + "defaultValue": 0, + "targetingKey": "8", + "attributes": {}, + "result": { + "value": 1, + "variant": "one", + "flagMetadata": { + "allocationKey": "50/50 split", + "variationType": "number", + "doLog": true + } + } + }, + { + "flag": "integer-flag", + "variationType": "INTEGER", + "defaultValue": 0, + "targetingKey": "9", + "attributes": {}, + "result": { + "value": 2, + "variant": "two", + "flagMetadata": { + "allocationKey": "50/50 split", + "variationType": "number", + "doLog": true + } + } + }, + { + "flag": "integer-flag", + "variationType": "INTEGER", + "defaultValue": 0, + "targetingKey": "10", + "attributes": {}, + "result": { + "value": 2, + "variant": "two", + "flagMetadata": { + "allocationKey": "50/50 split", + "variationType": "number", + "doLog": true + } + } + }, + { + "flag": "integer-flag", + "variationType": "INTEGER", + "defaultValue": 0, + "targetingKey": "11", + "attributes": {}, + "result": { + "value": 1, + "variant": "one", + "flagMetadata": { + "allocationKey": "50/50 split", + "variationType": "number", + "doLog": true + } + } + }, + { + "flag": "integer-flag", + "variationType": "INTEGER", + "defaultValue": 0, + "targetingKey": "12", + "attributes": {}, + "result": { + "value": 1, + "variant": "one", + "flagMetadata": { + "allocationKey": "50/50 split", + "variationType": "number", + "doLog": true + } + } + }, + { + "flag": "integer-flag", + "variationType": "INTEGER", + "defaultValue": 0, + "targetingKey": "13", + "attributes": {}, + "result": { + "value": 2, + "variant": "two", + "flagMetadata": { + "allocationKey": "50/50 split", + "variationType": "number", + "doLog": true + } + } + }, + { + "flag": "integer-flag", + "variationType": "INTEGER", + "defaultValue": 0, + "targetingKey": "14", + "attributes": {}, + "result": { + "value": 1, + "variant": "one", + "flagMetadata": { + "allocationKey": "50/50 split", + "variationType": "number", + "doLog": true + } + } + }, + { + "flag": "integer-flag", + "variationType": "INTEGER", + "defaultValue": 0, + "targetingKey": "15", + "attributes": {}, + "result": { + "value": 2, + "variant": "two", + "flagMetadata": { + "allocationKey": "50/50 split", + "variationType": "number", + "doLog": true + } + } + }, + { + "flag": "integer-flag", + "variationType": "INTEGER", + "defaultValue": 0, + "targetingKey": "16", + "attributes": {}, + "result": { + "value": 2, + "variant": "two", + "flagMetadata": { + "allocationKey": "50/50 split", + "variationType": "number", + "doLog": true + } + } + }, + { + "flag": "integer-flag", + "variationType": "INTEGER", + "defaultValue": 0, + "targetingKey": "17", + "attributes": {}, + "result": { + "value": 1, + "variant": "one", + "flagMetadata": { + "allocationKey": "50/50 split", + "variationType": "number", + "doLog": true + } + } + }, + { + "flag": "integer-flag", + "variationType": "INTEGER", + "defaultValue": 0, + "targetingKey": "18", + "attributes": {}, + "result": { + "value": 1, + "variant": "one", + "flagMetadata": { + "allocationKey": "50/50 split", + "variationType": "number", + "doLog": true + } + } + }, + { + "flag": "integer-flag", + "variationType": "INTEGER", + "defaultValue": 0, + "targetingKey": "19", + "attributes": {}, + "result": { + "value": 1, + "variant": "one", + "flagMetadata": { + "allocationKey": "50/50 split", + "variationType": "number", + "doLog": true + } + } + } +] diff --git a/spec/datadog/core/feature_flags/fixtures/test-case-kill-switch-flag.json b/spec/datadog/core/feature_flags/fixtures/test-case-kill-switch-flag.json new file mode 100644 index 00000000000..29e65ba5bb4 --- /dev/null +++ b/spec/datadog/core/feature_flags/fixtures/test-case-kill-switch-flag.json @@ -0,0 +1,434 @@ +[ + { + "flag": "kill-switch", + "variationType": "BOOLEAN", + "defaultValue": false, + "targetingKey": "alice", + "attributes": { + "email": "alice@mycompany.com", + "country": "US" + }, + "result": { + "value": true, + "variant": "on", + "flagMetadata": { + "allocationKey": "on-for-NA", + "variationType": "boolean", + "doLog": true + } + } + }, + { + "flag": "kill-switch", + "variationType": "BOOLEAN", + "defaultValue": false, + "targetingKey": "bob", + "attributes": { + "email": "bob@example.com", + "country": "Canada" + }, + "result": { + "value": true, + "variant": "on", + "flagMetadata": { + "allocationKey": "on-for-NA", + "variationType": "boolean", + "doLog": true + } + } + }, + { + "flag": "kill-switch", + "variationType": "BOOLEAN", + "defaultValue": false, + "targetingKey": "barbara", + "attributes": { + "email": "barbara@example.com", + "country": "canada" + }, + "result": { + "value": false, + "variant": "off", + "flagMetadata": { + "allocationKey": "off-for-all", + "variationType": "boolean", + "doLog": true + } + } + }, + { + "flag": "kill-switch", + "variationType": "BOOLEAN", + "defaultValue": false, + "targetingKey": "charlie", + "attributes": { + "age": 40 + }, + "result": { + "value": false, + "variant": "off", + "flagMetadata": { + "allocationKey": "off-for-all", + "variationType": "boolean", + "doLog": true + } + } + }, + { + "flag": "kill-switch", + "variationType": "BOOLEAN", + "defaultValue": false, + "targetingKey": "debra", + "attributes": { + "email": "test@test.com", + "country": "Mexico", + "age": 25 + }, + "result": { + "value": true, + "variant": "on", + "flagMetadata": { + "allocationKey": "on-for-NA", + "variationType": "boolean", + "doLog": true + } + } + }, + { + "flag": "kill-switch", + "variationType": "BOOLEAN", + "defaultValue": false, + "targetingKey": "1", + "attributes": {}, + "result": { + "value": false, + "variant": "off", + "flagMetadata": { + "allocationKey": "off-for-all", + "variationType": "boolean", + "doLog": true + } + } + }, + { + "flag": "kill-switch", + "variationType": "BOOLEAN", + "defaultValue": false, + "targetingKey": "2", + "attributes": { + "country": "Mexico" + }, + "result": { + "value": true, + "variant": "on", + "flagMetadata": { + "allocationKey": "on-for-NA", + "variationType": "boolean", + "doLog": true + } + } + }, + { + "flag": "kill-switch", + "variationType": "BOOLEAN", + "defaultValue": false, + "targetingKey": "3", + "attributes": { + "country": "UK", + "age": 50 + }, + "result": { + "value": true, + "variant": "on", + "flagMetadata": { + "allocationKey": "on-for-age-50+", + "variationType": "boolean", + "doLog": true + } + } + }, + { + "flag": "kill-switch", + "variationType": "BOOLEAN", + "defaultValue": false, + "targetingKey": "4", + "attributes": { + "country": "Germany" + }, + "result": { + "value": false, + "variant": "off", + "flagMetadata": { + "allocationKey": "off-for-all", + "variationType": "boolean", + "doLog": true + } + } + }, + { + "flag": "kill-switch", + "variationType": "BOOLEAN", + "defaultValue": false, + "targetingKey": "5", + "attributes": { + "country": "Germany" + }, + "result": { + "value": false, + "variant": "off", + "flagMetadata": { + "allocationKey": "off-for-all", + "variationType": "boolean", + "doLog": true + } + } + }, + { + "flag": "kill-switch", + "variationType": "BOOLEAN", + "defaultValue": false, + "targetingKey": "6", + "attributes": { + "country": "Germany" + }, + "result": { + "value": false, + "variant": "off", + "flagMetadata": { + "allocationKey": "off-for-all", + "variationType": "boolean", + "doLog": true + } + } + }, + { + "flag": "kill-switch", + "variationType": "BOOLEAN", + "defaultValue": false, + "targetingKey": "7", + "attributes": { + "country": "US", + "age": 12 + }, + "result": { + "value": true, + "variant": "on", + "flagMetadata": { + "allocationKey": "on-for-NA", + "variationType": "boolean", + "doLog": true + } + } + }, + { + "flag": "kill-switch", + "variationType": "BOOLEAN", + "defaultValue": false, + "targetingKey": "8", + "attributes": { + "country": "Italy", + "age": 60 + }, + "result": { + "value": true, + "variant": "on", + "flagMetadata": { + "allocationKey": "on-for-age-50+", + "variationType": "boolean", + "doLog": true + } + } + }, + { + "flag": "kill-switch", + "variationType": "BOOLEAN", + "defaultValue": false, + "targetingKey": "9", + "attributes": { + "email": "email@email.com" + }, + "result": { + "value": false, + "variant": "off", + "flagMetadata": { + "allocationKey": "off-for-all", + "variationType": "boolean", + "doLog": true + } + } + }, + { + "flag": "kill-switch", + "variationType": "BOOLEAN", + "defaultValue": false, + "targetingKey": "10", + "attributes": {}, + "result": { + "value": false, + "variant": "off", + "flagMetadata": { + "allocationKey": "off-for-all", + "variationType": "boolean", + "doLog": true + } + } + }, + { + "flag": "kill-switch", + "variationType": "BOOLEAN", + "defaultValue": false, + "targetingKey": "11", + "attributes": {}, + "result": { + "value": false, + "variant": "off", + "flagMetadata": { + "allocationKey": "off-for-all", + "variationType": "boolean", + "doLog": true + } + } + }, + { + "flag": "kill-switch", + "variationType": "BOOLEAN", + "defaultValue": false, + "targetingKey": "12", + "attributes": { + "country": "US" + }, + "result": { + "value": true, + "variant": "on", + "flagMetadata": { + "allocationKey": "on-for-NA", + "variationType": "boolean", + "doLog": true + } + } + }, + { + "flag": "kill-switch", + "variationType": "BOOLEAN", + "defaultValue": false, + "targetingKey": "13", + "attributes": { + "country": "Canada" + }, + "result": { + "value": true, + "variant": "on", + "flagMetadata": { + "allocationKey": "on-for-NA", + "variationType": "boolean", + "doLog": true + } + } + }, + { + "flag": "kill-switch", + "variationType": "BOOLEAN", + "defaultValue": false, + "targetingKey": "14", + "attributes": {}, + "result": { + "value": false, + "variant": "off", + "flagMetadata": { + "allocationKey": "off-for-all", + "variationType": "boolean", + "doLog": true + } + } + }, + { + "flag": "kill-switch", + "variationType": "BOOLEAN", + "defaultValue": false, + "targetingKey": "15", + "attributes": { + "country": "Denmark" + }, + "result": { + "value": false, + "variant": "off", + "flagMetadata": { + "allocationKey": "off-for-all", + "variationType": "boolean", + "doLog": true + } + } + }, + { + "flag": "kill-switch", + "variationType": "BOOLEAN", + "defaultValue": false, + "targetingKey": "16", + "attributes": { + "country": "Norway" + }, + "result": { + "value": false, + "variant": "off", + "flagMetadata": { + "allocationKey": "off-for-all", + "variationType": "boolean", + "doLog": true + } + } + }, + { + "flag": "kill-switch", + "variationType": "BOOLEAN", + "defaultValue": false, + "targetingKey": "17", + "attributes": { + "country": "UK" + }, + "result": { + "value": false, + "variant": "off", + "flagMetadata": { + "allocationKey": "off-for-all", + "variationType": "boolean", + "doLog": true + } + } + }, + { + "flag": "kill-switch", + "variationType": "BOOLEAN", + "defaultValue": false, + "targetingKey": "18", + "attributes": { + "country": "UK" + }, + "result": { + "value": false, + "variant": "off", + "flagMetadata": { + "allocationKey": "off-for-all", + "variationType": "boolean", + "doLog": true + } + } + }, + { + "flag": "kill-switch", + "variationType": "BOOLEAN", + "defaultValue": false, + "targetingKey": "19", + "attributes": { + "country": "UK" + }, + "result": { + "value": false, + "variant": "off", + "flagMetadata": { + "allocationKey": "off-for-all", + "variationType": "boolean", + "doLog": true + } + } + } +] diff --git a/spec/datadog/core/feature_flags/fixtures/test-case-new-user-onboarding-flag.json b/spec/datadog/core/feature_flags/fixtures/test-case-new-user-onboarding-flag.json new file mode 100644 index 00000000000..3845597270e --- /dev/null +++ b/spec/datadog/core/feature_flags/fixtures/test-case-new-user-onboarding-flag.json @@ -0,0 +1,420 @@ +[ + { + "flag": "new-user-onboarding", + "variationType": "STRING", + "defaultValue": "default", + "targetingKey": "alice", + "attributes": { + "email": "alice@mycompany.com", + "country": "US" + }, + "result": { + "value": "green", + "variant": "green", + "flagMetadata": { + "allocationKey": "internal users", + "variationType": "string", + "doLog": false + } + } + }, + { + "flag": "new-user-onboarding", + "variationType": "STRING", + "defaultValue": "default", + "targetingKey": "bob", + "attributes": { + "email": "bob@example.com", + "country": "Canada" + }, + "result": { + "value": "default" + } + }, + { + "flag": "new-user-onboarding", + "variationType": "STRING", + "defaultValue": "default", + "targetingKey": "charlie", + "attributes": { + "age": 50 + }, + "result": { + "value": "default" + } + }, + { + "flag": "new-user-onboarding", + "variationType": "STRING", + "defaultValue": "default", + "targetingKey": "debra", + "attributes": { + "email": "test@test.com", + "country": "Mexico", + "age": 25 + }, + "result": { + "value": "blue", + "variant": "blue", + "flagMetadata": { + "allocationKey": "rollout", + "variationType": "string", + "doLog": true + } + } + }, + { + "flag": "new-user-onboarding", + "variationType": "STRING", + "defaultValue": "default", + "targetingKey": "zach", + "attributes": { + "email": "test@test.com", + "country": "Mexico", + "age": 25 + }, + "result": { + "value": "purple", + "variant": "purple", + "flagMetadata": { + "allocationKey": "id rule", + "variationType": "string", + "doLog": false + } + } + }, + { + "flag": "new-user-onboarding", + "variationType": "STRING", + "defaultValue": "default", + "targetingKey": "zach", + "attributes": { + "id": "override-id", + "email": "test@test.com", + "country": "Mexico", + "age": 25 + }, + "result": { + "value": "blue", + "variant": "blue", + "flagMetadata": { + "allocationKey": "rollout", + "variationType": "string", + "doLog": true + } + } + }, + { + "flag": "new-user-onboarding", + "variationType": "STRING", + "defaultValue": "default", + "targetingKey": "Zach", + "attributes": { + "email": "test@test.com", + "country": "Mexico", + "age": 25 + }, + "result": { + "value": "default" + } + }, + { + "flag": "new-user-onboarding", + "variationType": "STRING", + "defaultValue": "default", + "targetingKey": "1", + "attributes": {}, + "result": { + "value": "default" + } + }, + { + "flag": "new-user-onboarding", + "variationType": "STRING", + "defaultValue": "default", + "targetingKey": "2", + "attributes": { + "country": "Mexico" + }, + "result": { + "value": "blue", + "variant": "blue", + "flagMetadata": { + "allocationKey": "rollout", + "variationType": "string", + "doLog": true + } + } + }, + { + "flag": "new-user-onboarding", + "variationType": "STRING", + "defaultValue": "default", + "targetingKey": "3", + "attributes": { + "country": "UK", + "age": 33 + }, + "result": { + "value": "control", + "variant": "control", + "flagMetadata": { + "allocationKey": "experiment", + "variationType": "string", + "doLog": true + } + } + }, + { + "flag": "new-user-onboarding", + "variationType": "STRING", + "defaultValue": "default", + "targetingKey": "4", + "attributes": { + "country": "Germany" + }, + "result": { + "value": "red", + "variant": "red", + "flagMetadata": { + "allocationKey": "experiment", + "variationType": "string", + "doLog": true + } + } + }, + { + "flag": "new-user-onboarding", + "variationType": "STRING", + "defaultValue": "default", + "targetingKey": "5", + "attributes": { + "country": "Germany" + }, + "result": { + "value": "yellow", + "variant": "yellow", + "flagMetadata": { + "allocationKey": "experiment", + "variationType": "string", + "doLog": true + } + } + }, + { + "flag": "new-user-onboarding", + "variationType": "STRING", + "defaultValue": "default", + "targetingKey": "6", + "attributes": { + "country": "Germany" + }, + "result": { + "value": "yellow", + "variant": "yellow", + "flagMetadata": { + "allocationKey": "experiment", + "variationType": "string", + "doLog": true + } + } + }, + { + "flag": "new-user-onboarding", + "variationType": "STRING", + "defaultValue": "default", + "targetingKey": "7", + "attributes": { + "country": "US" + }, + "result": { + "value": "blue", + "variant": "blue", + "flagMetadata": { + "allocationKey": "rollout", + "variationType": "string", + "doLog": true + } + } + }, + { + "flag": "new-user-onboarding", + "variationType": "STRING", + "defaultValue": "default", + "targetingKey": "8", + "attributes": { + "country": "Italy" + }, + "result": { + "value": "red", + "variant": "red", + "flagMetadata": { + "allocationKey": "experiment", + "variationType": "string", + "doLog": true + } + } + }, + { + "flag": "new-user-onboarding", + "variationType": "STRING", + "defaultValue": "default", + "targetingKey": "9", + "attributes": { + "email": "email@email.com" + }, + "result": { + "value": "default" + } + }, + { + "flag": "new-user-onboarding", + "variationType": "STRING", + "defaultValue": "default", + "targetingKey": "10", + "attributes": {}, + "result": { + "value": "default" + } + }, + { + "flag": "new-user-onboarding", + "variationType": "STRING", + "defaultValue": "default", + "targetingKey": "11", + "attributes": {}, + "result": { + "value": "default" + } + }, + { + "flag": "new-user-onboarding", + "variationType": "STRING", + "defaultValue": "default", + "targetingKey": "12", + "attributes": { + "country": "US" + }, + "result": { + "value": "blue", + "variant": "blue", + "flagMetadata": { + "allocationKey": "rollout", + "variationType": "string", + "doLog": true + } + } + }, + { + "flag": "new-user-onboarding", + "variationType": "STRING", + "defaultValue": "default", + "targetingKey": "13", + "attributes": { + "country": "Canada" + }, + "result": { + "value": "blue", + "variant": "blue", + "flagMetadata": { + "allocationKey": "rollout", + "variationType": "string", + "doLog": true + } + } + }, + { + "flag": "new-user-onboarding", + "variationType": "STRING", + "defaultValue": "default", + "targetingKey": "14", + "attributes": {}, + "result": { + "value": "default" + } + }, + { + "flag": "new-user-onboarding", + "variationType": "STRING", + "defaultValue": "default", + "targetingKey": "15", + "attributes": { + "country": "Denmark" + }, + "result": { + "value": "yellow", + "variant": "yellow", + "flagMetadata": { + "allocationKey": "experiment", + "variationType": "string", + "doLog": true + } + } + }, + { + "flag": "new-user-onboarding", + "variationType": "STRING", + "defaultValue": "default", + "targetingKey": "16", + "attributes": { + "country": "Norway" + }, + "result": { + "value": "control", + "variant": "control", + "flagMetadata": { + "allocationKey": "experiment", + "variationType": "string", + "doLog": true + } + } + }, + { + "flag": "new-user-onboarding", + "variationType": "STRING", + "defaultValue": "default", + "targetingKey": "17", + "attributes": { + "country": "UK" + }, + "result": { + "value": "control", + "variant": "control", + "flagMetadata": { + "allocationKey": "experiment", + "variationType": "string", + "doLog": true + } + } + }, + { + "flag": "new-user-onboarding", + "variationType": "STRING", + "defaultValue": "default", + "targetingKey": "18", + "attributes": { + "country": "UK" + }, + "result": { + "value": "default" + } + }, + { + "flag": "new-user-onboarding", + "variationType": "STRING", + "defaultValue": "default", + "targetingKey": "19", + "attributes": { + "country": "UK" + }, + "result": { + "value": "red", + "variant": "red", + "flagMetadata": { + "allocationKey": "experiment", + "variationType": "string", + "doLog": true + } + } + } +] diff --git a/spec/datadog/core/feature_flags/fixtures/test-case-no-allocations-flag.json b/spec/datadog/core/feature_flags/fixtures/test-case-no-allocations-flag.json new file mode 100644 index 00000000000..132c39db32a --- /dev/null +++ b/spec/datadog/core/feature_flags/fixtures/test-case-no-allocations-flag.json @@ -0,0 +1,52 @@ +[ + { + "flag": "no_allocations_flag", + "variationType": "JSON", + "defaultValue": { + "hello": "world" + }, + "targetingKey": "alice", + "attributes": { + "email": "alice@mycompany.com", + "country": "US" + }, + "result": { + "value": { + "hello": "world" + } + } + }, + { + "flag": "no_allocations_flag", + "variationType": "JSON", + "defaultValue": { + "hello": "world" + }, + "targetingKey": "bob", + "attributes": { + "email": "bob@example.com", + "country": "Canada" + }, + "result": { + "value": { + "hello": "world" + } + } + }, + { + "flag": "no_allocations_flag", + "variationType": "JSON", + "defaultValue": { + "hello": "world" + }, + "targetingKey": "charlie", + "attributes": { + "age": 50 + }, + "result": { + "value": { + "hello": "world" + } + } + } +] diff --git a/spec/datadog/core/feature_flags/fixtures/test-case-null-operator-flag.json b/spec/datadog/core/feature_flags/fixtures/test-case-null-operator-flag.json new file mode 100644 index 00000000000..dd5c687b893 --- /dev/null +++ b/spec/datadog/core/feature_flags/fixtures/test-case-null-operator-flag.json @@ -0,0 +1,94 @@ +[ + { + "flag": "null-operator-test", + "variationType": "STRING", + "defaultValue": "default-null", + "targetingKey": "alice", + "attributes": { + "size": 5, + "country": "US" + }, + "result": { + "value": "old", + "variant": "old", + "flagMetadata": { + "allocationKey": "null-operator", + "variationType": "string", + "doLog": true + } + } + }, + { + "flag": "null-operator-test", + "variationType": "STRING", + "defaultValue": "default-null", + "targetingKey": "bob", + "attributes": { + "size": 10, + "country": "Canada" + }, + "result": { + "value": "new", + "variant": "new", + "flagMetadata": { + "allocationKey": "not-null-operator", + "variationType": "string", + "doLog": true + } + } + }, + { + "flag": "null-operator-test", + "variationType": "STRING", + "defaultValue": "default-null", + "targetingKey": "charlie", + "attributes": { + "size": null + }, + "result": { + "value": "old", + "variant": "old", + "flagMetadata": { + "allocationKey": "null-operator", + "variationType": "string", + "doLog": true + } + } + }, + { + "flag": "null-operator-test", + "variationType": "STRING", + "defaultValue": "default-null", + "targetingKey": "david", + "attributes": { + "size": 26 + }, + "result": { + "value": "new", + "variant": "new", + "flagMetadata": { + "allocationKey": "not-null-operator", + "variationType": "string", + "doLog": true + } + } + }, + { + "flag": "null-operator-test", + "variationType": "STRING", + "defaultValue": "default-null", + "targetingKey": "elize", + "attributes": { + "country": "UK" + }, + "result": { + "value": "old", + "variant": "old", + "flagMetadata": { + "allocationKey": "null-operator", + "variationType": "string", + "doLog": true + } + } + } +] diff --git a/spec/datadog/core/feature_flags/fixtures/test-case-null-targeting-key.json b/spec/datadog/core/feature_flags/fixtures/test-case-null-targeting-key.json new file mode 100644 index 00000000000..5394bd91fa6 --- /dev/null +++ b/spec/datadog/core/feature_flags/fixtures/test-case-null-targeting-key.json @@ -0,0 +1,40 @@ +[ + { + "//": "successfull evaluation if flag has no sharding", + "flag": "numeric_flag", + "variationType": "NUMERIC", + "defaultValue": 42, + "targetingKey": null, + "attributes": {}, + "result": { + "value": 3.1415926 + } + }, + { + "//": "evaluation failure for flag with sharding", + "flag": "new-user-onboarding", + "variationType": "STRING", + "defaultValue": "default", + "targetingKey": null, + "attributes": { + "country": "France" + }, + "result": { + "value": "default" + } + }, + { + "//": "success if matched before sharding", + "flag": "new-user-onboarding", + "variationType": "STRING", + "defaultValue": "default", + "targetingKey": null, + "attributes": { + "email": "null@mycompany.com", + "country": "France" + }, + "result": { + "value": "green" + } + } +] diff --git a/spec/datadog/core/feature_flags/fixtures/test-case-numeric-flag.json b/spec/datadog/core/feature_flags/fixtures/test-case-numeric-flag.json new file mode 100644 index 00000000000..0e6ed8b1b3a --- /dev/null +++ b/spec/datadog/core/feature_flags/fixtures/test-case-numeric-flag.json @@ -0,0 +1,58 @@ +[ + { + "flag": "numeric_flag", + "variationType": "NUMERIC", + "defaultValue": 0.0, + "targetingKey": "alice", + "attributes": { + "email": "alice@mycompany.com", + "country": "US" + }, + "result": { + "value": 3.1415926, + "variant": "pi", + "flagMetadata": { + "allocationKey": "rollout", + "variationType": "number", + "doLog": true + } + } + }, + { + "flag": "numeric_flag", + "variationType": "NUMERIC", + "defaultValue": 0.0, + "targetingKey": "bob", + "attributes": { + "email": "bob@example.com", + "country": "Canada" + }, + "result": { + "value": 3.1415926, + "variant": "pi", + "flagMetadata": { + "allocationKey": "rollout", + "variationType": "number", + "doLog": true + } + } + }, + { + "flag": "numeric_flag", + "variationType": "NUMERIC", + "defaultValue": 0.0, + "targetingKey": "charlie", + "attributes": { + "age": 50 + }, + "result": { + "value": 3.1415926, + "variant": "pi", + "flagMetadata": { + "allocationKey": "rollout", + "variationType": "number", + "doLog": true + } + } + } +] diff --git a/spec/datadog/core/feature_flags/fixtures/test-case-numeric-one-of.json b/spec/datadog/core/feature_flags/fixtures/test-case-numeric-one-of.json new file mode 100644 index 00000000000..5ab68af5196 --- /dev/null +++ b/spec/datadog/core/feature_flags/fixtures/test-case-numeric-one-of.json @@ -0,0 +1,122 @@ +[ + { + "flag": "numeric-one-of", + "variationType": "INTEGER", + "defaultValue": 0, + "targetingKey": "alice", + "attributes": { + "number": 1 + }, + "result": { + "value": 1, + "variant": "1", + "flagMetadata": { + "allocationKey": "1-for-1", + "variationType": "number", + "doLog": true + } + } + }, + { + "flag": "numeric-one-of", + "variationType": "INTEGER", + "defaultValue": 0, + "targetingKey": "bob", + "attributes": { + "number": 2 + }, + "result": { + "value": 0 + } + }, + { + "flag": "numeric-one-of", + "variationType": "INTEGER", + "defaultValue": 0, + "targetingKey": "charlie", + "attributes": { + "number": 3 + }, + "result": { + "value": 3, + "variant": "3", + "flagMetadata": { + "allocationKey": "3-for-not-2", + "variationType": "number", + "doLog": true + } + } + }, + { + "flag": "numeric-one-of", + "variationType": "INTEGER", + "defaultValue": 0, + "targetingKey": "derek", + "attributes": { + "number": 4 + }, + "result": { + "value": 3, + "variant": "3", + "flagMetadata": { + "allocationKey": "3-for-not-2", + "variationType": "number", + "doLog": true + } + } + }, + { + "flag": "numeric-one-of", + "variationType": "INTEGER", + "defaultValue": 0, + "targetingKey": "erica", + "attributes": { + "number": "1" + }, + "result": { + "value": 1, + "variant": "1", + "flagMetadata": { + "allocationKey": "1-for-1", + "variationType": "number", + "doLog": true + } + } + }, + { + "flag": "numeric-one-of", + "variationType": "INTEGER", + "defaultValue": 0, + "targetingKey": "frank", + "attributes": { + "number": 1 + }, + "result": { + "value": 1, + "variant": "1", + "flagMetadata": { + "allocationKey": "1-for-1", + "variationType": "number", + "doLog": true + } + } + }, + { + "flag": "numeric-one-of", + "variationType": "INTEGER", + "defaultValue": 0, + "targetingKey": "george", + "attributes": { + "number": 123456789 + }, + "result": { + "value": 2, + "variant": "2", + "flagMetadata": { + "allocationKey": "2-for-123456789", + "variationType": "number", + "doLog": true + } + } + } +] diff --git a/spec/datadog/core/feature_flags/fixtures/test-case-regex-flag.json b/spec/datadog/core/feature_flags/fixtures/test-case-regex-flag.json new file mode 100644 index 00000000000..952a37aabcf --- /dev/null +++ b/spec/datadog/core/feature_flags/fixtures/test-case-regex-flag.json @@ -0,0 +1,65 @@ +[ + { + "flag": "regex-flag", + "variationType": "STRING", + "defaultValue": "none", + "targetingKey": "alice", + "attributes": { + "version": "1.15.0", + "email": "alice@example.com" + }, + "result": { + "value": "partial-example", + "variant": "partial-example", + "flagMetadata": { + "allocationKey": "partial-example", + "variationType": "string", + "doLog": true + } + } + }, + { + "flag": "regex-flag", + "variationType": "STRING", + "defaultValue": "none", + "targetingKey": "bob", + "attributes": { + "version": "0.20.1", + "email": "bob@test.com" + }, + "result": { + "value": "test", + "variant": "test", + "flagMetadata": { + "allocationKey": "test", + "variationType": "string", + "doLog": true + } + } + }, + { + "flag": "regex-flag", + "variationType": "STRING", + "defaultValue": "none", + "targetingKey": "charlie", + "attributes": { + "version": "2.1.13" + }, + "result": { + "value": "none" + } + }, + { + "flag": "regex-flag", + "variationType": "STRING", + "defaultValue": "none", + "targetingKey": "derek", + "attributes": { + "version": "2.1.13", + "email": "derek@gmail.com" + }, + "result": { + "value": "none" + } + } +] diff --git a/spec/datadog/core/feature_flags/fixtures/test-case-start-and-end-date-flag.json b/spec/datadog/core/feature_flags/fixtures/test-case-start-and-end-date-flag.json new file mode 100644 index 00000000000..caf805d1432 --- /dev/null +++ b/spec/datadog/core/feature_flags/fixtures/test-case-start-and-end-date-flag.json @@ -0,0 +1,58 @@ +[ + { + "flag": "start-and-end-date-test", + "variationType": "STRING", + "defaultValue": "unknown", + "targetingKey": "alice", + "attributes": { + "version": "1.15.0", + "country": "US" + }, + "result": { + "value": "current", + "variant": "current", + "flagMetadata": { + "allocationKey": "current-versions", + "variationType": "string", + "doLog": true + } + } + }, + { + "flag": "start-and-end-date-test", + "variationType": "STRING", + "defaultValue": "unknown", + "targetingKey": "bob", + "attributes": { + "version": "0.20.1", + "country": "Canada" + }, + "result": { + "value": "current", + "variant": "current", + "flagMetadata": { + "allocationKey": "current-versions", + "variationType": "string", + "doLog": true + } + } + }, + { + "flag": "start-and-end-date-test", + "variationType": "STRING", + "defaultValue": "unknown", + "targetingKey": "charlie", + "attributes": { + "version": "2.1.13" + }, + "result": { + "value": "current", + "variant": "current", + "flagMetadata": { + "allocationKey": "current-versions", + "variationType": "string", + "doLog": true + } + } + } +] diff --git a/spec/datadog/core/feature_flags/fixtures/test-flag-that-does-not-exist.json b/spec/datadog/core/feature_flags/fixtures/test-flag-that-does-not-exist.json new file mode 100644 index 00000000000..7499bba1c50 --- /dev/null +++ b/spec/datadog/core/feature_flags/fixtures/test-flag-that-does-not-exist.json @@ -0,0 +1,40 @@ +[ + { + "flag": "flag-that-does-not-exist", + "variationType": "NUMERIC", + "defaultValue": 0.0, + "targetingKey": "alice", + "attributes": { + "email": "alice@mycompany.com", + "country": "US" + }, + "result": { + "value": 0.0 + } + }, + { + "flag": "flag-that-does-not-exist", + "variationType": "NUMERIC", + "defaultValue": 0.0, + "targetingKey": "bob", + "attributes": { + "email": "bob@example.com", + "country": "Canada" + }, + "result": { + "value": 0.0 + } + }, + { + "flag": "flag-that-does-not-exist", + "variationType": "NUMERIC", + "defaultValue": 0.0, + "targetingKey": "charlie", + "attributes": { + "age": 50 + }, + "result": { + "value": 0.0 + } + } +] diff --git a/spec/datadog/core/feature_flags/fixtures/test-json-config-flag.json b/spec/datadog/core/feature_flags/fixtures/test-json-config-flag.json new file mode 100644 index 00000000000..3d4478ff711 --- /dev/null +++ b/spec/datadog/core/feature_flags/fixtures/test-json-config-flag.json @@ -0,0 +1,96 @@ +[ + { + "flag": "json-config-flag", + "variationType": "JSON", + "defaultValue": { + "foo": "bar" + }, + "targetingKey": "alice", + "attributes": { + "email": "alice@mycompany.com", + "country": "US" + }, + "result": { + "value": { + "integer": 1, + "string": "one", + "float": 1.0 + }, + "variant": "one", + "flagMetadata": { + "allocationKey": "50/50 split", + "variationType": "object", + "doLog": true + } + } + }, + { + "flag": "json-config-flag", + "variationType": "JSON", + "defaultValue": { + "foo": "bar" + }, + "targetingKey": "bob", + "attributes": { + "email": "bob@example.com", + "country": "Canada" + }, + "result": { + "value": { + "integer": 2, + "string": "two", + "float": 2.0 + }, + "variant": "two", + "flagMetadata": { + "allocationKey": "50/50 split", + "variationType": "object", + "doLog": true + } + } + }, + { + "flag": "json-config-flag", + "variationType": "JSON", + "defaultValue": { + "foo": "bar" + }, + "targetingKey": "charlie", + "attributes": { + "age": 50 + }, + "result": { + "value": { + "integer": 2, + "string": "two", + "float": 2.0 + }, + "variant": "two", + "flagMetadata": { + "allocationKey": "50/50 split", + "variationType": "object", + "doLog": true + } + } + }, + { + "flag": "json-config-flag", + "variationType": "JSON", + "defaultValue": { + "foo": "bar" + }, + "targetingKey": "diana", + "attributes": { + "Force Empty": true + }, + "result": { + "value": {}, + "variant": "empty", + "flagMetadata": { + "allocationKey": "Optionally Force Empty", + "variationType": "object", + "doLog": true + } + } + } +] diff --git a/spec/datadog/core/feature_flags/fixtures/test-no-allocations-flag.json b/spec/datadog/core/feature_flags/fixtures/test-no-allocations-flag.json new file mode 100644 index 00000000000..45867e5897c --- /dev/null +++ b/spec/datadog/core/feature_flags/fixtures/test-no-allocations-flag.json @@ -0,0 +1,52 @@ +[ + { + "flag": "no_allocations_flag", + "variationType": "JSON", + "defaultValue": { + "message": "Hello, world!" + }, + "targetingKey": "alice", + "attributes": { + "email": "alice@mycompany.com", + "country": "US" + }, + "result": { + "value": { + "message": "Hello, world!" + } + } + }, + { + "flag": "no_allocations_flag", + "variationType": "JSON", + "defaultValue": { + "message": "Hello, world!" + }, + "targetingKey": "bob", + "attributes": { + "email": "bob@example.com", + "country": "Canada" + }, + "result": { + "value": { + "message": "Hello, world!" + } + } + }, + { + "flag": "no_allocations_flag", + "variationType": "JSON", + "defaultValue": { + "message": "Hello, world!" + }, + "targetingKey": "charlie", + "attributes": { + "age": 50 + }, + "result": { + "value": { + "message": "Hello, world!" + } + } + } +] diff --git a/spec/datadog/core/feature_flags/fixtures/test-special-characters.json b/spec/datadog/core/feature_flags/fixtures/test-special-characters.json new file mode 100644 index 00000000000..59ef5cbe87d --- /dev/null +++ b/spec/datadog/core/feature_flags/fixtures/test-special-characters.json @@ -0,0 +1,78 @@ +[ + { + "flag": "special-characters", + "variationType": "JSON", + "defaultValue": {}, + "targetingKey": "ash", + "attributes": {}, + "result": { + "value": { + "a": "kümmert", + "b": "schön" + }, + "variant": "de", + "flagMetadata": { + "allocationKey": "allocation-test", + "variationType": "object", + "doLog": true + } + } + }, + { + "flag": "special-characters", + "variationType": "JSON", + "defaultValue": {}, + "targetingKey": "ben", + "attributes": {}, + "result": { + "value": { + "a": "піклуватися", + "b": "любов" + }, + "variant": "ua", + "flagMetadata": { + "allocationKey": "allocation-test", + "variationType": "object", + "doLog": true + } + } + }, + { + "flag": "special-characters", + "variationType": "JSON", + "defaultValue": {}, + "targetingKey": "cameron", + "attributes": {}, + "result": { + "value": { + "a": "照顾", + "b": "漂亮" + }, + "variant": "zh", + "flagMetadata": { + "allocationKey": "allocation-test", + "variationType": "object", + "doLog": true + } + } + }, + { + "flag": "special-characters", + "variationType": "JSON", + "defaultValue": {}, + "targetingKey": "darryl", + "attributes": {}, + "result": { + "value": { + "a": "🤗", + "b": "🌸" + }, + "variant": "emoji", + "flagMetadata": { + "allocationKey": "allocation-test", + "variationType": "object", + "doLog": true + } + } + } +] diff --git a/spec/datadog/core/feature_flags/fixtures/test-string-with-special-characters.json b/spec/datadog/core/feature_flags/fixtures/test-string-with-special-characters.json new file mode 100644 index 00000000000..27d063122c0 --- /dev/null +++ b/spec/datadog/core/feature_flags/fixtures/test-string-with-special-characters.json @@ -0,0 +1,1190 @@ +[ + { + "flag": "string_flag_with_special_characters", + "variationType": "STRING", + "defaultValue": "default_value", + "targetingKey": "string_with_spaces", + "attributes": { + "string_with_spaces": true + }, + "result": { + "value": " a b c d e f ", + "variant": "string_with_spaces", + "flagMetadata": { + "allocationKey": "allocation-test-string_with_spaces", + "variationType": "string", + "doLog": true + } + } + }, + { + "flag": "string_flag_with_special_characters", + "variationType": "STRING", + "defaultValue": "default_value", + "targetingKey": "string_with_only_one_space", + "attributes": { + "string_with_only_one_space": true + }, + "result": { + "value": " ", + "variant": "string_with_only_one_space", + "flagMetadata": { + "allocationKey": "allocation-test-string_with_only_one_space", + "variationType": "string", + "doLog": true + } + } + }, + { + "flag": "string_flag_with_special_characters", + "variationType": "STRING", + "defaultValue": "default_value", + "targetingKey": "string_with_only_multiple_spaces", + "attributes": { + "string_with_only_multiple_spaces": true + }, + "result": { + "value": " ", + "variant": "string_with_only_multiple_spaces", + "flagMetadata": { + "allocationKey": "allocation-test-string_with_only_multiple_spaces", + "variationType": "string", + "doLog": true + } + } + }, + { + "flag": "string_flag_with_special_characters", + "variationType": "STRING", + "defaultValue": "default_value", + "targetingKey": "string_with_dots", + "attributes": { + "string_with_dots": true + }, + "result": { + "value": ".a.b.c.d.e.f.", + "variant": "string_with_dots", + "flagMetadata": { + "allocationKey": "allocation-test-string_with_dots", + "variationType": "string", + "doLog": true + } + } + }, + { + "flag": "string_flag_with_special_characters", + "variationType": "STRING", + "defaultValue": "default_value", + "targetingKey": "string_with_only_one_dot", + "attributes": { + "string_with_only_one_dot": true + }, + "result": { + "value": ".", + "variant": "string_with_only_one_dot", + "flagMetadata": { + "allocationKey": "allocation-test-string_with_only_one_dot", + "variationType": "string", + "doLog": true + } + } + }, + { + "flag": "string_flag_with_special_characters", + "variationType": "STRING", + "defaultValue": "default_value", + "targetingKey": "string_with_only_multiple_dots", + "attributes": { + "string_with_only_multiple_dots": true + }, + "result": { + "value": ".......", + "variant": "string_with_only_multiple_dots", + "flagMetadata": { + "allocationKey": "allocation-test-string_with_only_multiple_dots", + "variationType": "string", + "doLog": true + } + } + }, + { + "flag": "string_flag_with_special_characters", + "variationType": "STRING", + "defaultValue": "default_value", + "targetingKey": "string_with_comas", + "attributes": { + "string_with_comas": true + }, + "result": { + "value": ",a,b,c,d,e,f,", + "variant": "string_with_comas", + "flagMetadata": { + "allocationKey": "allocation-test-string_with_comas", + "variationType": "string", + "doLog": true + } + } + }, + { + "flag": "string_flag_with_special_characters", + "variationType": "STRING", + "defaultValue": "default_value", + "targetingKey": "string_with_only_one_coma", + "attributes": { + "string_with_only_one_coma": true + }, + "result": { + "value": ",", + "variant": "string_with_only_one_coma", + "flagMetadata": { + "allocationKey": "allocation-test-string_with_only_one_coma", + "variationType": "string", + "doLog": true + } + } + }, + { + "flag": "string_flag_with_special_characters", + "variationType": "STRING", + "defaultValue": "default_value", + "targetingKey": "string_with_only_multiple_comas", + "attributes": { + "string_with_only_multiple_comas": true + }, + "result": { + "value": ",,,,,,,", + "variant": "string_with_only_multiple_comas", + "flagMetadata": { + "allocationKey": "allocation-test-string_with_only_multiple_comas", + "variationType": "string", + "doLog": true + } + } + }, + { + "flag": "string_flag_with_special_characters", + "variationType": "STRING", + "defaultValue": "default_value", + "targetingKey": "string_with_colons", + "attributes": { + "string_with_colons": true + }, + "result": { + "value": ":a:b:c:d:e:f:", + "variant": "string_with_colons", + "flagMetadata": { + "allocationKey": "allocation-test-string_with_colons", + "variationType": "string", + "doLog": true + } + } + }, + { + "flag": "string_flag_with_special_characters", + "variationType": "STRING", + "defaultValue": "default_value", + "targetingKey": "string_with_only_one_colon", + "attributes": { + "string_with_only_one_colon": true + }, + "result": { + "value": ":", + "variant": "string_with_only_one_colon", + "flagMetadata": { + "allocationKey": "allocation-test-string_with_only_one_colon", + "variationType": "string", + "doLog": true + } + } + }, + { + "flag": "string_flag_with_special_characters", + "variationType": "STRING", + "defaultValue": "default_value", + "targetingKey": "string_with_only_multiple_colons", + "attributes": { + "string_with_only_multiple_colons": true + }, + "result": { + "value": ":::::::", + "variant": "string_with_only_multiple_colons", + "flagMetadata": { + "allocationKey": "allocation-test-string_with_only_multiple_colons", + "variationType": "string", + "doLog": true + } + } + }, + { + "flag": "string_flag_with_special_characters", + "variationType": "STRING", + "defaultValue": "default_value", + "targetingKey": "string_with_semicolons", + "attributes": { + "string_with_semicolons": true + }, + "result": { + "value": ";a;b;c;d;e;f;", + "variant": "string_with_semicolons", + "flagMetadata": { + "allocationKey": "allocation-test-string_with_semicolons", + "variationType": "string", + "doLog": true + } + } + }, + { + "flag": "string_flag_with_special_characters", + "variationType": "STRING", + "defaultValue": "default_value", + "targetingKey": "string_with_only_one_semicolon", + "attributes": { + "string_with_only_one_semicolon": true + }, + "result": { + "value": ";", + "variant": "string_with_only_one_semicolon", + "flagMetadata": { + "allocationKey": "allocation-test-string_with_only_one_semicolon", + "variationType": "string", + "doLog": true + } + } + }, + { + "flag": "string_flag_with_special_characters", + "variationType": "STRING", + "defaultValue": "default_value", + "targetingKey": "string_with_only_multiple_semicolons", + "attributes": { + "string_with_only_multiple_semicolons": true + }, + "result": { + "value": ";;;;;;;", + "variant": "string_with_only_multiple_semicolons", + "flagMetadata": { + "allocationKey": "allocation-test-string_with_only_multiple_semicolons", + "variationType": "string", + "doLog": true + } + } + }, + { + "flag": "string_flag_with_special_characters", + "variationType": "STRING", + "defaultValue": "default_value", + "targetingKey": "string_with_slashes", + "attributes": { + "string_with_slashes": true + }, + "result": { + "value": "/a/b/c/d/e/f/", + "variant": "string_with_slashes", + "flagMetadata": { + "allocationKey": "allocation-test-string_with_slashes", + "variationType": "string", + "doLog": true + } + } + }, + { + "flag": "string_flag_with_special_characters", + "variationType": "STRING", + "defaultValue": "default_value", + "targetingKey": "string_with_only_one_slash", + "attributes": { + "string_with_only_one_slash": true + }, + "result": { + "value": "/", + "variant": "string_with_only_one_slash", + "flagMetadata": { + "allocationKey": "allocation-test-string_with_only_one_slash", + "variationType": "string", + "doLog": true + } + } + }, + { + "flag": "string_flag_with_special_characters", + "variationType": "STRING", + "defaultValue": "default_value", + "targetingKey": "string_with_only_multiple_slashes", + "attributes": { + "string_with_only_multiple_slashes": true + }, + "result": { + "value": "///////", + "variant": "string_with_only_multiple_slashes", + "flagMetadata": { + "allocationKey": "allocation-test-string_with_only_multiple_slashes", + "variationType": "string", + "doLog": true + } + } + }, + { + "flag": "string_flag_with_special_characters", + "variationType": "STRING", + "defaultValue": "default_value", + "targetingKey": "string_with_dashes", + "attributes": { + "string_with_dashes": true + }, + "result": { + "value": "-a-b-c-d-e-f-", + "variant": "string_with_dashes", + "flagMetadata": { + "allocationKey": "allocation-test-string_with_dashes", + "variationType": "string", + "doLog": true + } + } + }, + { + "flag": "string_flag_with_special_characters", + "variationType": "STRING", + "defaultValue": "default_value", + "targetingKey": "string_with_only_one_dash", + "attributes": { + "string_with_only_one_dash": true + }, + "result": { + "value": "-", + "variant": "string_with_only_one_dash", + "flagMetadata": { + "allocationKey": "allocation-test-string_with_only_one_dash", + "variationType": "string", + "doLog": true + } + } + }, + { + "flag": "string_flag_with_special_characters", + "variationType": "STRING", + "defaultValue": "default_value", + "targetingKey": "string_with_only_multiple_dashes", + "attributes": { + "string_with_only_multiple_dashes": true + }, + "result": { + "value": "-------", + "variant": "string_with_only_multiple_dashes", + "flagMetadata": { + "allocationKey": "allocation-test-string_with_only_multiple_dashes", + "variationType": "string", + "doLog": true + } + } + }, + { + "flag": "string_flag_with_special_characters", + "variationType": "STRING", + "defaultValue": "default_value", + "targetingKey": "string_with_underscores", + "attributes": { + "string_with_underscores": true + }, + "result": { + "value": "_a_b_c_d_e_f_", + "variant": "string_with_underscores", + "flagMetadata": { + "allocationKey": "allocation-test-string_with_underscores", + "variationType": "string", + "doLog": true + } + } + }, + { + "flag": "string_flag_with_special_characters", + "variationType": "STRING", + "defaultValue": "default_value", + "targetingKey": "string_with_only_one_underscore", + "attributes": { + "string_with_only_one_underscore": true + }, + "result": { + "value": "_", + "variant": "string_with_only_one_underscore", + "flagMetadata": { + "allocationKey": "allocation-test-string_with_only_one_underscore", + "variationType": "string", + "doLog": true + } + } + }, + { + "flag": "string_flag_with_special_characters", + "variationType": "STRING", + "defaultValue": "default_value", + "targetingKey": "string_with_only_multiple_underscores", + "attributes": { + "string_with_only_multiple_underscores": true + }, + "result": { + "value": "_______", + "variant": "string_with_only_multiple_underscores", + "flagMetadata": { + "allocationKey": "allocation-test-string_with_only_multiple_underscores", + "variationType": "string", + "doLog": true + } + } + }, + { + "flag": "string_flag_with_special_characters", + "variationType": "STRING", + "defaultValue": "default_value", + "targetingKey": "string_with_plus_signs", + "attributes": { + "string_with_plus_signs": true + }, + "result": { + "value": "+a+b+c+d+e+f+", + "variant": "string_with_plus_signs", + "flagMetadata": { + "allocationKey": "allocation-test-string_with_plus_signs", + "variationType": "string", + "doLog": true + } + } + }, + { + "flag": "string_flag_with_special_characters", + "variationType": "STRING", + "defaultValue": "default_value", + "targetingKey": "string_with_only_one_plus_sign", + "attributes": { + "string_with_only_one_plus_sign": true + }, + "result": { + "value": "+", + "variant": "string_with_only_one_plus_sign", + "flagMetadata": { + "allocationKey": "allocation-test-string_with_only_one_plus_sign", + "variationType": "string", + "doLog": true + } + } + }, + { + "flag": "string_flag_with_special_characters", + "variationType": "STRING", + "defaultValue": "default_value", + "targetingKey": "string_with_only_multiple_plus_signs", + "attributes": { + "string_with_only_multiple_plus_signs": true + }, + "result": { + "value": "+++++++", + "variant": "string_with_only_multiple_plus_signs", + "flagMetadata": { + "allocationKey": "allocation-test-string_with_only_multiple_plus_signs", + "variationType": "string", + "doLog": true + } + } + }, + { + "flag": "string_flag_with_special_characters", + "variationType": "STRING", + "defaultValue": "default_value", + "targetingKey": "string_with_equal_signs", + "attributes": { + "string_with_equal_signs": true + }, + "result": { + "value": "=a=b=c=d=e=f=", + "variant": "string_with_equal_signs", + "flagMetadata": { + "allocationKey": "allocation-test-string_with_equal_signs", + "variationType": "string", + "doLog": true + } + } + }, + { + "flag": "string_flag_with_special_characters", + "variationType": "STRING", + "defaultValue": "default_value", + "targetingKey": "string_with_only_one_equal_sign", + "attributes": { + "string_with_only_one_equal_sign": true + }, + "result": { + "value": "=", + "variant": "string_with_only_one_equal_sign", + "flagMetadata": { + "allocationKey": "allocation-test-string_with_only_one_equal_sign", + "variationType": "string", + "doLog": true + } + } + }, + { + "flag": "string_flag_with_special_characters", + "variationType": "STRING", + "defaultValue": "default_value", + "targetingKey": "string_with_only_multiple_equal_signs", + "attributes": { + "string_with_only_multiple_equal_signs": true + }, + "result": { + "value": "=======", + "variant": "string_with_only_multiple_equal_signs", + "flagMetadata": { + "allocationKey": "allocation-test-string_with_only_multiple_equal_signs", + "variationType": "string", + "doLog": true + } + } + }, + { + "flag": "string_flag_with_special_characters", + "variationType": "STRING", + "defaultValue": "default_value", + "targetingKey": "string_with_dollar_signs", + "attributes": { + "string_with_dollar_signs": true + }, + "result": { + "value": "$a$b$c$d$e$f$", + "variant": "string_with_dollar_signs", + "flagMetadata": { + "allocationKey": "allocation-test-string_with_dollar_signs", + "variationType": "string", + "doLog": true + } + } + }, + { + "flag": "string_flag_with_special_characters", + "variationType": "STRING", + "defaultValue": "default_value", + "targetingKey": "string_with_only_one_dollar_sign", + "attributes": { + "string_with_only_one_dollar_sign": true + }, + "result": { + "value": "$", + "variant": "string_with_only_one_dollar_sign", + "flagMetadata": { + "allocationKey": "allocation-test-string_with_only_one_dollar_sign", + "variationType": "string", + "doLog": true + } + } + }, + { + "flag": "string_flag_with_special_characters", + "variationType": "STRING", + "defaultValue": "default_value", + "targetingKey": "string_with_only_multiple_dollar_signs", + "attributes": { + "string_with_only_multiple_dollar_signs": true + }, + "result": { + "value": "$$$$$$$", + "variant": "string_with_only_multiple_dollar_signs", + "flagMetadata": { + "allocationKey": "allocation-test-string_with_only_multiple_dollar_signs", + "variationType": "string", + "doLog": true + } + } + }, + { + "flag": "string_flag_with_special_characters", + "variationType": "STRING", + "defaultValue": "default_value", + "targetingKey": "string_with_at_signs", + "attributes": { + "string_with_at_signs": true + }, + "result": { + "value": "@a@b@c@d@e@f@", + "variant": "string_with_at_signs", + "flagMetadata": { + "allocationKey": "allocation-test-string_with_at_signs", + "variationType": "string", + "doLog": true + } + } + }, + { + "flag": "string_flag_with_special_characters", + "variationType": "STRING", + "defaultValue": "default_value", + "targetingKey": "string_with_only_one_at_sign", + "attributes": { + "string_with_only_one_at_sign": true + }, + "result": { + "value": "@", + "variant": "string_with_only_one_at_sign", + "flagMetadata": { + "allocationKey": "allocation-test-string_with_only_one_at_sign", + "variationType": "string", + "doLog": true + } + } + }, + { + "flag": "string_flag_with_special_characters", + "variationType": "STRING", + "defaultValue": "default_value", + "targetingKey": "string_with_only_multiple_at_signs", + "attributes": { + "string_with_only_multiple_at_signs": true + }, + "result": { + "value": "@@@@@@@", + "variant": "string_with_only_multiple_at_signs", + "flagMetadata": { + "allocationKey": "allocation-test-string_with_only_multiple_at_signs", + "variationType": "string", + "doLog": true + } + } + }, + { + "flag": "string_flag_with_special_characters", + "variationType": "STRING", + "defaultValue": "default_value", + "targetingKey": "string_with_amp_signs", + "attributes": { + "string_with_amp_signs": true + }, + "result": { + "value": "&a&b&c&d&e&f&", + "variant": "string_with_amp_signs", + "flagMetadata": { + "allocationKey": "allocation-test-string_with_amp_signs", + "variationType": "string", + "doLog": true + } + } + }, + { + "flag": "string_flag_with_special_characters", + "variationType": "STRING", + "defaultValue": "default_value", + "targetingKey": "string_with_only_one_amp_sign", + "attributes": { + "string_with_only_one_amp_sign": true + }, + "result": { + "value": "&", + "variant": "string_with_only_one_amp_sign", + "flagMetadata": { + "allocationKey": "allocation-test-string_with_only_one_amp_sign", + "variationType": "string", + "doLog": true + } + } + }, + { + "flag": "string_flag_with_special_characters", + "variationType": "STRING", + "defaultValue": "default_value", + "targetingKey": "string_with_only_multiple_amp_signs", + "attributes": { + "string_with_only_multiple_amp_signs": true + }, + "result": { + "value": "&&&&&&&", + "variant": "string_with_only_multiple_amp_signs", + "flagMetadata": { + "allocationKey": "allocation-test-string_with_only_multiple_amp_signs", + "variationType": "string", + "doLog": true + } + } + }, + { + "flag": "string_flag_with_special_characters", + "variationType": "STRING", + "defaultValue": "default_value", + "targetingKey": "string_with_hash_signs", + "attributes": { + "string_with_hash_signs": true + }, + "result": { + "value": "#a#b#c#d#e#f#", + "variant": "string_with_hash_signs", + "flagMetadata": { + "allocationKey": "allocation-test-string_with_hash_signs", + "variationType": "string", + "doLog": true + } + } + }, + { + "flag": "string_flag_with_special_characters", + "variationType": "STRING", + "defaultValue": "default_value", + "targetingKey": "string_with_only_one_hash_sign", + "attributes": { + "string_with_only_one_hash_sign": true + }, + "result": { + "value": "#", + "variant": "string_with_only_one_hash_sign", + "flagMetadata": { + "allocationKey": "allocation-test-string_with_only_one_hash_sign", + "variationType": "string", + "doLog": true + } + } + }, + { + "flag": "string_flag_with_special_characters", + "variationType": "STRING", + "defaultValue": "default_value", + "targetingKey": "string_with_only_multiple_hash_signs", + "attributes": { + "string_with_only_multiple_hash_signs": true + }, + "result": { + "value": "#######", + "variant": "string_with_only_multiple_hash_signs", + "flagMetadata": { + "allocationKey": "allocation-test-string_with_only_multiple_hash_signs", + "variationType": "string", + "doLog": true + } + } + }, + { + "flag": "string_flag_with_special_characters", + "variationType": "STRING", + "defaultValue": "default_value", + "targetingKey": "string_with_percentage_signs", + "attributes": { + "string_with_percentage_signs": true + }, + "result": { + "value": "%a%b%c%d%e%f%", + "variant": "string_with_percentage_signs", + "flagMetadata": { + "allocationKey": "allocation-test-string_with_percentage_signs", + "variationType": "string", + "doLog": true + } + } + }, + { + "flag": "string_flag_with_special_characters", + "variationType": "STRING", + "defaultValue": "default_value", + "targetingKey": "string_with_only_one_percentage_sign", + "attributes": { + "string_with_only_one_percentage_sign": true + }, + "result": { + "value": "%", + "variant": "string_with_only_one_percentage_sign", + "flagMetadata": { + "allocationKey": "allocation-test-string_with_only_one_percentage_sign", + "variationType": "string", + "doLog": true + } + } + }, + { + "flag": "string_flag_with_special_characters", + "variationType": "STRING", + "defaultValue": "default_value", + "targetingKey": "string_with_only_multiple_percentage_signs", + "attributes": { + "string_with_only_multiple_percentage_signs": true + }, + "result": { + "value": "%%%%%%%", + "variant": "string_with_only_multiple_percentage_signs", + "flagMetadata": { + "allocationKey": "allocation-test-string_with_only_multiple_percentage_signs", + "variationType": "string", + "doLog": true + } + } + }, + { + "flag": "string_flag_with_special_characters", + "variationType": "STRING", + "defaultValue": "default_value", + "targetingKey": "string_with_tilde_signs", + "attributes": { + "string_with_tilde_signs": true + }, + "result": { + "value": "~a~b~c~d~e~f~", + "variant": "string_with_tilde_signs", + "flagMetadata": { + "allocationKey": "allocation-test-string_with_tilde_signs", + "variationType": "string", + "doLog": true + } + } + }, + { + "flag": "string_flag_with_special_characters", + "variationType": "STRING", + "defaultValue": "default_value", + "targetingKey": "string_with_only_one_tilde_sign", + "attributes": { + "string_with_only_one_tilde_sign": true + }, + "result": { + "value": "~", + "variant": "string_with_only_one_tilde_sign", + "flagMetadata": { + "allocationKey": "allocation-test-string_with_only_one_tilde_sign", + "variationType": "string", + "doLog": true + } + } + }, + { + "flag": "string_flag_with_special_characters", + "variationType": "STRING", + "defaultValue": "default_value", + "targetingKey": "string_with_only_multiple_tilde_signs", + "attributes": { + "string_with_only_multiple_tilde_signs": true + }, + "result": { + "value": "~~~~~~~", + "variant": "string_with_only_multiple_tilde_signs", + "flagMetadata": { + "allocationKey": "allocation-test-string_with_only_multiple_tilde_signs", + "variationType": "string", + "doLog": true + } + } + }, + { + "flag": "string_flag_with_special_characters", + "variationType": "STRING", + "defaultValue": "default_value", + "targetingKey": "string_with_asterix_signs", + "attributes": { + "string_with_asterix_signs": true + }, + "result": { + "value": "*a*b*c*d*e*f*", + "variant": "string_with_asterix_signs", + "flagMetadata": { + "allocationKey": "allocation-test-string_with_asterix_signs", + "variationType": "string", + "doLog": true + } + } + }, + { + "flag": "string_flag_with_special_characters", + "variationType": "STRING", + "defaultValue": "default_value", + "targetingKey": "string_with_only_one_asterix_sign", + "attributes": { + "string_with_only_one_asterix_sign": true + }, + "result": { + "value": "*", + "variant": "string_with_only_one_asterix_sign", + "flagMetadata": { + "allocationKey": "allocation-test-string_with_only_one_asterix_sign", + "variationType": "string", + "doLog": true + } + } + }, + { + "flag": "string_flag_with_special_characters", + "variationType": "STRING", + "defaultValue": "default_value", + "targetingKey": "string_with_only_multiple_asterix_signs", + "attributes": { + "string_with_only_multiple_asterix_signs": true + }, + "result": { + "value": "*******", + "variant": "string_with_only_multiple_asterix_signs", + "flagMetadata": { + "allocationKey": "allocation-test-string_with_only_multiple_asterix_signs", + "variationType": "string", + "doLog": true + } + } + }, + { + "flag": "string_flag_with_special_characters", + "variationType": "STRING", + "defaultValue": "default_value", + "targetingKey": "string_with_single_quotes", + "attributes": { + "string_with_single_quotes": true + }, + "result": { + "value": "'a'b'c'd'e'f'", + "variant": "string_with_single_quotes", + "flagMetadata": { + "allocationKey": "allocation-test-string_with_single_quotes", + "variationType": "string", + "doLog": true + } + } + }, + { + "flag": "string_flag_with_special_characters", + "variationType": "STRING", + "defaultValue": "default_value", + "targetingKey": "string_with_only_one_single_quote", + "attributes": { + "string_with_only_one_single_quote": true + }, + "result": { + "value": "'", + "variant": "string_with_only_one_single_quote", + "flagMetadata": { + "allocationKey": "allocation-test-string_with_only_one_single_quote", + "variationType": "string", + "doLog": true + } + } + }, + { + "flag": "string_flag_with_special_characters", + "variationType": "STRING", + "defaultValue": "default_value", + "targetingKey": "string_with_only_multiple_single_quotes", + "attributes": { + "string_with_only_multiple_single_quotes": true + }, + "result": { + "value": "'''''''", + "variant": "string_with_only_multiple_single_quotes", + "flagMetadata": { + "allocationKey": "allocation-test-string_with_only_multiple_single_quotes", + "variationType": "string", + "doLog": true + } + } + }, + { + "flag": "string_flag_with_special_characters", + "variationType": "STRING", + "defaultValue": "default_value", + "targetingKey": "string_with_question_marks", + "attributes": { + "string_with_question_marks": true + }, + "result": { + "value": "?a?b?c?d?e?f?", + "variant": "string_with_question_marks", + "flagMetadata": { + "allocationKey": "allocation-test-string_with_question_marks", + "variationType": "string", + "doLog": true + } + } + }, + { + "flag": "string_flag_with_special_characters", + "variationType": "STRING", + "defaultValue": "default_value", + "targetingKey": "string_with_only_one_question_mark", + "attributes": { + "string_with_only_one_question_mark": true + }, + "result": { + "value": "?", + "variant": "string_with_only_one_question_mark", + "flagMetadata": { + "allocationKey": "allocation-test-string_with_only_one_question_mark", + "variationType": "string", + "doLog": true + } + } + }, + { + "flag": "string_flag_with_special_characters", + "variationType": "STRING", + "defaultValue": "default_value", + "targetingKey": "string_with_only_multiple_question_marks", + "attributes": { + "string_with_only_multiple_question_marks": true + }, + "result": { + "value": "???????", + "variant": "string_with_only_multiple_question_marks", + "flagMetadata": { + "allocationKey": "allocation-test-string_with_only_multiple_question_marks", + "variationType": "string", + "doLog": true + } + } + }, + { + "flag": "string_flag_with_special_characters", + "variationType": "STRING", + "defaultValue": "default_value", + "targetingKey": "string_with_exclamation_marks", + "attributes": { + "string_with_exclamation_marks": true + }, + "result": { + "value": "!a!b!c!d!e!f!", + "variant": "string_with_exclamation_marks", + "flagMetadata": { + "allocationKey": "allocation-test-string_with_exclamation_marks", + "variationType": "string", + "doLog": true + } + } + }, + { + "flag": "string_flag_with_special_characters", + "variationType": "STRING", + "defaultValue": "default_value", + "targetingKey": "string_with_only_one_exclamation_mark", + "attributes": { + "string_with_only_one_exclamation_mark": true + }, + "result": { + "value": "!", + "variant": "string_with_only_one_exclamation_mark", + "flagMetadata": { + "allocationKey": "allocation-test-string_with_only_one_exclamation_mark", + "variationType": "string", + "doLog": true + } + } + }, + { + "flag": "string_flag_with_special_characters", + "variationType": "STRING", + "defaultValue": "default_value", + "targetingKey": "string_with_only_multiple_exclamation_marks", + "attributes": { + "string_with_only_multiple_exclamation_marks": true + }, + "result": { + "value": "!!!!!!!", + "variant": "string_with_only_multiple_exclamation_marks", + "flagMetadata": { + "allocationKey": "allocation-test-string_with_only_multiple_exclamation_marks", + "variationType": "string", + "doLog": true + } + } + }, + { + "flag": "string_flag_with_special_characters", + "variationType": "STRING", + "defaultValue": "default_value", + "targetingKey": "string_with_opening_parentheses", + "attributes": { + "string_with_opening_parentheses": true + }, + "result": { + "value": "(a(b(c(d(e(f(", + "variant": "string_with_opening_parentheses", + "flagMetadata": { + "allocationKey": "allocation-test-string_with_opening_parentheses", + "variationType": "string", + "doLog": true + } + } + }, + { + "flag": "string_flag_with_special_characters", + "variationType": "STRING", + "defaultValue": "default_value", + "targetingKey": "string_with_only_one_opening_parenthese", + "attributes": { + "string_with_only_one_opening_parenthese": true + }, + "result": { + "value": "(", + "variant": "string_with_only_one_opening_parenthese", + "flagMetadata": { + "allocationKey": "allocation-test-string_with_only_one_opening_parenthese", + "variationType": "string", + "doLog": true + } + } + }, + { + "flag": "string_flag_with_special_characters", + "variationType": "STRING", + "defaultValue": "default_value", + "targetingKey": "string_with_only_multiple_opening_parentheses", + "attributes": { + "string_with_only_multiple_opening_parentheses": true + }, + "result": { + "value": "(((((((", + "variant": "string_with_only_multiple_opening_parentheses", + "flagMetadata": { + "allocationKey": "allocation-test-string_with_only_multiple_opening_parentheses", + "variationType": "string", + "doLog": true + } + } + }, + { + "flag": "string_flag_with_special_characters", + "variationType": "STRING", + "defaultValue": "default_value", + "targetingKey": "string_with_closing_parentheses", + "attributes": { + "string_with_closing_parentheses": true + }, + "result": { + "value": ")a)b)c)d)e)f)", + "variant": "string_with_closing_parentheses", + "flagMetadata": { + "allocationKey": "allocation-test-string_with_closing_parentheses", + "variationType": "string", + "doLog": true + } + } + }, + { + "flag": "string_flag_with_special_characters", + "variationType": "STRING", + "defaultValue": "default_value", + "targetingKey": "string_with_only_one_closing_parenthese", + "attributes": { + "string_with_only_one_closing_parenthese": true + }, + "result": { + "value": ")", + "variant": "string_with_only_one_closing_parenthese", + "flagMetadata": { + "allocationKey": "allocation-test-string_with_only_one_closing_parenthese", + "variationType": "string", + "doLog": true + } + } + }, + { + "flag": "string_flag_with_special_characters", + "variationType": "STRING", + "defaultValue": "default_value", + "targetingKey": "string_with_only_multiple_closing_parentheses", + "attributes": { + "string_with_only_multiple_closing_parentheses": true + }, + "result": { + "value": ")))))))", + "variant": "string_with_only_multiple_closing_parentheses", + "flagMetadata": { + "allocationKey": "allocation-test-string_with_only_multiple_closing_parentheses", + "variationType": "string", + "doLog": true + } + } + } +] diff --git a/spec/datadog/core/feature_flags/flag_types_spec.rb b/spec/datadog/core/feature_flags/flag_types_spec.rb new file mode 100644 index 00000000000..d9bada27fe0 --- /dev/null +++ b/spec/datadog/core/feature_flags/flag_types_spec.rb @@ -0,0 +1,247 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe 'Feature Flags Flag Types', :feature_flags do + include FeatureFlagsHelpers + with_feature_flags_extension + + let(:config) { create_test_configuration } + let(:test_context) { build_user_context('test-user') } + + # Test flag type constants if they exist (from enhanced implementation) + describe 'Flag Type Constants' do + context 'when enhanced flag types are available' do + it 'defines flag type constants' do + if defined?(Datadog::Core::FeatureFlags::BOOLEAN) + expect(Datadog::Core::FeatureFlags::BOOLEAN).to eq(:boolean) + expect(Datadog::Core::FeatureFlags::STRING).to eq(:string) + expect(Datadog::Core::FeatureFlags::NUMBER).to eq(:number) + expect(Datadog::Core::FeatureFlags::OBJECT).to eq(:object) + else + skip 'Enhanced flag type constants not available' + end + end + end + end + + describe 'get_assignment method signatures' do + context 'original implementation (2 parameters)' do + it 'works with flag_key and context' do + result = config.get_assignment('empty_flag', test_context) + expect_valid_resolution_details(result) + end + end + + context 'enhanced implementation (3 parameters)' do + it 'works with flag_key, context, and flag_type' do + if defined?(Datadog::Core::FeatureFlags::STRING) + result = config.get_assignment('empty_flag', test_context, Datadog::Core::FeatureFlags::STRING) + expect_valid_resolution_details(result) + else + skip 'Enhanced 3-parameter method not available' + end + end + end + end + + describe 'Boolean flag evaluation' do + let(:boolean_test_cases) do + load_fixture_json('test-case-boolean-one-of-matches.json') + end + + it 'evaluates boolean attributes correctly' do + boolean_test_cases.each do |test_case| + context = build_user_context(test_case['targetingKey'], test_case['attributes'] || {}) + + # Test with original method + result = config.get_assignment(test_case['flag'], context) + expect_valid_resolution_details(result) + + # Test with enhanced method if available + if defined?(Datadog::Core::FeatureFlags::BOOLEAN) + result_enhanced = config.get_assignment(test_case['flag'], context, Datadog::Core::FeatureFlags::BOOLEAN) + expect_valid_resolution_details(result_enhanced) + + # Results should be consistent between methods + expect(result_enhanced.value).to eq(result.value) + end + + # Validate against expected result + if test_case['result'] + validate_test_case_result(result, test_case['result'], + "Boolean test for #{test_case['targetingKey']}") + end + end + end + end + + describe 'JSON/Object flag evaluation' do + let(:json_test_file) { File.join(__dir__, 'fixtures', 'test-json-config-flag.json') } + + context 'when JSON test cases are available' do + it 'evaluates JSON flags correctly' do + skip 'test-json-config-flag.json not found' unless File.exist?(json_test_file) + + json_test_cases = JSON.parse(File.read(json_test_file)) + + json_test_cases.each do |test_case| + context = build_user_context(test_case['targetingKey'], test_case['attributes'] || {}) + + # Test with original method + result = config.get_assignment(test_case['flag'], context) + expect_valid_resolution_details(result) + + # Test with enhanced method if available + if defined?(Datadog::Core::FeatureFlags::OBJECT) + result_enhanced = config.get_assignment(test_case['flag'], context, Datadog::Core::FeatureFlags::OBJECT) + expect_valid_resolution_details(result_enhanced) + end + + # Validate against expected result + if test_case['result'] && test_case['result']['value'] + expected_value = test_case['result']['value'] + if expected_value.is_a?(Hash) + expect_json_flag_result(result, expected_value) + end + end + end + end + end + end + + describe 'Integer flag evaluation' do + let(:integer_test_file) { File.join(__dir__, 'fixtures', 'test-case-integer-flag.json') } + + context 'when integer test cases are available' do + it 'evaluates integer flags correctly' do + skip 'test-case-integer-flag.json not found' unless File.exist?(integer_test_file) + + integer_test_cases = JSON.parse(File.read(integer_test_file)) + + integer_test_cases.each do |test_case| + context = build_user_context(test_case['targetingKey'], test_case['attributes'] || {}) + + # Test with original method + result = config.get_assignment(test_case['flag'], context) + expect_valid_resolution_details(result) + + # Test with enhanced method if available + if defined?(Datadog::Core::FeatureFlags::NUMBER) + result_enhanced = config.get_assignment(test_case['flag'], context, Datadog::Core::FeatureFlags::NUMBER) + expect_valid_resolution_details(result_enhanced) + end + + # Validate against expected result + if test_case['result'] + validate_test_case_result(result, test_case['result'], + "Integer test for #{test_case['targetingKey']}") + + if test_case['result']['value'].is_a?(Integer) + expect_numeric_flag_result(result, test_case['result']['value']) + end + end + end + end + end + end + + describe 'String flag evaluation' do + # Use one of the available test cases that returns strings + it 'evaluates string flags correctly' do + # Test basic string evaluation + result = config.get_assignment('empty_flag', test_context) + expect_valid_resolution_details(result) + + # Test with enhanced method if available + if defined?(Datadog::Core::FeatureFlags::STRING) + result_enhanced = config.get_assignment('empty_flag', test_context, Datadog::Core::FeatureFlags::STRING) + expect_valid_resolution_details(result_enhanced) + + # Results should be consistent + expect(result_enhanced.value).to eq(result.value) + expect(result_enhanced.reason).to eq(result.reason) + end + end + end + + describe 'Backward compatibility' do + it 'maintains compatibility with original API' do + # Original 2-parameter method should always work + result = config.get_assignment('empty_flag', test_context) + expect_valid_resolution_details(result) + expect_openfeature_compliance(result) + end + + context 'when enhanced API is available' do + it 'provides same results with default flag type' do + if defined?(Datadog::Core::FeatureFlags::STRING) + result_original = config.get_assignment('empty_flag', test_context) + result_enhanced = config.get_assignment('empty_flag', test_context, Datadog::Core::FeatureFlags::STRING) + + expect(result_enhanced.value).to eq(result_original.value) + expect(result_enhanced.reason).to eq(result_original.reason) + expect(result_enhanced.error?).to eq(result_original.error?) + end + end + end + end + + describe 'Error? predicate method' do + it 'correctly identifies error states' do + # Test with non-existent flag (should error) + result = config.get_assignment('definitely_does_not_exist', test_context) + expect_valid_resolution_details(result) + + # Should have error? method available + expect(result).to respond_to(:error?) + + if result.error_code + expect(result.error?).to be_truthy + else + expect(result.error?).to be_falsey + end + end + + it 'returns false for successful evaluations' do + result = config.get_assignment('empty_flag', test_context) + expect_valid_resolution_details(result) + + if result.error_code.nil? + expect(result.error?).to be_falsey + end + end + end + + describe 'OpenFeature compliance' do + it 'returns uppercase string constants for all evaluation types' do + test_flags = ['empty_flag', 'disabled_flag'] + + test_flags.each do |flag| + result = config.get_assignment(flag, test_context) + expect_valid_resolution_details(result) + expect_openfeature_compliance(result) + end + end + end + + describe 'Memory safety under load' do + it 'handles rapid evaluations without memory issues' do + # This tests the GC safety improvements + 100.times do |i| + context = build_user_context("user-#{i}", { "attribute_#{i % 10}" => "value_#{i}" }) + result = config.get_assignment('empty_flag', context) + expect_valid_resolution_details(result) + end + end + + it 'handles large context objects safely' do + # This tests the optimized hash iteration + large_context = build_user_context('test-user') + 100.times { |i| large_context["large_attr_#{i}"] = "large_value_#{i}_#{'x' * 100}" } + + result = config.get_assignment('empty_flag', large_context) + expect_valid_resolution_details(result) + end + end +end \ No newline at end of file diff --git a/spec/datadog/core/feature_flags/test_case_runner_spec.rb b/spec/datadog/core/feature_flags/test_case_runner_spec.rb new file mode 100644 index 00000000000..4f4573ae354 --- /dev/null +++ b/spec/datadog/core/feature_flags/test_case_runner_spec.rb @@ -0,0 +1,211 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe 'Datadog Feature Flags Test Cases', :feature_flags do + before do + # Skip tests if libdatadog_api extension is not available + skip 'libdatadog_api extension not available' unless defined?(Datadog::Core::FeatureFlags) + end + + let(:fixtures_path) { File.join(__dir__, 'fixtures') } + let(:main_config_path) { File.join(fixtures_path, 'flags-v1.json') } + let(:main_config_json) { File.read(main_config_path) } + let(:config) { Datadog::Core::FeatureFlags::Configuration.new(main_config_json) } + + # Helper method to run a single test case + def run_test_case(test_case, test_file_name) + flag_key = test_case['flag'] + targeting_key = test_case['targetingKey'] + attributes = test_case['attributes'] || {} + expected_result = test_case['result'] + + # Build context + context = {} + context['targeting_key'] = targeting_key if targeting_key + context.merge!(attributes) + + # Get assignment + result = config.get_assignment(flag_key, context) + + # Create description for better error messages + test_description = "#{test_file_name}: #{flag_key} for #{targeting_key || 'anonymous'}" + + if expected_result + # Validate expected result + expect(result.value).to eq(expected_result['value']), + "#{test_description} - Expected value #{expected_result['value']}, got #{result.value}" + + if expected_result['variant'] + expect(result.variant).to eq(expected_result['variant']), + "#{test_description} - Expected variant #{expected_result['variant']}, got #{result.variant}" + end + + if expected_result['flagMetadata'] + metadata = expected_result['flagMetadata'] + + if metadata['allocationKey'] + expect(result.allocation_key).to eq(metadata['allocationKey']), + "#{test_description} - Expected allocation key #{metadata['allocationKey']}, got #{result.allocation_key}" + end + + if metadata.key?('doLog') + expect(result.log?).to eq(metadata['doLog']), + "#{test_description} - Expected doLog #{metadata['doLog']}, got #{result.log?}" + end + end + + # Should not be an error case + expect(result.error?).to be_falsey, + "#{test_description} - Expected no error, but got error: #{result.error_code} - #{result.error_message}" + else + # Test expects no specific result (usually default behavior) + # This is typically for cases where targeting rules don't match + test_default_value = test_case['defaultValue'] + + # The result should be the default value or nil, and should not be an error + if test_default_value + expect([test_default_value, nil]).to include(result.value), + "#{test_description} - Expected default value #{test_default_value} or nil, got #{result.value}" + end + end + + result + end + + # Process all test case files + Dir.glob(File.join(fixtures_path, 'test-case-*.json')).each do |test_file| + test_file_name = File.basename(test_file, '.json') + + describe test_file_name do + let(:test_cases) { JSON.parse(File.read(test_file)) } + + it "passes all test cases" do + test_cases.each_with_index do |test_case, index| + # Run each test case and capture any failures + begin + run_test_case(test_case, "#{test_file_name}[#{index}]") + rescue RSpec::Expectations::ExpectationNotMetError => e + # Re-raise with additional context + raise RSpec::Expectations::ExpectationNotMetError, + "Test case #{index + 1} in #{test_file_name} failed: #{e.message}" + end + end + end + + # Also run individual test cases for better granular reporting + test_cases = JSON.parse(File.read(test_file)) + test_cases.each_with_index do |test_case, index| + context "case #{index + 1}: #{test_case['targetingKey'] || 'anonymous'}" do + it "evaluates #{test_case['flag']} correctly" do + run_test_case(test_case, test_file_name) + end + end + end + end + end + + describe 'Flag type specific validations' do + describe 'Boolean flags' do + it 'handles boolean attribute matching correctly' do + test_file = File.join(fixtures_path, 'test-case-boolean-one-of-matches.json') + test_cases = JSON.parse(File.read(test_file)) + + boolean_cases = test_cases.select { |tc| tc['attributes']&.values&.any? { |v| [true, false].include?(v) } } + expect(boolean_cases).not_to be_empty, "No boolean test cases found" + + boolean_cases.each do |test_case| + result = run_test_case(test_case, 'boolean-validation') + + # Ensure we get a proper result + expect(result).to be_a(Datadog::Core::FeatureFlags::ResolutionDetails) + end + end + end + + describe 'JSON flags' do + it 'handles JSON/object flags correctly' do + test_file = File.join(fixtures_path, 'test-json-config-flag.json') + skip "test-json-config-flag.json not found" unless File.exist?(test_file) + + test_cases = JSON.parse(File.read(test_file)) + + test_cases.each do |test_case| + result = run_test_case(test_case, 'json-validation') + + # JSON flags should return object values + if test_case['result'] && test_case['result']['value'].is_a?(Hash) + expect(result.value).to be_a(Hash).or(be_a(String)), + "JSON flag should return Hash or JSON string" + end + end + end + end + + describe 'Integer flags' do + it 'handles integer flags correctly' do + test_file = File.join(fixtures_path, 'test-case-integer-flag.json') + skip "test-case-integer-flag.json not found" unless File.exist?(test_file) + + test_cases = JSON.parse(File.read(test_file)) + + test_cases.each do |test_case| + result = run_test_case(test_case, 'integer-validation') + + # Integer flags should return numeric values + if test_case['result'] && test_case['result']['value'].is_a?(Integer) + expect(result.value).to be_a(Integer), + "Integer flag should return Integer" + end + end + end + end + end + + describe 'Edge cases' do + describe 'Disabled flags' do + it 'handles disabled flag correctly' do + test_file = File.join(fixtures_path, 'test-case-disabled-flag.json') + skip "test-case-disabled-flag.json not found" unless File.exist?(test_file) + + test_cases = JSON.parse(File.read(test_file)) + test_cases.each { |tc| run_test_case(tc, 'disabled-flag') } + end + end + + describe 'Empty flags' do + it 'handles empty flag correctly' do + test_file = File.join(fixtures_path, 'test-case-empty-flag.json') + skip "test-case-empty-flag.json not found" unless File.exist?(test_file) + + test_cases = JSON.parse(File.read(test_file)) + test_cases.each { |tc| run_test_case(tc, 'empty-flag') } + end + end + + describe 'Kill switch flags' do + it 'handles kill switch correctly' do + test_file = File.join(fixtures_path, 'test-case-kill-switch-flag.json') + skip "test-case-kill-switch-flag.json not found" unless File.exist?(test_file) + + test_cases = JSON.parse(File.read(test_file)) + test_cases.each { |tc| run_test_case(tc, 'kill-switch') } + end + end + end + + describe 'Error handling' do + it 'provides meaningful errors for invalid configurations' do + expect do + Datadog::Core::FeatureFlags::Configuration.new('{"invalid": json}') + end.to raise_error(RuntimeError, /Failed to create configuration/) + end + + it 'handles non-existent flags gracefully' do + result = config.get_assignment('definitely_does_not_exist', { 'targeting_key' => 'user' }) + + # Should either return an error or a default value, but not crash + expect(result).to be_a(Datadog::Core::FeatureFlags::ResolutionDetails) + end + end +end \ No newline at end of file diff --git a/spec/datadog/core/feature_flags_integration_spec.rb b/spec/datadog/core/feature_flags_integration_spec.rb new file mode 100644 index 00000000000..ee6fe30b9dd --- /dev/null +++ b/spec/datadog/core/feature_flags_integration_spec.rb @@ -0,0 +1,142 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe 'Feature Flags Integration Tests', :feature_flags do + before do + # Skip all feature flag tests if the extension is not available + skip 'libdatadog_api extension not available' unless defined?(Datadog::Core::FeatureFlags) + end + + describe 'Extension availability' do + it 'loads the libdatadog_api extension successfully' do + expect(defined?(Datadog::Core::FeatureFlags)).to be_truthy + expect(defined?(Datadog::Core::FeatureFlags::Configuration)).to be_truthy + expect(defined?(Datadog::Core::FeatureFlags::ResolutionDetails)).to be_truthy + end + + it 'has working Configuration class' do + expect(Datadog::Core::FeatureFlags::Configuration).to respond_to(:new) + end + + it 'has working ResolutionDetails methods' do + # Create a minimal test to ensure the extension loaded properly + simple_config = { "flags" => {} }.to_json + + begin + config = Datadog::Core::FeatureFlags::Configuration.new(simple_config) + result = config.get_assignment('test_flag', { 'targeting_key' => 'test' }) + + expect(result).to be_a(Datadog::Core::FeatureFlags::ResolutionDetails) + expect(result).to respond_to(:value, :reason, :error_code, :error_message, :variant, :allocation_key, :log?) + + # Test the enhanced error? method if available + if result.respond_to?(:error?) + expect(result.error?).to be_in([true, false]) + end + rescue => e + # If configuration creation fails, that's also useful information + puts "Configuration creation failed: #{e.message}" + puts "This might indicate libdatadog version mismatch or missing dependencies" + end + end + end + + describe 'Test data availability' do + let(:fixtures_dir) { File.join(__dir__, 'feature_flags', 'fixtures') } + + it 'has the main configuration file' do + config_file = File.join(fixtures_dir, 'flags-v1.json') + expect(File.exist?(config_file)).to be_truthy + expect(File.size(config_file)).to be > 1000 # Should be a substantial file + end + + it 'has test case files' do + test_files = Dir.glob(File.join(fixtures_dir, 'test-case-*.json')) + expect(test_files.length).to be > 10 # Should have multiple test case files + + # Verify some key test files exist + expected_files = [ + 'test-case-boolean-one-of-matches.json', + 'test-case-disabled-flag.json', + 'test-case-empty-flag.json' + ] + + expected_files.each do |filename| + file_path = File.join(fixtures_dir, filename) + expect(File.exist?(file_path)).to be_truthy, "Missing test file: #{filename}" + end + end + + it 'can parse all test files as valid JSON' do + test_files = Dir.glob(File.join(fixtures_dir, '*.json')) + + test_files.each do |file_path| + expect { JSON.parse(File.read(file_path)) }.not_to raise_error, + "Invalid JSON in #{File.basename(file_path)}" + end + end + end + + describe 'Test execution summary' do + let(:fixtures_dir) { File.join(__dir__, 'feature_flags', 'fixtures') } + + it 'counts total test cases available' do + total_cases = 0 + test_files = Dir.glob(File.join(fixtures_dir, 'test-case-*.json')) + + test_files.each do |file_path| + test_data = JSON.parse(File.read(file_path)) + case_count = test_data.is_a?(Array) ? test_data.length : 1 + total_cases += case_count + end + + puts "\nFeature Flags Test Summary:" + puts " Test files: #{test_files.length}" + puts " Total test cases: #{total_cases}" + puts " Main config file: #{File.exist?(File.join(fixtures_dir, 'flags-v1.json')) ? 'Present' : 'Missing'}" + + expect(total_cases).to be > 50 # Should have plenty of test cases + end + end + + describe 'Quick smoke test' do + let(:fixtures_dir) { File.join(__dir__, 'feature_flags', 'fixtures') } + let(:main_config) { JSON.parse(File.read(File.join(fixtures_dir, 'flags-v1.json'))) } + let(:config) { Datadog::Core::FeatureFlags::Configuration.new(main_config.to_json) } + + it 'can run a basic evaluation without errors' do + result = config.get_assignment('empty_flag', { 'targeting_key' => 'smoke-test-user' }) + + expect(result).to be_a(Datadog::Core::FeatureFlags::ResolutionDetails) + + # Basic method availability test + expect(result.value).to be_a(Object) # Could be any type or nil + expect(result.reason).to be_a(String).or(be_nil) + expect(result.error_code).to be_a(String).or(be_nil) + + puts "Smoke test result:" + puts " Value: #{result.value.inspect}" + puts " Reason: #{result.reason}" + puts " Error?: #{result.error? if result.respond_to?(:error?)}" + end + + it 'demonstrates enhanced API if available' do + if defined?(Datadog::Core::FeatureFlags::STRING) + puts "\nEnhanced API available:" + puts " BOOLEAN = #{Datadog::Core::FeatureFlags::BOOLEAN}" + puts " STRING = #{Datadog::Core::FeatureFlags::STRING}" + puts " NUMBER = #{Datadog::Core::FeatureFlags::NUMBER}" + puts " OBJECT = #{Datadog::Core::FeatureFlags::OBJECT}" + + # Test 3-parameter method + result = config.get_assignment('empty_flag', { 'targeting_key' => 'test' }, Datadog::Core::FeatureFlags::STRING) + expect(result).to be_a(Datadog::Core::FeatureFlags::ResolutionDetails) + + puts " Enhanced method works: ✓" + else + puts "\nOriginal API only (2-parameter method)" + end + end + end +end \ No newline at end of file diff --git a/spec/support/feature_flags_helpers.rb b/spec/support/feature_flags_helpers.rb new file mode 100644 index 00000000000..1187fa84ec7 --- /dev/null +++ b/spec/support/feature_flags_helpers.rb @@ -0,0 +1,161 @@ +# frozen_string_literal: true + +module FeatureFlagsHelpers + def self.included(base) + base.extend(ClassMethods) + end + + module ClassMethods + def with_feature_flags_extension + before do + # Skip tests if libdatadog_api extension is not available + skip 'libdatadog_api extension not available' unless defined?(Datadog::Core::FeatureFlags) + end + end + end + + def feature_flags_available? + defined?(Datadog::Core::FeatureFlags) + end + + def load_fixture_json(filename) + fixture_path = File.join(__dir__, '..', 'datadog', 'core', 'feature_flags', 'fixtures', filename) + JSON.parse(File.read(fixture_path)) + end + + def main_test_config + @main_test_config ||= load_fixture_json('flags-v1.json') + end + + def create_test_configuration + Datadog::Core::FeatureFlags::Configuration.new(main_test_config.to_json) + end + + def evaluate_flag(config, flag_key, context = {}) + default_context = { 'targeting_key' => 'test-user' } + full_context = default_context.merge(context) + config.get_assignment(flag_key, full_context) + end + + def expect_valid_resolution_details(result) + expect(result).to be_a(Datadog::Core::FeatureFlags::ResolutionDetails) + expect(result).to respond_to(:value) + expect(result).to respond_to(:reason) + expect(result).to respond_to(:error_code) + expect(result).to respond_to(:error_message) + expect(result).to respond_to(:error?) + expect(result).to respond_to(:variant) + expect(result).to respond_to(:allocation_key) + expect(result).to respond_to(:log?) + end + + def expect_successful_evaluation(result, expected_value = nil) + expect_valid_resolution_details(result) + expect(result.error?).to be_falsey + expect(result.value).to eq(expected_value) if expected_value + end + + def expect_error_evaluation(result, expected_error_code = nil) + expect_valid_resolution_details(result) + expect(result.error?).to be_truthy + expect(result.error_code).to eq(expected_error_code) if expected_error_code + end + + def expect_openfeature_compliance(result) + # Reasons should be uppercase strings + unless result.reason.nil? + expect(result.reason).to be_a(String) + expect(result.reason).to match(/^[A-Z_]+$/) + end + + # Error codes should be uppercase strings + unless result.error_code.nil? + expect(result.error_code).to be_a(String) + expect(result.error_code).to match(/^[A-Z_]+$/) + end + end + + # Test data validation helpers + def validate_test_case_result(actual_result, expected_result, context_description = '') + if expected_result + expect(actual_result.value).to eq(expected_result['value']), + "#{context_description} - Value mismatch" + + if expected_result['variant'] + expect(actual_result.variant).to eq(expected_result['variant']), + "#{context_description} - Variant mismatch" + end + + if expected_result['flagMetadata'] + metadata = expected_result['flagMetadata'] + + if metadata['allocationKey'] + expect(actual_result.allocation_key).to eq(metadata['allocationKey']), + "#{context_description} - Allocation key mismatch" + end + + if metadata.key?('doLog') + expect(actual_result.log?).to eq(metadata['doLog']), + "#{context_description} - DoLog flag mismatch" + end + end + + expect(actual_result.error?).to be_falsey, + "#{context_description} - Unexpected error: #{actual_result.error_code}" + else + # No expected result usually means default behavior + expect(actual_result).to be_a(Datadog::Core::FeatureFlags::ResolutionDetails), + "#{context_description} - Should return ResolutionDetails" + end + end + + # Context builders for common test scenarios + def build_user_context(targeting_key, additional_attrs = {}) + context = { 'targeting_key' => targeting_key.to_s } + context.merge!(additional_attrs.transform_keys(&:to_s)) + context + end + + def build_anonymous_context(additional_attrs = {}) + additional_attrs.transform_keys(&:to_s) + end + + # Flag type test helpers + def expect_boolean_flag_result(result, expected_bool) + expect_successful_evaluation(result) + expect(result.value).to be_in([true, false]) + expect(result.value).to eq(expected_bool) if expected_bool + end + + def expect_string_flag_result(result, expected_string = nil) + expect_successful_evaluation(result) + expect(result.value).to be_a(String).or(be_nil) + expect(result.value).to eq(expected_string) if expected_string + end + + def expect_numeric_flag_result(result, expected_number = nil) + expect_successful_evaluation(result) + expect(result.value).to be_a(Numeric).or(be_nil) + expect(result.value).to eq(expected_number) if expected_number + end + + def expect_json_flag_result(result, expected_json = nil) + expect_successful_evaluation(result) + # JSON flags can return Hash objects or JSON strings + expect(result.value).to be_a(Hash).or(be_a(String)).or(be_nil) + + if expected_json + if result.value.is_a?(String) + parsed_value = JSON.parse(result.value) rescue result.value + expect(parsed_value).to eq(expected_json) + else + expect(result.value).to eq(expected_json) + end + end + end +end + +# Add to RSpec configuration +RSpec.configure do |config| + config.include FeatureFlagsHelpers +end \ No newline at end of file