serverspec-extended-types 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,159 @@
1
+ ##############################################################################
2
+ # serverspec-extended-types - http_get
3
+ #
4
+ # <https://github.com/jantman/serverspec-extended-types>
5
+ #
6
+ # Copyright (C) 2015 Jason Antman <jason@jasonantman.com>
7
+ #
8
+ # Licensed under the MIT License - see LICENSE.txt
9
+ #
10
+ ##############################################################################
11
+
12
+ require 'timeout'
13
+ require 'faraday'
14
+ require 'serverspec_extended_types/version'
15
+
16
+ module Serverspec
17
+ module Type
18
+
19
+ # Perform an HTTP GET request against the serverspec target
20
+ # using {http://www.rubydoc.info/gems/faraday/ Faraday}.
21
+ class Http_Get < Base
22
+
23
+ # Initialize a bunch of instance variables, then call {#getpage}
24
+ #
25
+ # Calls {#getpage} within a {Timeout::timeout} block;
26
+ # If {#getpage} times out, set ``timed_out_status`` to [True]
27
+ #
28
+ # @param port [Int] the port to connect to HTTP over
29
+ # @param host_header [String] the value to set in the 'Host' HTTP request header
30
+ # @param path [String] the URI/path to request from the server
31
+ # @param timeout_sec [Int] how many seconds to allow request to run before timing out and setting @timed_out_status to True
32
+ #
33
+ # @example
34
+ # describe http_get(80, 'myhostname', '/') do
35
+ # # tests here
36
+ # end
37
+ #
38
+ # @api public
39
+ # @return [nil]
40
+ def initialize(port, host_header, path, timeout_sec=10)
41
+ @ip = ENV['TARGET_HOST']
42
+ @port = port
43
+ @host = host_header
44
+ @path = path
45
+ @timed_out_status = false
46
+ @content_str = nil
47
+ @headers_hash = nil
48
+ @response_code_int = nil
49
+ begin
50
+ Timeout::timeout(timeout_sec) do
51
+ getpage
52
+ end
53
+ rescue Timeout::Error
54
+ @timed_out_status = true
55
+ end
56
+ end
57
+
58
+ # Private method to actually get the page; must be called within a timeout block
59
+ #
60
+ # Gets the page using {http://www.rubydoc.info/gems/faraday/ Faraday} and then sets instance variables for the various attribute readers.
61
+ #
62
+ # @api private
63
+ # @return [nil]
64
+ def getpage
65
+ ip = @ip
66
+ port = @port
67
+ conn = Faraday.new("http://#{ip}:#{port}/")
68
+ version = ServerspecExtendedTypes::VERSION
69
+ conn.headers[:user_agent] = "Serverspec::Type::Http_Get/#{version} (https://github.com/jantman/serverspec-extended-types)"
70
+ conn.headers[:Host] = @host
71
+ response = conn.get(@path)
72
+ @response_code_int = response.status
73
+ @content_str = response.body
74
+ @headers_hash = Hash.new('')
75
+ response.headers.each do |header, val|
76
+ @headers_hash[header] = val
77
+ end
78
+ end
79
+
80
+ # Whether or not the request timed out
81
+ #
82
+ # @example
83
+ # describe http_get(80, 'myhostname', '/') do
84
+ # it { should_not be_timed_out }
85
+ # end
86
+ #
87
+ # @api public
88
+ # @return [Boolean]
89
+ def timed_out?
90
+ @timed_out_status
91
+ end
92
+
93
+ # Returns the HTTP response headers as a hash
94
+ #
95
+ # @example
96
+ # describe http_get(80, 'myhostname', '/') do
97
+ # its(:headers) { should include('HeaderName' => /value regex/) }
98
+ # end
99
+ #
100
+ # @api public
101
+ # @return [Hash]
102
+ def headers
103
+ @headers_hash
104
+ end
105
+
106
+ # Returns the HTTP status code, or 0 if timed out
107
+ #
108
+ # @example
109
+ # describe http_get(80, 'myhostname', '/') do
110
+ # its(:status) { should eq 200 }
111
+ # end
112
+ #
113
+ # @api public
114
+ # @return [Int]
115
+ def status
116
+ if @timed_out_status
117
+ 0
118
+ else
119
+ @response_code_int
120
+ end
121
+ end
122
+
123
+ # Returns the body/content of the HTTP response
124
+ #
125
+ # @example
126
+ # describe http_get(80, 'myhostname', '/') do
127
+ # its(:body) { should match /<html>/ }
128
+ # end
129
+ #
130
+ # @api public
131
+ # @return [String]
132
+ def body
133
+ @content_str
134
+ end
135
+
136
+ private :getpage
137
+ end
138
+
139
+ # ServerSpec Type wrapper for http_get
140
+ #
141
+ # @example
142
+ # describe http_get(80, 'myhostname', '/') do
143
+ # # tests here
144
+ # end
145
+ #
146
+ # @param port [Int] the port to connect to HTTP over
147
+ # @param host_header [String] the value to set in the 'Host' HTTP request header
148
+ # @param path [String] the URI/path to request from the server
149
+ # @param timeout_sec [Int] how many seconds to allow request to run before timing out and setting @timed_out_status to True
150
+ #
151
+ # @api public
152
+ # @return [Serverspec::Type::Http_Get]
153
+ def http_get(port, host_header, path, timeout_sec=10)
154
+ Http_Get.new(port, host_header, path, timeout_sec=timeout_sec)
155
+ end
156
+ end
157
+ end
158
+
159
+ include Serverspec::Type
@@ -0,0 +1,3 @@
1
+ module ServerspecExtendedTypes
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,161 @@
1
+ ##############################################################################
2
+ # serverspec-extended-types - virtualenv
3
+ #
4
+ # <https://github.com/jantman/serverspec-extended-types>
5
+ #
6
+ # Copyright (C) 2015 Jason Antman <jason@jasonantman.com>
7
+ #
8
+ # Licensed under the MIT License - see LICENSE.txt
9
+ #
10
+ ##############################################################################
11
+
12
+ module Serverspec::Type
13
+ class Virtualenv < Base
14
+
15
+ # Test whether this appears to be a working venv
16
+ #
17
+ # Tests performed:
18
+ # - venv_path/bin/pip executable by root?
19
+ # - venv_path/bin/python executable by root?
20
+ # - venv_path/bin/activate executable by root?
21
+ # - 'export VIRTUAL_ENV' in venv_path/bin/activate?
22
+ #
23
+ # @example
24
+ # describe virtualenv('/path/to/venv') do
25
+ # it { should be_virtualenv }
26
+ # end
27
+ #
28
+ # @api public
29
+ # @return [Boolean]
30
+ def virtualenv?
31
+ pip_path = ::File.join(@name, 'bin', 'pip')
32
+ python_path = ::File.join(@name, 'bin', 'python')
33
+ act_path = ::File.join(@name, 'bin', 'activate')
34
+ cmd = "grep -q 'export VIRTUAL_ENV' #{act_path}"
35
+ @runner.check_file_is_executable(pip_path, 'root') and
36
+ @runner.check_file_is_executable(python_path, 'root') and
37
+ @runner.check_file_is_executable(act_path, 'root') and
38
+ @runner.run_command(cmd).exit_status.to_i == 0
39
+ end
40
+
41
+ # Return the version of pip installed in the virtualenv
42
+ #
43
+ # @example
44
+ # describe virtualenv('/path/to/venv') do
45
+ # its(:pip_version) { should match /^6\.0\.6$/ }
46
+ # end
47
+ #
48
+ # @api public
49
+ # @return [String]
50
+ def pip_version
51
+ @pip_version || get_pip_version
52
+ end
53
+
54
+ # Return the version of python installed in the virtualenv
55
+ #
56
+ # @example
57
+ # describe virtualenv('/path/to/venv') do
58
+ # its(:python_version) { should match /^2\.7\.9$/ }
59
+ # end
60
+ #
61
+ # @api public
62
+ # @return [String]
63
+ def python_version
64
+ @python_version || get_python_version
65
+ end
66
+
67
+ # Return a hash of all packages present in `pip freeze` output for the venv
68
+ #
69
+ # Note that any editable packages (`-e something`) are returned as hash keys
70
+ # with an empty value
71
+ #
72
+ # @example
73
+ # describe virtualenv('/path/to/venv') do
74
+ # its(:pip_freeze) { should include('wsgiref' => '0.1.2') }
75
+ # its(:pip_freeze) { should include('requests') }
76
+ # its(:pip_freeze) { should include('pytest' => /^2\.6/) }
77
+ # its(:pip_freeze) { should include('-e git+git@github.com:jantman/someproject.git@1d8a380e3af9d081081d7ef685979200a7db4130#egg=someproject') }
78
+ # end
79
+ #
80
+ # @api public
81
+ # @return [Hash]
82
+ def pip_freeze
83
+ @pip_freeze || get_pip_freeze
84
+ end
85
+
86
+ private
87
+ # Get a hash for the `pip freeze` output; set @pip_freeze
88
+ #
89
+ # @api private
90
+ # @return [nil]
91
+ def get_pip_freeze()
92
+ pip_path = ::File.join(@name, 'bin', 'pip')
93
+ tmp = @runner.run_command("#{pip_path} freeze")
94
+ @pip_freeze = Hash.new()
95
+ if tmp.exit_status.to_i != 0
96
+ return @pip_freeze
97
+ end
98
+ lines = tmp.stdout.split("\n")
99
+ lines.each do |line|
100
+ line.strip!
101
+ if line =~ /^-e /
102
+ @pip_freeze[line] = ''
103
+ next
104
+ end
105
+ parts = line.split(/==/)
106
+ @pip_freeze[parts[0]] = parts[1]
107
+ end
108
+ @pip_freeze
109
+ end
110
+
111
+ private
112
+ # Get the pip version from the venv; set @pip_version
113
+ #
114
+ # @api private
115
+ # @return [nil]
116
+ def get_pip_version()
117
+ pip_path = ::File.join(@name, 'bin', 'pip')
118
+ pip_path = ::File.join(@name, 'bin', 'pip')
119
+ tmp = @runner.run_command("#{pip_path} --version")
120
+ if ( tmp.stdout =~ /^pip (\d+\S+)/ )
121
+ @pip_version = $1
122
+ else
123
+ @pip_version = ''
124
+ end
125
+ end
126
+
127
+ private
128
+ # Get the python version from the venv; set @python_version
129
+ #
130
+ # @api private
131
+ # @return [nil]
132
+ def get_python_version()
133
+ python_path = ::File.join(@name, 'bin', 'python')
134
+ tmp = @runner.run_command("#{python_path} --version")
135
+ if ( tmp.stderr =~ /^[Pp]ython (\d+\S+)/ )
136
+ @python_version = $1
137
+ elsif ( tmp.stdout =~ /^[Pp]ython (\d+\S+)/ )
138
+ @python_version = $1
139
+ else
140
+ @python_version = ''
141
+ end
142
+ end
143
+ end
144
+
145
+ # Serverspec Type wrapper method for Serverspec::Type::Virtualenv
146
+ #
147
+ # @example
148
+ # describe virtualenv('/path/to/venv') do
149
+ # # tests here
150
+ # end
151
+ #
152
+ # @param name [String] the absolute path to the virtualenv root
153
+ #
154
+ # @api public
155
+ # @return {Serverspec::Type::Virtualenv}
156
+ def virtualenv(name)
157
+ Virtualenv.new(name)
158
+ end
159
+ end
160
+
161
+ include Serverspec::Type
@@ -0,0 +1,31 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'serverspec_extended_types/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "serverspec-extended-types"
8
+ spec.version = ServerspecExtendedTypes::VERSION
9
+ spec.authors = ["Jason Antman"]
10
+ spec.email = ["jason@jasonantman.com"]
11
+ spec.summary = %q{A set of extended types for ServerSpec 2.x}
12
+ spec.description = %q{This gem provides some purpose-specific types for ServerSpec 2.x beyond the default ones. It is in no way associated with the ServerSpec project or developers.}
13
+ spec.homepage = "https://github.com/jantman/serverspec-extended-types"
14
+ spec.license = "MIT"
15
+ spec.files = `git ls-files -z`.split("\x0")
16
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
17
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
18
+ spec.require_paths = ["lib"]
19
+ spec.add_dependency "serverspec", "~> 2"
20
+ spec.add_dependency "specinfra", "~> 2"
21
+ spec.add_dependency 'rspec', '>= 3.0'
22
+ spec.add_dependency 'faraday'
23
+
24
+ spec.add_development_dependency "bundler", "~> 1.6"
25
+ spec.add_development_dependency "rake"
26
+ spec.add_development_dependency 'simplecov'
27
+ spec.add_development_dependency 'codecov'
28
+ spec.add_development_dependency 'yard'
29
+ spec.add_development_dependency 'yardstick'
30
+ spec.add_development_dependency 'redcarpet'
31
+ end
@@ -0,0 +1,42 @@
1
+ $LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
2
+
3
+ require 'simplecov'
4
+ SimpleCov.start do
5
+ add_filter "/vendor/"
6
+ end
7
+
8
+ require 'codecov'
9
+ if ENV['CI']=='true'
10
+ SimpleCov.formatter = SimpleCov::Formatter::Codecov
11
+ end
12
+
13
+ require 'specinfra'
14
+ require 'rspec/mocks/standalone'
15
+ require 'rspec/its'
16
+ require 'specinfra/helper/set'
17
+ include Specinfra::Helper::Set
18
+
19
+ set :backend, :exec
20
+
21
+ set :os, :family => 'linux'
22
+
23
+ module Specinfra
24
+ module Backend
25
+ class Ssh
26
+ def run_command(cmd, opts={})
27
+ CommandResult.new :stdout => nil, :exit_status => 0
28
+ end
29
+ end
30
+ end
31
+ end
32
+
33
+ module GetCommand
34
+ def get_command(method, *args)
35
+ Specinfra.command.get(method, *args)
36
+ end
37
+ end
38
+
39
+ include GetCommand
40
+
41
+
42
+ require 'serverspec_extended_types'
@@ -0,0 +1,189 @@
1
+ require 'spec_helper'
2
+ require 'serverspec_extended_types/http_get'
3
+
4
+ describe 'Serverspec::Type.bitlbee' do
5
+ it 'instantiates the class with the correct parameters' do
6
+ expect(Serverspec::Type::Bitlbee).to receive(:new).with(1, 'mynick', 'mypass', false)
7
+ bitlbee(1, 'mynick', 'mypass')
8
+ end
9
+ it 'returns the new object' do
10
+ expect(Serverspec::Type::Bitlbee).to receive(:new).and_return("foo")
11
+ expect(bitlbee(1, 'foo', 'bar')).to eq "foo"
12
+ end
13
+ end
14
+ describe 'Serverspec::Type::Bitlbee' do
15
+ context '#initialize' do
16
+ it 'sets instance variables' do
17
+ stub_const('ENV', ENV.to_hash.merge('TARGET_HOST' => 'myhost'))
18
+ x = bitlbee(1, 'foo', 'bar')
19
+ expect(x.instance_variable_get(:@port)).to eq 1
20
+ expect(x.instance_variable_get(:@host)).to eq 'myhost'
21
+ expect(x.instance_variable_get(:@nick)).to eq 'foo'
22
+ expect(x.instance_variable_get(:@password)).to eq 'bar'
23
+ expect(x.instance_variable_get(:@connected_status)).to eq false
24
+ expect(x.instance_variable_get(:@version_str)).to eq ''
25
+ expect(x.instance_variable_get(:@timed_out_status)).to eq false
26
+ expect(x.instance_variable_get(:@started)).to eq false
27
+ expect(x.instance_variable_get(:@use_ssl)).to eq false
28
+ end
29
+ it 'sets use_ssl instance variable' do
30
+ stub_const('ENV', ENV.to_hash.merge('TARGET_HOST' => 'myhost'))
31
+ x = bitlbee(1, 'foo', 'bar', use_ssl=true)
32
+ expect(x.instance_variable_get(:@use_ssl)).to eq true
33
+ end
34
+ end
35
+ context 'start' do
36
+ it 'calls connect_ssl if use_ssl=true' do
37
+ x = bitlbee(1, 'foo', 'bar', use_ssl=true)
38
+ expect(x).to receive(:connect_ssl).once
39
+ expect(x).to_not receive(:connect)
40
+ x.start
41
+ expect(x.instance_variable_get(:@timed_out_status)).to eq false
42
+ expect(x.instance_variable_get(:@connected_status)).to eq false
43
+ expect(x.instance_variable_get(:@started)).to eq true
44
+ end
45
+ it 'calls connect if use_ssl=false' do
46
+ x = bitlbee(1, 'foo', 'bar')
47
+ expect(x).to receive(:connect).once
48
+ expect(x).to_not receive(:connect_ssl)
49
+ x.start
50
+ expect(x.instance_variable_get(:@timed_out_status)).to eq false
51
+ expect(x.instance_variable_get(:@connected_status)).to eq false
52
+ expect(x.instance_variable_get(:@started)).to eq true
53
+ end
54
+ it 'runs with a timeout' do
55
+ x = bitlbee(1, 'foo', 'bar')
56
+ expect(Timeout).to receive(:timeout).with(10)
57
+ x.start
58
+ expect(x.instance_variable_get(:@timed_out_status)).to eq false
59
+ expect(x.instance_variable_get(:@connected_status)).to eq false
60
+ expect(x.instance_variable_get(:@started)).to eq true
61
+ expect(x.timed_out?).to eq false
62
+ end
63
+ it 'sets timed_out_status on timeout' do
64
+ x = bitlbee(1, 'foo', 'bar')
65
+ expect(Timeout).to receive(:timeout).with(10).and_raise(Timeout::Error)
66
+ x.start
67
+ expect(x.instance_variable_get(:@timed_out_status)).to eq true
68
+ expect(x.instance_variable_get(:@connected_status)).to eq false
69
+ expect(x.instance_variable_get(:@started)).to eq true
70
+ expect(x.timed_out?).to eq true
71
+ expect(x.connectable?).to eq false
72
+ end
73
+ it 'sets connected_status on ECONNREFUSED' do
74
+ x = bitlbee(1, 'foo', 'bar')
75
+ expect(x).to receive(:connect).and_raise(Errno::ECONNREFUSED)
76
+ x.start
77
+ expect(x.instance_variable_get(:@timed_out_status)).to eq false
78
+ expect(x.instance_variable_get(:@connected_status)).to eq false
79
+ expect(x.instance_variable_get(:@started)).to eq true
80
+ expect(x.timed_out?).to eq false
81
+ expect(x.connectable?).to eq false
82
+ end
83
+ end
84
+ context '#connect_ssl' do
85
+ it 'calls communicate' do
86
+ stub_const('ENV', ENV.to_hash.merge('TARGET_HOST' => 'myhost'))
87
+ sock_dbl = double
88
+ expect(TCPSocket).to receive(:open).with('myhost', 1).and_return(sock_dbl)
89
+ sslctx_dbl = double
90
+ expect(OpenSSL::SSL::SSLContext).to receive(:new).and_return(sslctx_dbl)
91
+ expect(sslctx_dbl).to receive(:set_params).with(verify_mode: OpenSSL::SSL::VERIFY_NONE).ordered
92
+ sslsock_dbl = double
93
+ expect(sslsock_dbl).to receive(:sync_close=).ordered.with(true)
94
+ expect(sslsock_dbl).to receive(:connect).ordered
95
+ expect(sslsock_dbl).to receive(:puts).ordered.with("communicate")
96
+ expect(sslsock_dbl).to receive(:puts).ordered.with("QUIT :\"outta here\"\n")
97
+ expect(sslsock_dbl).to receive(:close).ordered
98
+ expect(OpenSSL::SSL::SSLSocket).to receive(:new).with(sock_dbl, sslctx_dbl).and_return(sslsock_dbl)
99
+ #expect_any_instance_of(Serverspec::Type::Bitlbee).to receive(:communicate)
100
+ x = bitlbee(1, 'foo', 'bar')
101
+ x.stub(:communicate) do
102
+ x.instance_variable_get(:@socket).puts("communicate")
103
+ end
104
+ x.connect_ssl
105
+ expect(x.instance_variable_get(:@socket)).to eq sslsock_dbl
106
+ end
107
+ end
108
+ context '#connect' do
109
+ it 'calls communicate' do
110
+ stub_const('ENV', ENV.to_hash.merge('TARGET_HOST' => 'myhost'))
111
+ sock_dbl = double
112
+ expect(TCPSocket).to receive(:open).with('myhost', 1).and_return(sock_dbl)
113
+ expect(sock_dbl).to receive(:puts).ordered.with("communicate")
114
+ expect(sock_dbl).to receive(:puts).ordered.with("QUIT :\"outta here\"\n")
115
+ expect(sock_dbl).to receive(:close).ordered
116
+ x = bitlbee(1, 'foo', 'bar')
117
+ x.stub(:communicate) do
118
+ x.instance_variable_get(:@socket).puts("communicate")
119
+ end
120
+ x.connect
121
+ expect(x.instance_variable_get(:@socket)).to eq sock_dbl
122
+ end
123
+ end
124
+ context '#communicate' do
125
+ it 'logs in' do
126
+ x = bitlbee(1, 'foo', 'bar')
127
+ sock = double
128
+ x.instance_variable_set(:@socket, sock)
129
+ x.instance_variable_set(:@started, true)
130
+ expect(sock).to receive(:puts).ordered.with("PASS bar\n")
131
+ expect(sock).to receive(:puts).ordered.with("NICK foo\n")
132
+ expect(sock).to receive(:puts).ordered.with("USER foo foo servername :TestUser\n")
133
+ expect(sock).to receive(:readpartial).ordered.with(1024).and_return("Welcome to the foo")
134
+ expect(sock).to receive(:puts).ordered.with("MODE foo +i\n")
135
+ expect(sock).to receive(:readpartial).ordered.with(1024).and_return("If you've never something something")
136
+ expect(sock).to receive(:puts).ordered.with("PRIVMSG &bitlbee :identify bar\n")
137
+ expect(sock).to receive(:readpartial).ordered.with(1024).and_return("PING servername")
138
+ expect(sock).to receive(:puts).ordered.with(":servername PONG servername :servername\n")
139
+ expect(sock).to receive(:readpartial).ordered.with(1024).and_return("MODE foo :\+i")
140
+ expect(sock).to receive(:puts).ordered.with("PRIVMSG root :\001VERSION\001\n")
141
+ expect(sock).to receive(:readpartial).ordered.with(1024).and_return("foo bar baz blam\n")
142
+ expect(sock).to receive(:readpartial).ordered.with(1024).and_return("VERSION x.y.z\n")
143
+ x.communicate
144
+ expect(x.instance_variable_get(:@connected_status)).to eq true
145
+ expect(x.version).to eq 'x.y.z'
146
+ expect(x.connectable?).to eq true
147
+ expect(x.timed_out?).to eq false
148
+ end
149
+ end
150
+ context '#timed_out?' do
151
+ it 'calls start if not started' do
152
+ x = bitlbee(1, 'foo', 'bar')
153
+ expect(x).to receive(:start).once
154
+ expect(x.timed_out?).to eq false
155
+ end
156
+ it 'doesnt call start if started' do
157
+ x = bitlbee(1, 'foo', 'bar')
158
+ x.instance_variable_set(:@started, true)
159
+ expect(x).to_not receive(:start)
160
+ expect(x.timed_out?).to eq false
161
+ end
162
+ end
163
+ context '#connectable?' do
164
+ it 'calls start if not started' do
165
+ x = bitlbee(1, 'foo', 'bar')
166
+ expect(x).to receive(:start).once
167
+ expect(x.connectable?).to eq false
168
+ end
169
+ it 'doesnt call start if started' do
170
+ x = bitlbee(1, 'foo', 'bar')
171
+ x.instance_variable_set(:@started, true)
172
+ expect(x).to_not receive(:start)
173
+ expect(x.connectable?).to eq false
174
+ end
175
+ end
176
+ context '#version?' do
177
+ it 'calls start if not started' do
178
+ x = bitlbee(1, 'foo', 'bar')
179
+ expect(x).to receive(:start).once
180
+ expect(x.version).to eq ""
181
+ end
182
+ it 'doesnt call start if started' do
183
+ x = bitlbee(1, 'foo', 'bar')
184
+ x.instance_variable_set(:@started, true)
185
+ expect(x).to_not receive(:start)
186
+ expect(x.version).to eq ""
187
+ end
188
+ end
189
+ end