test-kitchen 1.3.1 → 1.4.0.beta.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (101) hide show
  1. checksums.yaml +4 -4
  2. data/.cane +2 -0
  3. data/.gitignore +4 -0
  4. data/CHANGELOG.md +45 -0
  5. data/Rakefile +15 -0
  6. data/features/kitchen_action_commands.feature +12 -9
  7. data/features/kitchen_defaults.feature +38 -0
  8. data/features/kitchen_init_command.feature +0 -1
  9. data/features/kitchen_list_command.feature +2 -2
  10. data/features/kitchen_login_command.feature +7 -1
  11. data/features/kitchen_test_command.feature +4 -4
  12. data/lib/kitchen.rb +40 -11
  13. data/lib/kitchen/cli.rb +38 -22
  14. data/lib/kitchen/command/list.rb +5 -2
  15. data/lib/kitchen/config.rb +45 -18
  16. data/lib/kitchen/configurable.rb +137 -1
  17. data/lib/kitchen/data_munger.rb +248 -17
  18. data/lib/kitchen/driver.rb +1 -1
  19. data/lib/kitchen/driver/base.rb +1 -83
  20. data/lib/kitchen/driver/dummy.rb +0 -5
  21. data/lib/kitchen/driver/ssh_base.rb +177 -22
  22. data/lib/kitchen/instance.rb +140 -20
  23. data/lib/kitchen/logger.rb +43 -8
  24. data/lib/kitchen/login_command.rb +14 -5
  25. data/lib/kitchen/platform.rb +19 -0
  26. data/lib/kitchen/provisioner.rb +5 -3
  27. data/lib/kitchen/provisioner/base.rb +46 -48
  28. data/lib/kitchen/provisioner/chef/common_sandbox.rb +322 -0
  29. data/lib/kitchen/provisioner/chef_base.rb +179 -286
  30. data/lib/kitchen/provisioner/chef_solo.rb +11 -5
  31. data/lib/kitchen/provisioner/chef_zero.rb +108 -94
  32. data/lib/kitchen/provisioner/dummy.rb +47 -0
  33. data/lib/kitchen/provisioner/shell.rb +45 -12
  34. data/lib/kitchen/rake_tasks.rb +1 -1
  35. data/lib/kitchen/ssh.rb +1 -1
  36. data/lib/kitchen/thor_tasks.rb +1 -1
  37. data/lib/kitchen/transport.rb +54 -0
  38. data/lib/kitchen/transport/base.rb +146 -0
  39. data/lib/kitchen/transport/dummy.rb +75 -0
  40. data/lib/kitchen/transport/ssh.rb +325 -0
  41. data/lib/kitchen/transport/winrm.rb +508 -0
  42. data/lib/kitchen/transport/winrm/command_executor.rb +188 -0
  43. data/lib/kitchen/transport/winrm/file_transporter.rb +454 -0
  44. data/lib/kitchen/transport/winrm/logging.rb +50 -0
  45. data/lib/kitchen/transport/winrm/template.rb +74 -0
  46. data/lib/kitchen/transport/winrm/tmp_zip.rb +187 -0
  47. data/lib/kitchen/verifier.rb +55 -0
  48. data/lib/kitchen/verifier/base.rb +191 -0
  49. data/lib/kitchen/verifier/busser.rb +266 -0
  50. data/lib/kitchen/verifier/dummy.rb +75 -0
  51. data/lib/kitchen/version.rb +1 -1
  52. data/spec/kitchen/cli_spec.rb +56 -0
  53. data/spec/kitchen/config_spec.rb +61 -20
  54. data/spec/kitchen/configurable_spec.rb +327 -1
  55. data/spec/kitchen/data_munger_spec.rb +777 -14
  56. data/spec/kitchen/driver/base_spec.rb +7 -38
  57. data/spec/kitchen/driver/dummy_spec.rb +0 -29
  58. data/spec/kitchen/driver/ssh_base_spec.rb +580 -236
  59. data/spec/kitchen/driver_spec.rb +1 -0
  60. data/spec/kitchen/instance_spec.rb +383 -83
  61. data/spec/kitchen/login_command_spec.rb +29 -10
  62. data/spec/kitchen/platform_spec.rb +58 -2
  63. data/spec/kitchen/provisioner/base_spec.rb +170 -18
  64. data/spec/kitchen/provisioner/chef_base_spec.rb +454 -104
  65. data/spec/kitchen/provisioner/chef_solo_spec.rb +307 -104
  66. data/spec/kitchen/provisioner/chef_zero_spec.rb +561 -230
  67. data/spec/kitchen/provisioner/dummy_spec.rb +91 -0
  68. data/spec/kitchen/provisioner/shell_spec.rb +158 -56
  69. data/spec/kitchen/provisioner_spec.rb +37 -0
  70. data/spec/kitchen/ssh_spec.rb +19 -19
  71. data/spec/kitchen/transport/base_spec.rb +89 -0
  72. data/spec/kitchen/transport/ssh_spec.rb +1147 -0
  73. data/spec/kitchen/transport/winrm/command_executor_spec.rb +400 -0
  74. data/spec/kitchen/transport/winrm/file_transporter_spec.rb +876 -0
  75. data/spec/kitchen/transport/winrm/logging_spec.rb +92 -0
  76. data/spec/kitchen/transport/winrm/template_spec.rb +51 -0
  77. data/spec/kitchen/transport/winrm/tmp_zip_spec.rb +132 -0
  78. data/spec/kitchen/transport/winrm_spec.rb +1069 -0
  79. data/spec/kitchen/transport_spec.rb +112 -0
  80. data/spec/kitchen/verifier/base_spec.rb +310 -0
  81. data/spec/kitchen/verifier/busser_spec.rb +540 -0
  82. data/spec/kitchen/verifier/dummy_spec.rb +91 -0
  83. data/spec/kitchen/verifier_spec.rb +120 -0
  84. data/spec/kitchen_spec.rb +7 -0
  85. data/spec/spec_helper.rb +8 -0
  86. data/spec/support/powershell_max_size_spec.rb +40 -0
  87. data/support/busser_install_command.ps1 +14 -0
  88. data/support/busser_install_command.sh +15 -0
  89. data/support/check_files.ps1.erb +48 -0
  90. data/support/chef_base_init_command.ps1 +18 -0
  91. data/support/chef_base_init_command.sh +2 -0
  92. data/support/chef_base_install_command.ps1 +76 -0
  93. data/support/chef_base_install_command.sh +137 -0
  94. data/support/chef_zero_prepare_command_legacy.ps1 +9 -0
  95. data/support/chef_zero_prepare_command_legacy.sh +10 -0
  96. data/support/decode_files.ps1.erb +61 -0
  97. data/test-kitchen.gemspec +2 -0
  98. metadata +97 -8
  99. data/lib/kitchen/busser.rb +0 -316
  100. data/spec/kitchen/busser_spec.rb +0 -490
  101. data/support/chef_helpers.sh +0 -16
@@ -0,0 +1,400 @@
1
+ # -*- encoding: utf-8 -*-
2
+ #
3
+ # Author:: Fletcher (<fnichol@nichol.ca>)
4
+ #
5
+ # Copyright (C) 2015, Fletcher Nichol
6
+ #
7
+ # Licensed under the Apache License, Version 2.0 (the "License");
8
+ # you may not use this file except in compliance with the License.
9
+ # You may obtain a copy of the License at
10
+ #
11
+ # http://www.apache.org/licenses/LICENSE-2.0
12
+ #
13
+ # Unless required by applicable law or agreed to in writing, software
14
+ # distributed under the License is distributed on an "AS IS" BASIS,
15
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ # See the License for the specific language governing permissions and
17
+ # limitations under the License.
18
+
19
+ require_relative "../../../spec_helper"
20
+
21
+ require "kitchen"
22
+ require "kitchen/transport/winrm/command_executor"
23
+
24
+ require "base64"
25
+ require "securerandom"
26
+ require "winrm"
27
+
28
+ describe Kitchen::Transport::Winrm::CommandExecutor do
29
+
30
+ let(:logged_output) { StringIO.new }
31
+ let(:logger) { Logger.new(logged_output) }
32
+ let(:shell_id) { "shell-123" }
33
+
34
+ let(:executor) do
35
+ Kitchen::Transport::Winrm::CommandExecutor.new(service, logger)
36
+ end
37
+
38
+ let(:service) do
39
+ s = mock("winrm_service")
40
+ s.responds_like_instance_of(WinRM::WinRMWebService)
41
+ s
42
+ end
43
+
44
+ let(:version_output) do
45
+ o = WinRM::Output.new
46
+ o[:exitcode] = 0
47
+ o[:data].concat([{ :stdout => "6.3.9600.0\r\n" }])
48
+ o
49
+ end
50
+
51
+ before do
52
+ service.stubs(:open_shell).returns(shell_id)
53
+
54
+ stub_powershell_script(shell_id,
55
+ "[environment]::OSVersion.Version.tostring()", version_output)
56
+ end
57
+
58
+ describe "#close" do
59
+
60
+ it "calls service#close_shell" do
61
+ executor.open
62
+ service.expects(:close_shell).with(shell_id)
63
+
64
+ executor.close
65
+ end
66
+
67
+ it "only calls service#close_shell once for multiple calls" do
68
+ executor.open
69
+ service.expects(:close_shell).with(shell_id).once
70
+
71
+ executor.close
72
+ executor.close
73
+ executor.close
74
+ end
75
+ end
76
+
77
+ describe "#open" do
78
+
79
+ it "calls service#open_shell" do
80
+ service.expects(:open_shell).returns(shell_id)
81
+
82
+ executor.open
83
+ end
84
+
85
+ it "returns a shell id as a string" do
86
+ executor.open.must_equal shell_id
87
+ end
88
+
89
+ describe "for modern windows distributions" do
90
+
91
+ let(:version_output) do
92
+ o = WinRM::Output.new
93
+ o[:exitcode] = 0
94
+ o[:data].concat([{ :stdout => "6.3.9600.0\r\n" }])
95
+ o
96
+ end
97
+
98
+ it "sets #max_commands to 1500 - 2" do
99
+ executor.max_commands.must_equal nil
100
+ executor.open
101
+
102
+ executor.max_commands.must_equal(1500 - 2)
103
+ end
104
+ end
105
+
106
+ describe "for older/legacy windows distributions" do
107
+
108
+ let(:version_output) do
109
+ o = WinRM::Output.new
110
+ o[:exitcode] = 0
111
+ o[:data].concat([{ :stdout => "6.1.8500.0\r\n" }])
112
+ o
113
+ end
114
+
115
+ it "sets #max_commands to 15 - 2" do
116
+ executor.max_commands.must_equal nil
117
+ executor.open
118
+
119
+ executor.max_commands.must_equal(15 - 2)
120
+ end
121
+ end
122
+ end
123
+
124
+ describe "#run_cmd" do
125
+
126
+ describe "when #open has not been previously called" do
127
+
128
+ it "raises a WinRMError error" do
129
+ err = proc { executor.run_cmd("nope") }.must_raise WinRM::WinRMError
130
+ err.message.must_equal "#{executor.class}#open must be called " \
131
+ "before any run methods are invoked"
132
+ end
133
+ end
134
+
135
+ describe "when #open has been previously called" do
136
+
137
+ let(:command_id) { "command-123" }
138
+
139
+ let(:echo_output) do
140
+ o = WinRM::Output.new
141
+ o[:exitcode] = 0
142
+ o[:data].concat([
143
+ { :stdout => "Hello\r\n" },
144
+ { :stderr => "Psst\r\n" }
145
+ ])
146
+ o
147
+ end
148
+
149
+ before do
150
+ stub_cmd(shell_id, "echo", ["Hello"], echo_output, command_id)
151
+
152
+ executor.open
153
+ end
154
+
155
+ it "calls service#run_command" do
156
+ service.expects(:run_command).with(shell_id, "echo", ["Hello"])
157
+
158
+ executor.run_cmd("echo", ["Hello"])
159
+ end
160
+
161
+ it "calls service#get_command_output to get results" do
162
+ service.expects(:get_command_output).with(shell_id, command_id)
163
+
164
+ executor.run_cmd("echo", ["Hello"])
165
+ end
166
+
167
+ it "calls service#get_command_output with a block to get results" do
168
+ blk = proc { |_, _| "something" }
169
+ service.expects(:get_command_output).with(shell_id, command_id, &blk)
170
+
171
+ executor.run_cmd("echo", ["Hello"], &blk)
172
+ end
173
+
174
+ it "returns an Output object hash" do
175
+ executor.run_cmd("echo", ["Hello"]).must_equal echo_output
176
+ end
177
+
178
+ it "runs the block in #get_command_output when given" do
179
+ io_out = StringIO.new
180
+ io_err = StringIO.new
181
+
182
+ output = executor.run_cmd("echo", ["Hello"]) do |stdout, stderr|
183
+ io_out << stdout if stdout
184
+ io_err << stderr if stderr
185
+ end
186
+
187
+ io_out.string.must_equal "Hello\r\n"
188
+ io_err.string.must_equal "Psst\r\n"
189
+ output.must_equal echo_output
190
+ end
191
+ end
192
+
193
+ describe "when called many times over time" do
194
+
195
+ # use a "old" version of windows with lower max_commands threshold
196
+ # to trigger quicker shell recyles
197
+ let(:version_output) do
198
+ o = WinRM::Output.new
199
+ o[:exitcode] = 0
200
+ o[:data].concat([{ :stdout => "6.1.8500.0\r\n" }])
201
+ o
202
+ end
203
+
204
+ let(:echo_output) do
205
+ o = WinRM::Output.new
206
+ o[:exitcode] = 0
207
+ o[:data].concat([{ :stdout => "Hello\r\n" }])
208
+ o
209
+ end
210
+
211
+ before do
212
+ service.stubs(:open_shell).returns("s1", "s2")
213
+ service.stubs(:close_shell)
214
+ service.stubs(:run_command).yields("command-xxx")
215
+ service.stubs(:get_command_output).returns(echo_output)
216
+ stub_powershell_script("s1",
217
+ "[environment]::OSVersion.Version.tostring()", version_output)
218
+ end
219
+
220
+ it "resets the shell when #max_commands threshold is tripped" do
221
+ iterations = 35
222
+ reset_times = iterations / (15 - 2)
223
+
224
+ service.expects(:close_shell).times(reset_times)
225
+ executor.open
226
+ iterations.times { executor.run_cmd("echo", ["Hello"]) }
227
+
228
+ logged_output.string.lines.select { |l|
229
+ l =~ debug_line_with("[CommandExecutor] Resetting WinRM shell")
230
+ }.size.must_equal reset_times
231
+ end
232
+ end
233
+ end
234
+
235
+ describe "#run_powershell_script" do
236
+
237
+ describe "when #open has not been previously called" do
238
+
239
+ it "raises a WinRMError error" do
240
+ err = proc {
241
+ executor.run_powershell_script("nope")
242
+ }.must_raise WinRM::WinRMError
243
+ err.message.must_equal "#{executor.class}#open must be called " \
244
+ "before any run methods are invoked"
245
+ end
246
+ end
247
+
248
+ describe "when #open has been previously called" do
249
+
250
+ let(:command_id) { "command-123" }
251
+
252
+ let(:echo_output) do
253
+ o = WinRM::Output.new
254
+ o[:exitcode] = 0
255
+ o[:data].concat([
256
+ { :stdout => "Hello\r\n" },
257
+ { :stderr => "Psst\r\n" }
258
+ ])
259
+ o
260
+ end
261
+
262
+ before do
263
+ stub_powershell_script(shell_id, "echo Hello", echo_output, command_id)
264
+
265
+ executor.open
266
+ end
267
+
268
+ it "calls service#run_command" do
269
+ service.expects(:run_command).with(
270
+ shell_id,
271
+ "powershell",
272
+ ["-encodedCommand", WinRM::PowershellScript.new("echo Hello").encoded]
273
+ )
274
+
275
+ executor.run_powershell_script("echo Hello")
276
+ end
277
+
278
+ it "calls service#get_command_output to get results" do
279
+ service.expects(:get_command_output).with(shell_id, command_id)
280
+
281
+ executor.run_powershell_script("echo Hello")
282
+ end
283
+
284
+ it "calls service#get_command_output with a block to get results" do
285
+ blk = proc { |_, _| "something" }
286
+ service.expects(:get_command_output).with(shell_id, command_id, &blk)
287
+
288
+ executor.run_powershell_script("echo Hello", &blk)
289
+ end
290
+
291
+ it "returns an Output object hash" do
292
+ executor.run_powershell_script("echo Hello").must_equal echo_output
293
+ end
294
+
295
+ it "runs the block in #get_command_output when given" do
296
+ io_out = StringIO.new
297
+ io_err = StringIO.new
298
+
299
+ output = executor.run_powershell_script("echo Hello") do |stdout, stderr|
300
+ io_out << stdout if stdout
301
+ io_err << stderr if stderr
302
+ end
303
+
304
+ io_out.string.must_equal "Hello\r\n"
305
+ io_err.string.must_equal "Psst\r\n"
306
+ output.must_equal echo_output
307
+ end
308
+ end
309
+
310
+ describe "when called many times over time" do
311
+
312
+ # use a "old" version of windows with lower max_commands threshold
313
+ # to trigger quicker shell recyles
314
+ let(:version_output) do
315
+ o = WinRM::Output.new
316
+ o[:exitcode] = 0
317
+ o[:data].concat([{ :stdout => "6.1.8500.0\r\n" }])
318
+ o
319
+ end
320
+
321
+ let(:echo_output) do
322
+ o = WinRM::Output.new
323
+ o[:exitcode] = 0
324
+ o[:data].concat([{ :stdout => "Hello\r\n" }])
325
+ o
326
+ end
327
+
328
+ before do
329
+ service.stubs(:open_shell).returns("s1", "s2")
330
+ service.stubs(:close_shell)
331
+ service.stubs(:run_command).yields("command-xxx")
332
+ service.stubs(:get_command_output).returns(echo_output)
333
+ stub_powershell_script("s1",
334
+ "[environment]::OSVersion.Version.tostring()", version_output)
335
+ end
336
+
337
+ it "resets the shell when #max_commands threshold is tripped" do
338
+ iterations = 35
339
+ reset_times = iterations / (15 - 2)
340
+
341
+ service.expects(:close_shell).times(reset_times)
342
+ executor.open
343
+ iterations.times { executor.run_powershell_script("echo Hello") }
344
+
345
+ logged_output.string.lines.select { |l|
346
+ l =~ debug_line_with("[CommandExecutor] Resetting WinRM shell")
347
+ }.size.must_equal reset_times
348
+ end
349
+ end
350
+ end
351
+
352
+ describe "#shell" do
353
+
354
+ it "is initially nil" do
355
+ executor.shell.must_equal nil
356
+ end
357
+
358
+ it "is set after #open is called" do
359
+ executor.open
360
+
361
+ executor.shell.must_equal shell_id
362
+ end
363
+ end
364
+
365
+ def decode(powershell)
366
+ Base64.strict_decode64(powershell).encode("UTF-8", "UTF-16LE")
367
+ end
368
+
369
+ def debug_line_with(msg)
370
+ %r{^D, .* : #{Regexp.escape(msg)}}
371
+ end
372
+
373
+ def regexify(string)
374
+ Regexp.new(Regexp.escape(string))
375
+ end
376
+
377
+ def regexify_line(string)
378
+ Regexp.new("^#{Regexp.escape(string)}$")
379
+ end
380
+
381
+ # rubocop:disable Metrics/ParameterLists
382
+ def stub_cmd(shell_id, cmd, args, output, command_id = nil, &block)
383
+ command_id ||= SecureRandom.uuid
384
+
385
+ service.stubs(:run_command).with(shell_id, cmd, args).yields(command_id)
386
+ service.stubs(:get_command_output).with(shell_id, command_id, &block).
387
+ yields(output.stdout, output.stderr).returns(output)
388
+ end
389
+
390
+ def stub_powershell_script(shell_id, script, output, command_id = nil)
391
+ stub_cmd(
392
+ shell_id,
393
+ "powershell",
394
+ ["-encodedCommand", WinRM::PowershellScript.new(script).encoded],
395
+ output,
396
+ command_id
397
+ )
398
+ end
399
+ # rubocop:enable Metrics/ParameterLists
400
+ end
@@ -0,0 +1,876 @@
1
+ # -*- encoding: utf-8 -*-
2
+ #
3
+ # Author:: Fletcher (<fnichol@nichol.ca>)
4
+ #
5
+ # Copyright (C) 2015, Fletcher Nichol
6
+ #
7
+ # Licensed under the Apache License, Version 2.0 (the "License");
8
+ # you may not use this file except in compliance with the License.
9
+ # You may obtain a copy of the License at
10
+ #
11
+ # http://www.apache.org/licenses/LICENSE-2.0
12
+ #
13
+ # Unless required by applicable law or agreed to in writing, software
14
+ # distributed under the License is distributed on an "AS IS" BASIS,
15
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ # See the License for the specific language governing permissions and
17
+ # limitations under the License.
18
+
19
+ require_relative "../../../spec_helper"
20
+
21
+ require "base64"
22
+ require "csv"
23
+ require "stringio"
24
+ require "logger"
25
+ require "winrm"
26
+
27
+ require "kitchen"
28
+ require "kitchen/transport/winrm/file_transporter"
29
+
30
+ describe Kitchen::Transport::Winrm::FileTransporter do
31
+
32
+ CheckEntry = Struct.new(
33
+ :chk_exists, :src_md5, :dst_md5, :chk_dirty, :verifies)
34
+ DecodeEntry = Struct.new(
35
+ :dst, :verifies, :src_md5, :dst_md5, :tmpfile, :tmpzip)
36
+
37
+ let(:logged_output) { StringIO.new }
38
+ let(:logger) { Logger.new(logged_output) }
39
+
40
+ let(:randomness) { %W[alpha beta charlie delta].each }
41
+ let(:id_generator) { -> { randomness.next } }
42
+
43
+ let(:service) do
44
+ s = mock("winrm_service")
45
+ s.responds_like_instance_of(WinRM::WinRMWebService)
46
+ s
47
+ end
48
+
49
+ let(:transporter) do
50
+ Kitchen::Transport::Winrm::FileTransporter.new(
51
+ service,
52
+ logger,
53
+ :id_generator => id_generator
54
+ )
55
+ end
56
+
57
+ before { @tempfiles = [] }
58
+
59
+ after { @tempfiles.each(&:unlink) }
60
+
61
+ describe "when uploading a single file" do
62
+
63
+ let(:content) { "." * 12003 }
64
+ let(:local) { create_tempfile("input.txt", content) }
65
+ let(:remote) { "C:\\dest" }
66
+ let(:dst) { "#{remote}\\#{File.basename(local)}" }
67
+ let(:src_md5) { md5sum(local) }
68
+ let(:size) { File.size(local) }
69
+ let(:cmd_tmpfile) { "%TEMP%\\b64-#{src_md5}.txt" }
70
+ let(:ps_tmpfile) { "$env:TEMP\\b64-#{src_md5}.txt" }
71
+
72
+ let(:upload) { transporter.upload(local, remote) }
73
+
74
+ # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
75
+ def self.common_specs_for_all_single_file_types
76
+ it "truncates a zero-byte hash_file for check_files" do
77
+ service.expects(:run_cmd).with { |cmd, *_|
78
+ cmd =~ regexify(%{echo|set /p=>"%TEMP%\\hash-alpha.txt"})
79
+ }.returns(cmd_output)
80
+
81
+ upload
82
+ end
83
+
84
+ it "uploads the hash_file in chunks for check_files" do
85
+ hash = Kitchen::Util.outdent!(<<-HASH.chomp)
86
+ @{
87
+ "#{dst}" = "#{src_md5}"
88
+ }
89
+ HASH
90
+
91
+ service.expects(:run_cmd).
92
+ with(%{echo #{base64(hash)} >> "%TEMP%\\hash-alpha.txt"}).
93
+ returns(cmd_output).times(1)
94
+
95
+ upload
96
+ end
97
+
98
+ it "sets hash_file and runs the check_files powershell script" do
99
+ service.expects(:run_powershell_script).with { |script|
100
+ script =~ regexify(%{$hash_file = "$env:TEMP\\hash-alpha.txt"}) &&
101
+ script =~ regexify(
102
+ "Check-Files (Invoke-Input $hash_file) | " \
103
+ "ConvertTo-Csv -NoTypeInformation")
104
+ }.returns(check_output)
105
+
106
+ upload
107
+ end
108
+ end
109
+ # rubocop:enable Metrics/MethodLength, Metrics/AbcSize
110
+
111
+ # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
112
+ def self.common_specs_for_all_single_dirty_file_types
113
+ it "truncates a zero-byte tempfile" do
114
+ service.expects(:run_cmd).with { |cmd, *_|
115
+ cmd =~ regexify(%{echo|set /p=>"#{cmd_tmpfile}"})
116
+ }.returns(cmd_output)
117
+
118
+ upload
119
+ end
120
+
121
+ it "uploads the file in 8k chunks" do
122
+ service.expects(:run_cmd).
123
+ with(%{echo #{base64("." * 6000)} >> "#{cmd_tmpfile}"}).
124
+ returns(cmd_output).times(2)
125
+ service.expects(:run_cmd).
126
+ with(%{echo #{base64("." * 3)} >> "#{cmd_tmpfile}"}).
127
+ returns(cmd_output).times(1)
128
+
129
+ upload
130
+ end
131
+
132
+ describe "with a small file" do
133
+
134
+ let(:content) { "hello, world" }
135
+
136
+ it "uploads the file in base64 encoding" do
137
+ service.expects(:run_cmd).
138
+ with(%{echo #{base64(content)} >> "#{cmd_tmpfile}"}).
139
+ returns(cmd_output)
140
+
141
+ upload
142
+ end
143
+ end
144
+
145
+ it "truncates a zero-byte hash_file for decode_files" do
146
+ service.expects(:run_cmd).with { |cmd, *_|
147
+ cmd =~ regexify(%{echo|set /p=>"%TEMP%\\hash-beta.txt"})
148
+ }.returns(cmd_output)
149
+
150
+ upload
151
+ end
152
+
153
+ it "uploads the hash_file in chunks for decode_files" do
154
+ hash = Kitchen::Util.outdent!(<<-HASH.chomp)
155
+ @{
156
+ "#{ps_tmpfile}" = @{
157
+ "dst" = "#{dst}"
158
+ }
159
+ }
160
+ HASH
161
+
162
+ service.expects(:run_cmd).
163
+ with(%{echo #{base64(hash)} >> "%TEMP%\\hash-beta.txt"}).
164
+ returns(cmd_output).times(1)
165
+
166
+ upload
167
+ end
168
+
169
+ it "sets hash_file and runs the decode_files powershell script" do
170
+ service.expects(:run_powershell_script).with { |script|
171
+ script =~ regexify(%{$hash_file = "$env:TEMP\\hash-beta.txt"}) &&
172
+ script =~ regexify(
173
+ "Decode-Files (Invoke-Input $hash_file) | " \
174
+ "ConvertTo-Csv -NoTypeInformation")
175
+ }.returns(check_output)
176
+
177
+ upload
178
+ end
179
+ end
180
+ # rubocop:enable Metrics/MethodLength, Metrics/AbcSize
181
+
182
+ describe "for a new file" do
183
+
184
+ # let(:check_output) do
185
+ def check_output
186
+ create_check_output([
187
+ CheckEntry.new("False", src_md5, nil, "True", "False")
188
+ ])
189
+ end
190
+
191
+ let(:cmd_output) do
192
+ o = WinRM::Output.new
193
+ o[:exitcode] = 0
194
+ o
195
+ end
196
+
197
+ # let(:decode_output) do
198
+ def decode_output
199
+ create_decode_output([
200
+ DecodeEntry.new(dst, "True", src_md5, src_md5, ps_tmpfile, nil)
201
+ ])
202
+ end
203
+
204
+ before do
205
+ service.stubs(:run_cmd).
206
+ returns(cmd_output)
207
+
208
+ service.stubs(:run_powershell_script).
209
+ with { |script| script =~ /^Check-Files .+ \| ConvertTo-Csv/ }.
210
+ returns(check_output)
211
+
212
+ service.stubs(:run_powershell_script).
213
+ with { |script| script =~ /^Decode-Files .+ \| ConvertTo-Csv/ }.
214
+ returns(decode_output)
215
+ end
216
+
217
+ common_specs_for_all_single_file_types
218
+
219
+ common_specs_for_all_single_dirty_file_types
220
+
221
+ it "returns a report hash" do
222
+ upload.must_equal(
223
+ src_md5 => {
224
+ "src" => local,
225
+ "dst" => dst,
226
+ "tmpfile" => ps_tmpfile,
227
+ "tmpzip" => nil,
228
+ "src_md5" => src_md5,
229
+ "dst_md5" => src_md5,
230
+ "chk_exists" => "False",
231
+ "chk_dirty" => "True",
232
+ "verifies" => "True",
233
+ "size" => size,
234
+ "xfered" => size / 3 * 4,
235
+ "chunks" => (size / 6000.to_f).ceil
236
+ }
237
+ )
238
+ end
239
+
240
+ describe "when a failed check command is returned" do
241
+
242
+ def check_output
243
+ o = WinRM::Output.new
244
+ o[:exitcode] = 10
245
+ o[:data].concat([{ :stderr => "Oh noes\n" }])
246
+ o
247
+ end
248
+
249
+ it "raises a FileTransporterFailed error" do
250
+ err = proc {
251
+ upload
252
+ }.must_raise Kitchen::Transport::Winrm::FileTransporterFailed
253
+ err.message.must_match regexify(
254
+ "Upload failed (exitcode: 10)", :partial_line)
255
+ end
256
+ end
257
+
258
+ describe "when a failed decode command is returned" do
259
+
260
+ def decode_output
261
+ o = WinRM::Output.new
262
+ o[:exitcode] = 10
263
+ o[:data].concat([{ :stderr => "Oh noes\n" }])
264
+ o
265
+ end
266
+
267
+ it "raises a FileTransporterFailed error" do
268
+ err = proc {
269
+ upload
270
+ }.must_raise Kitchen::Transport::Winrm::FileTransporterFailed
271
+ err.message.must_match regexify(
272
+ "Upload failed (exitcode: 10)", :partial_line)
273
+ end
274
+ end
275
+ end
276
+
277
+ describe "for an out of date (dirty) file" do
278
+
279
+ let(:check_output) do
280
+ create_check_output([
281
+ CheckEntry.new("True", src_md5, "aabbcc", "True", "False")
282
+ ])
283
+ end
284
+
285
+ let(:cmd_output) do
286
+ o = WinRM::Output.new
287
+ o[:exitcode] = 0
288
+ o
289
+ end
290
+
291
+ let(:decode_output) do
292
+ create_decode_output([
293
+ DecodeEntry.new(dst, "True", src_md5, src_md5, ps_tmpfile, nil)
294
+ ])
295
+ end
296
+
297
+ before do
298
+ service.stubs(:run_cmd).
299
+ returns(cmd_output)
300
+
301
+ service.stubs(:run_powershell_script).
302
+ with { |script| script =~ /^Check-Files .+ \| ConvertTo-Csv/ }.
303
+ returns(check_output)
304
+
305
+ service.stubs(:run_powershell_script).
306
+ with { |script| script =~ /^Decode-Files .+ \| ConvertTo-Csv/ }.
307
+ returns(decode_output)
308
+ end
309
+
310
+ common_specs_for_all_single_file_types
311
+
312
+ common_specs_for_all_single_dirty_file_types
313
+
314
+ it "returns a report hash" do
315
+ upload.must_equal(
316
+ src_md5 => {
317
+ "src" => local,
318
+ "dst" => dst,
319
+ "tmpfile" => ps_tmpfile,
320
+ "tmpzip" => nil,
321
+ "src_md5" => src_md5,
322
+ "dst_md5" => src_md5,
323
+ "chk_exists" => "True",
324
+ "chk_dirty" => "True",
325
+ "verifies" => "True",
326
+ "size" => size,
327
+ "xfered" => size / 3 * 4,
328
+ "chunks" => (size / 6000.to_f).ceil
329
+ }
330
+ )
331
+ end
332
+ end
333
+
334
+ describe "for an up to date (clean) file" do
335
+
336
+ let(:check_output) do
337
+ create_check_output([
338
+ CheckEntry.new("True", src_md5, src_md5, "False", "True")
339
+ ])
340
+ end
341
+
342
+ let(:cmd_output) do
343
+ o = WinRM::Output.new
344
+ o[:exitcode] = 0
345
+ o
346
+ end
347
+
348
+ before do
349
+ service.stubs(:run_cmd).
350
+ returns(cmd_output)
351
+
352
+ service.stubs(:run_powershell_script).
353
+ with { |script| script =~ /^Check-Files .+ \| ConvertTo-Csv/ }.
354
+ returns(check_output)
355
+ end
356
+
357
+ common_specs_for_all_single_file_types
358
+
359
+ it "uploads nothing" do
360
+ service.expects(:run_cmd).never
361
+
362
+ upload
363
+ end
364
+
365
+ it "skips the decode_files powershell script" do
366
+ service.expects(:run_powershell_script).with { |script|
367
+ script =~ regexify(
368
+ "Decode-Files $files | ConvertTo-Csv -NoTypeInformation")
369
+ }.never
370
+
371
+ upload
372
+ end
373
+
374
+ it "returns a report hash" do
375
+ upload.must_equal(
376
+ src_md5 => {
377
+ "src" => local,
378
+ "dst" => dst,
379
+ "size" => size,
380
+ "src_md5" => src_md5,
381
+ "dst_md5" => src_md5,
382
+ "chk_exists" => "True",
383
+ "chk_dirty" => "False",
384
+ "verifies" => "True"
385
+ }
386
+ )
387
+ end
388
+ end
389
+ end
390
+
391
+ describe "when uploading a single directory" do
392
+
393
+ let(:content) { "I'm a fake zip file" }
394
+ let(:local) { Dir.mktmpdir("input") }
395
+ let(:remote) { "C:\\dest" }
396
+ let(:src_zip) { create_tempfile("fake.zip", content) }
397
+ let(:dst) { remote }
398
+ let(:src_md5) { md5sum(src_zip) }
399
+ let(:size) { File.size(src_zip) }
400
+ let(:cmd_tmpfile) { "%TEMP%\\b64-#{src_md5}.txt" }
401
+ let(:ps_tmpfile) { "$env:TEMP\\b64-#{src_md5}.txt" }
402
+ let(:ps_tmpzip) { "$env:TEMP\\tmpzip-#{src_md5}.zip" }
403
+
404
+ let(:tmp_zip) do
405
+ s = mock("tmp_zip")
406
+ s.responds_like_instance_of(Kitchen::Transport::Winrm::TmpZip)
407
+ s.stubs(:path).returns(Pathname(src_zip))
408
+ s.stubs(:unlink)
409
+ s
410
+ end
411
+
412
+ let(:cmd_output) do
413
+ o = WinRM::Output.new
414
+ o[:exitcode] = 0
415
+ o
416
+ end
417
+
418
+ let(:check_output) do
419
+ create_check_output([
420
+ CheckEntry.new("False", src_md5, nil, "True", "False")
421
+ ])
422
+ end
423
+
424
+ let(:decode_output) do
425
+ create_decode_output([
426
+ DecodeEntry.new(dst, "True", src_md5, src_md5, ps_tmpfile, ps_tmpzip)
427
+ ])
428
+ end
429
+
430
+ before do
431
+ Kitchen::Transport::Winrm::TmpZip.stubs(:new).with("#{local}/", logger).
432
+ returns(tmp_zip)
433
+
434
+ service.stubs(:run_cmd).
435
+ returns(cmd_output)
436
+
437
+ # service.stubs(:run_cmd).with { |cmd, *_|
438
+ # if match = %r{^echo (\w+) >> "%TEMP%\\hash-alpha.txt"$}.match(cmd)
439
+ # hash = Base64.strict_decode64(match[1])
440
+ # zip_info[:tmpzip] = %r{"(\$env:TEMP\\tmpzip-\w+\.zip)"}.match(hash)[1]
441
+ # zip_info[:src_md5] = %r{tmpzip-(\w+)\.zip$}.match(zip_info[:tmpzip])[1]
442
+ # end
443
+ # }.returns(cmd_output)
444
+
445
+ service.stubs(:run_powershell_script).
446
+ with { |script| script =~ /^Check-Files .+ \| ConvertTo-Csv/ }.
447
+ returns(check_output)
448
+
449
+ service.stubs(:run_powershell_script).
450
+ with { |script| script =~ /^Decode-Files .+ \| ConvertTo-Csv/ }.
451
+ returns(decode_output)
452
+ end
453
+
454
+ after do
455
+ FileUtils.rm_rf(local)
456
+ end
457
+
458
+ let(:upload) { transporter.upload("#{local}/", remote) }
459
+
460
+ it "truncates a zero-byte hash_file for check_files" do
461
+ service.expects(:run_cmd).with { |cmd, *_|
462
+ cmd =~ regexify(%{echo|set /p=>"%TEMP%\\hash-alpha.txt"})
463
+ }.returns(cmd_output)
464
+
465
+ upload
466
+ end
467
+
468
+ it "uploads the hash_file in chunks for check_files" do
469
+ hash = Kitchen::Util.outdent!(<<-HASH.chomp)
470
+ @{
471
+ "#{ps_tmpzip}" = "#{src_md5}"
472
+ }
473
+ HASH
474
+
475
+ service.expects(:run_cmd).
476
+ with(%{echo #{base64(hash)} >> "%TEMP%\\hash-alpha.txt"}).
477
+ returns(cmd_output).times(1)
478
+
479
+ upload
480
+ end
481
+
482
+ it "sets hash_file and runs the check_files powershell script" do
483
+ service.expects(:run_powershell_script).with { |script|
484
+ script =~ regexify(%{$hash_file = "$env:TEMP\\hash-alpha.txt"}) &&
485
+ script =~ regexify(
486
+ "Check-Files (Invoke-Input $hash_file) | " \
487
+ "ConvertTo-Csv -NoTypeInformation")
488
+ }.returns(check_output)
489
+
490
+ upload
491
+ end
492
+
493
+ it "truncates a zero-byte tempfile" do
494
+ service.expects(:run_cmd).with { |cmd, *_|
495
+ cmd =~ regexify(%{echo|set /p=>"#{cmd_tmpfile}"})
496
+ }.returns(cmd_output)
497
+
498
+ upload
499
+ end
500
+
501
+ it "uploads the zip file in base64 encoding" do
502
+ service.expects(:run_cmd).
503
+ with(%{echo #{base64(content)} >> "#{cmd_tmpfile}"}).
504
+ returns(cmd_output)
505
+
506
+ upload
507
+ end
508
+
509
+ it "truncates a zero-byte hash_file for decode_files" do
510
+ service.expects(:run_cmd).with { |cmd, *_|
511
+ cmd =~ regexify(%{echo|set /p=>"%TEMP%\\hash-beta.txt"})
512
+ }.returns(cmd_output)
513
+
514
+ upload
515
+ end
516
+
517
+ it "uploads the hash_file in chunks for decode_files" do
518
+ hash = Kitchen::Util.outdent!(<<-HASH.chomp)
519
+ @{
520
+ "#{ps_tmpfile}" = @{
521
+ "dst" = "#{dst}"
522
+ "tmpzip" = "#{ps_tmpzip}"
523
+ }
524
+ }
525
+ HASH
526
+
527
+ service.expects(:run_cmd).
528
+ with(%{echo #{base64(hash)} >> "%TEMP%\\hash-beta.txt"}).
529
+ returns(cmd_output).times(1)
530
+
531
+ upload
532
+ end
533
+
534
+ it "sets hash_file and runs the decode_files powershell script" do
535
+ service.expects(:run_powershell_script).with { |script|
536
+ script =~ regexify(%{$hash_file = "$env:TEMP\\hash-beta.txt"}) &&
537
+ script =~ regexify(
538
+ "Decode-Files (Invoke-Input $hash_file) | " \
539
+ "ConvertTo-Csv -NoTypeInformation")
540
+ }.returns(check_output)
541
+
542
+ upload
543
+ end
544
+
545
+ it "returns a report hash" do
546
+ upload.must_equal(
547
+ src_md5 => {
548
+ "src" => "#{local}/",
549
+ "src_zip" => src_zip,
550
+ "dst" => dst,
551
+ "tmpfile" => ps_tmpfile,
552
+ "tmpzip" => ps_tmpzip,
553
+ "src_md5" => src_md5,
554
+ "dst_md5" => src_md5,
555
+ "chk_exists" => "False",
556
+ "chk_dirty" => "True",
557
+ "verifies" => "True",
558
+ "size" => size,
559
+ "xfered" => size / 3 * 4,
560
+ "chunks" => (size / 6000.to_f).ceil
561
+ }
562
+ )
563
+ end
564
+
565
+ it "cleans up the zip file" do
566
+ tmp_zip.expects(:unlink)
567
+
568
+ upload
569
+ end
570
+
571
+ describe "when a failed check command is returned" do
572
+
573
+ def check_output
574
+ o = WinRM::Output.new
575
+ o[:exitcode] = 10
576
+ o[:data].concat([{ :stderr => "Oh noes\n" }])
577
+ o
578
+ end
579
+
580
+ it "raises a FileTransporterFailed error" do
581
+ err = proc {
582
+ upload
583
+ }.must_raise Kitchen::Transport::Winrm::FileTransporterFailed
584
+ err.message.must_match regexify(
585
+ "Upload failed (exitcode: 10)", :partial_line)
586
+ end
587
+ end
588
+
589
+ describe "when a failed decode command is returned" do
590
+
591
+ def decode_output
592
+ o = WinRM::Output.new
593
+ o[:exitcode] = 10
594
+ o[:data].concat([{ :stderr => "Oh noes\n" }])
595
+ o
596
+ end
597
+
598
+ it "raises a FileTransporterFailed error" do
599
+ err = proc {
600
+ upload
601
+ }.must_raise Kitchen::Transport::Winrm::FileTransporterFailed
602
+ err.message.must_match regexify(
603
+ "Upload failed (exitcode: 10)", :partial_line)
604
+ end
605
+ end
606
+ end
607
+
608
+ describe "when uploading multiple files" do
609
+
610
+ let(:remote) { "C:\\Program Files" }
611
+
612
+ 1.upto(3).each do |i|
613
+ let(:"local#{i}") { create_tempfile("input#{i}.txt", "input#{i}") }
614
+ let(:"src#{i}_md5") { md5sum(send("local#{i}")) }
615
+ let(:"dst#{i}") { "#{remote}\\#{File.basename(send("local#{i}"))}" }
616
+ let(:"size#{i}") { File.size(send("local#{i}")) }
617
+ let(:"cmd#{i}_tmpfile") { "%TEMP%\\b64-#{send("src#{i}_md5")}.txt" }
618
+ let(:"ps#{i}_tmpfile") { "$env:TEMP\\b64-#{send("src#{i}_md5")}.txt" }
619
+ end
620
+
621
+ let(:check_output) do
622
+ create_check_output([
623
+ # new
624
+ CheckEntry.new("False", src1_md5, nil, "True", "False"),
625
+ # out-of-date
626
+ CheckEntry.new("True", src2_md5, "aabbcc", "True", "False"),
627
+ # current
628
+ CheckEntry.new("True", src3_md5, src3_md5, "False", "True")
629
+ ])
630
+ end
631
+
632
+ let(:cmd_output) do
633
+ o = WinRM::Output.new
634
+ o[:exitcode] = 0
635
+ o
636
+ end
637
+
638
+ let(:decode_output) do
639
+ create_decode_output([
640
+ DecodeEntry.new(dst1, "True", src1_md5, src1_md5, ps1_tmpfile, nil),
641
+ DecodeEntry.new(dst2, "True", src2_md5, src2_md5, ps2_tmpfile, nil)
642
+ ])
643
+ end
644
+
645
+ let(:upload) { transporter.upload([local1, local2, local3], remote) }
646
+
647
+ before do
648
+ service.stubs(:run_cmd).
649
+ returns(cmd_output)
650
+
651
+ service.stubs(:run_powershell_script).
652
+ with { |script| script =~ /^Check-Files .+ \| ConvertTo-Csv/ }.
653
+ returns(check_output)
654
+
655
+ service.stubs(:run_powershell_script).
656
+ with { |script| script =~ /^Decode-Files .+ \| ConvertTo-Csv/ }.
657
+ returns(decode_output)
658
+ end
659
+
660
+ it "truncates a zero-byte hash_file for check_files" do
661
+ service.expects(:run_cmd).with { |cmd, *_|
662
+ cmd =~ regexify(%{echo|set /p=>"%TEMP%\\hash-alpha.txt"})
663
+ }.returns(cmd_output)
664
+
665
+ upload
666
+ end
667
+
668
+ it "uploads the hash_file in chunks for check_files" do
669
+ hash = Kitchen::Util.outdent!(<<-HASH.chomp)
670
+ @{
671
+ "#{dst1}" = "#{src1_md5}"
672
+ "#{dst2}" = "#{src2_md5}"
673
+ "#{dst3}" = "#{src3_md5}"
674
+ }
675
+ HASH
676
+
677
+ service.expects(:run_cmd).
678
+ with(%{echo #{base64(hash)} >> "%TEMP%\\hash-alpha.txt"}).
679
+ returns(cmd_output).times(1)
680
+
681
+ upload
682
+ end
683
+
684
+ it "sets hash_file and runs the check_files powershell script" do
685
+ service.expects(:run_powershell_script).with { |script|
686
+ script =~ regexify(%{$hash_file = "$env:TEMP\\hash-alpha.txt"}) &&
687
+ script =~ regexify(
688
+ "Check-Files (Invoke-Input $hash_file) | " \
689
+ "ConvertTo-Csv -NoTypeInformation")
690
+ }.returns(check_output)
691
+
692
+ upload
693
+ end
694
+
695
+ it "only uploads dirty files" do
696
+ service.expects(:run_cmd).
697
+ with(%{echo #{base64(IO.read(local1))} >> "#{cmd1_tmpfile}"})
698
+ service.expects(:run_cmd).
699
+ with(%{echo #{base64(IO.read(local2))} >> "#{cmd2_tmpfile}"})
700
+ service.expects(:run_cmd).
701
+ with(%{echo #{base64(IO.read(local3))} >> "#{cmd3_tmpfile}"}).
702
+ never
703
+
704
+ upload
705
+ end
706
+
707
+ it "truncates a zero-byte hash_file for decode_files" do
708
+ service.expects(:run_cmd).with { |cmd, *_|
709
+ cmd =~ regexify(%{echo|set /p=>"%TEMP%\\hash-beta.txt"})
710
+ }.returns(cmd_output)
711
+
712
+ upload
713
+ end
714
+
715
+ it "uploads the hash_file in chunks for decode_files" do
716
+ hash = Kitchen::Util.outdent!(<<-HASH.chomp)
717
+ @{
718
+ "#{ps1_tmpfile}" = @{
719
+ "dst" = "#{dst1}"
720
+ }
721
+ "#{ps2_tmpfile}" = @{
722
+ "dst" = "#{dst2}"
723
+ }
724
+ }
725
+ HASH
726
+
727
+ service.expects(:run_cmd).
728
+ with(%{echo #{base64(hash)} >> "%TEMP%\\hash-beta.txt"}).
729
+ returns(cmd_output).times(1)
730
+
731
+ upload
732
+ end
733
+
734
+ it "sets hash_file and runs the decode_files powershell script" do
735
+ service.expects(:run_powershell_script).with { |script|
736
+ script =~ regexify(%{$hash_file = "$env:TEMP\\hash-beta.txt"}) &&
737
+ script =~ regexify(
738
+ "Decode-Files (Invoke-Input $hash_file) | " \
739
+ "ConvertTo-Csv -NoTypeInformation")
740
+ }.returns(check_output)
741
+
742
+ upload
743
+ end
744
+
745
+ it "returns a report hash" do
746
+ report = upload
747
+
748
+ report.fetch(src1_md5).must_equal(
749
+ "src" => local1,
750
+ "dst" => dst1,
751
+ "tmpfile" => ps1_tmpfile,
752
+ "tmpzip" => nil,
753
+ "src_md5" => src1_md5,
754
+ "dst_md5" => src1_md5,
755
+ "chk_exists" => "False",
756
+ "chk_dirty" => "True",
757
+ "verifies" => "True",
758
+ "size" => size1,
759
+ "xfered" => size1 / 3 * 4,
760
+ "chunks" => (size1 / 6000.to_f).ceil
761
+ )
762
+ report.fetch(src2_md5).must_equal(
763
+ "src" => local2,
764
+ "dst" => dst2,
765
+ "tmpfile" => ps2_tmpfile,
766
+ "tmpzip" => nil,
767
+ "src_md5" => src2_md5,
768
+ "dst_md5" => src2_md5,
769
+ "chk_exists" => "True",
770
+ "chk_dirty" => "True",
771
+ "verifies" => "True",
772
+ "size" => size2,
773
+ "xfered" => size2 / 3 * 4,
774
+ "chunks" => (size2 / 6000.to_f).ceil
775
+ )
776
+ report.fetch(src3_md5).must_equal(
777
+ "src" => local3,
778
+ "dst" => dst3,
779
+ "src_md5" => src3_md5,
780
+ "dst_md5" => src3_md5,
781
+ "chk_exists" => "True",
782
+ "chk_dirty" => "False",
783
+ "verifies" => "True",
784
+ "size" => size3
785
+ )
786
+ end
787
+
788
+ describe "when a failed check command is returned" do
789
+
790
+ def check_output
791
+ o = WinRM::Output.new
792
+ o[:exitcode] = 10
793
+ o[:data].concat([{ :stderr => "Oh noes\n" }])
794
+ o
795
+ end
796
+
797
+ it "raises a FileTransporterFailed error" do
798
+ err = proc {
799
+ upload
800
+ }.must_raise Kitchen::Transport::Winrm::FileTransporterFailed
801
+ err.message.must_match regexify(
802
+ "Upload failed (exitcode: 10)", :partial_line)
803
+ end
804
+ end
805
+
806
+ describe "when a failed decode command is returned" do
807
+
808
+ def decode_output
809
+ o = WinRM::Output.new
810
+ o[:exitcode] = 10
811
+ o[:data].concat([{ :stderr => "Oh noes\n" }])
812
+ o
813
+ end
814
+
815
+ it "raises a FileTransporterFailed error" do
816
+ err = proc {
817
+ upload
818
+ }.must_raise Kitchen::Transport::Winrm::FileTransporterFailed
819
+ err.message.must_match regexify(
820
+ "Upload failed (exitcode: 10)", :partial_line)
821
+ end
822
+ end
823
+ end
824
+
825
+ it "raises an exception when local file or directory is not found" do
826
+ proc { transporter.upload("/a/b/c/nope", "C:\\nopeland") }.
827
+ must_raise Errno::ENOENT
828
+ end
829
+
830
+ def base64(string)
831
+ Base64.strict_encode64(string)
832
+ end
833
+
834
+ def create_check_output(entries)
835
+ csv = CSV.generate(:force_quotes => true) do |rows|
836
+ rows << CheckEntry.new.members.map(&:to_s)
837
+ entries.each { |entry| rows << entry.to_a }
838
+ end
839
+
840
+ o = WinRM::Output.new
841
+ o[:exitcode] = 0
842
+ o[:data].concat(csv.lines.map { |line| { :stdout => line } })
843
+ o
844
+ end
845
+
846
+ def create_decode_output(entries)
847
+ csv = CSV.generate(:force_quotes => true) do |rows|
848
+ rows << DecodeEntry.new.members.map(&:to_s)
849
+ entries.each { |entry| rows << entry.to_a }
850
+ end
851
+
852
+ o = WinRM::Output.new
853
+ o[:exitcode] = 0
854
+ o[:data].concat(csv.lines.map { |line| { :stdout => line } })
855
+ o
856
+ end
857
+
858
+ def create_tempfile(name, content)
859
+ pre, _, ext = name.rpartition(".")
860
+ file = Tempfile.open(["#{pre}-", ".#{ext}"])
861
+ @tempfiles << file
862
+ file.write(content)
863
+ file.close
864
+ file.path
865
+ end
866
+
867
+ def md5sum(local)
868
+ Digest::MD5.file(local).hexdigest
869
+ end
870
+
871
+ def regexify(str, line = :whole_line)
872
+ r = Regexp.escape(str)
873
+ r = "^#{r}$" if line == :whole_line
874
+ Regexp.new(r)
875
+ end
876
+ end