File: local_test.rb

package info (click to toggle)
ruby-train 3.13.4-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 1,208 kB
  • sloc: ruby: 10,002; sh: 17; makefile: 8
file content (279 lines) | stat: -rw-r--r-- 7,726 bytes parent folder | download | duplicates (2)
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
# author: Christoph Hartmann
# author: Dominik Richter

require "minitest/autorun"
require "minitest/spec"
require "mocha/setup"
require "train"
require "tempfile" unless defined?(Tempfile)
require "logger"

# Loading here to ensure methods exist to be stubbed
require "train/transports/local"

describe "windows local command" do
  let(:backend) do
    # get final config
    target_config = Train.target_config({ logger: Logger.new(STDERR, level: :info) })
    # initialize train
    Train.create("local", target_config)
  end
  let(:conn) { backend.connection }

  it "verify os" do
    os = conn.os
    _(os[:name]).must_match(/windows_server.*/)
    _(os[:family]).must_equal "windows"
    _(os[:release]).must_match(/\d+(\.\d+)+/)
    _(os[:arch]).must_equal "x86_64"
  end

  it "run echo test" do
    cmd = conn.run_command('Write-Output "test"')
    _(cmd.stdout).must_equal "test\r\n"
    _(cmd.stderr).must_equal ""
    _(cmd.exit_status).must_equal 0
  end

  it "run script without exit code" do
    cmd = conn.run_command("powershell -file test/fixtures/PowerShell/exit_zero.ps1")
    _(cmd.stdout).must_equal "Hello\r\n"
    _(cmd.stderr).must_equal ""
    _(cmd.exit_status).must_equal 0
  end

  it "run script without exit code" do
    cmd = conn.run_command("powershell -file test/fixtures/PowerShell/exit_fortytwo.ps1")
    _(cmd.stdout).must_equal "Goodbye\r\n"
    _(cmd.stderr).must_equal ""
    _(cmd.exit_status).must_equal 42
  end

  it "returns exit code 1 for a script that throws" do
    cmd = conn.run_command("powershell -file test/fixtures/PowerShell/throws.ps1")
    _(cmd.stdout).must_match(/Next line throws/)
    _(cmd.stderr).must_equal ""
    _(cmd.exit_status).must_equal 1
  end

  describe "force 64 bit powershell command" do
    let(:runner) { conn.instance_variable_get(:@runner) }
    let(:powershell) { runner.instance_variable_get(:@powershell_cmd) }
    let(:server_pid) { runner.instance_variable_get(:@server_pid) }

    RUBY_PLATFORM_DUP = RUBY_PLATFORM.dup

    def override_platform(platform)
      ::Object.send(:remove_const, :RUBY_PLATFORM)
      ::Object.const_set(:RUBY_PLATFORM, platform)
    end

    after do
      backend.instance_variable_set(:@connection, nil)
      ::Object.send(:remove_const, :RUBY_PLATFORM)
      ::Object.const_set(:RUBY_PLATFORM, RUBY_PLATFORM_DUP)
    end

    it "use normal powershell with PipeRunner" do
      Train::Transports::Local::Connection::WindowsPipeRunner
        .any_instance
        .expects(:acquire_pipe)
        .returns("acquired")

      override_platform("x64-mingw32")
      _(powershell).must_equal "powershell"
    end

    it "use 64bit powershell with PipeRunner" do
      Train::Transports::Local::Connection::WindowsPipeRunner
        .any_instance
        .expects(:acquire_pipe)
        .returns("acquired")

      override_platform("i386-mingw32")
      _(powershell).must_equal "#{ENV["SystemRoot"]}\\sysnative\\WindowsPowerShell\\v1.0\\powershell.exe"
    end

    it "use normal powershell with ShellRunner" do
      Train::Transports::Local::Connection::WindowsPipeRunner
        .any_instance
        .expects(:acquire_pipe)
        .returns(nil)

      override_platform("x64-mingw32")
      _(runner.class).must_equal Train::Transports::Local::Connection::WindowsShellRunner
      _(powershell).must_equal "powershell"
    end

    it "use 64bit powershell with ShellRunner" do
      Train::Transports::Local::Connection::WindowsPipeRunner
        .any_instance
        .expects(:acquire_pipe)
        .returns(nil)

      override_platform("i386-mingw32")
      _(runner.class).must_equal Train::Transports::Local::Connection::WindowsShellRunner
      _(powershell).must_equal "#{ENV["SystemRoot"]}\\sysnative\\WindowsPowerShell\\v1.0\\powershell.exe"
    end

    it "sets server_pid and returns nil on close" do
      Train::Transports::Local::Connection::WindowsPipeRunner
        .any_instance
        .expects(:new)
        .never

      override_platform("x64-mingw32")
      _(powershell).must_equal "powershell"
      _(server_pid).wont_be_nil
      _(runner.close).must_be_nil
    end
  end

  it "use powershell piping" do
    cmd = conn.run_command("New-Object -Type PSObject | Add-Member -MemberType NoteProperty -Name A -Value (Write-Output 'PropertyA') -PassThru | Add-Member -MemberType NoteProperty -Name B -Value (Write-Output 'PropertyB') -PassThru | ConvertTo-Json")
    _(cmd.stdout).must_equal "{\r\n    \"A\":  \"PropertyA\",\r\n    \"B\":  \"PropertyB\"\r\n}\r\n"
    _(cmd.stderr).must_equal ""
  end

  it "can execute a command using a named pipe" do
    SecureRandom.expects(:hex).returns("via_pipe")

    Train::Transports::Local::Connection::WindowsShellRunner
      .any_instance
      .expects(:new)
      .never

    cmd = conn.run_command('Write-Output "Create pipe"')
    _(File.exist?("//./pipe/inspec_via_pipe")).must_equal true
    _(cmd.stdout).must_equal "Create pipe\r\n"
    _(cmd.stderr).must_equal ""
  end

  it "can execute a command via ShellRunner if pipe creation fails" do
    # By forcing `acquire_pipe` to fail to return a pipe, any attempts to create
    # a `WindowsPipeRunner` object should fail. If we can still run a command,
    # then we know that it was successfully executed by `Mixlib::ShellOut`.
    Train::Transports::Local::Connection::WindowsPipeRunner
      .any_instance
      .expects(:acquire_pipe)
      .at_least_once
      .returns(nil)

    proc { Train::Transports::Local::Connection::WindowsPipeRunner.new }
      .must_raise(Train::Transports::Local::PipeError)

    cmd = conn.run_command('Write-Output "test"')
    _(cmd.stdout).must_equal "test\r\n"
    _(cmd.stderr).must_equal ""
  end

  describe "file" do
    before do
      @temp = Tempfile.new("foo")
      @temp.write("hello world")
      @temp.rewind
    end

    let(:file) { conn.file(@temp.path) }

    it "exists" do
      _(file.exist?).must_equal(true)
    end

    it "is a file" do
      _(file.file?).must_equal(true)
    end

    it "has type :file" do
      _(file.type).must_equal(:file)
    end

    it "has content" do
      _(file.content).must_equal("hello world")
    end

    it "returns basename of file" do
      file_name = ::File.basename(@temp)
      _(file.basename).must_equal(file_name)
    end

    it "has owner name" do
      _(file.owner).wont_be_nil
    end

    it "has no group name" do
      _(file.group).must_be_nil
    end

    it "has no mode" do
      _(file.mode).wont_be_nil
    end

    it "has an md5sum" do
      _(file.md5sum).wont_be_nil
    end

    it "has an sha256sum" do
      _(file.sha256sum).wont_be_nil
    end

    it "has no modified time" do
      _(file.mtime).wont_be_nil
    end

    it "has no size" do
      _(file.size).wont_be_nil
    end

    it "has size 11" do
      size = ::File.size(@temp)
      _(file.size).must_equal size
    end

    it "has no selinux_label handling" do
      _(file.selinux_label).must_be_nil
    end

    it "has product_version" do
      _(file.product_version).wont_be_nil
    end

    it "has file_version" do
      _(file.file_version).wont_be_nil
    end

    it "provides a json representation" do
      j = file.to_json
      _(j).must_be_kind_of Hash
      _(j["type"]).must_equal :file
    end

    after do
      @temp.close
      @temp.unlink
    end
  end

  describe "file" do
    before do
      @temp = Tempfile.new("foo bar")
      @temp.rewind
    end

    let(:file) { conn.file(@temp.path) }

    it 'provides the full path with whitespace for path #{@temp.path}' do
      _(file.path).must_equal @temp.path
    end

    after do
      @temp.close
      @temp.unlink
    end
  end

  after do
    # close the connection
    conn.close
  end
end