prefab-cloud-ruby 0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
 - data/.envrc.sample +3 -0
 - data/.github/workflows/ruby.yml +46 -0
 - data/.gitmodules +3 -0
 - data/.rubocop.yml +13 -0
 - data/.tool-versions +1 -0
 - data/CHANGELOG.md +169 -0
 - data/CODEOWNERS +1 -0
 - data/Gemfile +26 -0
 - data/Gemfile.lock +188 -0
 - data/LICENSE.txt +20 -0
 - data/README.md +94 -0
 - data/Rakefile +50 -0
 - data/VERSION +1 -0
 - data/bin/console +21 -0
 - data/compile_protos.sh +18 -0
 - data/lib/prefab/client.rb +153 -0
 - data/lib/prefab/config_client.rb +292 -0
 - data/lib/prefab/config_client_presenter.rb +18 -0
 - data/lib/prefab/config_loader.rb +84 -0
 - data/lib/prefab/config_resolver.rb +77 -0
 - data/lib/prefab/config_value_unwrapper.rb +115 -0
 - data/lib/prefab/config_value_wrapper.rb +18 -0
 - data/lib/prefab/context.rb +179 -0
 - data/lib/prefab/context_shape.rb +20 -0
 - data/lib/prefab/context_shape_aggregator.rb +65 -0
 - data/lib/prefab/criteria_evaluator.rb +136 -0
 - data/lib/prefab/encryption.rb +65 -0
 - data/lib/prefab/error.rb +6 -0
 - data/lib/prefab/errors/env_var_parse_error.rb +11 -0
 - data/lib/prefab/errors/initialization_timeout_error.rb +13 -0
 - data/lib/prefab/errors/invalid_api_key_error.rb +19 -0
 - data/lib/prefab/errors/missing_default_error.rb +13 -0
 - data/lib/prefab/errors/missing_env_var_error.rb +11 -0
 - data/lib/prefab/errors/uninitialized_error.rb +13 -0
 - data/lib/prefab/evaluation.rb +52 -0
 - data/lib/prefab/evaluation_summary_aggregator.rb +87 -0
 - data/lib/prefab/example_contexts_aggregator.rb +78 -0
 - data/lib/prefab/exponential_backoff.rb +21 -0
 - data/lib/prefab/feature_flag_client.rb +42 -0
 - data/lib/prefab/http_connection.rb +41 -0
 - data/lib/prefab/internal_logger.rb +16 -0
 - data/lib/prefab/local_config_parser.rb +151 -0
 - data/lib/prefab/log_path_aggregator.rb +69 -0
 - data/lib/prefab/logger_client.rb +264 -0
 - data/lib/prefab/murmer3.rb +50 -0
 - data/lib/prefab/options.rb +208 -0
 - data/lib/prefab/periodic_sync.rb +69 -0
 - data/lib/prefab/prefab.rb +56 -0
 - data/lib/prefab/rate_limit_cache.rb +41 -0
 - data/lib/prefab/resolved_config_presenter.rb +86 -0
 - data/lib/prefab/time_helpers.rb +7 -0
 - data/lib/prefab/weighted_value_resolver.rb +42 -0
 - data/lib/prefab/yaml_config_parser.rb +34 -0
 - data/lib/prefab-cloud-ruby.rb +57 -0
 - data/lib/prefab_pb.rb +93 -0
 - data/prefab-cloud-ruby.gemspec +155 -0
 - data/test/.prefab.default.config.yaml +2 -0
 - data/test/.prefab.unit_tests.config.yaml +28 -0
 - data/test/integration_test.rb +150 -0
 - data/test/integration_test_helpers.rb +151 -0
 - data/test/support/common_helpers.rb +180 -0
 - data/test/support/mock_base_client.rb +42 -0
 - data/test/support/mock_config_client.rb +19 -0
 - data/test/support/mock_config_loader.rb +1 -0
 - data/test/test_client.rb +444 -0
 - data/test/test_config_client.rb +109 -0
 - data/test/test_config_loader.rb +117 -0
 - data/test/test_config_resolver.rb +430 -0
 - data/test/test_config_value_unwrapper.rb +224 -0
 - data/test/test_config_value_wrapper.rb +42 -0
 - data/test/test_context.rb +203 -0
 - data/test/test_context_shape.rb +50 -0
 - data/test/test_context_shape_aggregator.rb +147 -0
 - data/test/test_criteria_evaluator.rb +726 -0
 - data/test/test_encryption.rb +16 -0
 - data/test/test_evaluation_summary_aggregator.rb +162 -0
 - data/test/test_example_contexts_aggregator.rb +238 -0
 - data/test/test_exponential_backoff.rb +18 -0
 - data/test/test_feature_flag_client.rb +48 -0
 - data/test/test_helper.rb +17 -0
 - data/test/test_integration.rb +58 -0
 - data/test/test_local_config_parser.rb +147 -0
 - data/test/test_log_path_aggregator.rb +62 -0
 - data/test/test_logger.rb +621 -0
 - data/test/test_logger_initialization.rb +12 -0
 - data/test/test_options.rb +75 -0
 - data/test/test_prefab.rb +12 -0
 - data/test/test_rate_limit_cache.rb +44 -0
 - data/test/test_weighted_value_resolver.rb +71 -0
 - metadata +337 -0
 
| 
         @@ -0,0 +1,16 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            require 'test_helper'
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            class TestEncryption < Minitest::Test
         
     | 
| 
      
 6 
     | 
    
         
            +
              def test_encryption
         
     | 
| 
      
 7 
     | 
    
         
            +
                secret = Prefab::Encryption.generate_new_hex_key
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
                enc = Prefab::Encryption.new(secret)
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
                clear_text = "hello world"
         
     | 
| 
      
 12 
     | 
    
         
            +
                encrypted = enc.encrypt(clear_text)
         
     | 
| 
      
 13 
     | 
    
         
            +
                decrypted = enc.decrypt(encrypted)
         
     | 
| 
      
 14 
     | 
    
         
            +
                assert_equal clear_text, decrypted
         
     | 
| 
      
 15 
     | 
    
         
            +
              end
         
     | 
| 
      
 16 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,162 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            require 'test_helper'
         
     | 
| 
      
 4 
     | 
    
         
            +
            require 'timecop'
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
            class TestEvaluationSummaryAggregator < Minitest::Test
         
     | 
| 
      
 7 
     | 
    
         
            +
              EFFECTIVELY_NEVER = 99_999 # we sync manually
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
              EXAMPLE_VALUE_1 = PrefabProto::ConfigValue.new(bool: true)
         
     | 
| 
      
 10 
     | 
    
         
            +
              EXAMPLE_VALUE_2 = PrefabProto::ConfigValue.new(bool: false)
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
              EXAMPLE_COUNTER = {
         
     | 
| 
      
 13 
     | 
    
         
            +
                config_id: 1,
         
     | 
| 
      
 14 
     | 
    
         
            +
                selected_index: 2,
         
     | 
| 
      
 15 
     | 
    
         
            +
                config_row_index: 3,
         
     | 
| 
      
 16 
     | 
    
         
            +
                conditional_value_index: 4,
         
     | 
| 
      
 17 
     | 
    
         
            +
                weighted_value_index: 5,
         
     | 
| 
      
 18 
     | 
    
         
            +
                seleced_value: EXAMPLE_VALUE_1
         
     | 
| 
      
 19 
     | 
    
         
            +
              }.freeze
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
              def test_increments_counts
         
     | 
| 
      
 22 
     | 
    
         
            +
                aggregator = Prefab::EvaluationSummaryAggregator.new(client: MockBaseClient.new, max_keys: 10,
         
     | 
| 
      
 23 
     | 
    
         
            +
                                                                     sync_interval: EFFECTIVELY_NEVER)
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
                aggregator.record(config_key: 'foo', config_type: 'bar', counter: EXAMPLE_COUNTER)
         
     | 
| 
      
 26 
     | 
    
         
            +
             
     | 
| 
      
 27 
     | 
    
         
            +
                assert_equal 1, aggregator.data[%w[foo bar]][EXAMPLE_COUNTER]
         
     | 
| 
      
 28 
     | 
    
         
            +
             
     | 
| 
      
 29 
     | 
    
         
            +
                2.times { aggregator.record(config_key: 'foo', config_type: 'bar', counter: EXAMPLE_COUNTER) }
         
     | 
| 
      
 30 
     | 
    
         
            +
                assert_equal 3, aggregator.data[%w[foo bar]][EXAMPLE_COUNTER]
         
     | 
| 
      
 31 
     | 
    
         
            +
             
     | 
| 
      
 32 
     | 
    
         
            +
                another_counter = EXAMPLE_COUNTER.merge(selected_index: EXAMPLE_COUNTER[:selected_index] + 1)
         
     | 
| 
      
 33 
     | 
    
         
            +
             
     | 
| 
      
 34 
     | 
    
         
            +
                aggregator.record(config_key: 'foo', config_type: 'bar', counter: another_counter)
         
     | 
| 
      
 35 
     | 
    
         
            +
                assert_equal 3, aggregator.data[%w[foo bar]][EXAMPLE_COUNTER]
         
     | 
| 
      
 36 
     | 
    
         
            +
                assert_equal 1, aggregator.data[%w[foo bar]][another_counter]
         
     | 
| 
      
 37 
     | 
    
         
            +
              end
         
     | 
| 
      
 38 
     | 
    
         
            +
             
     | 
| 
      
 39 
     | 
    
         
            +
              def test_prepare_data
         
     | 
| 
      
 40 
     | 
    
         
            +
                aggregator = Prefab::EvaluationSummaryAggregator.new(client: MockBaseClient.new, max_keys: 10,
         
     | 
| 
      
 41 
     | 
    
         
            +
                                                                     sync_interval: EFFECTIVELY_NEVER)
         
     | 
| 
      
 42 
     | 
    
         
            +
             
     | 
| 
      
 43 
     | 
    
         
            +
                expected = {
         
     | 
| 
      
 44 
     | 
    
         
            +
                  ['config-1', :CONFIG] => {
         
     | 
| 
      
 45 
     | 
    
         
            +
                    { config_id: 1, selected_index: 2, config_row_index: 3, conditional_value_index: 4,
         
     | 
| 
      
 46 
     | 
    
         
            +
                      weighted_value_index: 5, selected_value: EXAMPLE_VALUE_1 } => 3,
         
     | 
| 
      
 47 
     | 
    
         
            +
                    { config_id: 1, selected_index: 3, config_row_index: 7, conditional_value_index: 8,
         
     | 
| 
      
 48 
     | 
    
         
            +
                      weighted_value_index: 10, selected_value: EXAMPLE_VALUE_2 } => 1
         
     | 
| 
      
 49 
     | 
    
         
            +
                  },
         
     | 
| 
      
 50 
     | 
    
         
            +
                  ['config-2', :FEATURE_FLAG] => {
         
     | 
| 
      
 51 
     | 
    
         
            +
                    { config_id: 2, selected_index: 3, config_row_index: 5, conditional_value_index: 7,
         
     | 
| 
      
 52 
     | 
    
         
            +
                      weighted_value_index: 6, selected_value: EXAMPLE_VALUE_1 } => 9
         
     | 
| 
      
 53 
     | 
    
         
            +
                  }
         
     | 
| 
      
 54 
     | 
    
         
            +
                }
         
     | 
| 
      
 55 
     | 
    
         
            +
             
     | 
| 
      
 56 
     | 
    
         
            +
                add_example_data(aggregator)
         
     | 
| 
      
 57 
     | 
    
         
            +
                assert_equal expected, aggregator.prepare_data
         
     | 
| 
      
 58 
     | 
    
         
            +
                assert aggregator.data.empty?
         
     | 
| 
      
 59 
     | 
    
         
            +
              end
         
     | 
| 
      
 60 
     | 
    
         
            +
             
     | 
| 
      
 61 
     | 
    
         
            +
              def test_sync
         
     | 
| 
      
 62 
     | 
    
         
            +
                awhile_ago = Time.now - 60
         
     | 
| 
      
 63 
     | 
    
         
            +
                now = Time.now
         
     | 
| 
      
 64 
     | 
    
         
            +
             
     | 
| 
      
 65 
     | 
    
         
            +
                client = MockBaseClient.new
         
     | 
| 
      
 66 
     | 
    
         
            +
             
     | 
| 
      
 67 
     | 
    
         
            +
                aggregator = nil
         
     | 
| 
      
 68 
     | 
    
         
            +
             
     | 
| 
      
 69 
     | 
    
         
            +
                Timecop.freeze(awhile_ago) do
         
     | 
| 
      
 70 
     | 
    
         
            +
                  # start the aggregator in the past
         
     | 
| 
      
 71 
     | 
    
         
            +
                  aggregator = Prefab::EvaluationSummaryAggregator.new(client: client, max_keys: 10,
         
     | 
| 
      
 72 
     | 
    
         
            +
                                                                       sync_interval: EFFECTIVELY_NEVER)
         
     | 
| 
      
 73 
     | 
    
         
            +
                end
         
     | 
| 
      
 74 
     | 
    
         
            +
             
     | 
| 
      
 75 
     | 
    
         
            +
                add_example_data(aggregator)
         
     | 
| 
      
 76 
     | 
    
         
            +
             
     | 
| 
      
 77 
     | 
    
         
            +
                expected_post = PrefabProto::TelemetryEvents.new(
         
     | 
| 
      
 78 
     | 
    
         
            +
                  instance_hash: client.instance_hash,
         
     | 
| 
      
 79 
     | 
    
         
            +
                  events: [
         
     | 
| 
      
 80 
     | 
    
         
            +
                    PrefabProto::TelemetryEvent.new(
         
     | 
| 
      
 81 
     | 
    
         
            +
                      summaries:
         
     | 
| 
      
 82 
     | 
    
         
            +
             
     | 
| 
      
 83 
     | 
    
         
            +
                  PrefabProto::ConfigEvaluationSummaries.new(
         
     | 
| 
      
 84 
     | 
    
         
            +
                    start: awhile_ago.to_i * 1000,
         
     | 
| 
      
 85 
     | 
    
         
            +
                    end: now.to_i * 1000,
         
     | 
| 
      
 86 
     | 
    
         
            +
                    summaries: [
         
     | 
| 
      
 87 
     | 
    
         
            +
                      PrefabProto::ConfigEvaluationSummary.new(
         
     | 
| 
      
 88 
     | 
    
         
            +
                        key: 'config-1',
         
     | 
| 
      
 89 
     | 
    
         
            +
                        type: :CONFIG,
         
     | 
| 
      
 90 
     | 
    
         
            +
                        counters: [
         
     | 
| 
      
 91 
     | 
    
         
            +
                          PrefabProto::ConfigEvaluationCounter.new(
         
     | 
| 
      
 92 
     | 
    
         
            +
                            config_id: 1,
         
     | 
| 
      
 93 
     | 
    
         
            +
                            selected_index: 2,
         
     | 
| 
      
 94 
     | 
    
         
            +
                            config_row_index: 3,
         
     | 
| 
      
 95 
     | 
    
         
            +
                            conditional_value_index: 4,
         
     | 
| 
      
 96 
     | 
    
         
            +
                            weighted_value_index: 5,
         
     | 
| 
      
 97 
     | 
    
         
            +
                            selected_value: EXAMPLE_VALUE_1,
         
     | 
| 
      
 98 
     | 
    
         
            +
                            count: 3
         
     | 
| 
      
 99 
     | 
    
         
            +
                          ),
         
     | 
| 
      
 100 
     | 
    
         
            +
                          PrefabProto::ConfigEvaluationCounter.new(
         
     | 
| 
      
 101 
     | 
    
         
            +
                            config_id: 1,
         
     | 
| 
      
 102 
     | 
    
         
            +
                            selected_index: 3,
         
     | 
| 
      
 103 
     | 
    
         
            +
                            config_row_index: 7,
         
     | 
| 
      
 104 
     | 
    
         
            +
                            conditional_value_index: 8,
         
     | 
| 
      
 105 
     | 
    
         
            +
                            weighted_value_index: 10,
         
     | 
| 
      
 106 
     | 
    
         
            +
                            selected_value: EXAMPLE_VALUE_2,
         
     | 
| 
      
 107 
     | 
    
         
            +
                            count: 1
         
     | 
| 
      
 108 
     | 
    
         
            +
                          )
         
     | 
| 
      
 109 
     | 
    
         
            +
                        ]
         
     | 
| 
      
 110 
     | 
    
         
            +
                      ),
         
     | 
| 
      
 111 
     | 
    
         
            +
                      PrefabProto::ConfigEvaluationSummary.new(
         
     | 
| 
      
 112 
     | 
    
         
            +
                        key: 'config-2',
         
     | 
| 
      
 113 
     | 
    
         
            +
                        type: :FEATURE_FLAG,
         
     | 
| 
      
 114 
     | 
    
         
            +
                        counters: [
         
     | 
| 
      
 115 
     | 
    
         
            +
                          PrefabProto::ConfigEvaluationCounter.new(
         
     | 
| 
      
 116 
     | 
    
         
            +
                            config_id: 2,
         
     | 
| 
      
 117 
     | 
    
         
            +
                            selected_index: 3,
         
     | 
| 
      
 118 
     | 
    
         
            +
                            config_row_index: 5,
         
     | 
| 
      
 119 
     | 
    
         
            +
                            conditional_value_index: 7,
         
     | 
| 
      
 120 
     | 
    
         
            +
                            weighted_value_index: 6,
         
     | 
| 
      
 121 
     | 
    
         
            +
                            selected_value: EXAMPLE_VALUE_1,
         
     | 
| 
      
 122 
     | 
    
         
            +
                            count: 9
         
     | 
| 
      
 123 
     | 
    
         
            +
                          )
         
     | 
| 
      
 124 
     | 
    
         
            +
                        ]
         
     | 
| 
      
 125 
     | 
    
         
            +
                      )
         
     | 
| 
      
 126 
     | 
    
         
            +
                    ]
         
     | 
| 
      
 127 
     | 
    
         
            +
                  )
         
     | 
| 
      
 128 
     | 
    
         
            +
                    )
         
     | 
| 
      
 129 
     | 
    
         
            +
                  ]
         
     | 
| 
      
 130 
     | 
    
         
            +
                )
         
     | 
| 
      
 131 
     | 
    
         
            +
             
     | 
| 
      
 132 
     | 
    
         
            +
                requests = wait_for_post_requests(client) do
         
     | 
| 
      
 133 
     | 
    
         
            +
                  Timecop.freeze(now) do
         
     | 
| 
      
 134 
     | 
    
         
            +
                    aggregator.sync
         
     | 
| 
      
 135 
     | 
    
         
            +
                  end
         
     | 
| 
      
 136 
     | 
    
         
            +
                end
         
     | 
| 
      
 137 
     | 
    
         
            +
             
     | 
| 
      
 138 
     | 
    
         
            +
                assert_equal [[
         
     | 
| 
      
 139 
     | 
    
         
            +
                  '/api/v1/telemetry',
         
     | 
| 
      
 140 
     | 
    
         
            +
                  expected_post
         
     | 
| 
      
 141 
     | 
    
         
            +
                ]], requests
         
     | 
| 
      
 142 
     | 
    
         
            +
              end
         
     | 
| 
      
 143 
     | 
    
         
            +
             
     | 
| 
      
 144 
     | 
    
         
            +
              private
         
     | 
| 
      
 145 
     | 
    
         
            +
             
     | 
| 
      
 146 
     | 
    
         
            +
              def add_example_data(aggregator)
         
     | 
| 
      
 147 
     | 
    
         
            +
                data = {
         
     | 
| 
      
 148 
     | 
    
         
            +
                  ['config-1', :CONFIG] => {
         
     | 
| 
      
 149 
     | 
    
         
            +
                    { config_id: 1, selected_index: 2, config_row_index: 3, conditional_value_index: 4,
         
     | 
| 
      
 150 
     | 
    
         
            +
                      weighted_value_index: 5, selected_value: EXAMPLE_VALUE_1 } => 3,
         
     | 
| 
      
 151 
     | 
    
         
            +
                    { config_id: 1, selected_index: 3, config_row_index: 7, conditional_value_index: 8,
         
     | 
| 
      
 152 
     | 
    
         
            +
                      weighted_value_index: 10, selected_value: EXAMPLE_VALUE_2 } => 1
         
     | 
| 
      
 153 
     | 
    
         
            +
                  },
         
     | 
| 
      
 154 
     | 
    
         
            +
                  ['config-2', :FEATURE_FLAG] => {
         
     | 
| 
      
 155 
     | 
    
         
            +
                    { config_id: 2, selected_index: 3, config_row_index: 5, conditional_value_index: 7,
         
     | 
| 
      
 156 
     | 
    
         
            +
                      weighted_value_index: 6, selected_value: EXAMPLE_VALUE_1 } => 9
         
     | 
| 
      
 157 
     | 
    
         
            +
                  }
         
     | 
| 
      
 158 
     | 
    
         
            +
                }
         
     | 
| 
      
 159 
     | 
    
         
            +
             
     | 
| 
      
 160 
     | 
    
         
            +
                aggregator.instance_variable_set('@data', data)
         
     | 
| 
      
 161 
     | 
    
         
            +
              end
         
     | 
| 
      
 162 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,238 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            require 'test_helper'
         
     | 
| 
      
 4 
     | 
    
         
            +
            require 'timecop'
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
            class TestExampleContextsAggregator < Minitest::Test
         
     | 
| 
      
 7 
     | 
    
         
            +
              EFFECTIVELY_NEVER = 99_999 # we sync manually
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
              def test_record
         
     | 
| 
      
 10 
     | 
    
         
            +
                aggregator = Prefab::ExampleContextsAggregator.new(client: MockBaseClient.new, max_contexts: 2,
         
     | 
| 
      
 11 
     | 
    
         
            +
                                                                   sync_interval: EFFECTIVELY_NEVER)
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
                context = Prefab::Context.new(
         
     | 
| 
      
 14 
     | 
    
         
            +
                  user: { key: 'abc' },
         
     | 
| 
      
 15 
     | 
    
         
            +
                  device: { key: 'def', mobile: true }
         
     | 
| 
      
 16 
     | 
    
         
            +
                )
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
                aggregator.record(context)
         
     | 
| 
      
 19 
     | 
    
         
            +
                assert_equal [context], aggregator.data
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
                # This doesn't get updated because we already have a context for this user/device
         
     | 
| 
      
 22 
     | 
    
         
            +
                aggregator.record(context)
         
     | 
| 
      
 23 
     | 
    
         
            +
                assert_equal [context], aggregator.data
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
                new_context = Prefab::Context.new(
         
     | 
| 
      
 26 
     | 
    
         
            +
                  user: { key: 'ghi', admin: true },
         
     | 
| 
      
 27 
     | 
    
         
            +
                  team: { key: '999' }
         
     | 
| 
      
 28 
     | 
    
         
            +
                )
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
      
 30 
     | 
    
         
            +
                aggregator.record(new_context)
         
     | 
| 
      
 31 
     | 
    
         
            +
                assert_equal [context, new_context], aggregator.data
         
     | 
| 
      
 32 
     | 
    
         
            +
             
     | 
| 
      
 33 
     | 
    
         
            +
                # this doesn't get recorded because we're at max_contexts
         
     | 
| 
      
 34 
     | 
    
         
            +
                aggregator.record(Prefab::Context.new(user: { key: 'new' }))
         
     | 
| 
      
 35 
     | 
    
         
            +
                assert_equal [context, new_context], aggregator.data
         
     | 
| 
      
 36 
     | 
    
         
            +
              end
         
     | 
| 
      
 37 
     | 
    
         
            +
             
     | 
| 
      
 38 
     | 
    
         
            +
              def test_prepare_data
         
     | 
| 
      
 39 
     | 
    
         
            +
                aggregator = Prefab::ExampleContextsAggregator.new(client: MockBaseClient.new, max_contexts: 10,
         
     | 
| 
      
 40 
     | 
    
         
            +
                                                                   sync_interval: EFFECTIVELY_NEVER)
         
     | 
| 
      
 41 
     | 
    
         
            +
             
     | 
| 
      
 42 
     | 
    
         
            +
                context = Prefab::Context.new(
         
     | 
| 
      
 43 
     | 
    
         
            +
                  user: { key: 'abc' },
         
     | 
| 
      
 44 
     | 
    
         
            +
                  device: { key: 'def', mobile: true }
         
     | 
| 
      
 45 
     | 
    
         
            +
                )
         
     | 
| 
      
 46 
     | 
    
         
            +
             
     | 
| 
      
 47 
     | 
    
         
            +
                aggregator.record(context)
         
     | 
| 
      
 48 
     | 
    
         
            +
             
     | 
| 
      
 49 
     | 
    
         
            +
                assert_equal [context], aggregator.prepare_data
         
     | 
| 
      
 50 
     | 
    
         
            +
                assert aggregator.data.empty?
         
     | 
| 
      
 51 
     | 
    
         
            +
              end
         
     | 
| 
      
 52 
     | 
    
         
            +
             
     | 
| 
      
 53 
     | 
    
         
            +
              def test_record_with_expiry
         
     | 
| 
      
 54 
     | 
    
         
            +
                aggregator = Prefab::ExampleContextsAggregator.new(client: MockBaseClient.new, max_contexts: 10,
         
     | 
| 
      
 55 
     | 
    
         
            +
                                                                   sync_interval: EFFECTIVELY_NEVER)
         
     | 
| 
      
 56 
     | 
    
         
            +
             
     | 
| 
      
 57 
     | 
    
         
            +
                context = Prefab::Context.new(
         
     | 
| 
      
 58 
     | 
    
         
            +
                  user: { key: 'abc' },
         
     | 
| 
      
 59 
     | 
    
         
            +
                  device: { key: 'def', mobile: true }
         
     | 
| 
      
 60 
     | 
    
         
            +
                )
         
     | 
| 
      
 61 
     | 
    
         
            +
             
     | 
| 
      
 62 
     | 
    
         
            +
                aggregator.record(context)
         
     | 
| 
      
 63 
     | 
    
         
            +
             
     | 
| 
      
 64 
     | 
    
         
            +
                assert_equal [context], aggregator.data
         
     | 
| 
      
 65 
     | 
    
         
            +
             
     | 
| 
      
 66 
     | 
    
         
            +
                Timecop.travel(Time.now + (60 * 60) - 1) do
         
     | 
| 
      
 67 
     | 
    
         
            +
                  aggregator.record(context)
         
     | 
| 
      
 68 
     | 
    
         
            +
             
     | 
| 
      
 69 
     | 
    
         
            +
                  # This doesn't get updated because we already have a context for this user/device in the timeframe
         
     | 
| 
      
 70 
     | 
    
         
            +
                  assert_equal [context], aggregator.data
         
     | 
| 
      
 71 
     | 
    
         
            +
                end
         
     | 
| 
      
 72 
     | 
    
         
            +
             
     | 
| 
      
 73 
     | 
    
         
            +
                Timecop.travel(Time.now + ((60 * 60) + 1)) do
         
     | 
| 
      
 74 
     | 
    
         
            +
                  # this is new because we've passed the expiry
         
     | 
| 
      
 75 
     | 
    
         
            +
                  aggregator.record(context)
         
     | 
| 
      
 76 
     | 
    
         
            +
             
     | 
| 
      
 77 
     | 
    
         
            +
                  assert_equal [context, context], aggregator.data
         
     | 
| 
      
 78 
     | 
    
         
            +
                end
         
     | 
| 
      
 79 
     | 
    
         
            +
              end
         
     | 
| 
      
 80 
     | 
    
         
            +
             
     | 
| 
      
 81 
     | 
    
         
            +
              def test_sync
         
     | 
| 
      
 82 
     | 
    
         
            +
                now = Time.now
         
     | 
| 
      
 83 
     | 
    
         
            +
             
     | 
| 
      
 84 
     | 
    
         
            +
                client = MockBaseClient.new
         
     | 
| 
      
 85 
     | 
    
         
            +
             
     | 
| 
      
 86 
     | 
    
         
            +
                aggregator = Prefab::ExampleContextsAggregator.new(client: client, max_contexts: 10,
         
     | 
| 
      
 87 
     | 
    
         
            +
                                                                   sync_interval: EFFECTIVELY_NEVER)
         
     | 
| 
      
 88 
     | 
    
         
            +
             
     | 
| 
      
 89 
     | 
    
         
            +
                context = Prefab::Context.new(
         
     | 
| 
      
 90 
     | 
    
         
            +
                  user: { key: 'abc' },
         
     | 
| 
      
 91 
     | 
    
         
            +
                  device: { key: 'def', mobile: true }
         
     | 
| 
      
 92 
     | 
    
         
            +
                )
         
     | 
| 
      
 93 
     | 
    
         
            +
                aggregator.record(context)
         
     | 
| 
      
 94 
     | 
    
         
            +
             
     | 
| 
      
 95 
     | 
    
         
            +
                # This is the same as above so we shouldn't get anything new
         
     | 
| 
      
 96 
     | 
    
         
            +
                aggregator.record(context)
         
     | 
| 
      
 97 
     | 
    
         
            +
             
     | 
| 
      
 98 
     | 
    
         
            +
                aggregator.record(
         
     | 
| 
      
 99 
     | 
    
         
            +
                  Prefab::Context.new(
         
     | 
| 
      
 100 
     | 
    
         
            +
                    user: { key: 'ghi' },
         
     | 
| 
      
 101 
     | 
    
         
            +
                    device: { key: 'jkl', mobile: false }
         
     | 
| 
      
 102 
     | 
    
         
            +
                  )
         
     | 
| 
      
 103 
     | 
    
         
            +
                )
         
     | 
| 
      
 104 
     | 
    
         
            +
             
     | 
| 
      
 105 
     | 
    
         
            +
                aggregator.record(Prefab::Context.new(user: { key: 'kev', name: 'kevin', age: 48.5 }))
         
     | 
| 
      
 106 
     | 
    
         
            +
             
     | 
| 
      
 107 
     | 
    
         
            +
                assert_equal 3, aggregator.cache.data.size
         
     | 
| 
      
 108 
     | 
    
         
            +
             
     | 
| 
      
 109 
     | 
    
         
            +
                expected_post = PrefabProto::TelemetryEvents.new(
         
     | 
| 
      
 110 
     | 
    
         
            +
                  instance_hash: client.instance_hash,
         
     | 
| 
      
 111 
     | 
    
         
            +
                  events: [
         
     | 
| 
      
 112 
     | 
    
         
            +
                    PrefabProto::TelemetryEvent.new(
         
     | 
| 
      
 113 
     | 
    
         
            +
                      example_contexts: PrefabProto::ExampleContexts.new(
         
     | 
| 
      
 114 
     | 
    
         
            +
                        examples: [
         
     | 
| 
      
 115 
     | 
    
         
            +
                          PrefabProto::ExampleContext.new(
         
     | 
| 
      
 116 
     | 
    
         
            +
                            timestamp: now.utc.to_i * 1000,
         
     | 
| 
      
 117 
     | 
    
         
            +
                            contextSet: PrefabProto::ContextSet.new(
         
     | 
| 
      
 118 
     | 
    
         
            +
                              contexts: [
         
     | 
| 
      
 119 
     | 
    
         
            +
                                PrefabProto::Context.new(
         
     | 
| 
      
 120 
     | 
    
         
            +
                                  type: 'user',
         
     | 
| 
      
 121 
     | 
    
         
            +
                                  values: {
         
     | 
| 
      
 122 
     | 
    
         
            +
                                    'key' => PrefabProto::ConfigValue.new(string: 'abc')
         
     | 
| 
      
 123 
     | 
    
         
            +
                                  }
         
     | 
| 
      
 124 
     | 
    
         
            +
                                ),
         
     | 
| 
      
 125 
     | 
    
         
            +
                                PrefabProto::Context.new(
         
     | 
| 
      
 126 
     | 
    
         
            +
                                  type: 'device',
         
     | 
| 
      
 127 
     | 
    
         
            +
                                  values: {
         
     | 
| 
      
 128 
     | 
    
         
            +
                                    'key' => PrefabProto::ConfigValue.new(string: 'def'),
         
     | 
| 
      
 129 
     | 
    
         
            +
                                    'mobile' => PrefabProto::ConfigValue.new(bool: true)
         
     | 
| 
      
 130 
     | 
    
         
            +
                                  }
         
     | 
| 
      
 131 
     | 
    
         
            +
                                )
         
     | 
| 
      
 132 
     | 
    
         
            +
                              ]
         
     | 
| 
      
 133 
     | 
    
         
            +
                            )
         
     | 
| 
      
 134 
     | 
    
         
            +
                          ),
         
     | 
| 
      
 135 
     | 
    
         
            +
             
     | 
| 
      
 136 
     | 
    
         
            +
                          PrefabProto::ExampleContext.new(
         
     | 
| 
      
 137 
     | 
    
         
            +
                            timestamp: now.utc.to_i * 1000,
         
     | 
| 
      
 138 
     | 
    
         
            +
                            contextSet: PrefabProto::ContextSet.new(
         
     | 
| 
      
 139 
     | 
    
         
            +
                              contexts: [
         
     | 
| 
      
 140 
     | 
    
         
            +
                                PrefabProto::Context.new(
         
     | 
| 
      
 141 
     | 
    
         
            +
                                  type: 'user',
         
     | 
| 
      
 142 
     | 
    
         
            +
                                  values: {
         
     | 
| 
      
 143 
     | 
    
         
            +
                                    'key' => PrefabProto::ConfigValue.new(string: 'ghi')
         
     | 
| 
      
 144 
     | 
    
         
            +
                                  }
         
     | 
| 
      
 145 
     | 
    
         
            +
                                ),
         
     | 
| 
      
 146 
     | 
    
         
            +
                                PrefabProto::Context.new(
         
     | 
| 
      
 147 
     | 
    
         
            +
                                  type: 'device',
         
     | 
| 
      
 148 
     | 
    
         
            +
                                  values: {
         
     | 
| 
      
 149 
     | 
    
         
            +
                                    'key' => PrefabProto::ConfigValue.new(string: 'jkl'),
         
     | 
| 
      
 150 
     | 
    
         
            +
                                    'mobile' => PrefabProto::ConfigValue.new(bool: false)
         
     | 
| 
      
 151 
     | 
    
         
            +
                                  }
         
     | 
| 
      
 152 
     | 
    
         
            +
                                )
         
     | 
| 
      
 153 
     | 
    
         
            +
                              ]
         
     | 
| 
      
 154 
     | 
    
         
            +
                            )
         
     | 
| 
      
 155 
     | 
    
         
            +
                          ),
         
     | 
| 
      
 156 
     | 
    
         
            +
             
     | 
| 
      
 157 
     | 
    
         
            +
                          PrefabProto::ExampleContext.new(
         
     | 
| 
      
 158 
     | 
    
         
            +
                            timestamp: now.utc.to_i * 1000,
         
     | 
| 
      
 159 
     | 
    
         
            +
                            contextSet: PrefabProto::ContextSet.new(
         
     | 
| 
      
 160 
     | 
    
         
            +
                              contexts: [
         
     | 
| 
      
 161 
     | 
    
         
            +
                                PrefabProto::Context.new(
         
     | 
| 
      
 162 
     | 
    
         
            +
                                  type: 'user',
         
     | 
| 
      
 163 
     | 
    
         
            +
                                  values: {
         
     | 
| 
      
 164 
     | 
    
         
            +
                                    'key' => PrefabProto::ConfigValue.new(string: 'kev'),
         
     | 
| 
      
 165 
     | 
    
         
            +
                                    'name' => PrefabProto::ConfigValue.new(string: 'kevin'),
         
     | 
| 
      
 166 
     | 
    
         
            +
                                    'age' => PrefabProto::ConfigValue.new(double: 48.5)
         
     | 
| 
      
 167 
     | 
    
         
            +
                                  }
         
     | 
| 
      
 168 
     | 
    
         
            +
                                )
         
     | 
| 
      
 169 
     | 
    
         
            +
                              ]
         
     | 
| 
      
 170 
     | 
    
         
            +
                            )
         
     | 
| 
      
 171 
     | 
    
         
            +
                          )
         
     | 
| 
      
 172 
     | 
    
         
            +
                        ]
         
     | 
| 
      
 173 
     | 
    
         
            +
                      )
         
     | 
| 
      
 174 
     | 
    
         
            +
                    )
         
     | 
| 
      
 175 
     | 
    
         
            +
                  ]
         
     | 
| 
      
 176 
     | 
    
         
            +
                )
         
     | 
| 
      
 177 
     | 
    
         
            +
             
     | 
| 
      
 178 
     | 
    
         
            +
                requests = wait_for_post_requests(client) do
         
     | 
| 
      
 179 
     | 
    
         
            +
                  Timecop.freeze(now + (60 * 60) - 1) do
         
     | 
| 
      
 180 
     | 
    
         
            +
                    aggregator.sync
         
     | 
| 
      
 181 
     | 
    
         
            +
                  end
         
     | 
| 
      
 182 
     | 
    
         
            +
                end
         
     | 
| 
      
 183 
     | 
    
         
            +
             
     | 
| 
      
 184 
     | 
    
         
            +
                assert_equal [[
         
     | 
| 
      
 185 
     | 
    
         
            +
                  '/api/v1/telemetry',
         
     | 
| 
      
 186 
     | 
    
         
            +
                  expected_post
         
     | 
| 
      
 187 
     | 
    
         
            +
                ]], requests
         
     | 
| 
      
 188 
     | 
    
         
            +
             
     | 
| 
      
 189 
     | 
    
         
            +
                # this hasn't changed because not enough time has passed
         
     | 
| 
      
 190 
     | 
    
         
            +
                assert_equal 3, aggregator.cache.data.size
         
     | 
| 
      
 191 
     | 
    
         
            +
             
     | 
| 
      
 192 
     | 
    
         
            +
                # a sync past the expiry should clear the cache
         
     | 
| 
      
 193 
     | 
    
         
            +
                Timecop.freeze(now + (60 * 60) + 1) do
         
     | 
| 
      
 194 
     | 
    
         
            +
                  # we need a new piece of data for the sync to happen
         
     | 
| 
      
 195 
     | 
    
         
            +
                  aggregator.record(Prefab::Context.new(user: { key: 'bozo', name: 'Bozo', age: 99 }))
         
     | 
| 
      
 196 
     | 
    
         
            +
             
     | 
| 
      
 197 
     | 
    
         
            +
                  requests = wait_for_post_requests(client) do
         
     | 
| 
      
 198 
     | 
    
         
            +
                    aggregator.sync
         
     | 
| 
      
 199 
     | 
    
         
            +
                  end
         
     | 
| 
      
 200 
     | 
    
         
            +
                end
         
     | 
| 
      
 201 
     | 
    
         
            +
             
     | 
| 
      
 202 
     | 
    
         
            +
                expected_post = PrefabProto::TelemetryEvents.new(
         
     | 
| 
      
 203 
     | 
    
         
            +
                  instance_hash: client.instance_hash,
         
     | 
| 
      
 204 
     | 
    
         
            +
                  events: [
         
     | 
| 
      
 205 
     | 
    
         
            +
                    PrefabProto::TelemetryEvent.new(
         
     | 
| 
      
 206 
     | 
    
         
            +
                      example_contexts: PrefabProto::ExampleContexts.new(
         
     | 
| 
      
 207 
     | 
    
         
            +
                        examples: [
         
     | 
| 
      
 208 
     | 
    
         
            +
                          PrefabProto::ExampleContext.new(
         
     | 
| 
      
 209 
     | 
    
         
            +
                            timestamp: (now.utc.to_i + (60 * 60) + 1) * 1000,
         
     | 
| 
      
 210 
     | 
    
         
            +
                            contextSet: PrefabProto::ContextSet.new(
         
     | 
| 
      
 211 
     | 
    
         
            +
                              contexts: [
         
     | 
| 
      
 212 
     | 
    
         
            +
                                PrefabProto::Context.new(
         
     | 
| 
      
 213 
     | 
    
         
            +
                                  type: 'user',
         
     | 
| 
      
 214 
     | 
    
         
            +
                                  values: {
         
     | 
| 
      
 215 
     | 
    
         
            +
                                    'key' => PrefabProto::ConfigValue.new(string: 'bozo'),
         
     | 
| 
      
 216 
     | 
    
         
            +
                                    'name' => PrefabProto::ConfigValue.new(string: 'Bozo'),
         
     | 
| 
      
 217 
     | 
    
         
            +
                                    'age' => PrefabProto::ConfigValue.new(int: 99)
         
     | 
| 
      
 218 
     | 
    
         
            +
                                  }
         
     | 
| 
      
 219 
     | 
    
         
            +
                                )
         
     | 
| 
      
 220 
     | 
    
         
            +
                              ]
         
     | 
| 
      
 221 
     | 
    
         
            +
                            )
         
     | 
| 
      
 222 
     | 
    
         
            +
                          )
         
     | 
| 
      
 223 
     | 
    
         
            +
                        ]
         
     | 
| 
      
 224 
     | 
    
         
            +
                      )
         
     | 
| 
      
 225 
     | 
    
         
            +
                    )
         
     | 
| 
      
 226 
     | 
    
         
            +
                  ]
         
     | 
| 
      
 227 
     | 
    
         
            +
                )
         
     | 
| 
      
 228 
     | 
    
         
            +
             
     | 
| 
      
 229 
     | 
    
         
            +
                assert_equal [[
         
     | 
| 
      
 230 
     | 
    
         
            +
                  '/api/v1/telemetry',
         
     | 
| 
      
 231 
     | 
    
         
            +
                  expected_post
         
     | 
| 
      
 232 
     | 
    
         
            +
                ]], requests
         
     | 
| 
      
 233 
     | 
    
         
            +
             
     | 
| 
      
 234 
     | 
    
         
            +
                # The last sync should have cleared the cache of everything except the latest context
         
     | 
| 
      
 235 
     | 
    
         
            +
                assert_equal 1, aggregator.cache.data.size
         
     | 
| 
      
 236 
     | 
    
         
            +
                assert_equal ['user:bozo'], aggregator.cache.data.keys
         
     | 
| 
      
 237 
     | 
    
         
            +
              end
         
     | 
| 
      
 238 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,18 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            require 'test_helper'
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            class TestExponentialBackoff < Minitest::Test
         
     | 
| 
      
 6 
     | 
    
         
            +
              def test_backoff
         
     | 
| 
      
 7 
     | 
    
         
            +
                backoff = Prefab::ExponentialBackoff.new(max_delay: 120)
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
                assert_equal 2, backoff.call
         
     | 
| 
      
 10 
     | 
    
         
            +
                assert_equal 4, backoff.call
         
     | 
| 
      
 11 
     | 
    
         
            +
                assert_equal 8, backoff.call
         
     | 
| 
      
 12 
     | 
    
         
            +
                assert_equal 16, backoff.call
         
     | 
| 
      
 13 
     | 
    
         
            +
                assert_equal 32, backoff.call
         
     | 
| 
      
 14 
     | 
    
         
            +
                assert_equal 64, backoff.call
         
     | 
| 
      
 15 
     | 
    
         
            +
                assert_equal 120, backoff.call
         
     | 
| 
      
 16 
     | 
    
         
            +
                assert_equal 120, backoff.call
         
     | 
| 
      
 17 
     | 
    
         
            +
              end
         
     | 
| 
      
 18 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,48 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            require 'test_helper'
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            class TestFeatureFlagClient < Minitest::Test
         
     | 
| 
      
 6 
     | 
    
         
            +
              DEFAULT = 'default'
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
              def test_feature_is_on
         
     | 
| 
      
 9 
     | 
    
         
            +
                ff_client = new_client
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
                assert_equal false, ff_client.feature_is_on?('something-that-does-not-exist')
         
     | 
| 
      
 12 
     | 
    
         
            +
                assert_equal false, ff_client.feature_is_on?('disabled_flag')
         
     | 
| 
      
 13 
     | 
    
         
            +
                assert_equal true, ff_client.feature_is_on?('enabled_flag')
         
     | 
| 
      
 14 
     | 
    
         
            +
                assert_equal false, ff_client.feature_is_on?('flag_with_a_value')
         
     | 
| 
      
 15 
     | 
    
         
            +
              end
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
              def test_feature_is_on_for
         
     | 
| 
      
 18 
     | 
    
         
            +
                ff_client = new_client
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
                assert_equal false, ff_client.feature_is_on_for?('something-that-does-not-exist', {})
         
     | 
| 
      
 21 
     | 
    
         
            +
                assert_equal false, ff_client.feature_is_on_for?('user_key_match', {})
         
     | 
| 
      
 22 
     | 
    
         
            +
                assert_equal false, ff_client.feature_is_on_for?('user_key_match', { user: { key: 'not-included' } })
         
     | 
| 
      
 23 
     | 
    
         
            +
                assert_equal true, ff_client.feature_is_on_for?('user_key_match', { user: { key:  'abc123' } })
         
     | 
| 
      
 24 
     | 
    
         
            +
                assert_equal true, ff_client.feature_is_on_for?('user_key_match', { user: { key: 'xyz987' } })
         
     | 
| 
      
 25 
     | 
    
         
            +
              end
         
     | 
| 
      
 26 
     | 
    
         
            +
             
     | 
| 
      
 27 
     | 
    
         
            +
              def test_get
         
     | 
| 
      
 28 
     | 
    
         
            +
                ff_client = new_client
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
      
 30 
     | 
    
         
            +
                # No default
         
     | 
| 
      
 31 
     | 
    
         
            +
                assert_equal false, ff_client.get('something-that-does-not-exist', {})
         
     | 
| 
      
 32 
     | 
    
         
            +
                assert_equal false, ff_client.get('disabled_flag', {})
         
     | 
| 
      
 33 
     | 
    
         
            +
                assert_equal true, ff_client.get('enabled_flag', {})
         
     | 
| 
      
 34 
     | 
    
         
            +
                assert_equal 'all-features', ff_client.get('flag_with_a_value', {})
         
     | 
| 
      
 35 
     | 
    
         
            +
             
     | 
| 
      
 36 
     | 
    
         
            +
                # with defaults
         
     | 
| 
      
 37 
     | 
    
         
            +
                assert_equal DEFAULT, ff_client.get('something-that-does-not-exist', {}, default: DEFAULT)
         
     | 
| 
      
 38 
     | 
    
         
            +
                assert_equal false, ff_client.get('disabled_flag', {}, default: DEFAULT)
         
     | 
| 
      
 39 
     | 
    
         
            +
                assert_equal true, ff_client.get('enabled_flag', {}, default: DEFAULT)
         
     | 
| 
      
 40 
     | 
    
         
            +
                assert_equal 'all-features', ff_client.get('flag_with_a_value', {}, default: DEFAULT)
         
     | 
| 
      
 41 
     | 
    
         
            +
              end
         
     | 
| 
      
 42 
     | 
    
         
            +
             
     | 
| 
      
 43 
     | 
    
         
            +
              private
         
     | 
| 
      
 44 
     | 
    
         
            +
             
     | 
| 
      
 45 
     | 
    
         
            +
              def new_client(overrides = {})
         
     | 
| 
      
 46 
     | 
    
         
            +
                super(overrides).feature_flag_client
         
     | 
| 
      
 47 
     | 
    
         
            +
              end
         
     | 
| 
      
 48 
     | 
    
         
            +
            end
         
     | 
    
        data/test/test_helper.rb
    ADDED
    
    | 
         @@ -0,0 +1,17 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            require 'minitest/autorun'
         
     | 
| 
      
 4 
     | 
    
         
            +
            require 'minitest/focus'
         
     | 
| 
      
 5 
     | 
    
         
            +
            require 'minitest/reporters'
         
     | 
| 
      
 6 
     | 
    
         
            +
            Minitest::Reporters.use! unless ENV['RM_INFO']
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
            require 'prefab-cloud-ruby'
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
            Dir.glob(File.join(File.dirname(__FILE__), 'support', '**', '*.rb')).each do |file|
         
     | 
| 
      
 11 
     | 
    
         
            +
              require file
         
     | 
| 
      
 12 
     | 
    
         
            +
            end
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
            MiniTest::Test.class_eval do
         
     | 
| 
      
 15 
     | 
    
         
            +
              include CommonHelpers
         
     | 
| 
      
 16 
     | 
    
         
            +
              extend CommonHelpers
         
     | 
| 
      
 17 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,58 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            require 'test_helper'
         
     | 
| 
      
 4 
     | 
    
         
            +
            require 'integration_test_helpers'
         
     | 
| 
      
 5 
     | 
    
         
            +
            require 'integration_test'
         
     | 
| 
      
 6 
     | 
    
         
            +
            require 'yaml'
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
            class TestIntegration < Minitest::Test
         
     | 
| 
      
 9 
     | 
    
         
            +
              IntegrationTestHelpers.find_integration_tests.map do |test_file|
         
     | 
| 
      
 10 
     | 
    
         
            +
                tests = YAML.load(File.read(test_file))['tests']
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
                tests.each do |test|
         
     | 
| 
      
 13 
     | 
    
         
            +
                  parent_context = test['context']
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
                  test['cases'].each do |test_case|
         
     | 
| 
      
 16 
     | 
    
         
            +
                    define_method(:"test_#{test['name']}_#{test_case['name']}") do
         
     | 
| 
      
 17 
     | 
    
         
            +
                      it = IntegrationTest.new(test_case)
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
                      IntegrationTestHelpers.with_parent_context_maybe(parent_context) do
         
     | 
| 
      
 20 
     | 
    
         
            +
                        case it.test_type
         
     | 
| 
      
 21 
     | 
    
         
            +
                        when :raise
         
     | 
| 
      
 22 
     | 
    
         
            +
                          err = assert_raises(it.expected[:error]) do
         
     | 
| 
      
 23 
     | 
    
         
            +
                            it.test_client.send(it.func, *it.input)
         
     | 
| 
      
 24 
     | 
    
         
            +
                          end
         
     | 
| 
      
 25 
     | 
    
         
            +
                          assert_match(/#{it.expected[:message]}/, err.message)
         
     | 
| 
      
 26 
     | 
    
         
            +
                        when :nil
         
     | 
| 
      
 27 
     | 
    
         
            +
                          assert_nil it.test_client.send(it.func, *it.input)
         
     | 
| 
      
 28 
     | 
    
         
            +
                        when :simple_equality
         
     | 
| 
      
 29 
     | 
    
         
            +
                          if it.func == :enabled?
         
     | 
| 
      
 30 
     | 
    
         
            +
                            flag, _default, context = *it.input
         
     | 
| 
      
 31 
     | 
    
         
            +
                            assert_equal it.expected[:value], it.test_client.send(it.func, flag, context)
         
     | 
| 
      
 32 
     | 
    
         
            +
                          else
         
     | 
| 
      
 33 
     | 
    
         
            +
                            assert_equal it.expected[:value], it.test_client.send(it.func, *it.input)
         
     | 
| 
      
 34 
     | 
    
         
            +
                          end
         
     | 
| 
      
 35 
     | 
    
         
            +
                        when :log_level
         
     | 
| 
      
 36 
     | 
    
         
            +
                          assert_equal it.expected[:value].to_sym, it.test_client.send(it.func, *it.input)
         
     | 
| 
      
 37 
     | 
    
         
            +
                        when :telemetry
         
     | 
| 
      
 38 
     | 
    
         
            +
                          aggregator, get_actual_data, expected = IntegrationTestHelpers.prepare_post_data(it)
         
     | 
| 
      
 39 
     | 
    
         
            +
                          aggregator.sync
         
     | 
| 
      
 40 
     | 
    
         
            +
             
     | 
| 
      
 41 
     | 
    
         
            +
                          wait_for -> { it.last_post_result&.status == 200 }
         
     | 
| 
      
 42 
     | 
    
         
            +
             
     | 
| 
      
 43 
     | 
    
         
            +
                          assert it.endpoint == it.last_post_endpoint
         
     | 
| 
      
 44 
     | 
    
         
            +
             
     | 
| 
      
 45 
     | 
    
         
            +
                          actual = get_actual_data[it.last_data_sent]
         
     | 
| 
      
 46 
     | 
    
         
            +
             
     | 
| 
      
 47 
     | 
    
         
            +
                          expected.all? do |expected|
         
     | 
| 
      
 48 
     | 
    
         
            +
                            assert actual.include?(expected)
         
     | 
| 
      
 49 
     | 
    
         
            +
                          end
         
     | 
| 
      
 50 
     | 
    
         
            +
                        else
         
     | 
| 
      
 51 
     | 
    
         
            +
                          raise "Unknown test type: #{it.test_type}"
         
     | 
| 
      
 52 
     | 
    
         
            +
                        end
         
     | 
| 
      
 53 
     | 
    
         
            +
                      end
         
     | 
| 
      
 54 
     | 
    
         
            +
                    end
         
     | 
| 
      
 55 
     | 
    
         
            +
                  end
         
     | 
| 
      
 56 
     | 
    
         
            +
                end
         
     | 
| 
      
 57 
     | 
    
         
            +
              end
         
     | 
| 
      
 58 
     | 
    
         
            +
            end
         
     |