silver_spurs 1.0.1 → 1.0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,509 @@
1
+ require 'spec_helper'
2
+ require 'logger'
3
+
4
+ describe SilverSpurs::Asyncifier do
5
+ before :each do
6
+ SilverSpurs::Asyncifier.logger = Logger.new('/dev/null')
7
+ end
8
+
9
+ describe :timeout do
10
+ it 'defaults to 60 minutes' do
11
+ SilverSpurs::Asyncifier.timeout.should eq 60 * 60
12
+ end
13
+ end
14
+
15
+ describe :logger do
16
+ before :each do
17
+ SilverSpurs::Asyncifier.logger = nil
18
+ end
19
+
20
+ it 'defaults to STDERR' do
21
+ logger_dbl = double('logger')
22
+ logger_dbl.stub(:level=)
23
+ Logger.should_receive(:new).with(STDERR).and_return logger_dbl
24
+
25
+ SilverSpurs::Asyncifier.instance.logger
26
+ end
27
+ end
28
+
29
+ describe :spawn_process do
30
+ before :each do
31
+ SilverSpurs::Asyncifier.instance.stub(:create_directory_tree)
32
+ Process.stub(:spawn)
33
+ File.stub(:open)
34
+ Process.stub(:detach)
35
+ end
36
+
37
+ it 'creates the directory tree' do
38
+ SilverSpurs::Asyncifier.instance.should_receive(:create_directory_tree)
39
+ SilverSpurs::Asyncifier.spawn_process('foo', 'echo foo')
40
+ end
41
+
42
+ it 'spawns the process' do
43
+ Process.should_receive(:spawn)
44
+ SilverSpurs::Asyncifier.spawn_process('foo', 'echo foo')
45
+ end
46
+
47
+ it 'writes the pid to the lock file' do
48
+ File.should_receive(:open)
49
+ SilverSpurs::Asyncifier.instance.should_receive(:pid_file_path)
50
+ SilverSpurs::Asyncifier.spawn_process('foo', 'echo foo')
51
+ end
52
+
53
+ it 'detaches the process' do
54
+ Process.should_receive(:detach)
55
+ SilverSpurs::Asyncifier.spawn_process('foo', 'echo foo')
56
+ end
57
+ end
58
+
59
+ describe :has_lock? do
60
+ context 'when there is no pid file' do
61
+ before :each do
62
+ File.stub(:exists?).and_return false
63
+ end
64
+
65
+ it 'returns false' do
66
+ SilverSpurs::Asyncifier.has_lock?('foo').should be_false
67
+ end
68
+
69
+ it 'checks for the pid file' do
70
+ SilverSpurs::Asyncifier.instance.should_receive(:pid_file_path)
71
+ SilverSpurs::Asyncifier.has_lock?('foo')
72
+ end
73
+ end
74
+
75
+ context 'when there is a pid file' do
76
+ before :each do
77
+ File.stub(:exists?).and_return true
78
+ File.stub(:read).and_return '1234'
79
+ end
80
+
81
+ it 'checks to see if the process is running' do
82
+ IO.should_receive(:popen).with('ps -o command -p 1234').and_return StringIO.new("1\n2")
83
+ SilverSpurs::Asyncifier.has_lock? '1234'
84
+ end
85
+
86
+ context 'when the process is running' do
87
+ before :each do
88
+ IO.stub(:popen).and_return StringIO.new("COMMAND\nknife whatever")
89
+ end
90
+
91
+ it 'returns true' do
92
+ SilverSpurs::Asyncifier.has_lock?('1234').should be_true
93
+ end
94
+ end
95
+
96
+ context 'when the process is not running' do
97
+ before :each do
98
+ IO.stub(:popen).and_return StringIO.new("COMMAND")
99
+ end
100
+
101
+ it 'returns false' do
102
+ SilverSpurs::Asyncifier.has_lock?('1234').should be_false
103
+ end
104
+ end
105
+
106
+ end
107
+
108
+ end
109
+
110
+ describe :success? do
111
+ before :each do
112
+ SilverSpurs::Asyncifier.instance.stub(:success_file_path).and_return 'success_file'
113
+ end
114
+
115
+ it 'checks for a success file' do
116
+ File.should_receive(:exists?).with('success_file').and_return false
117
+
118
+ SilverSpurs::Asyncifier.success?('foo')
119
+ end
120
+
121
+ context 'when the success file does not exist' do
122
+ before :each do
123
+ File.stub(:exists?).and_return false
124
+ end
125
+
126
+ it 'returns false' do
127
+ SilverSpurs::Asyncifier.success?('foo').should be_false
128
+ end
129
+ end
130
+
131
+ context 'when the success file exists' do
132
+ before :each do
133
+ SilverSpurs::Asyncifier.instance.stub(:pid_file_path).and_return 'pid_file'
134
+ File.stub(:exists?).with('success_file').and_return true
135
+ end
136
+
137
+ it 'checks to see if the pid file exists' do
138
+ File.should_receive(:exists?).twice.and_return(true, false)
139
+ SilverSpurs::Asyncifier.success? 'foo'
140
+ end
141
+
142
+ context 'when the pid file exists' do
143
+ before :each do
144
+ File.stub(:exists?).and_return(true, true)
145
+ end
146
+
147
+ it 'checks the modified time of the success and pid file' do
148
+ File.should_receive(:mtime).twice.and_return(Time.now)
149
+ SilverSpurs::Asyncifier.success? 'foo'
150
+ end
151
+
152
+ context 'when the success file is younger than the pid file' do
153
+ before :each do
154
+ File.stub(:mtime).and_return(Time.new(2013), Time.new(2012))
155
+ end
156
+
157
+ it 'should return true' do
158
+ SilverSpurs::Asyncifier.success?('foo').should be_true
159
+ end
160
+ end
161
+
162
+ context 'when the pid file is younger than the success file' do
163
+ before :each do
164
+ File.stub(:mtime).and_return(Time.new(2012), Time.new(2013))
165
+ end
166
+
167
+ it 'should return false' do
168
+ SilverSpurs::Asyncifier.success?('foo').should be_false
169
+ end
170
+ end
171
+
172
+ end
173
+
174
+ end
175
+
176
+ end
177
+
178
+ describe :get_log do
179
+ before :each do
180
+ SilverSpurs::Asyncifier.instance.stub(:log_file_path).and_return 'log_file'
181
+ end
182
+
183
+ it 'checks to see if the log file exists' do
184
+ File.should_receive(:exists?).and_return false
185
+ SilverSpurs::Asyncifier.get_log 'foo'
186
+ end
187
+
188
+ context 'when the log file exists' do
189
+ before :each do
190
+ File.stub(:exists?).and_return true
191
+ end
192
+
193
+ it 'reads the log' do
194
+ File.stub(:read).and_return 'logs are cool'
195
+ SilverSpurs::Asyncifier.get_log('foo').should eq 'logs are cool'
196
+ end
197
+ end
198
+
199
+ context 'when the log file does not exist' do
200
+ before :each do
201
+ File.stub(:exists?).and_return false
202
+ end
203
+
204
+ it 'returns nil' do
205
+ SilverSpurs::Asyncifier.get_log('foo').should be_nil
206
+ end
207
+ end
208
+
209
+ end
210
+
211
+ describe :reap_orphaned_lock do
212
+ before :each do
213
+ SilverSpurs::Asyncifier.instance.stub(:pid_file_path).and_return 'pid_file'
214
+ end
215
+
216
+ it 'checks to see if the process is active' do
217
+ SilverSpurs::Asyncifier.instance.should_receive(:has_lock?).and_return true
218
+ SilverSpurs::Asyncifier.reap_orphaned_lock 'foo'
219
+ end
220
+
221
+ context 'when the process is still active' do
222
+ before :each do
223
+ SilverSpurs::Asyncifier.instance.stub(:has_lock?).and_return true
224
+ end
225
+
226
+ it 'should not try to delete the lock' do
227
+ File.should_not_receive(:delete)
228
+ SilverSpurs::Asyncifier.reap_orphaned_lock 'foo'
229
+ end
230
+ end
231
+
232
+ context 'when the process is not active' do
233
+ before :each do
234
+ SilverSpurs::Asyncifier.instance.stub(:has_lock?).and_return false
235
+ SilverSpurs::Asyncifier.instance.stub(:pid_file_path).and_return 'pid_file'
236
+ end
237
+
238
+ it 'should delete the lock file' do
239
+ File.should_receive(:delete).with('pid_file')
240
+ SilverSpurs::Asyncifier.reap_orphaned_lock 'foo'
241
+ end
242
+ end
243
+
244
+ end
245
+
246
+ describe :reap_process do
247
+ before :each do
248
+ SilverSpurs::Asyncifier.instance.stub(:sleep)
249
+ SilverSpurs::Asyncifier.instance.stub(:reap_orphaned_lock)
250
+ SilverSpurs::Asyncifier.instance.stub(:pid_file_path).and_return 'pid_file'
251
+ end
252
+
253
+ it 'checks to see if the process is active' do
254
+ SilverSpurs::Asyncifier.instance.should_receive(:has_lock?).and_return false
255
+ SilverSpurs::Asyncifier.reap_process 'foo'
256
+ end
257
+
258
+ it 'reaps the lock file' do
259
+ SilverSpurs::Asyncifier.instance.stub(:has_lock?).and_return false
260
+ SilverSpurs::Asyncifier.instance.should_receive(:reap_orphaned_lock)
261
+ SilverSpurs::Asyncifier.reap_process 'foo'
262
+ end
263
+
264
+ context 'when the process is active' do
265
+ before :each do
266
+ File.stub(:read).and_return '1234'
267
+ Process.stub(:kill)
268
+ SilverSpurs::Asyncifier.instance.stub(:has_lock?).and_return true
269
+ end
270
+
271
+ it 'kills the process' do
272
+ Process.should_receive(:kill)
273
+ SilverSpurs::Asyncifier.instance.stub(:has_lock?).and_return(true, false)
274
+ SilverSpurs::Asyncifier.reap_process 'foo'
275
+ end
276
+
277
+ it 'checks to see if the process really died' do
278
+ SilverSpurs::Asyncifier.instance.should_receive(:has_lock?).twice.and_return(true, false)
279
+ SilverSpurs::Asyncifier.reap_process 'foo'
280
+ end
281
+
282
+ context 'when the process dies when asked nicely' do
283
+ before :each do
284
+ SilverSpurs::Asyncifier.instance.stub(:has_lock?).and_return(true, false)
285
+ end
286
+
287
+ it 'just walks away' do
288
+ Process.should_receive(:kill).once
289
+ SilverSpurs::Asyncifier.reap_process 'foo'
290
+ end
291
+ end
292
+
293
+ context "when the process doesn't die after being asked nicely" do
294
+ before :each do
295
+ SilverSpurs::Asyncifier.instance.stub(:has_lock?).and_return(true, true)
296
+ end
297
+
298
+ it 'kills the process with fire' do
299
+ Process.should_receive(:kill).twice
300
+ SilverSpurs::Asyncifier.reap_process 'foo'
301
+ end
302
+ end
303
+
304
+ end
305
+
306
+ context 'if the process is inactive' do
307
+ before :each do
308
+ SilverSpurs::Asyncifier.instance.stub(:has_lock?).and_return false
309
+ end
310
+
311
+ it 'skips to reaping the lock file' do
312
+ File.should_not_receive(:read)
313
+ Process.should_not_receive(:kill)
314
+
315
+ SilverSpurs::Asyncifier.instance.should_receive(:reap_orphaned_lock)
316
+
317
+ SilverSpurs::Asyncifier.reap_process 'foo'
318
+ end
319
+ end
320
+
321
+ end
322
+
323
+ describe :reap_old_process do
324
+ before :each do
325
+ SilverSpurs::Asyncifier.instance.stub(:pid_file_path).and_return 'pid_file'
326
+ end
327
+
328
+ it 'checks if the process is active' do
329
+ SilverSpurs::Asyncifier.instance.should_receive(:has_lock?).and_return false
330
+ SilverSpurs::Asyncifier.reap_old_process 'foo'
331
+ end
332
+
333
+ context 'when the process is active' do
334
+ before :each do
335
+ SilverSpurs::Asyncifier.instance.stub(:has_lock?).and_return true
336
+ end
337
+
338
+ context 'when the process is beyond the timeout window' do
339
+ before :each do
340
+ Time.stub(:now).and_return Time.new(2013)
341
+ SilverSpurs::Asyncifier.instance.stub(:timeout).and_return 100
342
+ File.stub(:mtime).and_return Time.new(2013) - 101
343
+ end
344
+
345
+ it 'reaps the process' do
346
+ SilverSpurs::Asyncifier.instance.should_receive(:reap_process)
347
+ SilverSpurs::Asyncifier.reap_old_process 'foo'
348
+ end
349
+ end
350
+
351
+ context 'when the process is within the timeout window' do
352
+ before :each do
353
+ Time.stub(:now).and_return Time.new(2013)
354
+ SilverSpurs::Asyncifier.instance.stub(:timeout).and_return 100
355
+ File.stub(:mtime).and_return Time.new(2013) - 99
356
+ end
357
+
358
+ it 'does not reap the process' do
359
+ SilverSpurs::Asyncifier.instance.should_not_receive(:reap_process)
360
+ SilverSpurs::Asyncifier.reap_old_process 'foo'
361
+ end
362
+ end
363
+
364
+ end
365
+
366
+ context 'when the process is not active' do
367
+ before :each do
368
+ SilverSpurs::Asyncifier.instance.stub(:has_lock?).and_return false
369
+ end
370
+
371
+ it 'does not attempt to reap the process' do
372
+ SilverSpurs::Asyncifier.instance.should_not_receive :reap_process
373
+ SilverSpurs::Asyncifier.reap_old_process 'foo'
374
+ end
375
+ end
376
+
377
+ end
378
+
379
+ describe :exists? do
380
+ context 'when the process is active' do
381
+ before :each do
382
+ SilverSpurs::Asyncifier.instance.stub(:has_lock?).and_return true
383
+ end
384
+
385
+ it 'returns true' do
386
+ SilverSpurs::Asyncifier.exists?('foo').should be_true
387
+ end
388
+
389
+ end
390
+
391
+ context 'when the process is not active' do
392
+ before :each do
393
+ SilverSpurs::Asyncifier.instance.stub(:has_lock?).and_return false
394
+ SilverSpurs::Asyncifier.instance.stub(:log_file_path).and_return 'log_file'
395
+ end
396
+
397
+ context 'if the log file exists' do
398
+ before :each do
399
+ File.stub(:exists?).and_return true
400
+ end
401
+
402
+ it 'returns true' do
403
+ SilverSpurs::Asyncifier.exists?('foo').should be_true
404
+ end
405
+ end
406
+
407
+ context 'if the log file does not exist' do
408
+ before :each do
409
+ File.stub(:exists?).and_return false
410
+ end
411
+
412
+ it 'returns false' do
413
+ SilverSpurs::Asyncifier.exists?('foo').should be_false
414
+ end
415
+ end
416
+
417
+ end
418
+
419
+ end
420
+
421
+ describe :log_file_path do
422
+ it 'builds a path in the base directory' do
423
+ SilverSpurs::Asyncifier.instance.should_receive(:base_path).and_return 'base_path'
424
+ File.should_receive(:join)
425
+ SilverSpurs::Asyncifier.instance.send(:log_file_path, 'foo')
426
+ end
427
+
428
+ it 'returns a file name' do
429
+ SilverSpurs::Asyncifier.instance.send(:log_file_path, 'foo').should be_a_kind_of(String)
430
+ end
431
+ end
432
+
433
+ describe :success_file_path do
434
+ it 'builds a path in the base directory' do
435
+ SilverSpurs::Asyncifier.instance.should_receive(:base_path).and_return 'base_path'
436
+ File.should_receive(:join)
437
+ SilverSpurs::Asyncifier.instance.send(:success_file_path, 'foo')
438
+ end
439
+
440
+ it 'returns a file name' do
441
+ SilverSpurs::Asyncifier.instance.send(:success_file_path, 'foo').should be_a_kind_of(String)
442
+ end
443
+ end
444
+
445
+ describe :pid_file_path do
446
+ it 'builds a path in the base directory' do
447
+ SilverSpurs::Asyncifier.instance.should_receive(:base_path).and_return 'base_path'
448
+ File.should_receive(:join)
449
+ SilverSpurs::Asyncifier.instance.send(:pid_file_path, 'foo')
450
+ end
451
+
452
+ it 'returns a file name' do
453
+ SilverSpurs::Asyncifier.instance.send(:pid_file_path, 'foo').should be_a_kind_of(String)
454
+ end
455
+ end
456
+
457
+ describe :create_directory_tree do
458
+ it 'checks to see if the base path already exists' do
459
+ Dir.should_receive(:exists?).at_least(:once).and_return true
460
+ SilverSpurs::Asyncifier.instance.send(:create_directory_tree)
461
+ end
462
+
463
+ context 'if the base path already exists' do
464
+ before :each do
465
+ Dir.stub(:exists?).and_return true
466
+ end
467
+
468
+ it 'does not create the base directory' do
469
+ Dir.should_not_receive(:mkdir)
470
+ SilverSpurs::Asyncifier.instance.send(:create_directory_tree)
471
+ end
472
+ end
473
+
474
+ context 'if the base path does not exist' do
475
+ before :each do
476
+ Dir.stub(:exists?).and_return(false, true)
477
+ end
478
+
479
+ it 'creates the base directory' do
480
+ Dir.should_receive(:mkdir)
481
+ SilverSpurs::Asyncifier.instance.send(:create_directory_tree)
482
+ end
483
+ end
484
+
485
+ context 'if a directory exists' do
486
+ before :each do
487
+ Dir.stub(:exists?).and_return true
488
+ end
489
+
490
+ it 'does not create the directory' do
491
+ Dir.should_not_receive(:mkdir)
492
+ SilverSpurs::Asyncifier.instance.send(:create_directory_tree)
493
+ end
494
+ end
495
+
496
+ context 'if a directory does not exist' do
497
+ before :each do
498
+ Dir.stub(:exists?).and_return(true, false)
499
+ end
500
+
501
+ it 'creates the directory' do
502
+ Dir.should_receive(:mkdir).exactly(3).times
503
+ SilverSpurs::Asyncifier.instance.send(:create_directory_tree)
504
+ end
505
+ end
506
+
507
+ end
508
+
509
+ end