File: spawner_spec.rb

package info (click to toggle)
ruby-passenger 3.0.13debian-1%2Bdeb7u2
  • links: PTS, VCS
  • area: main
  • in suites: wheezy
  • size: 15,920 kB
  • sloc: cpp: 99,104; ruby: 18,098; ansic: 9,846; sh: 8,632; python: 141; makefile: 30
file content (273 lines) | stat: -rw-r--r-- 8,437 bytes parent folder | download
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
require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
require 'yaml'
require 'etc'

module PhusionPassenger

shared_examples_for "a spawner" do
	def ping_app(app, connect_password)
		if app.server_sockets[:main][1] == "unix"
			client = UNIXSocket.new(app.server_sockets[:main][0])
		else
			addr, port = app.server_sockets[:main][0].split(/:/)
			client = TCPSocket.new(addr, port.to_i)
		end
		begin
			channel = MessageChannel.new(client)
			channel.write_scalar("REQUEST_METHOD\0PING\0PASSENGER_CONNECT_PASSWORD\0#{connect_password}\0")
			return client.read
		ensure
			client.close
		end
	end
	
	it "returns a valid AppProcess object" do
		app = spawn_some_application
		lambda { Process.kill(0, app.pid) }.should_not raise_error
	end
	
	it "sets the working directory of the app to its app root" do
		before_start %q{
			File.touch("cwd.txt")
		}
		app = spawn_some_application
		File.exist?("#{app.app_root}/cwd.txt").should be_true
	end
	
	it "sets ENV['RAILS_ENV'] and ENV['RACK_ENV']" do
		before_start %q{
			File.write("rails_env.txt", ENV['RAILS_ENV'])
			File.write("rack_env.txt", ENV['RACK_ENV'])
		}
		app = spawn_some_application("environment" => "staging")
		File.read("#{app.app_root}/rails_env.txt").should == "staging"
		File.read("#{app.app_root}/rack_env.txt").should == "staging"
	end
	
	it "sets ENV['RAILS_RELATIVE_URL_ROOT'] and ENV['RACK_BASE_URI'] if the 'base_uri' option is set to a valid value" do
		before_start %q{
			File.write("rails_relative_url_root.txt", ENV['RAILS_RELATIVE_URL_ROOT'])
			File.write("rack_base_uri.txt", ENV['RACK_BASE_URI'])
		}
		app = spawn_some_application("base_uri" => "/foo")
		File.read("#{app.app_root}/rails_relative_url_root.txt").should == "/foo"
		File.read("#{app.app_root}/rack_base_uri.txt").should == "/foo"
	end
	
	it "doesn't set ENV['RAILS_RELATIVE_URL_ROOT'] and ENV['RACK_BASE_URI'] if 'base_uri' is not given" do
		before_start %q{
			if ENV['RAILS_RELATIVE_URL_ROOT']
				File.touch("rails_relative_url_root.txt")
			end
			if ENV['RACK_BASE_URI']
				File.touch("rack_base_uri.txt")
			end
		}
		app = spawn_some_application
		File.exist?("#{app.app_root}/rails_relative_url_root.txt").should be_false
		File.exist?("#{app.app_root}/rack_base_uri.txt").should be_false
	end
	
	it "doesn't set ENV['RAILS_RELATIVE_URL_ROOT'] and ENV['RACK_BASE_URI'] if 'base_uri' is empty" do
		before_start %q{
			if ENV['RAILS_RELATIVE_URL_ROOT']
				File.touch("rails_relative_url_root.txt")
			end
			if ENV['RACK_BASE_URI']
				File.touch("rack_base_uri.txt")
			end
		}
		app = spawn_some_application("base_uri" => "")
		File.exist?("#{app.app_root}/rails_relative_url_root.txt").should be_false
		File.exist?("#{app.app_root}/rack_base_uri.txt").should be_false
	end
	
	it "doesn't set ENV['RAILS_RELATIVE_URL_ROOT'] and ENV['RACK_BASE_URI'] if 'base_uri' is '/'" do
		before_start %q{
			if ENV['RAILS_RELATIVE_URL_ROOT']
				File.touch("rails_relative_url_root.txt")
			end
			if ENV['RACK_BASE_URI']
				File.touch("rack_base_uri.txt")
			end
		}
		app = spawn_some_application("base_uri" => "/")
		File.exist?("#{app.app_root}/rails_relative_url_root.txt").should be_false
		File.exist?("#{app.app_root}/rack_base_uri.txt").should be_false
	end
	
	it "sets the environment variables in the 'environment_variables' option" do
		before_start %q{
			File.open("env.txt", "w") do |f|
				f.puts
				ENV.each_pair do |key, value|
					f.puts("#{key} = #{value}")
				end
			end
		}
		
		env_vars_string = "PATH\0/usr/bin:/opt/sw/bin\0FOO\0foo bar!\0"
		options = { "environment_variables" => [env_vars_string].pack("m") }
		app = spawn_some_application(options)
		
		contents = File.read("#{app.app_root}/env.txt")
		contents.should =~ %r(\nPATH = /usr/bin:/opt/sw/bin\n)
		contents.should =~ %r(\nFOO = foo bar\!\n)
	end
	
	it "does not cache things like the connect password" do
		app1 = spawn_some_application("connect_password" => "1234")
		app2 = spawn_some_application("connect_password" => "5678")
		ping_app(app1, "1234").should == "pong"
		ping_app(app2, "5678").should == "pong"
	end
	
	it "calls the starting_worker_process event after the startup file has been loaded" do
		after_start %q{
			history_file = "#{PhusionPassenger::Utils.passenger_tmpdir}/history.txt"
			PhusionPassenger.on_event(:starting_worker_process) do
				::File.append(history_file, "worker_process_started\n")
			end
			::File.append(history_file, "end of startup file\n");
		}
		spawn_some_application.close
		app = spawn_some_application
		app.close
		
		history_file = "#{PhusionPassenger::Utils.passenger_tmpdir}/history.txt"
		eventually do
			contents = File.read(history_file)
			lines = contents.split("\n")
			lines[0] == "end of startup file" &&
				lines.count("worker_process_started") == 2
		end
	end
	
	it "calls the stopping_worker_process event" do
		after_start %q{
			history_file = "#{PhusionPassenger::Utils.passenger_tmpdir}/history.txt"
			PhusionPassenger.on_event(:stopping_worker_process) do
				::File.append(history_file, "worker_process_stopped\n")
			end
			::File.append(history_file, "end of startup file\n");
		}
		spawn_some_application.close
		app = spawn_some_application
		app.close
		
		history_file = "#{PhusionPassenger::Utils.passenger_tmpdir}/history.txt"
		eventually do
			contents = File.read(history_file)
			lines = contents.split("\n")
			lines[0] == "end of startup file" &&
				lines.count("worker_process_stopped") == 2
		end
	end
	
	it "calls #at_exit blocks upon exiting" do
		before_start %q{
			history_file = "#{PhusionPassenger::Utils.passenger_tmpdir}/history.txt"
			at_exit do
				File.open(history_file, "a") do |f|
					f.puts "at_exit 1"
				end
			end
			at_exit do
				File.open(history_file, "a") do |f|
					f.puts "at_exit 2"
				end
			end
		}
		
		spawn_some_application.close
		history_file = "#{PhusionPassenger::Utils.passenger_tmpdir}/history.txt"
		eventually do
			File.exist?(history_file) &&
			File.read(history_file) ==
				"at_exit 2\n" +
				"at_exit 1\n"
		end
	end
	
	it "lowers privilege using Utils#lower_privilege" do
		filename = "#{PhusionPassenger::Utils.passenger_tmpdir}/called.txt"
		PhusionPassenger::Utils.stub!(:lower_privilege_called).and_return do
			File.touch(filename)
		end
		spawn_some_application.close
		eventually do
			File.exist?(filename).should be_true
		end
	end
	
	describe "error handling" do
		it "raises an AppInitError if the spawned app raises a standard exception during startup" do
			before_start %q{
				raise 'This is a dummy exception.'
			}
			begin
				spawn_some_application("print_exceptions" => false)
				violated "Spawning the application should have raised an AppInitError."
			rescue AppInitError => e
				e.child_exception.message.should == "This is a dummy exception."
			end
		end
		
		it "raises an AppInitError if the spawned app raises a custom-defined exception during startup" do
			before_start %q{
				class MyError < StandardError
				end
				
				raise MyError, "This is a custom exception."
			}
			begin
				spawn_some_application("print_exceptions" => false)
				violated "Spawning the application should have raised an AppInitError."
			rescue AppInitError => e
				e.child_exception.message.should == "This is a custom exception. (MyError)"
			end
		end
		
		it "raises an AppInitError if the spawned app calls exit() during startup" do
			before_start %q{
				exit
			}
			begin
				spawn_some_application("print_exceptions" => false).close
				violated "Spawning the application should have raised an AppInitError."
			rescue AppInitError => e
				e.child_exception.class.should == SystemExit
			end
		end
		
		it "prints the exception to STDERR if the spawned app raised an error" do
			old_stderr = STDERR
			file = File.new('output.tmp', 'w+')
			begin
				Object.send(:remove_const, "STDERR") rescue nil
				Object.const_set("STDERR", file)
				
				before_start %q{
					def dummy_function
						raise 'This is a dummy exception.'
					end
					dummy_function
				}
				block = lambda { spawn_some_application }
				block.should raise_error(AppInitError)
				
				file.rewind
				data = file.read
				data.should =~ /This is a dummy exception/
				data.should =~ /dummy_function/
			ensure
				Object.send(:remove_const, "STDERR") rescue nil
				Object.const_set("STDERR", old_stderr)
				file.close rescue nil
				File.unlink('output.tmp') rescue nil
			end
		end
	end
end

end # module PhusionPassenger