puppet 4.9.1-universal-darwin → 4.9.2-universal-darwin

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of puppet might be problematic. Click here for more details.

Files changed (37) hide show
  1. data/Gemfile +1 -0
  2. data/lib/puppet/forge/repository.rb +1 -1
  3. data/lib/puppet/functions/assert_type.rb +3 -5
  4. data/lib/puppet/functions/yaml_data.rb +6 -1
  5. data/lib/puppet/pops/adapters.rb +10 -11
  6. data/lib/puppet/pops/evaluator/evaluator_impl.rb +2 -2
  7. data/lib/puppet/pops/evaluator/relationship_operator.rb +1 -1
  8. data/lib/puppet/pops/evaluator/runtime3_support.rb +1 -1
  9. data/lib/puppet/pops/loader/loader_paths.rb +4 -1
  10. data/lib/puppet/pops/loaders.rb +2 -0
  11. data/lib/puppet/pops/lookup/environment_data_provider.rb +10 -2
  12. data/lib/puppet/pops/lookup/hiera_config.rb +40 -8
  13. data/lib/puppet/pops/lookup/interpolation.rb +4 -2
  14. data/lib/puppet/pops/lookup/invocation.rb +2 -2
  15. data/lib/puppet/pops/lookup/lookup_adapter.rb +8 -2
  16. data/lib/puppet/pops/lookup/lookup_key_function_provider.rb +7 -3
  17. data/lib/puppet/pops/merge_strategy.rb +1 -1
  18. data/lib/puppet/pops/types/type_parser.rb +5 -11
  19. data/lib/puppet/ssl/certificate.rb +1 -1
  20. data/lib/puppet/ssl/certificate_request.rb +1 -1
  21. data/lib/puppet/version.rb +1 -1
  22. data/spec/lib/puppet_spec/language.rb +3 -1
  23. data/spec/unit/face/module/search_spec.rb +1 -1
  24. data/spec/unit/forge/repository_spec.rb +27 -0
  25. data/spec/unit/functions/lookup_spec.rb +396 -43
  26. data/spec/unit/network/http/factory_spec.rb +4 -0
  27. data/spec/unit/pops/evaluator/evaluating_parser_spec.rb +5 -0
  28. data/spec/unit/pops/evaluator/evaluator_rspec_helper.rb +28 -24
  29. data/spec/unit/pops/loaders/loaders_spec.rb +117 -22
  30. data/spec/unit/pops/types/ruby_generator_spec.rb +13 -13
  31. data/spec/unit/pops/types/type_calculator_spec.rb +13 -25
  32. data/spec/unit/pops/types/type_parser_spec.rb +1 -4
  33. data/spec/unit/resource/type_spec.rb +8 -2
  34. data/spec/unit/ssl/certificate_factory_spec.rb +2 -2
  35. data/spec/unit/ssl/certificate_request_spec.rb +2 -3
  36. data/spec/unit/util/http_proxy_spec.rb +4 -0
  37. metadata +2 -2
data/Gemfile CHANGED
@@ -64,6 +64,7 @@ group(:development, :test) do
64
64
  gem 'webmock', '~> 1.24'
65
65
  gem 'vcr', '~> 2.9'
66
66
  gem "hocon", :require => false
67
+ gem "hiera-eyaml", :require => false
67
68
  end
68
69
 
69
70
  group(:development) do
@@ -45,7 +45,7 @@ class Puppet::Forge
45
45
  # Return a Net::HTTPResponse read for this +path+.
46
46
  def make_http_request(path, io = nil)
47
47
  Puppet.debug "HTTP GET #{@host}#{path}"
48
- request = get_request_object(path)
48
+ request = get_request_object(@uri.path.chomp('/')+path)
49
49
  return read_response(request, io)
50
50
  end
51
51
 
@@ -50,7 +50,7 @@
50
50
  #
51
51
  # @since 4.0.0
52
52
  #
53
- Puppet::Functions.create_function(:assert_type, Puppet::Functions::InternalFunction) do
53
+ Puppet::Functions.create_function(:assert_type) do
54
54
  dispatch :assert_type do
55
55
  param 'Type', :type
56
56
  param 'Any', :value
@@ -58,7 +58,6 @@ Puppet::Functions.create_function(:assert_type, Puppet::Functions::InternalFunct
58
58
  end
59
59
 
60
60
  dispatch :assert_type_s do
61
- scope_param
62
61
  param 'String', :type_string
63
62
  param 'Any', :value
64
63
  optional_block_param 'Callable[Type, Type]', :block
@@ -84,12 +83,11 @@ Puppet::Functions.create_function(:assert_type, Puppet::Functions::InternalFunct
84
83
  value
85
84
  end
86
85
 
87
- # @param scope [Puppet::Parser::Scope] scope used when obtaining loader for defined types
88
86
  # @param type_string [String] the type the value must be an instance of given in String form
89
87
  # @param value [Object] the value to assert
90
88
  #
91
- def assert_type_s(scope, type_string, value, &proc)
92
- t = Puppet::Pops::Types::TypeParser.singleton.parse(type_string, scope)
89
+ def assert_type_s(type_string, value, &proc)
90
+ t = Puppet::Pops::Types::TypeParser.singleton.parse(type_string)
93
91
  block_given? ? assert_type(t, value, &proc) : assert_type(t, value)
94
92
  end
95
93
  end
@@ -10,7 +10,12 @@ Puppet::Functions.create_function(:yaml_data) do
10
10
 
11
11
  def yaml_data(options, context)
12
12
  begin
13
- data = YAML.load_file(options['path'])
13
+ path = options['path']
14
+ data = YAML.load_file(path)
15
+ unless data.is_a?(Hash)
16
+ Puppet.warning("#{path}: file does not contain a valid yaml hash")
17
+ data = {}
18
+ end
14
19
  Puppet::Pops::Lookup::HieraConfig.symkeys_to_string(data.nil? ? {} : data)
15
20
  rescue YAML::SyntaxError => ex
16
21
  # Psych errors includes the absolute path to the file, so no need to add that
@@ -118,19 +118,18 @@ module Adapters
118
118
 
119
119
  # Finds the loader to use when loading originates from the source position of the given argument.
120
120
  #
121
- # @param [Model::PopsObject] instance The model object
122
- # @param [Puppet::Parser::Scope] scope The scope to use
123
- # @param [String] file the file from where the model was parsed
124
- # @return [Loader,nil] the found loader or `nil` if it could not be found
121
+ # @param instance [Model::PopsObject] The model object
122
+ # @param file [String] the file from where the model was parsed
123
+ # @param default_loader [Loader] the loader to return if no loader is found for the model
124
+ # @return [Loader] the found loader or default_loader if it could not be found
125
125
  #
126
- def self.loader_for_model_object(model, scope, file = nil)
127
- if scope.nil?
128
- loaders = Puppet.lookup(:loaders) { nil }
129
- loaders.nil? ? nil : loaders.private_environment_loader
126
+ def self.loader_for_model_object(model, file = nil, default_loader = nil)
127
+ loaders = Puppet.lookup(:loaders) { nil }
128
+ if loaders.nil?
129
+ default_loader
130
130
  else
131
- loaders = scope.compiler.loaders
132
- loader_name = loader_name_by_source(scope.environment, model, file)
133
- loader_name.nil? ? loaders.private_environment_loader : loaders[loader_name]
131
+ loader_name = loader_name_by_source(loaders.environment, model, file)
132
+ loader_name.nil? ? default_loader || loaders.find_loader(nil) : loaders[loader_name]
134
133
  end
135
134
  end
136
135
 
@@ -309,7 +309,7 @@ class EvaluatorImpl
309
309
  # A QualifiedReference (i.e. a capitalized qualified name such as Foo, or Foo::Bar) evaluates to a PType
310
310
  #
311
311
  def eval_QualifiedReference(o, scope)
312
- type = Types::TypeParser.singleton.interpret(o, scope)
312
+ type = Types::TypeParser.singleton.interpret(o)
313
313
  fail(Issues::UNKNOWN_RESOURCE_TYPE, o, {:type_name => type.type_string }) if type.is_a?(Types::PTypeReferenceType)
314
314
  type
315
315
  end
@@ -473,7 +473,7 @@ class EvaluatorImpl
473
473
  keys = o.keys || []
474
474
  if left.is_a?(Types::PHostClassType)
475
475
  # Evaluate qualified references without errors no undefined types
476
- keys = keys.map {|key| key.is_a?(Model::QualifiedReference) ? Types::TypeParser.singleton.interpret(key, scope) : evaluate(key, scope) }
476
+ keys = keys.map {|key| key.is_a?(Model::QualifiedReference) ? Types::TypeParser.singleton.interpret(key) : evaluate(key, scope) }
477
477
  else
478
478
  keys = keys.map {|key| evaluate(key, scope) }
479
479
  # Resource[File] becomes File
@@ -56,7 +56,7 @@ class RelationshipOperator
56
56
  # A string must be a type reference in string format
57
57
  # @api private
58
58
  def transform_String(o, scope)
59
- assert_catalog_type(Types::TypeParser.singleton.parse(o, scope), scope)
59
+ assert_catalog_type(Types::TypeParser.singleton.parse(o), scope)
60
60
  end
61
61
 
62
62
  # A qualified name is short hand for a class with this name
@@ -277,7 +277,7 @@ module Runtime3Support
277
277
 
278
278
  def call_function(name, args, o, scope, &block)
279
279
  file, line = extract_file_line(o)
280
- loader = Adapters::LoaderAdapter.loader_for_model_object(o, scope, file)
280
+ loader = Adapters::LoaderAdapter.loader_for_model_object(o, file)
281
281
  if loader && func = loader.load(:function, name)
282
282
  Puppet::Util::Profiler.profile(name, [:functions, name]) do
283
283
  # Add stack frame when calling. See Puppet::Pops::PuppetStack
@@ -110,7 +110,10 @@ module Puppet::Pops::Loader::LoaderPaths
110
110
  # Puppet name to path always skips the name-space as that is part of the generic path
111
111
  # i.e. <module>/mymodule/functions/foo.pp is the function mymodule::foo
112
112
  parts = typed_name.name_parts
113
- parts = parts[start_index_in_name..-1] if parts.size > 1
113
+ if start_index_in_name > 0
114
+ return nil if start_index_in_name >= parts.size
115
+ parts = parts[start_index_in_name..-1]
116
+ end
114
117
  "#{File.join(generic_path, parts)}.pp"
115
118
  end
116
119
  end
@@ -16,6 +16,7 @@ class Loaders
16
16
  attr_reader :public_environment_loader
17
17
  attr_reader :private_environment_loader
18
18
  attr_reader :implementation_registry
19
+ attr_reader :environment
19
20
 
20
21
  def self.new(environment, for_agent = false)
21
22
  obj = environment.loaders
@@ -30,6 +31,7 @@ class Loaders
30
31
  # Protect against environment havoc
31
32
  raise ArgumentError.new("Attempt to redefine already initialized loaders for environment") unless environment.loaders.nil?
32
33
  environment.loaders = self
34
+ @environment = environment
33
35
  @loaders_by_name = {}
34
36
 
35
37
  add_loader_by_name(self.class.static_loader)
@@ -11,8 +11,16 @@ class EnvironmentDataProvider < ConfiguredDataProvider
11
11
  protected
12
12
 
13
13
  def assert_config_version(config)
14
- raise Puppet::DataBinding::LookupError, "#{config.name} cannot be used in an environment" unless config.version > 3
15
- config
14
+ if config.version > 3
15
+ config
16
+ else
17
+ if Puppet[:strict] == :error
18
+ raise Puppet::DataBinding::LookupError, "#{config.name} cannot be used in an environment"
19
+ else
20
+ Puppet.warn_once(:hiera_v3_at_env_root, config.config_path, 'hiera.yaml version 3 found at the environment root was ignored')
21
+ end
22
+ nil
23
+ end
16
24
  end
17
25
 
18
26
  # Return the root of the environment
@@ -12,11 +12,11 @@ class ScopeLookupCollectingInvocation < Invocation
12
12
 
13
13
  def initialize(scope)
14
14
  super(scope)
15
- @scope_interpolations = {}
15
+ @scope_interpolations = []
16
16
  end
17
17
 
18
- def remember_scope_lookup(key, value)
19
- @scope_interpolations[key] = value
18
+ def remember_scope_lookup(*lookup_result)
19
+ @scope_interpolations << lookup_result
20
20
  end
21
21
  end
22
22
 
@@ -155,20 +155,28 @@ class HieraConfig
155
155
  # @param parent_data_provider [DataProvider] The data provider that loaded this configuration
156
156
  # @return [Array<DataProvider>] the data providers
157
157
  def configured_data_providers(lookup_invocation, parent_data_provider)
158
- scope = lookup_invocation.scope
159
- unless @data_providers && scope_interpolations_stable?(scope)
158
+ unless @data_providers && scope_interpolations_stable?(lookup_invocation)
160
159
  if @data_providers
161
160
  lookup_invocation.report_text { 'Hiera configuration recreated due to change of scope variables used in interpolation expressions' }
162
161
  end
163
- slc_invocation = ScopeLookupCollectingInvocation.new(scope)
162
+ slc_invocation = ScopeLookupCollectingInvocation.new(lookup_invocation.scope)
164
163
  @data_providers = create_configured_data_providers(slc_invocation, parent_data_provider)
165
164
  @scope_interpolations = slc_invocation.scope_interpolations
166
165
  end
167
166
  @data_providers
168
167
  end
169
168
 
170
- def scope_interpolations_stable?(scope)
171
- @scope_interpolations.all? { |key, value| scope[key].eql?(value) }
169
+ def scope_interpolations_stable?(lookup_invocation)
170
+ scope = lookup_invocation.scope
171
+ @scope_interpolations.all? do |key, root_key, segments, old_value|
172
+ value = scope[root_key]
173
+ unless value.nil? || segments.empty?
174
+ found = '';
175
+ catch(:no_such_key) { found = sub_lookup(key, lookup_invocation, segments, value) }
176
+ value = found;
177
+ end
178
+ old_value.eql?(value)
179
+ end
172
180
  end
173
181
 
174
182
  # @api private
@@ -207,6 +215,30 @@ class HieraConfig
207
215
  Hiera.logger = 'puppet'
208
216
  end
209
217
  end
218
+
219
+ unless Hiera::Interpolate.const_defined?(:PATCHED_BY_HIERA_5)
220
+ # Replace the class methods 'hiera_interpolate' and 'alias_interpolate' with a method that wires back and performs global
221
+ # lookups using the lookup framework. This is necessary since the classic Hiera is made aware only of custom backends.
222
+ class << Hiera::Interpolate
223
+ hiera_interpolate = Proc.new do |data, key, scope, extra_data, context|
224
+ override = context[:order_override]
225
+ invocation = Puppet::Pops::Lookup::Invocation.current
226
+ unless override.nil? && invocation.global_only?
227
+ invocation = Puppet::Pops::Lookup::Invocation.new(scope)
228
+ invocation.set_global_only
229
+ invocation.set_hiera_v3_location_overrides(override) unless override.nil?
230
+ end
231
+ Puppet::Pops::Lookup::LookupAdapter.adapt(scope.compiler).lookup(key, invocation, nil)
232
+ end
233
+
234
+ send(:remove_method, :hiera_interpolate)
235
+ send(:remove_method, :alias_interpolate)
236
+ send(:define_method, :hiera_interpolate, hiera_interpolate)
237
+ send(:define_method, :alias_interpolate, hiera_interpolate)
238
+ end
239
+ Hiera::Interpolate.send(:const_set, :PATCHED_BY_HIERA_5, true)
240
+ end
241
+
210
242
  Hiera::Config.instance_variable_set(:@config, hiera3_config)
211
243
 
212
244
  # Use a special lookup_key that delegates to the backend
@@ -20,7 +20,9 @@ module Interpolation
20
20
  when Array
21
21
  value.map { |element| interpolate(element, context, allow_methods) }
22
22
  when Hash
23
- Hash[value.map { |k, v| [k, interpolate(v, context, allow_methods)] }]
23
+ result = {}
24
+ value.each_pair { |k, v| result[interpolate(k, context, allow_methods)] = interpolate(v, context, allow_methods) }
25
+ result
24
26
  else
25
27
  value
26
28
  end
@@ -95,7 +97,7 @@ module Interpolation
95
97
  catch(:no_such_key) { found = sub_lookup(key, lookup_invocation, segments, value) }
96
98
  value = found;
97
99
  end
98
- lookup_invocation.remember_scope_lookup(key, value)
100
+ lookup_invocation.remember_scope_lookup(key, root_key, segments, value)
99
101
  value
100
102
  end
101
103
 
@@ -74,7 +74,7 @@ class Invocation
74
74
  rescue Puppet::DataBinding::LookupError
75
75
  raise
76
76
  rescue Puppet::Error => detail
77
- raise Puppet::DataBinding::LookupError.new(detail.message, detail)
77
+ raise Puppet::DataBinding::LookupError.new(detail.message, nil, nil, nil, detail)
78
78
  ensure
79
79
  @name_stack.pop
80
80
  end
@@ -93,7 +93,7 @@ class Invocation
93
93
  # values that the configuration was based on
94
94
  #
95
95
  # @api private
96
- def remember_scope_lookup(key, value)
96
+ def remember_scope_lookup(*lookup_result)
97
97
  # Does nothing by default
98
98
  end
99
99
 
@@ -318,7 +318,10 @@ class LookupAdapter < DataAdapter
318
318
  if mod.has_hiera_conf?
319
319
  mp = ModuleDataProvider.new(module_name)
320
320
  # A version 5 hiera.yaml trumps a data provider setting or binding in the module
321
- if mp.config(lookup_invocation).version >= 5
321
+ mp_config = mp.config(lookup_invocation)
322
+ if mp_config.nil?
323
+ mp = nil
324
+ elsif mp_config.version >= 5
322
325
  unless provider_name.nil? || Puppet[:strict] == :off
323
326
  if binding
324
327
  Puppet.warn_once(:deprecation, "ModuleBinding#data_provider-#{module_name}",
@@ -388,7 +391,10 @@ class LookupAdapter < DataAdapter
388
391
  if config_path.exist?
389
392
  ep = EnvironmentDataProvider.new
390
393
  # A version 5 hiera.yaml trumps any data provider setting in the environment.conf
391
- if ep.config(lookup_invocation).version >= 5
394
+ ep_config = ep.config(lookup_invocation)
395
+ if ep_config.nil?
396
+ ep = nil
397
+ elsif ep_config.version >= 5
392
398
  unless provider_name.nil? || Puppet[:strict] == :off
393
399
  Puppet.warn_once(:deprecation, 'environment.conf#data_provider',
394
400
  "Defining environment_data_provider='#{provider_name}' in environment.conf is deprecated", env_path + 'environment.conf')
@@ -97,13 +97,17 @@ class V3BackendFunctionProvider < LookupKeyFunctionProvider
97
97
  # Equivalent to Hiera :hash with default :native merge behavior. A Hash must be passed here
98
98
  # to override possible Hiera deep merge config settings.
99
99
  { :behavior => :native }
100
- when 'deep'
100
+ when 'deep', 'unconstrained_deep'
101
101
  # Equivalent to Hiera :hash with :deeper merge behavior.
102
102
  { :behavior => :deeper }
103
+ when 'reverse_deep'
104
+ # Equivalent to Hiera :hash with :deep merge behavior.
105
+ { :behavior => :deep }
103
106
  when Hash
104
107
  strategy = merge['strategy']
105
- if strategy == 'deep'
106
- result = { :behavior => :deeper }
108
+ case strategy
109
+ when 'deep', 'unconstrained_deep', 'reverse_deep'
110
+ result = { :behavior => strategy == 'reverse_deep' ? :deep : :deeper }
107
111
  # Remaining entries must have symbolic keys
108
112
  merge.each_pair { |k,v| result[k.to_sym] = v unless k == 'strategy' }
109
113
  result
@@ -380,7 +380,7 @@ module Puppet::Pops
380
380
  end
381
381
 
382
382
  def value_t
383
- @value_t ||= Types::TypeParser.singleton.parse('Variant[Array[Data],Hash[String,Data]]')
383
+ @value_t ||= Types::PAnyType::DEFAULT
384
384
  end
385
385
 
386
386
  MergeStrategy.add_strategy(self)
@@ -25,9 +25,9 @@ class TypeParser
25
25
  # parser.parse('Array[String]')
26
26
  # parser.parse('Hash[Integer, Array[String]]')
27
27
  #
28
- # @param string [String] a string with the type expressed in stringified form as produced by the
28
+ # @param string [String] a string with the type expressed in stringified form as produced by the
29
29
  # types {"#to_s} method.
30
- # @param context [Puppet::Parser::Scope,Loader::Loader] scope or loader to use when loading type aliases
30
+ # @param context [Loader::Loader] optional loader used as no adapted loader is found
31
31
  # @return [PAnyType] a specialization of the PAnyType representing the type.
32
32
  #
33
33
  # @api public
@@ -44,11 +44,11 @@ class TypeParser
44
44
  end
45
45
 
46
46
  # @param ast [Puppet::Pops::Model::PopsObject] the ast to interpret
47
- # @param context [Puppet::Parser::Scope,Loader::Loader, nil] scope or loader to use when loading type aliases
47
+ # @param context [Loader::Loader] optional loader used when no adapted loader is found
48
48
  # @return [PAnyType] a specialization of the PAnyType representing the type.
49
49
  #
50
50
  # @api public
51
- def interpret(ast, context)
51
+ def interpret(ast, context = nil)
52
52
  result = @type_transformer.visit_this_1(self, ast, context)
53
53
  raise_invalid_type_specification_error(ast) unless result.is_a?(PAnyType)
54
54
  result
@@ -200,13 +200,7 @@ class TypeParser
200
200
 
201
201
  # @api private
202
202
  def loader_from_context(ast, context)
203
- if context.nil?
204
- nil
205
- elsif context.is_a?(Puppet::Pops::Loader::Loader)
206
- context
207
- else
208
- Puppet::Pops::Adapters::LoaderAdapter.loader_for_model_object(ast, context)
209
- end
203
+ Adapters::LoaderAdapter.loader_for_model_object(ast, nil, context)
210
204
  end
211
205
 
212
206
  # @api private
@@ -69,7 +69,7 @@ DOC
69
69
  # is where the extensions are stored."
70
70
  @extensions_tag ||= 3
71
71
 
72
- @exts_seq ||= OpenSSL::ASN1.decode(content.to_der).value[0].find do |data|
72
+ @exts_seq ||= OpenSSL::ASN1.decode(content.to_der).value[0].value.find do |data|
73
73
  (data.tag == @extensions_tag) && (data.tag_class == :CONTEXT_SPECIFIC)
74
74
  end.value[0]
75
75
  end
@@ -204,7 +204,7 @@ DOC
204
204
  end
205
205
 
206
206
  x509_attributes.map do |attr|
207
- {"oid" => attr.oid, "value" => attr.value.first.value}
207
+ {"oid" => attr.oid, "value" => attr.value.value.first.value}
208
208
  end
209
209
  end
210
210
 
@@ -7,7 +7,7 @@
7
7
 
8
8
 
9
9
  module Puppet
10
- PUPPETVERSION = '4.9.1'
10
+ PUPPETVERSION = '4.9.2'
11
11
 
12
12
  ##
13
13
  # version is a public API method intended to always provide a fast and
@@ -21,7 +21,9 @@ module PuppetSpec::Language
21
21
 
22
22
  compiler.send(:instance_variable_set, :@catalog, catalog)
23
23
 
24
- expect(evaluator.evaluate_string(compiler.topscope, resources)).to eq(true)
24
+ Puppet.override(:loaders => compiler.loaders) do
25
+ expect(evaluator.evaluate_string(compiler.topscope, resources)).to eq(true)
26
+ end
25
27
  when Array
26
28
  catalog = PuppetSpec::Compiler.compile_to_catalog(manifest)
27
29
 
@@ -171,7 +171,7 @@ describe "puppet module search" do
171
171
  end
172
172
  end
173
173
 
174
- it "should accept the --module-repository option" do
174
+ it "should accept the --module_repository option" do
175
175
  forge = mock("Puppet::Forge")
176
176
  searcher = mock("Searcher")
177
177
  options[:module_repository] = "http://forge.example.com"
@@ -6,6 +6,11 @@ require 'puppet/forge/cache'
6
6
  require 'puppet/forge/errors'
7
7
 
8
8
  describe Puppet::Forge::Repository do
9
+ before(:all) do
10
+ # any local http proxy will break these tests
11
+ ENV['http_proxy'] = nil
12
+ ENV['HTTP_PROXY'] = nil
13
+ end
9
14
  let(:agent) { "Test/1.0" }
10
15
  let(:repository) { Puppet::Forge::Repository.new('http://fake.com', agent) }
11
16
  let(:ssl_repository) { Puppet::Forge::Repository.new('https://fake.com', agent) }
@@ -43,6 +48,28 @@ describe Puppet::Forge::Repository do
43
48
  expect(repository.make_http_request("the_path")).to eq(result)
44
49
  end
45
50
 
51
+ it "merges forge URI and path specified" do
52
+ result = "#{Object.new}"
53
+
54
+ performs_an_http_request result do |http|
55
+ http.expects(:request).with(responds_with(:path, "/test/the_path/"))
56
+ end
57
+
58
+ repository = Puppet::Forge::Repository.new('http://fake.com/test', agent)
59
+ expect(repository.make_http_request("/the_path/")).to eq(result)
60
+ end
61
+
62
+ it "handles trailing slashes when merging URI and path" do
63
+ result = "#{Object.new}"
64
+
65
+ performs_an_http_request result do |http|
66
+ http.expects(:request).with(responds_with(:path, "/test/the_path"))
67
+ end
68
+
69
+ repository = Puppet::Forge::Repository.new('http://fake.com/test/', agent)
70
+ expect(repository.make_http_request("/the_path")).to eq(result)
71
+ end
72
+
46
73
  it 'returns the result object from a request with ssl' do
47
74
  result = "#{Object.new}"
48
75
  performs_an_https_request result do |http|