is_it_working 1.0.10
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.
- data/License.txt +674 -0
- data/README.rdoc +75 -0
- data/Rakefile +38 -0
- data/lib/is_it_working.rb +18 -0
- data/lib/is_it_working/checks/action_mailer_check.rb +25 -0
- data/lib/is_it_working/checks/active_record_check.rb +28 -0
- data/lib/is_it_working/checks/dalli_check.rb +48 -0
- data/lib/is_it_working/checks/directory_check.rb +44 -0
- data/lib/is_it_working/checks/memcache_check.rb +46 -0
- data/lib/is_it_working/checks/ping_check.rb +53 -0
- data/lib/is_it_working/checks/url_check.rb +81 -0
- data/lib/is_it_working/filter.rb +55 -0
- data/lib/is_it_working/handler.rb +152 -0
- data/lib/is_it_working/status.rb +50 -0
- data/spec/action_mailer_check_spec.rb +51 -0
- data/spec/active_record_check_spec.rb +51 -0
- data/spec/dalli_check_spec.rb +53 -0
- data/spec/directory_check_spec.rb +70 -0
- data/spec/filter_spec.rb +46 -0
- data/spec/handler_spec.rb +140 -0
- data/spec/memecache_check_spec.rb +51 -0
- data/spec/ping_check_spec.rb +47 -0
- data/spec/spec_helper.rb +11 -0
- data/spec/status_spec.rb +44 -0
- data/spec/url_check_spec.rb +144 -0
- metadata +121 -0
@@ -0,0 +1,55 @@
|
|
1
|
+
module IsItWorking
|
2
|
+
# Wrapper around a status check.
|
3
|
+
class Filter
|
4
|
+
class AsyncRunner < Thread
|
5
|
+
attr_accessor :filter_status
|
6
|
+
end
|
7
|
+
|
8
|
+
class SyncRunner
|
9
|
+
attr_accessor :filter_status
|
10
|
+
|
11
|
+
def initialize
|
12
|
+
yield
|
13
|
+
end
|
14
|
+
|
15
|
+
def join
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
attr_reader :name, :async
|
20
|
+
|
21
|
+
# Create a new filter to run a status check. The name is used for display purposes.
|
22
|
+
def initialize(name, check, async = true)
|
23
|
+
@name = name
|
24
|
+
@check = check
|
25
|
+
@async = async
|
26
|
+
end
|
27
|
+
|
28
|
+
# Run a status the status check. This method keeps track of the time it took to run
|
29
|
+
# the check and will trap any unexpected exceptions and report them as failures.
|
30
|
+
def run
|
31
|
+
status = Status.new(name)
|
32
|
+
runner = (async ? AsyncRunner : SyncRunner).new do
|
33
|
+
t = Time.now
|
34
|
+
begin
|
35
|
+
@check.call(status)
|
36
|
+
rescue Exception => e
|
37
|
+
status.fail("#{name} error: #{e.inspect}")
|
38
|
+
end
|
39
|
+
status.time = Time.now - t
|
40
|
+
end
|
41
|
+
runner.filter_status = status
|
42
|
+
runner
|
43
|
+
end
|
44
|
+
|
45
|
+
class << self
|
46
|
+
# Run a list of filters and return their status objects
|
47
|
+
def run_filters (filters)
|
48
|
+
runners = filters.collect{|f| f.run}
|
49
|
+
statuses = runners.collect{|runner| runner.filter_status}
|
50
|
+
runners.each{|runner| runner.join}
|
51
|
+
statuses
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,152 @@
|
|
1
|
+
module IsItWorking
|
2
|
+
# Rack handler that will run a set of status checks on the application and report the
|
3
|
+
# results. The results are formatted in plain text. If any of the checks fails, the
|
4
|
+
# response code will be 500 Server Error.
|
5
|
+
#
|
6
|
+
# The checks to perform are defined in the initialization block. Each check needs a name
|
7
|
+
# and can either be a predefined check, block, or an object that responds to the +call+
|
8
|
+
# method. When a check is called, its +call+ method will be called with a Status object.
|
9
|
+
#
|
10
|
+
# === Example
|
11
|
+
#
|
12
|
+
# IsItWorkingHandler.new do |h|
|
13
|
+
# # Predefined check to determine if a directory is accessible
|
14
|
+
# h.check :directory, "/var/myapp", :read, :write
|
15
|
+
#
|
16
|
+
# # Custom check using a block
|
17
|
+
# h.check :solr do
|
18
|
+
# SolrServer.available? ? ok("solr is up") : fail("solr is down")
|
19
|
+
# end
|
20
|
+
# end
|
21
|
+
class Handler
|
22
|
+
PATH_INFO = "PATH_INFO".freeze
|
23
|
+
|
24
|
+
# Create a new handler. This method can take a block which will yield itself so it can
|
25
|
+
# be configured.
|
26
|
+
#
|
27
|
+
# The handler can be set up in one of two ways. If no arguments are supplied, it will
|
28
|
+
# return a regular Rack handler that can be used with a rackup +run+ method or in a
|
29
|
+
# Rails 3+ routes.rb file. Otherwise, an application stack can be supplied in the first
|
30
|
+
# argument and a routing path in the second (defaults to <tt>/is_it_working</tt>) so
|
31
|
+
# it can be used with the rackup +use+ method or in Rails.middleware.
|
32
|
+
def initialize(app=nil, route_path="/is_it_working", &block)
|
33
|
+
@app = app
|
34
|
+
@route_path = route_path
|
35
|
+
@hostname = `hostname`.chomp
|
36
|
+
@filters = []
|
37
|
+
@mutex = Mutex.new
|
38
|
+
yield self if block_given?
|
39
|
+
end
|
40
|
+
|
41
|
+
def call(env)
|
42
|
+
if @app.nil? || env[PATH_INFO] == @route_path
|
43
|
+
statuses = []
|
44
|
+
t = Time.now
|
45
|
+
statuses = Filter.run_filters(@filters)
|
46
|
+
render(statuses, Time.now - t)
|
47
|
+
else
|
48
|
+
@app.call(env)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
# Set the hostname reported the the application is running on. By default this is set
|
53
|
+
# the system hostname. You should override it if the value reported as the hostname by
|
54
|
+
# the system is not useful or if exposing it publicly would create a security risk.
|
55
|
+
def hostname=(val)
|
56
|
+
@hostname = val
|
57
|
+
end
|
58
|
+
|
59
|
+
# Add a status check to the handler.
|
60
|
+
#
|
61
|
+
# If a block is given, it will be used as the status check and will be yielded to
|
62
|
+
# with a Status object.
|
63
|
+
#
|
64
|
+
# If the name matches one of the pre-defined status check classes, a new instance will
|
65
|
+
# be created using the rest of the arguments as the arguments to the initializer. The
|
66
|
+
# pre-defined classes are:
|
67
|
+
#
|
68
|
+
# * <tt>:action_mailer</tt> - Check if the send mail configuration used by ActionMailer is available
|
69
|
+
# * <tt>:active_record</tt> - Check if the database connection for an ActiveRecord class is up
|
70
|
+
# * <tt>:dalli</tt> - DalliCheck checks if all the servers in a MemCache cluster are available using dalli
|
71
|
+
# * <tt>:directory</tt> - DirectoryCheck checks for the accessibilty of a file system directory
|
72
|
+
# * <tt>:memcache</tt> - MemcacheCheck checks if all the servers in a MemCache cluster are available using memcache-client
|
73
|
+
# * <tt>:ping</tt> - Check if a host is reachable and accepting connections on a port
|
74
|
+
# * <tt>:url</tt> - Check if a getting a URL returns a success response
|
75
|
+
def check (name, *options_or_check, &block)
|
76
|
+
raise ArgumentError("Too many arguments to #{self.class.name}#check") if options_or_check.size > 2
|
77
|
+
check = nil
|
78
|
+
options = {:async => true}
|
79
|
+
|
80
|
+
unless options_or_check.empty?
|
81
|
+
if options_or_check[0].is_a?(Hash)
|
82
|
+
options = options.merge(options_or_check[0])
|
83
|
+
else
|
84
|
+
check = options_or_check[0]
|
85
|
+
end
|
86
|
+
if options_or_check[1].is_a?(Hash)
|
87
|
+
options = options.merge(options_or_check[1])
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
unless check
|
92
|
+
if block
|
93
|
+
check = block
|
94
|
+
else
|
95
|
+
check = lookup_check(name, options)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
@filters << Filter.new(name, check, options[:async])
|
100
|
+
end
|
101
|
+
|
102
|
+
# Helper method to synchronize a block of code so it can be thread safe.
|
103
|
+
# This method uses a Mutex and is not re-entrant. The synchronization will
|
104
|
+
# be only on calls to this handler.
|
105
|
+
def synchronize
|
106
|
+
@mutex.synchronize do
|
107
|
+
yield
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
protected
|
112
|
+
# Lookup a status check filter from the name and arguments
|
113
|
+
def lookup_check(name, options) #:nodoc:
|
114
|
+
check_class_name = "#{name.to_s.gsub(/(^|_)([a-z])/){|m| m.sub('_', '').upcase}}Check"
|
115
|
+
check = nil
|
116
|
+
if IsItWorking.const_defined?(check_class_name)
|
117
|
+
check_class = IsItWorking.const_get(check_class_name)
|
118
|
+
check = check_class.new(options)
|
119
|
+
else
|
120
|
+
raise ArgumentError.new("Check not defined #{check_class_name}")
|
121
|
+
end
|
122
|
+
check
|
123
|
+
end
|
124
|
+
|
125
|
+
# Output the plain text response from calling all the filters.
|
126
|
+
def render(statuses, elapsed_time) #:nodoc:
|
127
|
+
fail = statuses.all?{|s| s.success?}
|
128
|
+
headers = {
|
129
|
+
"Content-Type" => "text/plain; charset=utf8",
|
130
|
+
"Cache-Control" => "no-cache",
|
131
|
+
"Date" => Time.now.httpdate,
|
132
|
+
}
|
133
|
+
|
134
|
+
messages = []
|
135
|
+
statuses.each do |status|
|
136
|
+
status.messages.each do |m|
|
137
|
+
messages << "#{m.ok? ? 'OK: ' : 'FAIL:'} #{status.name} - #{m.message} (#{status.time ? sprintf('%0.000f', status.time * 1000) : '?'}ms)"
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
info = []
|
142
|
+
info << "Host: #{@hostname}" unless @hostname.size == 0
|
143
|
+
info << "PID: #{$$}"
|
144
|
+
info << "Timestamp: #{Time.now.iso8601}"
|
145
|
+
info << "Elapsed Time: #{(elapsed_time * 1000).round}ms"
|
146
|
+
|
147
|
+
code = (fail ? 200 : 500)
|
148
|
+
|
149
|
+
[code, headers, [info.join("\n"), "\n\n", messages.join("\n")]]
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module IsItWorking
|
2
|
+
# This class is used to pass the status of a monitoring check. Each status can have multiple
|
3
|
+
# messages added to it by calling the +ok+ or +fail+ methods. The status check will only be
|
4
|
+
# considered a success if all messages are ok.
|
5
|
+
class Status
|
6
|
+
# This class is used to contain individual status messages. Eache method can represent either
|
7
|
+
# and +ok+ message or a +fail+ message.
|
8
|
+
class Message
|
9
|
+
attr_reader :message
|
10
|
+
|
11
|
+
def initialize(message, ok)
|
12
|
+
@message = message
|
13
|
+
@ok = ok
|
14
|
+
end
|
15
|
+
|
16
|
+
def ok?
|
17
|
+
@ok
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
# The name of the status check for display purposes.
|
22
|
+
attr_reader :name
|
23
|
+
|
24
|
+
# The messages set on the status check.
|
25
|
+
attr_reader :messages
|
26
|
+
|
27
|
+
# The amount of time it takes to complete the status check.
|
28
|
+
attr_accessor :time
|
29
|
+
|
30
|
+
def initialize(name)
|
31
|
+
@name = name
|
32
|
+
@messages = []
|
33
|
+
end
|
34
|
+
|
35
|
+
# Add a message indicating that the check passed.
|
36
|
+
def ok(message)
|
37
|
+
@messages << Message.new(message, true)
|
38
|
+
end
|
39
|
+
|
40
|
+
# Add a message indicating that the check failed.
|
41
|
+
def fail(message)
|
42
|
+
@messages << Message.new(message, false)
|
43
|
+
end
|
44
|
+
|
45
|
+
# Returns +true+ only if all checks were OK.
|
46
|
+
def success?
|
47
|
+
@messages.all?{|m| m.ok?}
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe IsItWorking::ActionMailerCheck do
|
4
|
+
|
5
|
+
let(:status){ IsItWorking::Status.new(:ping) }
|
6
|
+
|
7
|
+
it "should succeed if the default mail host is accepting connections" do
|
8
|
+
ActionMailer::Base.smtp_settings[:address] = 'localhost'
|
9
|
+
ActionMailer::Base.smtp_settings[:port] = 25
|
10
|
+
TCPSocket.should_receive(:new).with('localhost', 25).and_return(mock(:socket, :close => true))
|
11
|
+
check = IsItWorking::ActionMailerCheck.new
|
12
|
+
check.call(status)
|
13
|
+
status.should be_success
|
14
|
+
status.messages.first.message.should == "ActionMailer::Base is accepting connections on port 25"
|
15
|
+
end
|
16
|
+
|
17
|
+
it "should succeed if the default mail host is not accepting connections" do
|
18
|
+
ActionMailer::Base.smtp_settings[:address] = 'localhost'
|
19
|
+
ActionMailer::Base.smtp_settings[:port] = 25
|
20
|
+
TCPSocket.should_receive(:new).with('localhost', 25).and_raise(Errno::ECONNREFUSED)
|
21
|
+
check = IsItWorking::ActionMailerCheck.new
|
22
|
+
check.call(status)
|
23
|
+
status.should_not be_success
|
24
|
+
status.messages.first.message.should == "ActionMailer::Base is not accepting connections on port 25"
|
25
|
+
end
|
26
|
+
|
27
|
+
it "should get the smtp configuration from a specified ActionMailer class" do
|
28
|
+
class IsItWorking::ActionMailerCheck::Tester < ActionMailer::Base
|
29
|
+
end
|
30
|
+
|
31
|
+
IsItWorking::ActionMailerCheck::Tester.smtp_settings[:address] = 'mail.example.com'
|
32
|
+
IsItWorking::ActionMailerCheck::Tester.smtp_settings[:port] = 'smtp'
|
33
|
+
TCPSocket.should_receive(:new).with('mail.example.com', 'smtp').and_return(mock(:socket, :close => true))
|
34
|
+
check = IsItWorking::ActionMailerCheck.new(:class => IsItWorking::ActionMailerCheck::Tester)
|
35
|
+
check.call(status)
|
36
|
+
status.should be_success
|
37
|
+
status.messages.first.message.should == "IsItWorking::ActionMailerCheck::Tester is accepting connections on port \"smtp\""
|
38
|
+
end
|
39
|
+
|
40
|
+
it "should allow aliasing the ActionMailer host alias" do
|
41
|
+
ActionMailer::Base.smtp_settings[:address] = 'localhost'
|
42
|
+
ActionMailer::Base.smtp_settings[:port] = 25
|
43
|
+
TCPSocket.should_receive(:new).with('localhost', 25).and_return(mock(:socket, :close => true))
|
44
|
+
check = IsItWorking::ActionMailerCheck.new(:alias => "smtp host")
|
45
|
+
check.call(status)
|
46
|
+
status.should be_success
|
47
|
+
status.messages.first.message.should == "smtp host is accepting connections on port 25"
|
48
|
+
end
|
49
|
+
|
50
|
+
|
51
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe IsItWorking::ActiveRecordCheck do
|
4
|
+
|
5
|
+
let(:status){ IsItWorking::Status.new(:active_record) }
|
6
|
+
|
7
|
+
class IsItWorking::TestActiveRecord < ActiveRecord::Base
|
8
|
+
end
|
9
|
+
|
10
|
+
it "should succeed if the ActiveRecord connection is active" do
|
11
|
+
connection = ActiveRecord::ConnectionAdapters::AbstractAdapter.new(mock(:connection))
|
12
|
+
connection.reconnect!
|
13
|
+
ActiveRecord::Base.stub!(:connection).and_return(connection)
|
14
|
+
check = IsItWorking::ActiveRecordCheck.new
|
15
|
+
check.call(status)
|
16
|
+
status.should be_success
|
17
|
+
status.messages.first.message.should == "ActiveRecord::Base.connection is active"
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should allow specifying the class to check the connection for" do
|
21
|
+
connection = ActiveRecord::ConnectionAdapters::AbstractAdapter.new(mock(:connection))
|
22
|
+
connection.reconnect!
|
23
|
+
IsItWorking::TestActiveRecord.stub!(:connection).and_return(connection)
|
24
|
+
check = IsItWorking::ActiveRecordCheck.new(:class => IsItWorking::TestActiveRecord)
|
25
|
+
check.call(status)
|
26
|
+
status.should be_success
|
27
|
+
status.messages.first.message.should == "IsItWorking::TestActiveRecord.connection is active"
|
28
|
+
end
|
29
|
+
|
30
|
+
it "should succeed if the ActiveRecord connection can be reconnected" do
|
31
|
+
connection = ActiveRecord::ConnectionAdapters::AbstractAdapter.new(mock(:connection))
|
32
|
+
connection.disconnect!
|
33
|
+
ActiveRecord::Base.stub!(:connection).and_return(connection)
|
34
|
+
check = IsItWorking::ActiveRecordCheck.new
|
35
|
+
check.call(status)
|
36
|
+
status.should be_success
|
37
|
+
status.messages.first.message.should == "ActiveRecord::Base.connection is active"
|
38
|
+
end
|
39
|
+
|
40
|
+
it "should fail if the ActiveRecord connection is not active" do
|
41
|
+
connection = ActiveRecord::ConnectionAdapters::AbstractAdapter.new(mock(:connection))
|
42
|
+
connection.disconnect!
|
43
|
+
connection.stub!(:verify!)
|
44
|
+
ActiveRecord::Base.stub!(:connection).and_return(connection)
|
45
|
+
check = IsItWorking::ActiveRecordCheck.new
|
46
|
+
check.call(status)
|
47
|
+
status.should_not be_success
|
48
|
+
status.messages.first.message.should == "ActiveRecord::Base.connection is not active"
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe IsItWorking::DalliCheck do
|
4
|
+
|
5
|
+
let(:status){ IsItWorking::Status.new(:memcache) }
|
6
|
+
let(:memcache){ Dalli::Client.new(['cache-1.example.com', 'cache-2.example.com']) }
|
7
|
+
let(:servers){ memcache.send(:ring).servers }
|
8
|
+
|
9
|
+
it "should succeed if all servers are responding" do
|
10
|
+
check = IsItWorking::DalliCheck.new(:cache => memcache)
|
11
|
+
servers.first.should_receive(:alive?).and_return(true)
|
12
|
+
servers.last.should_receive(:alive?).and_return(true)
|
13
|
+
check.call(status)
|
14
|
+
status.should be_success
|
15
|
+
status.messages.first.message.should == "cache-1.example.com:11211 is available"
|
16
|
+
status.messages.last.message.should == "cache-2.example.com:11211 is available"
|
17
|
+
end
|
18
|
+
|
19
|
+
it "should fail if any server is not responding" do
|
20
|
+
check = IsItWorking::DalliCheck.new(:cache => memcache)
|
21
|
+
servers.first.should_receive(:alive?).and_return(true)
|
22
|
+
servers.last.should_receive(:alive?).and_return(false)
|
23
|
+
check.call(status)
|
24
|
+
status.should_not be_success
|
25
|
+
status.messages.first.message.should == "cache-1.example.com:11211 is available"
|
26
|
+
status.messages.last.message.should == "cache-2.example.com:11211 is not available"
|
27
|
+
end
|
28
|
+
|
29
|
+
it "should be able to get the MemCache object from an ActiveSupport::Cache" do
|
30
|
+
require 'active_support/cache'
|
31
|
+
require 'active_support/cache/dalli_store'
|
32
|
+
ActiveSupport::Cache::DalliStore.should_receive(:new).with('cache-1.example.com', 'cache-2.example.com').and_return(memcache)
|
33
|
+
rails_cache = ActiveSupport::Cache::DalliStore.new('cache-1.example.com', 'cache-2.example.com')
|
34
|
+
check = IsItWorking::DalliCheck.new(:cache => rails_cache)
|
35
|
+
servers.first.should_receive(:alive?).and_return(true)
|
36
|
+
servers.last.should_receive(:alive?).and_return(true)
|
37
|
+
check.call(status)
|
38
|
+
status.should be_success
|
39
|
+
status.messages.first.message.should == "cache-1.example.com:11211 is available"
|
40
|
+
status.messages.last.message.should == "cache-2.example.com:11211 is available"
|
41
|
+
end
|
42
|
+
|
43
|
+
it "should be able to alias the memcache host names in the output" do
|
44
|
+
check = IsItWorking::DalliCheck.new(:cache => memcache, :alias => "memcache")
|
45
|
+
servers.first.should_receive(:alive?).and_return(true)
|
46
|
+
servers.last.should_receive(:alive?).and_return(true)
|
47
|
+
check.call(status)
|
48
|
+
status.should be_success
|
49
|
+
status.messages.first.message.should == "memcache 1 is available"
|
50
|
+
status.messages.last.message.should == "memcache 2 is available"
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe IsItWorking::DirectoryCheck do
|
4
|
+
|
5
|
+
let(:status){ IsItWorking::Status.new(:directory) }
|
6
|
+
let(:directory_path){ File.expand_path(".") }
|
7
|
+
|
8
|
+
it "should fail if a directory can't be found" do
|
9
|
+
check = IsItWorking::DirectoryCheck.new(:path => File.expand_path("../no_such_thing", __FILE__))
|
10
|
+
check.call(status)
|
11
|
+
status.should_not be_success
|
12
|
+
status.messages.first.message.should include("does not exist")
|
13
|
+
end
|
14
|
+
|
15
|
+
it "should fail if a path isn't a directory" do
|
16
|
+
check = IsItWorking::DirectoryCheck.new(:path => __FILE__)
|
17
|
+
check.call(status)
|
18
|
+
status.should_not be_success
|
19
|
+
status.messages.first.message.should include("is not a directory")
|
20
|
+
end
|
21
|
+
|
22
|
+
it "should succeed if a directory exists" do
|
23
|
+
check = IsItWorking::DirectoryCheck.new(:path => directory_path)
|
24
|
+
File.should_receive(:stat).with(directory_path).and_return(mock(:stat, :directory? => true, :readable? => false, :writable? => false))
|
25
|
+
check.call(status)
|
26
|
+
status.should be_success
|
27
|
+
status.messages.first.message.should include("exists")
|
28
|
+
end
|
29
|
+
|
30
|
+
it "should fail if a directory is not readable" do
|
31
|
+
check = IsItWorking::DirectoryCheck.new(:path => directory_path, :permission => :read)
|
32
|
+
File.should_receive(:stat).with(directory_path).and_return(mock(:stat, :directory? => true, :readable? => false, :writable? => true))
|
33
|
+
check.call(status)
|
34
|
+
status.should_not be_success
|
35
|
+
status.messages.first.message.should include("is not readable")
|
36
|
+
end
|
37
|
+
|
38
|
+
it "should fail if a directory is not writable" do
|
39
|
+
check = IsItWorking::DirectoryCheck.new(:path => directory_path, :permission => :write)
|
40
|
+
File.should_receive(:stat).with(directory_path).and_return(mock(:stat, :directory? => true, :readable? => true, :writable? => false))
|
41
|
+
check.call(status)
|
42
|
+
status.should_not be_success
|
43
|
+
status.messages.first.message.should include("is not writable")
|
44
|
+
end
|
45
|
+
|
46
|
+
it "should succeed if a directory exists and is readable" do
|
47
|
+
check = IsItWorking::DirectoryCheck.new(:path => directory_path, :permission => :read)
|
48
|
+
File.should_receive(:stat).with(directory_path).and_return(mock(:stat, :directory? => true, :readable? => true, :writable? => false))
|
49
|
+
check.call(status)
|
50
|
+
status.should be_success
|
51
|
+
status.messages.first.message.should include("exists with")
|
52
|
+
end
|
53
|
+
|
54
|
+
it "should succeed if a directory exists and is writable" do
|
55
|
+
check = IsItWorking::DirectoryCheck.new(:path => directory_path, :permission => :write)
|
56
|
+
File.should_receive(:stat).with(directory_path).and_return(mock(:stat, :directory? => true, :readable? => false, :writable? => true))
|
57
|
+
check.call(status)
|
58
|
+
status.should be_success
|
59
|
+
status.messages.first.message.should include("exists with")
|
60
|
+
end
|
61
|
+
|
62
|
+
it "should succeed if a directory exists and is readable and writable" do
|
63
|
+
check = IsItWorking::DirectoryCheck.new(:path => directory_path, :permission => [:read, :write])
|
64
|
+
File.should_receive(:stat).with(directory_path).and_return(mock(:stat, :directory? => true, :readable? => true, :writable? => true))
|
65
|
+
check.call(status)
|
66
|
+
status.should be_success
|
67
|
+
status.messages.first.message.should include("exists with")
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|