serverspec-extended-types 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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