prefab-cloud-ruby 0.20.0 → 0.21.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.
Files changed (53) hide show
  1. checksums.yaml +4 -4
  2. data/.envrc.sample +3 -0
  3. data/.github/workflows/ruby.yml +4 -0
  4. data/.gitmodules +3 -0
  5. data/Gemfile +12 -12
  6. data/Gemfile.lock +16 -14
  7. data/README.md +1 -1
  8. data/Rakefile +13 -14
  9. data/VERSION +1 -1
  10. data/lib/prefab/auth_interceptor.rb +2 -1
  11. data/lib/prefab/cancellable_interceptor.rb +8 -7
  12. data/lib/prefab/client.rb +33 -24
  13. data/lib/prefab/config_client.rb +55 -66
  14. data/lib/prefab/config_loader.rb +7 -114
  15. data/lib/prefab/config_resolver.rb +27 -57
  16. data/lib/prefab/config_value_unwrapper.rb +23 -0
  17. data/lib/prefab/criteria_evaluator.rb +96 -0
  18. data/lib/prefab/errors/invalid_api_key_error.rb +1 -1
  19. data/lib/prefab/feature_flag_client.rb +13 -145
  20. data/lib/prefab/internal_logger.rb +6 -5
  21. data/lib/prefab/local_config_parser.rb +110 -0
  22. data/lib/prefab/logger_client.rb +26 -31
  23. data/lib/prefab/murmer3.rb +3 -4
  24. data/lib/prefab/noop_cache.rb +5 -7
  25. data/lib/prefab/noop_stats.rb +2 -3
  26. data/lib/prefab/options.rb +11 -9
  27. data/lib/prefab/ratelimit_client.rb +11 -13
  28. data/lib/prefab/sse_logger.rb +3 -2
  29. data/lib/prefab/weighted_value_resolver.rb +42 -0
  30. data/lib/prefab/yaml_config_parser.rb +32 -0
  31. data/lib/prefab-cloud-ruby.rb +7 -2
  32. data/lib/prefab_pb.rb +49 -43
  33. data/lib/prefab_services_pb.rb +0 -1
  34. data/prefab-cloud-ruby.gemspec +28 -19
  35. data/test/.prefab.unit_tests.config.yaml +3 -2
  36. data/test/integration_test.rb +98 -0
  37. data/test/integration_test_helpers.rb +37 -0
  38. data/test/test_client.rb +32 -31
  39. data/test/test_config_client.rb +21 -20
  40. data/test/test_config_loader.rb +48 -37
  41. data/test/test_config_resolver.rb +312 -135
  42. data/test/test_config_value_unwrapper.rb +83 -0
  43. data/test/test_criteria_evaluator.rb +533 -0
  44. data/test/test_feature_flag_client.rb +35 -347
  45. data/test/test_helper.rb +18 -14
  46. data/test/test_integration.rb +33 -0
  47. data/test/test_local_config_parser.rb +78 -0
  48. data/test/test_logger.rb +47 -46
  49. data/test/test_weighted_value_resolver.rb +65 -0
  50. metadata +24 -27
  51. data/lib/prefab/config_helper.rb +0 -31
  52. data/run_test_harness_server.sh +0 -8
  53. data/test/harness_server.rb +0 -64
data/lib/prefab_pb.rb CHANGED
@@ -17,12 +17,22 @@ Google::Protobuf::DescriptorPool.generated_pool.build do
17
17
  optional :bytes, :bytes, 3
18
18
  optional :double, :double, 4
19
19
  optional :bool, :bool, 5
20
- optional :feature_flag, :message, 6, "prefab.FeatureFlag"
20
+ optional :weighted_values, :message, 6, "prefab.WeightedValues"
21
21
  optional :limit_definition, :message, 7, "prefab.LimitDefinition"
22
- optional :segment, :message, 8, "prefab.Segment"
23
22
  optional :log_level, :enum, 9, "prefab.LogLevel"
23
+ optional :string_list, :message, 10, "prefab.StringList"
24
24
  end
25
25
  end
26
+ add_message "prefab.StringList" do
27
+ repeated :values, :string, 1
28
+ end
29
+ add_message "prefab.WeightedValue" do
30
+ optional :weight, :int32, 1
31
+ optional :value, :message, 2, "prefab.ConfigValue"
32
+ end
33
+ add_message "prefab.WeightedValues" do
34
+ repeated :weighted_values, :message, 1, "prefab.WeightedValue"
35
+ end
26
36
  add_message "prefab.Configs" do
27
37
  repeated :configs, :message, 1, "prefab.Config"
28
38
  optional :config_service_pointer, :message, 2, "prefab.ConfigServicePointer"
@@ -31,14 +41,24 @@ Google::Protobuf::DescriptorPool.generated_pool.build do
31
41
  optional :id, :int64, 1
32
42
  optional :project_id, :int64, 2
33
43
  optional :key, :string, 3
34
- optional :changed_by, :string, 4
44
+ optional :changed_by, :message, 4, "prefab.ChangedBy"
35
45
  repeated :rows, :message, 5, "prefab.ConfigRow"
36
- repeated :variants, :message, 6, "prefab.FeatureFlagVariant"
46
+ repeated :allowable_values, :message, 6, "prefab.ConfigValue"
47
+ optional :config_type, :enum, 7, "prefab.ConfigType"
48
+ proto3_optional :draftId, :int64, 8
49
+ end
50
+ add_message "prefab.ChangedBy" do
51
+ optional :user_id, :int64, 1
52
+ optional :email, :string, 2
37
53
  end
38
54
  add_message "prefab.ConfigRow" do
39
- optional :project_env_id, :int64, 1
40
- optional :namespace, :string, 2
41
- optional :value, :message, 3, "prefab.ConfigValue"
55
+ proto3_optional :project_env_id, :int64, 1
56
+ repeated :values, :message, 2, "prefab.ConditionalValue"
57
+ map :properties, :string, :message, 3, "prefab.ConfigValue"
58
+ end
59
+ add_message "prefab.ConditionalValue" do
60
+ repeated :criteria, :message, 1, "prefab.Criterion"
61
+ optional :value, :message, 2, "prefab.ConfigValue"
42
62
  end
43
63
  add_message "prefab.LimitResponse" do
44
64
  optional :passed, :bool, 1
@@ -75,20 +95,12 @@ Google::Protobuf::DescriptorPool.generated_pool.build do
75
95
  value :MINIMUM, 1
76
96
  value :MAXIMUM, 2
77
97
  end
78
- add_message "prefab.FeatureFlagVariant" do
79
- proto3_optional :int, :int64, 1
80
- proto3_optional :string, :string, 2
81
- proto3_optional :double, :double, 3
82
- proto3_optional :bool, :bool, 4
83
- optional :name, :string, 5
84
- optional :description, :string, 6
85
- end
86
- add_message "prefab.Criteria" do
87
- optional :property, :string, 1
88
- optional :operator, :enum, 2, "prefab.Criteria.CriteriaOperator"
89
- repeated :values, :string, 3
98
+ add_message "prefab.Criterion" do
99
+ optional :property_name, :string, 1
100
+ optional :operator, :enum, 2, "prefab.Criterion.CriterionOperator"
101
+ optional :value_to_match, :message, 3, "prefab.ConfigValue"
90
102
  end
91
- add_enum "prefab.Criteria.CriteriaOperator" do
103
+ add_enum "prefab.Criterion.CriterionOperator" do
92
104
  value :NOT_SET, 0
93
105
  value :LOOKUP_KEY_IN, 1
94
106
  value :LOOKUP_KEY_NOT_IN, 2
@@ -99,22 +111,7 @@ Google::Protobuf::DescriptorPool.generated_pool.build do
99
111
  value :PROP_IS_NOT_ONE_OF, 7
100
112
  value :PROP_ENDS_WITH_ONE_OF, 8
101
113
  value :PROP_DOES_NOT_END_WITH_ONE_OF, 9
102
- end
103
- add_message "prefab.Rule" do
104
- optional :criteria, :message, 1, "prefab.Criteria"
105
- repeated :variant_weights, :message, 2, "prefab.VariantWeight"
106
- end
107
- add_message "prefab.Segment" do
108
- repeated :criterion, :message, 1, "prefab.Criteria"
109
- end
110
- add_message "prefab.VariantWeight" do
111
- optional :weight, :int32, 1
112
- optional :variant_idx, :int32, 2
113
- end
114
- add_message "prefab.FeatureFlag" do
115
- optional :active, :bool, 1
116
- optional :inactive_variant_idx, :int32, 2
117
- repeated :rules, :message, 5, "prefab.Rule"
114
+ value :HIERARCHICAL_MATCH, 10
118
115
  end
119
116
  add_message "prefab.Identity" do
120
117
  proto3_optional :lookup, :string, 1
@@ -184,6 +181,14 @@ Google::Protobuf::DescriptorPool.generated_pool.build do
184
181
  optional :sequence_name, :string, 3
185
182
  optional :size, :int64, 4
186
183
  end
184
+ add_enum "prefab.ConfigType" do
185
+ value :NOT_SET_CONFIG_TYPE, 0
186
+ value :CONFIG, 1
187
+ value :FEATURE_FLAG, 2
188
+ value :LOG_LEVEL, 3
189
+ value :SEGMENT, 4
190
+ value :LIMIT_DEFINITION, 5
191
+ end
187
192
  add_enum "prefab.LogLevel" do
188
193
  value :NOT_SET_LOG_LEVEL, 0
189
194
  value :TRACE, 1
@@ -205,20 +210,20 @@ end
205
210
  module Prefab
206
211
  ConfigServicePointer = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("prefab.ConfigServicePointer").msgclass
207
212
  ConfigValue = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("prefab.ConfigValue").msgclass
213
+ StringList = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("prefab.StringList").msgclass
214
+ WeightedValue = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("prefab.WeightedValue").msgclass
215
+ WeightedValues = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("prefab.WeightedValues").msgclass
208
216
  Configs = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("prefab.Configs").msgclass
209
217
  Config = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("prefab.Config").msgclass
218
+ ChangedBy = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("prefab.ChangedBy").msgclass
210
219
  ConfigRow = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("prefab.ConfigRow").msgclass
220
+ ConditionalValue = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("prefab.ConditionalValue").msgclass
211
221
  LimitResponse = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("prefab.LimitResponse").msgclass
212
222
  LimitResponse::LimitPolicyNames = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("prefab.LimitResponse.LimitPolicyNames").enummodule
213
223
  LimitRequest = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("prefab.LimitRequest").msgclass
214
224
  LimitRequest::LimitCombiner = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("prefab.LimitRequest.LimitCombiner").enummodule
215
- FeatureFlagVariant = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("prefab.FeatureFlagVariant").msgclass
216
- Criteria = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("prefab.Criteria").msgclass
217
- Criteria::CriteriaOperator = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("prefab.Criteria.CriteriaOperator").enummodule
218
- Rule = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("prefab.Rule").msgclass
219
- Segment = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("prefab.Segment").msgclass
220
- VariantWeight = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("prefab.VariantWeight").msgclass
221
- FeatureFlag = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("prefab.FeatureFlag").msgclass
225
+ Criterion = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("prefab.Criterion").msgclass
226
+ Criterion::CriterionOperator = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("prefab.Criterion.CriterionOperator").enummodule
222
227
  Identity = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("prefab.Identity").msgclass
223
228
  ClientConfigValue = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("prefab.ClientConfigValue").msgclass
224
229
  ConfigEvaluations = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("prefab.ConfigEvaluations").msgclass
@@ -231,6 +236,7 @@ module Prefab
231
236
  CreationResponse = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("prefab.CreationResponse").msgclass
232
237
  IdBlock = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("prefab.IdBlock").msgclass
233
238
  IdBlockRequest = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("prefab.IdBlockRequest").msgclass
239
+ ConfigType = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("prefab.ConfigType").enummodule
234
240
  LogLevel = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("prefab.LogLevel").enummodule
235
241
  OnFailure = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("prefab.OnFailure").enummodule
236
242
  end
@@ -1,4 +1,3 @@
1
- # frozen_string_literal: true
2
1
  # Generated by the protocol buffer compiler. DO NOT EDIT!
3
2
  # Source: prefab.proto for package 'prefab'
4
3
 
@@ -2,16 +2,16 @@
2
2
  # DO NOT EDIT THIS FILE DIRECTLY
3
3
  # Instead, edit Juwelier::Tasks in Rakefile, and run 'rake gemspec'
4
4
  # -*- encoding: utf-8 -*-
5
- # stub: prefab-cloud-ruby 0.20.0 ruby lib
5
+ # stub: prefab-cloud-ruby 0.21.0 ruby lib
6
6
 
7
7
  Gem::Specification.new do |s|
8
8
  s.name = "prefab-cloud-ruby".freeze
9
- s.version = "0.20.0"
9
+ s.version = "0.21.0"
10
10
 
11
11
  s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version=
12
12
  s.require_paths = ["lib".freeze]
13
13
  s.authors = ["Jeff Dwyer".freeze]
14
- s.date = "2022-12-02"
14
+ s.date = "2023-02-27"
15
15
  s.description = "RateLimits & Config as a service".freeze
16
16
  s.email = "jdwyer@prefab.cloud".freeze
17
17
  s.extra_rdoc_files = [
@@ -20,7 +20,9 @@ Gem::Specification.new do |s|
20
20
  ]
21
21
  s.files = [
22
22
  ".envrc",
23
+ ".envrc.sample",
23
24
  ".github/workflows/ruby.yml",
25
+ ".gitmodules",
24
26
  ".tool-versions",
25
27
  "CODEOWNERS",
26
28
  "Gemfile",
@@ -35,15 +37,17 @@ Gem::Specification.new do |s|
35
37
  "lib/prefab/cancellable_interceptor.rb",
36
38
  "lib/prefab/client.rb",
37
39
  "lib/prefab/config_client.rb",
38
- "lib/prefab/config_helper.rb",
39
40
  "lib/prefab/config_loader.rb",
40
41
  "lib/prefab/config_resolver.rb",
42
+ "lib/prefab/config_value_unwrapper.rb",
43
+ "lib/prefab/criteria_evaluator.rb",
41
44
  "lib/prefab/error.rb",
42
45
  "lib/prefab/errors/initialization_timeout_error.rb",
43
46
  "lib/prefab/errors/invalid_api_key_error.rb",
44
47
  "lib/prefab/errors/missing_default_error.rb",
45
48
  "lib/prefab/feature_flag_client.rb",
46
49
  "lib/prefab/internal_logger.rb",
50
+ "lib/prefab/local_config_parser.rb",
47
51
  "lib/prefab/logger_client.rb",
48
52
  "lib/prefab/murmer3.rb",
49
53
  "lib/prefab/noop_cache.rb",
@@ -51,20 +55,27 @@ Gem::Specification.new do |s|
51
55
  "lib/prefab/options.rb",
52
56
  "lib/prefab/ratelimit_client.rb",
53
57
  "lib/prefab/sse_logger.rb",
58
+ "lib/prefab/weighted_value_resolver.rb",
59
+ "lib/prefab/yaml_config_parser.rb",
54
60
  "lib/prefab_pb.rb",
55
61
  "lib/prefab_services_pb.rb",
56
62
  "prefab-cloud-ruby.gemspec",
57
- "run_test_harness_server.sh",
58
63
  "test/.prefab.default.config.yaml",
59
64
  "test/.prefab.unit_tests.config.yaml",
60
- "test/harness_server.rb",
65
+ "test/integration_test.rb",
66
+ "test/integration_test_helpers.rb",
61
67
  "test/test_client.rb",
62
68
  "test/test_config_client.rb",
63
69
  "test/test_config_loader.rb",
64
70
  "test/test_config_resolver.rb",
71
+ "test/test_config_value_unwrapper.rb",
72
+ "test/test_criteria_evaluator.rb",
65
73
  "test/test_feature_flag_client.rb",
66
74
  "test/test_helper.rb",
67
- "test/test_logger.rb"
75
+ "test/test_integration.rb",
76
+ "test/test_local_config_parser.rb",
77
+ "test/test_logger.rb",
78
+ "test/test_weighted_value_resolver.rb"
68
79
  ]
69
80
  s.homepage = "http://github.com/prefab-cloud/prefab-cloud-ruby".freeze
70
81
  s.licenses = ["MIT".freeze]
@@ -78,31 +89,29 @@ Gem::Specification.new do |s|
78
89
  if s.respond_to? :add_runtime_dependency then
79
90
  s.add_runtime_dependency(%q<concurrent-ruby>.freeze, ["~> 1.0", ">= 1.0.5"])
80
91
  s.add_runtime_dependency(%q<faraday>.freeze, [">= 0"])
81
- s.add_runtime_dependency(%q<ld-eventsource>.freeze, [">= 0"])
82
- s.add_runtime_dependency(%q<grpc>.freeze, [">= 0"])
83
- s.add_runtime_dependency(%q<google-protobuf>.freeze, [">= 0"])
84
92
  s.add_runtime_dependency(%q<googleapis-common-protos-types>.freeze, [">= 0"])
93
+ s.add_runtime_dependency(%q<google-protobuf>.freeze, [">= 0"])
94
+ s.add_runtime_dependency(%q<grpc>.freeze, [">= 0"])
95
+ s.add_runtime_dependency(%q<ld-eventsource>.freeze, [">= 0"])
85
96
  s.add_development_dependency(%q<benchmark-ips>.freeze, [">= 0"])
86
- s.add_development_dependency(%q<grpc-tools>.freeze, [">= 0"])
87
- s.add_development_dependency(%q<rdoc>.freeze, [">= 0"])
88
97
  s.add_development_dependency(%q<bundler>.freeze, [">= 0"])
98
+ s.add_development_dependency(%q<grpc-tools>.freeze, [">= 0"])
89
99
  s.add_development_dependency(%q<juwelier>.freeze, ["~> 2.4.9"])
100
+ s.add_development_dependency(%q<rdoc>.freeze, [">= 0"])
90
101
  s.add_development_dependency(%q<simplecov>.freeze, [">= 0"])
91
- s.add_development_dependency(%q<thin>.freeze, [">= 0"])
92
102
  else
93
103
  s.add_dependency(%q<concurrent-ruby>.freeze, ["~> 1.0", ">= 1.0.5"])
94
104
  s.add_dependency(%q<faraday>.freeze, [">= 0"])
95
- s.add_dependency(%q<ld-eventsource>.freeze, [">= 0"])
96
- s.add_dependency(%q<grpc>.freeze, [">= 0"])
97
- s.add_dependency(%q<google-protobuf>.freeze, [">= 0"])
98
105
  s.add_dependency(%q<googleapis-common-protos-types>.freeze, [">= 0"])
106
+ s.add_dependency(%q<google-protobuf>.freeze, [">= 0"])
107
+ s.add_dependency(%q<grpc>.freeze, [">= 0"])
108
+ s.add_dependency(%q<ld-eventsource>.freeze, [">= 0"])
99
109
  s.add_dependency(%q<benchmark-ips>.freeze, [">= 0"])
100
- s.add_dependency(%q<grpc-tools>.freeze, [">= 0"])
101
- s.add_dependency(%q<rdoc>.freeze, [">= 0"])
102
110
  s.add_dependency(%q<bundler>.freeze, [">= 0"])
111
+ s.add_dependency(%q<grpc-tools>.freeze, [">= 0"])
103
112
  s.add_dependency(%q<juwelier>.freeze, ["~> 2.4.9"])
113
+ s.add_dependency(%q<rdoc>.freeze, [">= 0"])
104
114
  s.add_dependency(%q<simplecov>.freeze, [">= 0"])
105
- s.add_dependency(%q<thin>.freeze, [">= 0"])
106
115
  end
107
116
  end
108
117
 
@@ -9,8 +9,9 @@ sample: test sample value
9
9
  enabled_flag: true
10
10
  disabled_flag: false
11
11
  flag_with_a_value: { "feature_flag": "true", value: "all-features" }
12
- in_lookup_key: { "feature_flag": "true", value: true, criteria: { operator: LOOKUP_KEY_IN, values: [ "abc123", "xyz987" ] } }
13
- just_my_domain: { "feature_flag": "true", value: "new-version", criteria: { operator: PROP_IS_ONE_OF, property: "domain", values: [ "prefab.cloud", "example.com" ] } }
12
+ in_lookup_key: { "feature_flag": "true", value: true, criterion: { operator: LOOKUP_KEY_IN, values: [ "abc123", "xyz987" ] } }
13
+ just_my_domain: { "feature_flag": "true", value: "new-version", criterion: { operator: PROP_IS_ONE_OF, property: "domain", values: [ "prefab.cloud", "example.com" ] } }
14
+
14
15
  nested:
15
16
  values:
16
17
  _: top level
@@ -0,0 +1,98 @@
1
+ # frozen_string_literal: true
2
+
3
+ class IntegrationTest
4
+ attr_reader :func
5
+ attr_reader :input
6
+ attr_reader :expected
7
+ attr_reader :test_client
8
+
9
+ def initialize(test_data)
10
+ @client_overrides = parse_client_overrides(test_data['client_overrides'])
11
+ @func = parse_function(test_data['function'])
12
+ @input = parse_input(test_data['input'])
13
+ @expected = parse_expected(test_data['expected'])
14
+ test_client = :"#{test_data['client']}"
15
+ @test_client = base_client.send(test_client)
16
+ end
17
+
18
+ def test_type
19
+ if @expected[:status] == 'raise'
20
+ :raise
21
+ elsif @expected[:value].nil?
22
+ :nil
23
+ elsif @func == :feature_is_on_for?
24
+ :feature_flag
25
+ else
26
+ :simple_equality
27
+ end
28
+ end
29
+
30
+ private
31
+
32
+ def parse_client_overrides(overrides)
33
+ Hash[
34
+ (overrides || {}).map do |(k, v)|
35
+ [k.to_sym, v]
36
+ end
37
+ ]
38
+ end
39
+
40
+ def parse_function(function)
41
+ case function
42
+ when 'get_or_raise' then :get
43
+ when 'enabled' then :feature_is_on_for?
44
+ else :"#{function}"
45
+ end
46
+ end
47
+
48
+ def parse_input(input)
49
+ if input['key']
50
+ parse_config_input(input)
51
+ elsif input['flag']
52
+ parse_ff_input(input)
53
+ end
54
+ end
55
+
56
+ def parse_config_input(input)
57
+ if !input['default'].nil?
58
+ [input['key'], input['default']]
59
+ else
60
+ [input['key']]
61
+ end
62
+ end
63
+
64
+ def parse_ff_input(input)
65
+ [input['flag'], input['lookup_key'], input['properties'] || {}]
66
+ end
67
+
68
+ def parse_expected(expected)
69
+ {
70
+ status: expected['status'],
71
+ error: parse_error_type(expected['error']),
72
+ message: expected['message'],
73
+ value: expected['value']
74
+ }
75
+ end
76
+
77
+ def parse_error_type(error_type)
78
+ case error_type
79
+ when 'missing_default' then Prefab::Errors::MissingDefaultError
80
+ end
81
+ end
82
+
83
+ def base_client
84
+ @_base_client ||= Prefab::Client.new(base_client_options)
85
+ end
86
+
87
+ def base_client_options
88
+ @_options ||= Prefab::Options.new(**{
89
+ prefab_config_override_dir: 'none',
90
+ prefab_config_classpath_dir: 'test',
91
+ prefab_envs: ['unit_tests'],
92
+ prefab_datasources: Prefab::Options::DATASOURCES::ALL,
93
+ api_key: ENV['PREFAB_INTEGRATION_TEST_API_KEY'],
94
+ prefab_api_url: 'https://api.staging-prefab.cloud',
95
+ prefab_grpc_url: 'grpc.staging-prefab.cloud:443'
96
+ }.merge(@client_overrides))
97
+ end
98
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module IntegrationTestHelpers
4
+ SUBMODULE_PATH = 'test/prefab-cloud-integration-test-data'
5
+ RAISE_IF_NO_TESTS_FOUND = ENV['PREFAB_INTEGRATION_TEST_RAISE'] == 'true'
6
+
7
+ def self.find_integration_tests
8
+ version = find_integration_test_version
9
+
10
+ files = find_versioned_test_files(version)
11
+
12
+ if files.none?
13
+ message = "No integration tests found for version: #{version}"
14
+ raise message if RAISE_IF_NO_TESTS_FOUND
15
+
16
+ puts message
17
+
18
+ end
19
+
20
+ files
21
+ end
22
+
23
+ def self.find_integration_test_version
24
+ File.read(File.join(SUBMODULE_PATH, 'version')).strip
25
+ rescue StandardError => e
26
+ puts "No version found for integration tests: #{e.message}"
27
+ end
28
+
29
+ def self.find_versioned_test_files(version)
30
+ if version.nil?
31
+ []
32
+ else
33
+ Dir[File.join(SUBMODULE_PATH, "tests/#{version}/**/*")]
34
+ .select { |file| file =~ /\.ya?ml$/ }
35
+ end
36
+ end
37
+ end
data/test/test_client.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require 'test_helper'
3
4
 
4
5
  class TestClient < Minitest::Test
@@ -7,25 +8,25 @@ class TestClient < Minitest::Test
7
8
  end
8
9
 
9
10
  def test_get
10
- assert_equal "test sample value", @client.get("sample")
11
- assert_equal 123, @client.get("sample_int")
11
+ assert_equal 'test sample value', @client.get('sample')
12
+ assert_equal 123, @client.get('sample_int')
12
13
  end
13
14
 
14
15
  def test_get_with_default
15
16
  # A `false` value is not replaced with the default
16
- assert_equal false, @client.get("false_value", "red")
17
+ assert_equal false, @client.get('false_value', 'red')
17
18
 
18
19
  # A falsy value is not replaced with the default
19
- assert_equal 0, @client.get("zero_value", "red")
20
+ assert_equal 0, @client.get('zero_value', 'red')
20
21
 
21
22
  # A missing value returns the default
22
- assert_equal "buckets", @client.get("missing_value", "buckets")
23
+ assert_equal 'buckets', @client.get('missing_value', 'buckets')
23
24
  end
24
25
 
25
26
  def test_get_with_missing_default
26
27
  # it raises by default
27
28
  err = assert_raises(Prefab::Errors::MissingDefaultError) do
28
- assert_nil @client.get("missing_value")
29
+ assert_nil @client.get('missing_value')
29
30
  end
30
31
 
31
32
  assert_match(/No value found for key/, err.message)
@@ -33,51 +34,51 @@ class TestClient < Minitest::Test
33
34
 
34
35
  # you can opt-in to return `nil` instead
35
36
  client = new_client(on_no_default: Prefab::Options::ON_NO_DEFAULT::RETURN_NIL)
36
- assert_nil client.get("missing_value")
37
+ assert_nil client.get('missing_value')
37
38
  end
38
39
 
39
40
  def test_enabled
40
- assert_equal false, @client.enabled?("does_not_exist")
41
- assert_equal true, @client.enabled?("enabled_flag")
42
- assert_equal false, @client.enabled?("disabled_flag")
43
- assert_equal false, @client.enabled?("flag_with_a_value")
41
+ assert_equal false, @client.enabled?('does_not_exist')
42
+ assert_equal true, @client.enabled?('enabled_flag')
43
+ assert_equal false, @client.enabled?('disabled_flag')
44
+ assert_equal false, @client.enabled?('flag_with_a_value')
44
45
  end
45
46
 
46
47
  def test_ff_enabled_with_lookup_key
47
- assert_equal false, @client.enabled?("in_lookup_key", "jimmy")
48
- assert_equal true, @client.enabled?("in_lookup_key", "abc123")
49
- assert_equal true, @client.enabled?("in_lookup_key", "xyz987")
48
+ assert_equal false, @client.enabled?('in_lookup_key', 'jimmy')
49
+ assert_equal true, @client.enabled?('in_lookup_key', 'abc123')
50
+ assert_equal true, @client.enabled?('in_lookup_key', 'xyz987')
50
51
  end
51
52
 
52
53
  def test_ff_get_with_lookup_key
53
- assert_nil @client.get("in_lookup_key", "jimmy")
54
- assert_equal "DEFAULT", @client.get("in_lookup_key", "jimmy", {}, "DEFAULT")
54
+ assert_nil @client.get('in_lookup_key', 'jimmy')
55
+ assert_equal 'DEFAULT', @client.get('in_lookup_key', 'jimmy', {}, 'DEFAULT')
55
56
 
56
- assert_equal true, @client.get("in_lookup_key", "abc123")
57
- assert_equal true, @client.get("in_lookup_key", "xyz987")
57
+ assert_equal true, @client.get('in_lookup_key', 'abc123')
58
+ assert_equal true, @client.get('in_lookup_key', 'xyz987')
58
59
  end
59
60
 
60
61
  def test_ff_enabled_with_attributes
61
- assert_equal false, @client.enabled?("just_my_domain", "abc123", { domain: "gmail.com" })
62
- assert_equal false, @client.enabled?("just_my_domain", "abc123", { domain: "prefab.cloud" })
63
- assert_equal false, @client.enabled?("just_my_domain", "abc123", { domain: "example.com" })
62
+ assert_equal false, @client.enabled?('just_my_domain', 'abc123', { domain: 'gmail.com' })
63
+ assert_equal false, @client.enabled?('just_my_domain', 'abc123', { domain: 'prefab.cloud' })
64
+ assert_equal false, @client.enabled?('just_my_domain', 'abc123', { domain: 'example.com' })
64
65
  end
65
66
 
66
67
  def test_ff_get_with_attributes
67
- assert_nil @client.get("just_my_domain", "abc123", { domain: "gmail.com" })
68
- assert_equal "DEFAULT", @client.get("just_my_domain", "abc123", { domain: "gmail.com" }, "DEFAULT")
68
+ assert_nil @client.get('just_my_domain', 'abc123', { domain: 'gmail.com' })
69
+ assert_equal 'DEFAULT', @client.get('just_my_domain', 'abc123', { domain: 'gmail.com' }, 'DEFAULT')
69
70
 
70
- assert_equal "new-version", @client.get("just_my_domain", "abc123", { domain: "prefab.cloud" })
71
- assert_equal "new-version", @client.get("just_my_domain", "abc123", { domain: "example.com" })
71
+ assert_equal 'new-version', @client.get('just_my_domain', 'abc123', { domain: 'prefab.cloud' })
72
+ assert_equal 'new-version', @client.get('just_my_domain', 'abc123', { domain: 'example.com' })
72
73
  end
73
74
 
74
75
  def test_getting_feature_flag_value
75
- assert_equal false, @client.enabled?("flag_with_a_value")
76
- assert_equal "all-features", @client.get("flag_with_a_value")
76
+ assert_equal false, @client.enabled?('flag_with_a_value')
77
+ assert_equal 'all-features', @client.get('flag_with_a_value')
77
78
  end
78
79
 
79
80
  def test_ssl_certs
80
- certs = @client.send(:ssl_certs).split("-----BEGIN CERTIFICATE-----")
81
+ certs = @client.send(:ssl_certs).split('-----BEGIN CERTIFICATE-----')
81
82
 
82
83
  # This is a smoke test to make sure multiple certs are loaded
83
84
  assert certs.length > 1
@@ -87,9 +88,9 @@ class TestClient < Minitest::Test
87
88
 
88
89
  def new_client(overrides = {})
89
90
  options = Prefab::Options.new(**{
90
- prefab_config_override_dir: "none",
91
- prefab_config_classpath_dir: "test",
92
- prefab_envs: ["unit_tests"],
91
+ prefab_config_override_dir: 'none',
92
+ prefab_config_classpath_dir: 'test',
93
+ prefab_envs: ['unit_tests'],
93
94
  prefab_datasources: Prefab::Options::DATASOURCES::LOCAL_ONLY
94
95
  }.merge(overrides))
95
96
 
@@ -1,12 +1,13 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require 'test_helper'
3
4
 
4
5
  class TestConfigClient < Minitest::Test
5
6
  def setup
6
7
  options = Prefab::Options.new(
7
- prefab_config_override_dir: "none",
8
- prefab_config_classpath_dir: "test",
9
- prefab_envs: "unit_tests",
8
+ prefab_config_override_dir: 'none',
9
+ prefab_config_classpath_dir: 'test',
10
+ prefab_envs: 'unit_tests',
10
11
  prefab_datasources: Prefab::Options::DATASOURCES::LOCAL_ONLY
11
12
  )
12
13
 
@@ -14,59 +15,59 @@ class TestConfigClient < Minitest::Test
14
15
  end
15
16
 
16
17
  def test_load
17
- assert_equal "test sample value", @config_client.get("sample")
18
- assert_equal 123, @config_client.get("sample_int")
19
- assert_equal 12.12, @config_client.get("sample_double")
20
- assert_equal true, @config_client.get("sample_bool")
21
- assert_equal :ERROR, @config_client.get("log-level.app")
18
+ assert_equal 'test sample value', @config_client.get('sample')
19
+ assert_equal 123, @config_client.get('sample_int')
20
+ assert_equal 12.12, @config_client.get('sample_double')
21
+ assert_equal true, @config_client.get('sample_bool')
22
+ assert_equal :ERROR, @config_client.get('log-level.app')
22
23
  end
23
24
 
24
25
  def test_initialization_timeout_error
25
26
  options = Prefab::Options.new(
26
- api_key: "123-ENV-KEY-SDK",
27
+ api_key: '123-ENV-KEY-SDK',
27
28
  initialization_timeout_sec: 0.01,
28
29
  logdev: StringIO.new
29
30
  )
30
31
 
31
32
  err = assert_raises(Prefab::Errors::InitializationTimeoutError) do
32
- Prefab::Client.new(options).config_client.get("anything")
33
+ Prefab::Client.new(options).config_client.get('anything')
33
34
  end
34
35
 
35
36
  assert_match(/couldn't initialize in 0.01 second timeout/, err.message)
36
37
  end
37
38
 
38
39
  def test_prefab_envs_is_forgiving
39
- assert_equal ["my_env"], Prefab::Options.new(
40
- prefab_envs: "my_env",
40
+ assert_equal ['my_env'], Prefab::Options.new(
41
+ prefab_envs: 'my_env'
41
42
  ).prefab_envs
42
43
 
43
- assert_equal ["my_env", "a_second_env"], Prefab::Options.new(
44
- prefab_envs: ["my_env", "a_second_env"],
44
+ assert_equal %w[my_env a_second_env], Prefab::Options.new(
45
+ prefab_envs: %w[my_env a_second_env]
45
46
  ).prefab_envs
46
47
  end
47
48
 
48
49
  def test_prefab_envs_env_var
49
- ENV["PREFAB_ENVS"] = "one,two"
50
- assert_equal ["one", "two"], Prefab::Options.new().prefab_envs
50
+ ENV['PREFAB_ENVS'] = 'one,two'
51
+ assert_equal %w[one two], Prefab::Options.new.prefab_envs
51
52
  end
52
53
 
53
54
  def test_invalid_api_key_error
54
55
  options = Prefab::Options.new(
55
- api_key: "",
56
+ api_key: ''
56
57
  )
57
58
 
58
59
  err = assert_raises(Prefab::Errors::InvalidApiKeyError) do
59
- Prefab::Client.new(options).config_client.get("anything")
60
+ Prefab::Client.new(options).config_client.get('anything')
60
61
  end
61
62
 
62
63
  assert_match(/No API key/, err.message)
63
64
 
64
65
  options = Prefab::Options.new(
65
- api_key: "invalid",
66
+ api_key: 'invalid'
66
67
  )
67
68
 
68
69
  err = assert_raises(Prefab::Errors::InvalidApiKeyError) do
69
- Prefab::Client.new(options).config_client.get("anything")
70
+ Prefab::Client.new(options).config_client.get('anything')
70
71
  end
71
72
 
72
73
  assert_match(/format is invalid/, err.message)