1
0
Fork 0
mirror of https://github.com/capistrano/capistrano synced 2023-03-27 23:21:18 -04:00

Integration tests with Vagrant and Cucumber

This commit removes the existing 'local' integration tests and replaces
them with Cucumber features running against VMs.  At this stage,
some of the assertions are pending due to the limited nature of the
response returned when executing commands through Vagrant, but the
framework is there as a starting point to build upon.

To run the suite:

    bundle exec cucumber

During development, avoid scraping the VM between runs:

    bundle exec cucumber KEEPING_RUNNING=1

Ultimately I would like to see the `TestApp` helpers along with the Vagrant
integration packaged and available for use when developing gems that work with
Cap. For now though, this closes #641
This commit is contained in:
seenmyfate 2013-09-27 11:45:55 +01:00
parent f8d98c6588
commit e7399f4692
19 changed files with 297 additions and 324 deletions

View file

@ -28,5 +28,8 @@ Gem::Specification.new do |gem|
gem.add_development_dependency 'rspec' gem.add_development_dependency 'rspec'
gem.add_development_dependency 'mocha' gem.add_development_dependency 'mocha'
gem.add_development_dependency 'vagrant', '~> 1.0.7'
gem.add_development_dependency 'kuroko'
gem.add_development_dependency 'cucumber'
end end

52
features/deploy.feature Normal file
View file

@ -0,0 +1,52 @@
Feature: Deploy
Background:
Given a test app with the default configuration
And servers with the roles app and web
Scenario: Creating the repo
When I run cap "git:check"
Then references in the remote repo are listed
Scenario: Creating the directory structure
When I run cap "deploy:check:directories"
Then the shared path is created
And the releases path is created
Scenario: Creating linked directories
When I run cap "deploy:check:linked_dirs"
Then directories in :linked_dirs are created in shared
Scenario: Creating linked directories for linked files
When I run cap "deploy:check:make_linked_dirs"
Then directories referenced in :linked_files are created in shared
Scenario: Checking linked files - missing file
Given a required file
But the file does not exist
When I run cap "deploy:check:linked_files"
Then the task will exit
Scenario: Checking linked files - file exists
Given a required file
And that file exists
When I run cap "deploy:check:linked_files"
Then the task will be successful
Scenario: Creating a release
When I run cap "git:create_release" as part of a release
Then the repo is cloned
And the release is created
Scenario: Symlink linked files
When I run cap "deploy:symlink:linked_files" as part of a release
Then file symlinks are created in the new release
Scenario: Symlink linked dirs
When I run cap "deploy:symlink:linked_dirs" as part of a release
Then directory symlinks are created in the new release
Scenario: Publishing
When I run cap "deploy:symlink:release"
Then the current directory will be a symlink to the release

View file

@ -0,0 +1,16 @@
Feature: Installation
Background:
Given a test app with the default configuration
Scenario: With default stages
When I run cap "install"
Then the deploy.rb file is created
And the default stage files are created
And the tasks folder is created
Scenario: With specified stages
When I run cap "install STAGES=qa,production"
Then the deploy.rb file is created
And the specified stage files are created
And the tasks folder is created

View file

@ -0,0 +1,14 @@
Feature: Remote file task
Background:
Given a test app with the default configuration
And a custom task to generate a file
And servers with the roles app and web
Scenario: Where the file does not exist
When I run cap "deploy:check:linked_files"
Then it creates the file with the remote_task prerequisite
Scenario: Where the file already exists
When I run cap "deploy:check:linked_files"
Then it will not recreate the file

View file

@ -0,0 +1,90 @@
Then(/^references in the remote repo are listed$/) do
end
Then(/^the shared path is created$/) do
run_vagrant_command(test_dir_exists(TestApp.shared_path))
end
Then(/^the releases path is created$/) do
run_vagrant_command(test_dir_exists(TestApp.releases_path))
end
Then(/^directories in :linked_dirs are created in shared$/) do
TestApp.linked_dirs.each do |dir|
run_vagrant_command(test_dir_exists(TestApp.shared_path.join(dir)))
end
end
Then(/^directories referenced in :linked_files are created in shared$/) do
dirs = TestApp.linked_files.map { |path| TestApp.shared_path.join(path).dirname }
dirs.each do | dir|
run_vagrant_command(test_dir_exists(dir))
end
end
Then(/^the task will be successful$/) do
end
Then(/^the task will exit$/) do
end
Then(/^the repo is cloned$/) do
run_vagrant_command(test_dir_exists(TestApp.repo_path))
end
Then(/^the release is created$/) do
run_vagrant_command("ls -g #{TestApp.releases_path}")
end
Then(/^file symlinks are created in the new release$/) do
pending
TestApp.linked_files.each do |file|
run_vagrant_command(test_symlink_exists(TestApp.release_path.join(file)))
end
end
Then(/^directory symlinks are created in the new release$/) do
pending
TestApp.linked_dirs.each do |dir|
run_vagrant_command(test_symlink_exists(TestApp.release_path.join(dir)))
end
end
Then(/^the current directory will be a symlink to the release$/) do
run_vagrant_command(test_symlink_exists(TestApp.current_path))
end
Then(/^the deploy\.rb file is created$/) do
file = TestApp.test_app_path.join('config/deploy.rb')
expect(File.exists?(file)).to be_true
end
Then(/^the default stage files are created$/) do
staging = TestApp.test_app_path.join('config/deploy/staging.rb')
production = TestApp.test_app_path.join('config/deploy/production.rb')
expect(File.exists?(staging)).to be_true
expect(File.exists?(production)).to be_true
end
Then(/^the tasks folder is created$/) do
path = TestApp.test_app_path.join('lib/capistrano/tasks')
expect(Dir.exists?(path)).to be_true
end
Then(/^the specified stage files are created$/) do
qa = TestApp.test_app_path.join('config/deploy/qa.rb')
production = TestApp.test_app_path.join('config/deploy/production.rb')
expect(File.exists?(qa)).to be_true
expect(File.exists?(production)).to be_true
end
Then(/^it creates the file with the remote_task prerequisite$/) do
TestApp.linked_files.each do |file|
run_vagrant_command(test_file_exists(TestApp.shared_path.join(file)))
end
end
Then(/^it will not recreate the file$/) do
#
end

View file

@ -0,0 +1,8 @@
When(/^I run cap "(.*?)"$/) do |task|
TestApp.cap(task)
end
When(/^I run cap "(.*?)" as part of a release$/) do |task|
TestApp.cap("deploy:new_release_path #{task}")
end

View file

@ -0,0 +1,25 @@
Given(/^a test app with the default configuration$/) do
TestApp.install
end
Given(/^servers with the roles app and web$/) do
vagrant_cli_command('up')
end
Given(/^a required file$/) do
end
Given(/^that file exists$/) do
run_vagrant_command("touch #{TestApp.linked_file}")
end
Given(/^the file does not exist$/) do
pending
file = TestApp.linked_file
run_vagrant_command("[ -f #{file} ] && rm #{file}")
end
Given(/^a custom task to generate a file$/) do
TestApp.copy_task_to_test_app('spec/support/tasks/database.cap')
end

12
features/support/env.rb Normal file
View file

@ -0,0 +1,12 @@
require 'kuroko'
project_root = File.expand_path('../../../', __FILE__)
vagrant_root = File.join(project_root, 'spec/support')
Kuroko.configure do |config|
config.vagrant_root = 'spec/support'
end
puts vagrant_root.inspect
require_relative '../../spec/support/test_app'

View file

@ -0,0 +1,20 @@
module RemoteCommandHelpers
def test_dir_exists(path)
exists?('d', path)
end
def test_symlink_exists(path)
exists?('L', path)
end
def test_file_exists(path)
exists?('f', path)
end
def exists?(type, path)
%{[ -#{type} "#{path}" ] && echo "#{path} exists." || echo "Error: #{path} does not exist."}
end
end
World(RemoteCommandHelpers)

View file

@ -1,34 +0,0 @@
require 'integration_spec_helper'
describe 'cap deploy:finished', slow: true do
before do
install_test_app_with(config)
end
describe 'deploy' do
let(:config) {
%{
set :stage, :#{stage}
set :deploy_to, '#{deploy_to}'
set :repo_url, 'git://github.com/capistrano/capistrano.git'
set :branch, 'v3'
server 'localhost', roles: %w{web app}, user: '#{current_user}'
set :linked_files, %w{config/database.yml}
set :linked_dirs, %w{bin log public/system vendor/bundle}
}
}
describe 'log_revision' do
before do
cap 'deploy:started'
cap 'deploy:updating'
cap 'deploy:publishing'
cap 'deploy:finished'
end
it 'writes the log file' do
expect(deploy_to.join('revisions.log')).to be_a_file
end
end
end
end

View file

@ -1,38 +0,0 @@
require 'integration_spec_helper'
describe 'cap deploy:finished', slow: true do
before do
install_test_app_with(config)
end
describe 'deploy' do
let(:config) {
%{
set :stage, :#{stage}
set :deploy_to, '#{deploy_to}'
set :repo_url, 'git://github.com/capistrano/capistrano.git'
set :branch, 'v3'
server 'localhost', roles: %w{web app}, user: '#{current_user}'
set :linked_files, %w{config/database.yml}
set :linked_dirs, %w{bin log public/system vendor/bundle}
set :release_path, '#{deploy_to}/releases/1234'
}
}
describe 'symlink' do
before do
cap 'deploy:started'
cap 'deploy:updating'
cap 'deploy:publishing'
cap 'deploy:finished'
end
describe 'release' do
it 'symlinks the release to `current`' do
expect(File.symlink?(current_path)).to be_true
expect(File.readlink(current_path)).to eq '/tmp/test_app/deploy_to/releases/1234'
end
end
end
end
end

View file

@ -1,74 +0,0 @@
require 'integration_spec_helper'
describe 'cap deploy:started', slow: true do
before do
install_test_app_with(config)
end
describe 'deploy:check' do
let(:config) {
%{
set :stage, :#{stage}
set :deploy_to, '#{deploy_to}'
set :repo_url, 'git://github.com/capistrano/capistrano.git'
set :branch, 'v3'
server 'localhost', roles: %w{web app}, user: '#{current_user}'
set :linked_files, %w{config/database.yml}
set :linked_dirs, %w{bin log public/system vendor/bundle}
}
}
describe 'directories' do
before do
cap 'deploy:check:directories'
end
it 'ensures the directory structure' do
expect(shared_path).to be_a_directory
expect(releases_path).to be_a_directory
end
end
describe 'linked_dirs' do
before do
cap 'deploy:check:linked_dirs'
end
it 'ensure directories to be linked in `shared`' do
[
shared_path.join('bin'),
shared_path.join('log'),
shared_path.join('public/system'),
shared_path.join('vendor/bundle'),
].each do |dir|
expect(dir).to be_a_directory
end
end
end
describe 'linked_files' do
subject { cap 'deploy:check:linked_files' }
context 'file does not exist' do
it 'fails' do
expect(subject).to match 'config/database.yml does not exist'
end
end
context 'file exists' do
before do
create_shared_directory('config')
create_shared_file('config/database.yml')
end
it 'suceeds' do
expect(subject).not_to match 'config/database.yml does not exist'
expect(subject).to match 'successful'
end
end
end
end
end

View file

@ -1,44 +0,0 @@
require 'integration_spec_helper'
describe 'cap deploy:updating', slow: true do
before do
install_test_app_with(config)
end
describe 'deploy' do
let(:config) {
%{
set :stage, :#{stage}
set :deploy_to, '#{deploy_to}'
set :repo_url, 'git://github.com/capistrano/capistrano.git'
set :branch, 'v3'
server 'localhost', roles: %w{web app}, user: '#{current_user}'
set :linked_files, %w{config/database.yml}
set :linked_dirs, %w{bin log public/system vendor/bundle}
}
}
describe 'symlink' do
before do
create_shared_directory('config')
create_shared_file('config/database.yml')
cap 'deploy'
end
describe 'linked_dirs' do
it 'symlinks the directories in shared to `current`' do
%w{bin log public/system vendor/bundle}.each do |dir|
expect(current_path.join(dir)).to be_a_symlink_to shared_path.join(dir)
end
end
end
describe 'linked_files' do
it 'symlinks the files in shared to `current`' do
file = 'config/database.yml'
expect(current_path.join(file)).to be_a_symlink_to shared_path.join(file)
end
end
end
end
end

View file

@ -1,76 +0,0 @@
require 'integration_spec_helper'
describe 'cap install' do
context 'with defaults' do
before :all do
create_test_app
Dir.chdir(test_app_path) do
%x[bundle exec cap install]
end
end
describe 'installation' do
it 'creates config/deploy' do
path = test_app_path.join('config/deploy')
expect(Dir.exists?(path)).to be_true
end
it 'creates lib/capistrano/tasks' do
path = test_app_path.join('lib/capistrano/tasks')
expect(Dir.exists?(path)).to be_true
end
it 'creates the deploy file' do
file = test_app_path.join('config/deploy.rb')
expect(File.exists?(file)).to be_true
end
it 'creates the stage files' do
staging = test_app_path.join('config/deploy/staging.rb')
production = test_app_path.join('config/deploy/production.rb')
expect(File.exists?(staging)).to be_true
expect(File.exists?(production)).to be_true
end
end
end
context 'with STAGES' do
before :all do
create_test_app
Dir.chdir(test_app_path) do
%x[bundle exec cap install STAGES=qa,production]
end
end
describe 'installation' do
it 'creates config/deploy' do
path = test_app_path.join('config/deploy')
expect(Dir.exists?(path)).to be_true
end
it 'creates lib/capistrano/tasks' do
path = test_app_path.join('lib/capistrano/tasks')
expect(Dir.exists?(path)).to be_true
end
it 'creates the deploy file' do
file = test_app_path.join('config/deploy.rb')
expect(File.exists?(file)).to be_true
end
it 'creates the stage files specified, not the defaults' do
qa = test_app_path.join('config/deploy/qa.rb')
production = test_app_path.join('config/deploy/production.rb')
staging = test_app_path.join('config/deploy/staging.rb')
expect(File.exists?(qa)).to be_true
expect(File.exists?(production)).to be_true
expect(File.exists?(staging)).to be_false
end
end
end
end

View file

@ -1,51 +0,0 @@
require 'integration_spec_helper'
describe 'cap deploy:started', slow: true do
before do
install_test_app_with(config)
copy_task_to_test_app('spec/support/tasks/database.cap')
end
let(:config) {
%{
set :stage, :#{stage}
set :deploy_to, '#{deploy_to}'
set :repo_url, 'git://github.com/capistrano/capistrano.git'
set :branch, 'v3'
server 'localhost', roles: %w{web app}, user: '#{current_user}'
set :linked_files, %w{config/database.yml}
set :linked_dirs, %w{config}
}
}
describe 'linked_files' do
before do
cap 'deploy:check:linked_dirs'
end
subject { cap 'deploy:check:linked_files' }
context 'where the file does not exist' do
it 'creates the file with the remote_task prerequisite' do
expect(subject).to match 'Uploading'
expect(subject).not_to match 'config/database.yml does not exist'
expect(subject).to match 'successful'
end
end
context 'where the file already exists' do
before do
FileUtils.touch(shared_path.join('config/database.yml'))
end
it 'will not recreate the file if it already exists' do
expect(subject).not_to match 'Uploading'
expect(subject).not_to match 'config/database.yml does not exist'
expect(subject).to match 'successful'
end
end
end
end

View file

@ -12,5 +12,4 @@ RSpec.configure do |config|
config.treat_symbols_as_metadata_keys_with_true_values = true config.treat_symbols_as_metadata_keys_with_true_values = true
config.mock_framework = :mocha config.mock_framework = :mocha
config.order = 'random' config.order = 'random'
config.filter_run_excluding :slow
end end

1
spec/support/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
.vagrant

13
spec/support/Vagrantfile vendored Normal file
View file

@ -0,0 +1,13 @@
Vagrant::Config.run do |config|
[:app].each_with_index do |role, i|
config.vm.define(role, primary: true) do |config|
config.vm.box = role
config.vm.box = 'precise64'
config.vm.box_url = 'http://files.vagrantup.com/precise64.box'
config.vm.forward_port 22, "222#{i}".to_i
config.vm.provision :shell, inline: 'yes | sudo apt-get install git-core'
end
end
end

View file

@ -1,11 +1,40 @@
require 'fileutils' require 'fileutils'
module TestApp module TestApp
def create_test_app extend self
[test_app_path, deploy_to].each do |path|
FileUtils.rm_rf(path) def install
FileUtils.mkdir(path) install_test_app_with(default_config)
end end
def default_config
%{
set :stage, :#{stage}
set :deploy_to, '#{deploy_to}'
set :repo_url, 'git://github.com/capistrano/capistrano.git'
set :branch, 'v3'
set :ssh_options, { keys: "\#{ENV['HOME']}/.vagrant.d/insecure_private_key" }
server 'vagrant@localhost:2220', roles: %w{web app}
set :linked_files, #{linked_files}
set :linked_dirs, #{linked_dirs}
}
end
def linked_files
%w{config/database.yml}
end
def linked_file
shared_path.join(linked_files.first)
end
def linked_dirs
%w{bin log public/system vendor/bundle}
end
def create_test_app
FileUtils.rm_rf(test_app_path)
FileUtils.mkdir(test_app_path)
File.open(gemfile, 'w+') do |file| File.open(gemfile, 'w+') do |file|
file.write "gem 'capistrano', path: '#{path_to_cap}'" file.write "gem 'capistrano', path: '#{path_to_cap}'"
end end
@ -56,7 +85,7 @@ module TestApp
end end
def deploy_to def deploy_to
Pathname.new('/tmp/test_app/deploy_to') Pathname.new('/home/vagrant/var/www/deploy')
end end
def shared_path def shared_path
@ -72,7 +101,15 @@ module TestApp
end end
def release_path def release_path
releases_path.join(Dir.entries(releases_path).last) releases_path.join(timestamp)
end
def timestamp
Time.now.utc.strftime("%Y%m%d%H%M%S")
end
def repo_path
deploy_to.join('repo')
end end
def path_to_cap def path_to_cap