summaryrefslogtreecommitdiff
path: root/spec/spec_helper.rb
blob: e6d1bcc3ce4637e177be40602ea6f6eabb25c2ec (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
#
# Author:: Adam Jacob (<adam@chef.io>)
# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

# If you need to add anything in here, don't.
# Add it to one of the files in spec/support

# Abuse ruby's constant lookup to avoid undefined constant errors
module Shell
  JUST_TESTING_MOVE_ALONG = true unless defined? JUST_TESTING_MOVE_ALONG
  IRB = nil unless defined? IRB
end

$LOAD_PATH.unshift File.expand_path("..", __dir__)

$LOAD_PATH.unshift File.expand_path("../chef-config/lib", __dir__)
$LOAD_PATH.unshift File.expand_path("../chef-utils/lib", __dir__)

require "rubygems"
require "rspec/mocks"

require "webmock/rspec"

require "chef"
require "chef/knife"

Dir["lib/chef/knife/**/*.rb"]
  .map { |f| f.gsub("lib/", "") }
  .map { |f| f.gsub(/\.rb$/, "") }
  .each { |f| require f }

require "chef/resource_resolver"
require "chef/provider_resolver"

require "chef/mixins"
require "chef/dsl"
require "chef/application"
require "chef/applications"

require "chef/shell"
require "chef/util/file_edit"

require "chef/config"

require "chef/chef_fs/file_system_cache"

require "chef/api_client_v1"

require "chef/mixin/versioned_api"
require "chef/server_api_versions"

if ENV["CHEF_FIPS"] == "1"
  Chef::Config.init_openssl
end

# If you want to load anything into the testing environment
# without versioning it, add it to spec/support/local_gems.rb
require "spec/support/local_gems.rb" if File.exists?(File.join(File.dirname(__FILE__), "support", "local_gems.rb"))

# Explicitly require spec helpers that need to load first
require "spec/support/platform_helpers"
require "spec/support/shared/unit/mock_shellout"

require "spec/support/recipe_dsl_helper"

# Autoloads support files
# Excludes support/platforms by default
# Do not change the gsub.
Dir["spec/support/**/*.rb"]
  .reject { |f| f =~ %r{^spec/support/platforms} }
  .reject { |f| f =~ %r{^spec/support/pedant} }
  .map { |f| f.gsub(/.rb$/, "") }
  .map { |f| f.gsub(%r{spec/}, "") }
  .each { |f| require f }

OHAI_SYSTEM = Ohai::System.new
OHAI_SYSTEM.all_plugins(["platform", "hostname", "languages/powershell"])

test_node = Chef::Node.new
test_node.automatic["os"] = (OHAI_SYSTEM["os"] || "unknown_os").dup.freeze
test_node.automatic["platform_family"] = (OHAI_SYSTEM["platform_family"] || "unknown_platform_family").dup.freeze
test_node.automatic["platform"] = (OHAI_SYSTEM["platform"] || "unknown_platform").dup.freeze
test_node.automatic["platform_version"] = (OHAI_SYSTEM["platform_version"] || "unknown_platform_version").dup.freeze
TEST_NODE = test_node.freeze
TEST_OS = TEST_NODE["os"]
TEST_PLATFORM = TEST_NODE["platform"]
TEST_PLATFORM_VERSION = TEST_NODE["platform_version"]
TEST_PLATFORM_FAMILY = TEST_NODE["platform_family"]

provider_priority_map ||= nil
resource_priority_map ||= nil
provider_handler_map ||= nil
resource_handler_map ||= nil

class UnexpectedSystemExit < RuntimeError
  def self.from(system_exit)
    new(system_exit.message).tap { |e| e.set_backtrace(system_exit.backtrace) }
  end
end

RSpec.configure do |config|
  config.include(Matchers)
  config.include(MockShellout::RSpec)
  config.filter_run focus: true
  config.filter_run_excluding external: true
  config.raise_on_warning = true

  # Explicitly disable :should syntax
  # And set max_formatted_output_length to nil to prevent RSpec from doing truncation.
  config.expect_with :rspec do |c|
    c.syntax = :expect
    c.max_formatted_output_length = nil
  end
  config.mock_with :rspec do |c|
    c.syntax = :expect
    c.allow_message_expectations_on_nil = false
  end

  # Only run these tests on platforms that are also chef workstations
  config.filter_run_excluding :workstation if solaris? || aix?

  # Tests that randomly fail, but may have value.
  config.filter_run_excluding volatile: true
  config.filter_run_excluding volatile_on_solaris: true if solaris?
  config.filter_run_excluding volatile_from_verify: false

  config.filter_run_excluding skip_buildkite: true if ENV["BUILDKITE"]

  config.filter_run_excluding windows_only: true unless windows?
  config.filter_run_excluding not_supported_on_windows: true if windows?
  config.filter_run_excluding not_supported_on_macos: true if mac_osx?
  config.filter_run_excluding macos_only: true unless mac_osx?
  config.filter_run_excluding macos_1014: true unless mac_osx_1014?
  config.filter_run_excluding not_supported_on_aix: true if aix?
  config.filter_run_excluding not_supported_on_solaris: true if solaris?
  config.filter_run_excluding not_supported_on_gce: true if gce?
  config.filter_run_excluding win2012r2_only: true unless windows_2012r2?
  config.filter_run_excluding windows64_only: true unless windows64?
  config.filter_run_excluding windows32_only: true unless windows32?
  config.filter_run_excluding windows_gte_10: true unless windows_gte_10?
  config.filter_run_excluding windows_lt_10: true if windows_gte_10?
  config.filter_run_excluding ruby64_only: true unless ruby_64bit?
  config.filter_run_excluding ruby32_only: true unless ruby_32bit?
  config.filter_run_excluding windows_powershell_dsc_only: true unless windows_powershell_dsc?
  config.filter_run_excluding windows_powershell_no_dsc_only: true if windows_powershell_dsc?
  config.filter_run_excluding windows_domain_joined_only: true unless windows_domain_joined?
  config.filter_run_excluding windows_not_domain_joined_only: true if windows_domain_joined?
  # We think this line was causing rspec tests to not run on the Jenkins windows
  # testers. If we ever fix it we should restore it.
  # config.filter_run_excluding :windows_service_requires_assign_token => true if !STDOUT.isatty && !windows_user_right?("SeAssignPrimaryTokenPrivilege")
  config.filter_run_excluding windows_service_requires_assign_token: true
  config.filter_run_excluding solaris_only: true unless solaris?
  config.filter_run_excluding system_windows_service_gem_only: true unless system_windows_service_gem?
  config.filter_run_excluding unix_only: true unless unix?
  config.filter_run_excluding linux_only: true unless linux?
  config.filter_run_excluding aix_only: true unless aix?
  config.filter_run_excluding suse_only: true unless suse?
  config.filter_run_excluding opensuse: true unless opensuse?
  config.filter_run_excluding debian_family_only: true unless debian_family?
  config.filter_run_excluding supports_cloexec: true unless supports_cloexec?
  config.filter_run_excluding selinux_only: true unless selinux_enabled?
  config.filter_run_excluding requires_root: true unless root?
  config.filter_run_excluding requires_root_or_running_windows: true unless root? || windows?
  config.filter_run_excluding requires_unprivileged_user: true if root?
  config.filter_run_excluding uses_diff: true unless has_diff?
  config.filter_run_excluding openssl_gte_101: true unless openssl_gte_101?
  config.filter_run_excluding openssl_lt_101: true unless openssl_lt_101?
  config.filter_run_excluding aes_256_gcm_only: true unless aes_256_gcm?
  config.filter_run_excluding broken: true
  config.filter_run_excluding not_wpar: true unless wpar?
  config.filter_run_excluding not_supported_under_fips: true if fips?
  config.filter_run_excluding rhel: true unless rhel?
  config.filter_run_excluding rhel6: true unless rhel6?
  config.filter_run_excluding rhel7: true unless rhel7?
  config.filter_run_excluding rhel8: true unless rhel8?
  config.filter_run_excluding rhel_gte_8: true unless rhel_gte_8?
  config.filter_run_excluding intel_64bit: true unless intel_64bit?
  config.filter_run_excluding not_rhel: true if rhel?
  config.filter_run_excluding not_rhel6: true if rhel6?
  config.filter_run_excluding not_rhel7: true if rhel7?
  config.filter_run_excluding not_intel_64bit: true if intel_64bit?

  # these let us use chef: ">= 13" or ruby: "~> 2.0.0" or any other Gem::Dependency-style constraint
  config.filter_run_excluding chef: DependencyProc.with(Chef::VERSION)
  config.filter_run_excluding ruby: DependencyProc.with(RUBY_VERSION)

  # check for particular binaries we need
  config.filter_run_excluding choco_installed: true unless choco_installed?
  config.filter_run_excluding requires_ifconfig: true unless ifconfig?

  running_platform_arch = `uname -m`.strip unless windows?

  config.filter_run_excluding arch: lambda { |target_arch|
    running_platform_arch != target_arch
  }

  # Functional Resource tests that are provider-specific:
  # context "on platforms that use useradd", :provider => {:user => Chef::Provider::User::Useradd}} do #...
  config.filter_run_excluding provider: lambda { |criteria|
    type, target_provider = criteria.first

    node = TEST_NODE.dup
    resource_class = Chef::ResourceResolver.resolve(type, node: node)
    if resource_class
      resource = resource_class.new("test", Chef::RunContext.new(node, nil, nil))
      begin
        provider = resource.provider_for_action(Array(resource_class.default_action).first)
        provider.class != target_provider
      rescue Chef::Exceptions::ProviderNotFound # no provider for platform
        true
      end
    else
      true
    end
  }

  config.run_all_when_everything_filtered = true

  config.before(:each) do
    # it'd be nice to run this with connections blocked or only to localhost, but we do make lots
    # of real connections, so cannot.  we reset it to allow connections every time to avoid
    # tests setting connections to be disabled and that state leaking into other tests.
    WebMock.allow_net_connect!

    Chef.reset!

    Chef::ChefFS::FileSystemCache.instance.reset!

    Chef::Config.reset

    Chef::Log.setup!

    # By default, treat deprecation warnings as errors in tests.
    Chef::Config.treat_deprecation_warnings_as_errors(true)

    # Set environment variable so the setting persists in child processes
    ENV["CHEF_TREAT_DEPRECATION_WARNINGS_AS_ERRORS"] = "1"

    # we don't perfectly reset the priority/handler maps here, but by dup'ing the top level hash we
    # throw away all the garbage resources and providers that we setup.  if we mutate something like
    # :package then that'll carry over from test-to-test, but the solution would be to deep-dup on every
    # single test we run which is much more expensive.  by throwing away the garbage top level keys we
    # significantly speed up test runs.
    provider_handler_map ||= Chef.provider_handler_map.send(:map).dup
    resource_handler_map ||= Chef.resource_handler_map.send(:map).dup
    provider_priority_map ||= Chef.provider_priority_map.send(:map).dup
    resource_priority_map ||= Chef.resource_priority_map.send(:map).dup
    Chef.provider_handler_map.instance_variable_set(:@map, provider_handler_map.dup)
    Chef.resource_handler_map.instance_variable_set(:@map, resource_handler_map.dup)
    Chef.provider_priority_map.instance_variable_set(:@map, provider_priority_map.dup)
    Chef.resource_priority_map.instance_variable_set(:@map, resource_priority_map.dup)
  end

  # This bit of jankiness guards against specs which accidentally drop privs when running as
  # root -- which are nearly impossible to debug and so we bail out very hard if this
  # condition ever happens.  If a spec stubs Process.[e]uid this can throw a false positive
  # which the spec must work around by unmocking Process.[e]uid to and_call_original in its
  # after block.
  if Process.euid == 0 && Process.uid == 0
    config.after(:each) do
      if Process.uid != 0
        RSpec.configure { |c| c.fail_fast = true }
        raise "rspec was invoked as root, but the last test dropped real uid to #{Process.uid}"
      end
      if Process.euid != 0
        RSpec.configure { |c| c.fail_fast = true }
        raise "rspec was invoked as root, but the last test dropped effective uid to #{Process.euid}"
      end
    end
  end

  # raise if anyone commits any test to CI with :focus set on it
  if ENV["CI"]
    config.before(:example, :focus) do
      raise "This example was committed with `:focus` and should not have been"
    end
  end

  config.before(:suite) do
    ARGV.clear
  end

  # Protect Rspec from accidental exit(0) causing rspec to terminate without error
  config.around(:example) do |ex|
    begin
      ex.run
    rescue SystemExit => e
      raise UnexpectedSystemExit.from(e)
    end
  end
end

require "webrick/utils"
require "thread"

#    Webrick uses a centralized/synchronized timeout manager. It works by
#    starting a thread to check for timeouts on an interval. The timeout
#    checker thread cannot be stopped or canceled in any easy way, and it
#    makes calls to Time.new, which fail when rspec is in the process of
#    creating a method stub for that method. Since our tests don't rely on
#    any timeout behavior enforced by webrick, disable the timeout manager
#    via a monkey patch.
#
#    Hopefully this fails loudly if the webrick code should change. As of this
#    writing, the relevant code is in webrick/utils, which can be located on
#    your system with:
#
#    $ gem which webrick/utils
module WEBrick
  module Utils
    class TimeoutHandler
      def initialize; end

      def register(*args); end

      def cancel(*args); end
    end
  end
end

# Enough stuff needs json serialization that I'm just adding it here for equality asserts
require "chef/json_compat"