fir 0.0.9 → 0.0.10
Sign up to get free protection for your applications and to get access to all the features.
- data/README.markdown +33 -8
- data/bin/fir +7 -12
- data/example/Rakefile +4 -1
- data/example/deploy.yml +3 -0
- data/example/public/cache/404.html +46 -0
- data/example/public/cache/contact-us.html +48 -0
- data/example/public/cache/index.html +52 -0
- data/example/public/cache/products/dynamite-plus.html +52 -0
- data/example/public/cache/products/dynamite.html +52 -0
- data/example/public/cache/products/index.html +58 -0
- data/example/public/cache/products/mouse-trap.html +52 -0
- data/example/users.yml +0 -1
- data/lib/fir/admin.rb +9 -6
- data/lib/fir/generator.rb +33 -0
- data/lib/fir/tasks.rb +58 -7
- data/skeleton/public/dispatch.cgi +34 -0
- data/skeleton/public/htaccess +3 -0
- data/spec/admin_spec.rb +172 -0
- data/spec/fir_spec.rb +75 -48
- data/spec/generator_spec.rb +132 -0
- data/spec/rack_integration_spec.rb +44 -0
- data/spec/spec_helper.rb +16 -2
- data/spec/support/cache_testing.rb +20 -0
- data/spec/support/file_matchers.rb +22 -0
- data/spec/support/server_matchers.rb +39 -0
- data/spec/support/server_testing.rb +35 -0
- data/spec/support/test_app_setup.rb +11 -0
- data/spec/tasks_spec.rb +132 -0
- metadata +85 -9
@@ -0,0 +1,132 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
+
|
3
|
+
describe 'Fir.generate!' do
|
4
|
+
def fir_root
|
5
|
+
@fir_root ||= File.expand_path(File.join(File.dirname(__FILE__), '..', 'gen_test_example'))
|
6
|
+
end
|
7
|
+
|
8
|
+
# Args should be a string, e.g. '--with-dispatch-cgi --with-htaccess'
|
9
|
+
def generate(args = '')
|
10
|
+
begin
|
11
|
+
original_dir = Dir.getwd
|
12
|
+
bin = File.join(File.dirname(__FILE__), '..', 'bin', 'fir')
|
13
|
+
`export TESTING_FIR_BIN=1;#{bin} #{fir_root} #{args}`
|
14
|
+
Dir.chdir fir_root
|
15
|
+
yield
|
16
|
+
ensure
|
17
|
+
Dir.chdir original_dir
|
18
|
+
FileUtils::rm_rf fir_root
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def assert_generator_creates(should_create, path_components)
|
23
|
+
path_components.unshift(fir_root) unless path_components.first == fir_root
|
24
|
+
generate do
|
25
|
+
if should_create
|
26
|
+
File.join(path_components).should exist_on_disk
|
27
|
+
else
|
28
|
+
File.join(path_components).should_not exist_on_disk
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def generator_should_create(*path_components)
|
34
|
+
assert_generator_creates(true, path_components)
|
35
|
+
end
|
36
|
+
|
37
|
+
def generator_should_not_create(*path_components)
|
38
|
+
assert_generator_creates(false, path_components)
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'creates a Fir site that can be booted with Rackup' do
|
42
|
+
generate do
|
43
|
+
[fir_root, 'rackup -p 3001'].should spawn_working_server
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'creates the root directory' do
|
48
|
+
generator_should_create fir_root
|
49
|
+
end
|
50
|
+
|
51
|
+
it 'creates the pages directory' do
|
52
|
+
generator_should_create 'pages'
|
53
|
+
end
|
54
|
+
|
55
|
+
it 'creates the layouts directory' do
|
56
|
+
generator_should_create 'layouts'
|
57
|
+
end
|
58
|
+
|
59
|
+
it 'creates the public directory' do
|
60
|
+
generator_should_create 'public'
|
61
|
+
end
|
62
|
+
|
63
|
+
it 'creates the tmp directory' do
|
64
|
+
generator_should_create 'tmp'
|
65
|
+
end
|
66
|
+
|
67
|
+
it 'creates config.rb' do
|
68
|
+
generator_should_create 'config.rb'
|
69
|
+
end
|
70
|
+
|
71
|
+
it 'does not create .htaccess by default' do
|
72
|
+
generator_should_not_create 'public', '.htaccess'
|
73
|
+
end
|
74
|
+
|
75
|
+
it 'creates .htaccess if --with-htaccess is given' do
|
76
|
+
generate('--with-htaccess') do
|
77
|
+
File.join(fir_root, 'public', '.htaccess').should exist_on_disk
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
it 'does not create dispatch.cgi by default' do
|
82
|
+
generator_should_not_create 'public', 'dispatch.cgi'
|
83
|
+
end
|
84
|
+
|
85
|
+
it 'creates dispatch.cgi if --with-dispatch-cgi is given' do
|
86
|
+
generate('--with-dispatch-cgi') do
|
87
|
+
File.join(fir_root, 'public', 'dispatch.cgi').should exist_on_disk
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
describe 'config.rb' do
|
92
|
+
it 'calls Fir.config' do
|
93
|
+
generate do
|
94
|
+
Fir.should_receive(:config)
|
95
|
+
load File.join(fir_root, 'config.rb')
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
it 'sets the default site name' do
|
100
|
+
generate do
|
101
|
+
Fir.config.should_receive(:site_name=)
|
102
|
+
load File.join(fir_root, 'config.rb')
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
it 'creates menus.yml' do
|
108
|
+
generator_should_create 'menus.yml'
|
109
|
+
end
|
110
|
+
|
111
|
+
it 'creates pages.yml' do
|
112
|
+
generator_should_create 'pages.yml'
|
113
|
+
end
|
114
|
+
|
115
|
+
it 'creates Rakefile' do
|
116
|
+
generator_should_create 'Rakefile'
|
117
|
+
end
|
118
|
+
|
119
|
+
describe 'Rakefile' do
|
120
|
+
it 'has a task called users:add' do
|
121
|
+
generate do
|
122
|
+
`rake -D`.should include('users:add')
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
it 'has a task called export' do
|
127
|
+
generate do
|
128
|
+
`rake -D`.should include('export')
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
+
|
3
|
+
# This is where we test with real, honest-to-goodness server processes.
|
4
|
+
# The point is to ensure that Fir can integrate with Rack-compliant servers.
|
5
|
+
|
6
|
+
describe 'Rack integration' do
|
7
|
+
def fir_root
|
8
|
+
@fir_root ||= File.expand_path(File.join(File.dirname(__FILE__), '..', 'example'))
|
9
|
+
end
|
10
|
+
|
11
|
+
def home_should_be_accessible(options = {})
|
12
|
+
options = {:host => 'localhost', :port => 3001}.merge(options)
|
13
|
+
retry_request do
|
14
|
+
Net::HTTP.get(URI.parse("http://#{options[:host]}:#{options[:port]}")).should contain('Welcome to Acme Company!')
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
context 'with rackup' do
|
19
|
+
it 'allows access to the home page' do
|
20
|
+
with_spawned_server(fir_root, 'rackup -p 3001') do
|
21
|
+
home_should_be_accessible
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
context 'with CGI' do
|
27
|
+
# This is a simple way to test CGI support. We can't
|
28
|
+
# test against Apache, because that requires installing and configuring
|
29
|
+
# it, which we obviously can't do in this test suite. Sadly, this means we
|
30
|
+
# can't test .htaccess. That's a shame, because there is a very real
|
31
|
+
# possibility of errors creeping into .htaccess.
|
32
|
+
#
|
33
|
+
# Instead, we set up a mock CGI environment by setting environment variables.
|
34
|
+
#
|
35
|
+
# It would be even better if we could test this against a light web server running
|
36
|
+
# in a forked process, but there doesn't seem to be an easy way to do that.
|
37
|
+
it 'allows access to the home page' do
|
38
|
+
dispatch = File.join(fir_root, 'public', 'dispatch.cgi')
|
39
|
+
response = `export PATH_INFO=/; ruby #{dispatch}`
|
40
|
+
response.should include('Status: 200')
|
41
|
+
response.should include('Welcome to Acme Company!')
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
data/spec/spec_helper.rb
CHANGED
@@ -1,9 +1,23 @@
|
|
1
1
|
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
2
2
|
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
3
3
|
require 'fir'
|
4
|
+
require 'fir/generator'
|
4
5
|
require 'spec'
|
5
6
|
require 'spec/autorun'
|
7
|
+
require 'rack/test'
|
8
|
+
require 'webrat'
|
9
|
+
require 'rake'
|
10
|
+
|
11
|
+
Dir[File.expand_path(File.join(File.dirname(__FILE__),'support','**','*.rb'))].each {|f| require f}
|
12
|
+
|
13
|
+
FIR_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..', 'example'))
|
6
14
|
|
7
15
|
Spec::Runner.configure do |config|
|
8
|
-
|
9
|
-
|
16
|
+
config.include Rack::Test::Methods
|
17
|
+
config.include Webrat::Matchers
|
18
|
+
config.include Fir::TestAppSetup
|
19
|
+
config.include Fir::CacheTesting
|
20
|
+
config.include Fir::ServerTesting
|
21
|
+
config.include Fir::FileMatchers
|
22
|
+
config.include Fir::ServerMatchers
|
23
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Fir
|
2
|
+
module CacheTesting
|
3
|
+
# Path should be like this:
|
4
|
+
# directory/page
|
5
|
+
# Not this:
|
6
|
+
# directory/page.html
|
7
|
+
def with_cache(path, contents)
|
8
|
+
filename = File.join(FIR_ROOT, 'public', 'cache', path + '.html')
|
9
|
+
dirname = File.dirname(filename)
|
10
|
+
unless File.directory?(dirname)
|
11
|
+
FileUtils.mkdir_p(dirname)
|
12
|
+
end
|
13
|
+
File.open(filename, 'w') do |file|
|
14
|
+
file.write(contents)
|
15
|
+
end
|
16
|
+
yield
|
17
|
+
File.delete(filename)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Fir
|
2
|
+
module FileMatchers
|
3
|
+
class ExistOnDisk
|
4
|
+
def matches?(path)
|
5
|
+
@path = path
|
6
|
+
File.exists?(path)
|
7
|
+
end
|
8
|
+
|
9
|
+
def failure_message
|
10
|
+
"Expected #{@path} to exist"
|
11
|
+
end
|
12
|
+
|
13
|
+
def negative_failure_message
|
14
|
+
"Expected #{@path} not to exist"
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def exist_on_disk
|
19
|
+
ExistOnDisk.new
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), 'server_testing.rb'))
|
2
|
+
|
3
|
+
module Fir
|
4
|
+
module ServerMatchers
|
5
|
+
class SpawnWorkingServer
|
6
|
+
include Fir::ServerTesting
|
7
|
+
|
8
|
+
def matches?(command_arr)
|
9
|
+
@dir = command_arr[0]
|
10
|
+
@command = command_arr[1]
|
11
|
+
@host_and_port = command_arr[2] || 'localhost:3001'
|
12
|
+
@response = nil
|
13
|
+
with_spawned_server(@dir, @command) do
|
14
|
+
retry_request do
|
15
|
+
@response = Net::HTTP.get(URI.parse("http://#{@host_and_port}"))
|
16
|
+
end
|
17
|
+
end
|
18
|
+
@response.is_a?(String) and @response.include?('Home')
|
19
|
+
end
|
20
|
+
|
21
|
+
def failure_message
|
22
|
+
"Expected that running #{@command} in #{@dir} would spawn a working server at #{@host_and_port}. Response: #{@response.inspect}"
|
23
|
+
end
|
24
|
+
|
25
|
+
def negative_failure_message
|
26
|
+
"Expected that running #{@command} in #{@dir} would not spawn a working server at #{@host_and_port}. Response: #{@response.inspect}"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
# Usage:
|
31
|
+
# [dir, command, host_and_port].should spawn_working_server
|
32
|
+
# This will cd to dir, try to spawn a server with command,
|
33
|
+
# and try to GET http://host_and_port. host_and_port is optional.
|
34
|
+
# It defaults to localhost:3001.
|
35
|
+
def spawn_working_server
|
36
|
+
SpawnWorkingServer.new
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module Fir
|
2
|
+
module ServerTesting
|
3
|
+
def with_spawned_server(dir, command)
|
4
|
+
original_dir = Dir.getwd
|
5
|
+
Dir.chdir(dir)
|
6
|
+
begin
|
7
|
+
server_pid = fork do
|
8
|
+
puts 'About to spawn server.'
|
9
|
+
exec command
|
10
|
+
end
|
11
|
+
response = ''
|
12
|
+
yield
|
13
|
+
ensure
|
14
|
+
puts "About to kill server. The interrupt message is normal. Don't worry about it."
|
15
|
+
Process.kill('INT', server_pid)
|
16
|
+
Dir.chdir(original_dir)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def retry_request
|
21
|
+
failures = 0
|
22
|
+
begin
|
23
|
+
yield
|
24
|
+
rescue Errno::ECONNREFUSED => exc
|
25
|
+
failures += 1
|
26
|
+
if failures == 3
|
27
|
+
raise exc
|
28
|
+
else
|
29
|
+
sleep((2 ** failures) * 0.3)
|
30
|
+
retry
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
data/spec/tasks_spec.rb
ADDED
@@ -0,0 +1,132 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
+
|
3
|
+
describe 'Fir Rake tasks' do
|
4
|
+
Fir::Tasks.define # Some tasks need to be executed within this Ruby instance so that we can use stubs, method expectations, etc.
|
5
|
+
|
6
|
+
def fir_root
|
7
|
+
@fir_root ||= File.expand_path(File.join(File.dirname(__FILE__), '..', 'example'))
|
8
|
+
end
|
9
|
+
|
10
|
+
def rake(args)
|
11
|
+
original_wd = Dir.getwd
|
12
|
+
output = nil
|
13
|
+
begin
|
14
|
+
Dir.chdir(fir_root)
|
15
|
+
output = `export TESTING_FIR_TASKS=1;rake #{args}`
|
16
|
+
ensure
|
17
|
+
Dir.chdir(original_wd)
|
18
|
+
end
|
19
|
+
output
|
20
|
+
end
|
21
|
+
|
22
|
+
describe 'users:add' do
|
23
|
+
it 'adds a new user with hashed and salted password' do
|
24
|
+
yaml_path = File.join(fir_root, 'users.yml')
|
25
|
+
original_yaml = YAML::load(File.read(yaml_path))
|
26
|
+
begin
|
27
|
+
rake('users:add user=someone pass=password')
|
28
|
+
new_yaml = YAML::load(File.read(yaml_path))
|
29
|
+
new_yaml.should have_key('someone')
|
30
|
+
salt = new_yaml['someone']['salt']
|
31
|
+
new_yaml['someone']['crypted_password'].should == Fir.encrypt_password('password', salt)
|
32
|
+
ensure
|
33
|
+
File.open(yaml_path, 'w') do |file|
|
34
|
+
file.write(YAML::dump(original_yaml))
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
describe 'export' do
|
41
|
+
def export(extra_args = nil)
|
42
|
+
output = nil
|
43
|
+
begin
|
44
|
+
output = rake("export dir=\"../exported_example\" #{extra_args}")
|
45
|
+
yield if block_given?
|
46
|
+
ensure
|
47
|
+
FileUtils.rm_rf export_root
|
48
|
+
end
|
49
|
+
output
|
50
|
+
end
|
51
|
+
|
52
|
+
def export_root
|
53
|
+
File.expand_path File.join(fir_root, '..', 'exported_example')
|
54
|
+
end
|
55
|
+
|
56
|
+
def should_export(*path_components)
|
57
|
+
path_components.unshift(export_root) unless path_components.first == export_root
|
58
|
+
export do
|
59
|
+
File.join(path_components).should exist_on_disk
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def with_existing_files
|
64
|
+
begin
|
65
|
+
Dir.mkdir(export_root)
|
66
|
+
old_file_path = File.join(export_root, 'old.txt')
|
67
|
+
File.open(old_file_path, 'w') { |file| file.write 'Old' }
|
68
|
+
yield
|
69
|
+
ensure
|
70
|
+
FileUtils.rm_rf export_root
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
it 'creates the root directory' do
|
75
|
+
should_export export_root
|
76
|
+
end
|
77
|
+
|
78
|
+
it 'fails if the root directory is not empty and the force option was not given' do
|
79
|
+
with_existing_files do
|
80
|
+
export.should include('Directory is not empty')
|
81
|
+
File.join(export_root, 'index.html').should_not exist_on_disk
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
it 'overwrites everything if the root directory exists and the force option was given' do
|
86
|
+
with_existing_files do
|
87
|
+
export('force=1') do
|
88
|
+
File.join(export_root, 'old.txt').should_not exist_on_disk
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
describe 'export_to_server' do
|
95
|
+
def run_export_to_server
|
96
|
+
ENV['dest'] = 'username@host.com:/home/username/public_html'
|
97
|
+
Rake::Task['export_to_server'].execute
|
98
|
+
end
|
99
|
+
|
100
|
+
before :each do
|
101
|
+
@rsyncer = ::Fir::Tasks::Rsyncer.new
|
102
|
+
::Fir::Tasks::Rsyncer.stub(:new).and_return(@rsyncer)
|
103
|
+
@rsyncer.stub(:run_command)
|
104
|
+
end
|
105
|
+
|
106
|
+
it 'exports the site first' do
|
107
|
+
begin
|
108
|
+
export_root = File.expand_path(File.join(File.dirname(__FILE__), 'testable_export_tmp'))
|
109
|
+
@rsyncer.stub(:run_command).and_return do
|
110
|
+
FileUtils.cp_r('.server_export_tmp', export_root)
|
111
|
+
end
|
112
|
+
run_export_to_server
|
113
|
+
export_root.should exist_on_disk
|
114
|
+
File.join(export_root, 'index.html').should exist_on_disk
|
115
|
+
File.join(export_root, 'products', 'index.html').should exist_on_disk
|
116
|
+
ensure
|
117
|
+
FileUtils.rm_rf export_root
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
it 'rsyncs the exported site to the server' do
|
122
|
+
@rsyncer.should_receive(:run_command).with('rsync -ave ssh .server_export_tmp/* username@host.com:/home/username/public_html')
|
123
|
+
run_export_to_server
|
124
|
+
end
|
125
|
+
|
126
|
+
it 'uses deploy.yml if it exists' do
|
127
|
+
ENV['dest'] = nil
|
128
|
+
@rsyncer.should_receive(:run_command).with('rsync -ave ssh .server_export_tmp/* me@example.com:/www-root')
|
129
|
+
Rake::Task['export_to_server'].execute
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|