Skip to content

Commit

Permalink
Be able to use os detection within test blocks (closes #2)
Browse files Browse the repository at this point in the history
  • Loading branch information
zuazo committed Mar 19, 2017
1 parent 5c24e51 commit 8046c8a
Show file tree
Hide file tree
Showing 9 changed files with 112 additions and 150 deletions.
33 changes: 33 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,39 @@ describe docker_compose('docker-compose.yml', wait: 60) do
end
```

### Run Different Tests on Each Platform

Sometimes, you may want to be able to run different tests depending on the platform. You can use Serverspec's `os` helper method for that:

```ruby
require 'dockerspec/serverspec'

describe docker_build('.', tag: 'mywebapp') do
describe docker_run('mywebapp') do
case os[:family]
when 'debian'

describe file('/etc/debian_version') do
it { should exist }
end

# [...]

when 'alpine'

describe file('/etc/alpine-release') do
it { should exist }
end

# [...]

end
end
end
```

For more details, see [Serverspec documenation on how to get OS information](http://serverspec.org/advanced_tips.html#how-to-get-os-information).

### Real-world Examples

* [`alpine-tor`](https://github.com/zuazo/alpine-tor-docker) image ([*spec/*](https://github.com/zuazo/alpine-tor-docker/tree/master/spec), [*Gemfile*](https://github.com/zuazo/alpine-tor-docker/tree/master/Gemfile), [*.travis.yml*](https://github.com/zuazo/alpine-tor-docker/tree/master/.travis.yml)).
Expand Down
19 changes: 0 additions & 19 deletions lib/dockerspec/helper/rspec_example_helpers.rb
Original file line number Diff line number Diff line change
Expand Up @@ -53,25 +53,6 @@ def self.metadata_parent(metadata)
end
end

#
# Searches for an object in the description of the parent RSpec examples.
#
# @param metadata [Hash] RSpec metadata.
# @param klass [Class] Type of object to search.
#
# @return [Object] Returns the object if found. `nil` if not found.
#
# @api public
#
def self.search_object(metadata, klass)
return nil if metadata.nil?
if metadata[:described_class].is_a?(klass)
metadata[:described_class]
elsif metadata_has_parent?(metadata)
search_object(metadata_parent(metadata), klass)
end
end

#
# Searches for an object in the description of the parent RSpec examples
# that implements a specific method.
Expand Down
4 changes: 3 additions & 1 deletion lib/dockerspec/rspec/resources.rb
Original file line number Diff line number Diff line change
Expand Up @@ -356,6 +356,7 @@ def docker_build(*opts)
def docker_run(*opts)
runner = Dockerspec::Configuration.docker_runner.new(*opts)
runner.run
runner.restore_rspec_context
described_container(runner.container_name)
runner
end
Expand Down Expand Up @@ -514,8 +515,9 @@ def its_container(container, *opts, &block)
end
container_opts = opts[0].is_a?(Hash) ? opts[0] : {}
compose.select_container(container, container_opts)
compose.restore_rspec_context
described_container(compose.container_name)
describe(ItsContainer.new(container), *opts, &block)
describe(ItsContainer.new(container, compose), *opts, &block)
end

#
Expand Down
35 changes: 12 additions & 23 deletions lib/dockerspec/rspec/resources/its_container.rb
Original file line number Diff line number Diff line change
Expand Up @@ -54,34 +54,26 @@ class ItsContainer
# Constructs a `its_container` object.
#
# @param container [String] The name of the container.
# @param compose [Dockerspec::Runner::Compose] The compose object we
# working with.
#
# @return [Dockerspec::RSpec::Resource::ItsContainer] The
# `its_container` object.
#
# @api public
#
def initialize(container)
def initialize(container, compose)
@container = container
@compose = compose
end

#
# Gets the compose object from the RSpec metadata.
# Restores the testing context.
#
# @return [Dockerspec::Runner::Compose] The compose object.
#
# @api private
#
def compose_from_metadata
metadata = ::RSpec.current_example.metadata
Helper::RSpecExampleHelpers
.search_object(metadata, Dockerspec::Runner::Compose)
end

#
# Restores the testing context from the RSpec metadata.
#
# Searches for {Dockerspec::Runner::Compose} objects in the RSpec
# metadata and restores their context to run the tests.
# This is required for tests to run correctly if we are testing
# different containers within the same tests. That is because RSpec has
# two stages, one in which it generates the tests and another in which
# it runs them.
#
# This is called from the `before` block in the
# *lib/dockerspec/runner/base.rb* file:
Expand All @@ -101,10 +93,8 @@ def compose_from_metadata
# @api public
#
def restore_rspec_context
compose = compose_from_metadata
raise ItsContainerError, NO_DOCKER_COMPOSE_MESSAGE if compose.nil?
compose.restore_rspec_context
compose.select_container(@container)
@compose.restore_rspec_context
@compose.select_container(@container)
end

#
Expand All @@ -121,8 +111,7 @@ def restore_rspec_context
# @api public
#
def container
compose = compose_from_metadata
compose.container
@compose.container
end

#
Expand Down
26 changes: 25 additions & 1 deletion spec/integration/compose/docker_compose_yml_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,18 @@
end

serverspec_tests do
# Issue #2: https://github.com/zuazo/dockerspec/issues/2
case os[:family]
when 'debian'
it 'is Debian' do
expect(file('/etc/debian_version')).to exist
end
else
it 'Wrong OS' do
raise "Wrong OS: #{os[:family]}"
end
end

it 'detects the OS family' do
expect(command('uname -a').stdout).to match(/Debian|Ubuntu/i)
expect(file('/etc/alpine-release').exists?).to be false
Expand Down Expand Up @@ -197,7 +209,7 @@
end

infrataster_tests do
describe server(described_container) do
describe server(described_container), retry: 30 do
describe http('/wp-admin/install.php') do
it 'responds content including "Wordpress Installation"' do
expect(response.body).to match(/WordPress .* Installation/i)
Expand All @@ -213,6 +225,18 @@

its_container(:alpine) do
serverspec_tests do
# Issue #2: https://github.com/zuazo/dockerspec/issues/2
case os[:family]
when 'alpine'
it 'is Alpine' do
expect(file('/etc/alpine-release')).to exist
end
else
it 'Wrong OS' do
raise "Wrong OS: #{os[:family]}"
end
end

it 'detects the OS family' do
expect(file('/etc/alpine-release').exists?).to be true
expect(property[:os][:family]).to eq 'alpine'
Expand Down
16 changes: 16 additions & 0 deletions spec/integration/dockerfiles/from_file_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,22 @@
its(:stdout) { should eq "STDOUT\n" }
its(:stderr) { should eq "STDERR\n" }

# Issue #2: https://github.com/zuazo/dockerspec/issues/2
case os[:family]
when 'debian'
it 'is Debian' do
expect(file('/etc/debian_version')).to exist
end
when 'alpine'
it 'is Alpine' do
expect(file('/etc/alpine-release')).to exist
end
else
it 'Unknown OS' do
raise "Unknown OS: #{os[:family]}"
end
end

describe package('alpine-base') do
it { should be_installed }
end
Expand Down
60 changes: 0 additions & 60 deletions spec/unit/helper/rspec_example_helpers_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,66 +20,6 @@
require 'spec_helper'

describe Dockerspec::Helper::RSpecExampleHelpers do
context '.search_object' do
context 'when the object exists inside the described_class' do
let(:klass) { String }
let(:object) { klass.new }
let(:metadata) { { described_class: object } }

it 'founds the object' do
expect(described_class.search_object(metadata, klass)).to eq(object)
end
end

context 'when the object exists inside an example group' do
let(:klass) { String }
let(:object) { klass.new }
let(:metadata) { { example_group: { described_class: object } } }

it 'founds the object' do
expect(described_class.search_object(metadata, klass)).to eq(object)
end
end

context 'when the object exists inside parent example group' do
let(:klass) { String }
let(:object) { klass.new }
let(:metadata) do
{
example_group: { parent_example_group: { described_class: object } }
}
end

it 'founds the object' do
expect(described_class.search_object(metadata, klass)).to eq(object)
end
end

context 'when the object does not exist' do
let(:klass) { String }
let(:object) { klass.new }
let(:metadata) { { random: :stuff } }

it 'founds the object' do
expect(described_class.search_object(metadata, Integer)).to eq(nil)
end
end

context 'when the class does not match' do
let(:klass) { String }
let(:object) { klass.new }
let(:metadata) do
{
example_group: { parent_example_group: { described_class: object } }
}
end

it 'founds the object' do
expect(described_class.search_object(metadata, Integer)).to eq(nil)
end
end
end

context '.search_objects_with' do
let(:obj1) { 'dony' }
let(:obj2) { 'leo' }
Expand Down
51 changes: 7 additions & 44 deletions spec/unit/rspec/resources/its_container_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,81 +20,44 @@
require 'spec_helper'

describe Dockerspec::RSpec::Resources::ItsContainer do
let(:container) { 'webapp' }
subject { described_class.new(container) }
let(:container_name) { 'webapp' }
let(:container) { double('Docker::Container') }
let(:compose) { double('Dockerspec::Runner::Compose', container: container) }
subject { described_class.new(container_name, compose) }

context '.new' do
it 'creates an instance without errors' do
expect(subject).to be_a(described_class)
end
end

context '#restore_rspec_context' do
let(:example) { 'example' }
let(:metadata) { 'metadata' }
let(:compose) { double('Dockerspec::Runner::Compose') }
before do
allow(RSpec).to receive(:current_example).and_return(example)
allow(example).to receive(:metadata).and_return(metadata)
allow(Dockerspec::Helper::RSpecExampleHelpers)
.to receive(:search_object).and_return(compose)
allow(compose).to receive(:restore_rspec_context)
allow(compose).to receive(:select_container)
end

it 'reads RSpec current metadata' do
expect(example).to receive(:metadata).once
subject.restore_rspec_context
end

it 'searches RSpec metadata' do
expect(Dockerspec::Helper::RSpecExampleHelpers)
.to receive(:search_object).once
.with(metadata, Dockerspec::Runner::Compose).and_return(compose)
subject.restore_rspec_context
end

it 'raises an error if not found in RSpec metadata' do
allow(Dockerspec::Helper::RSpecExampleHelpers)
.to receive(:search_object).and_return(nil)
expect { subject.restore_rspec_context }.to raise_error(
Dockerspec::ItsContainerError, /used with.*`docker_compose`/
)
end

it 'restores rspec context' do
expect(compose).to receive(:restore_rspec_context).once.with(no_args)
subject.restore_rspec_context
end

it 'selects the container' do
allow(compose).to receive(:select_container).once.with(container)
allow(compose).to receive(:select_container).once.with(container_name)
subject.restore_rspec_context
end
end

context '#container' do
let(:example) { 'example' }
let(:metadata) { 'metadata' }
let(:container) { double('Docker::Container') }
let(:compose) do
double('Dockerspec::Runner::Compose', container: container)
end
before do
allow(RSpec).to receive(:current_example).and_return(example)
allow(example).to receive(:metadata).and_return(metadata)
end

it 'returns the selected container' do
expect(Dockerspec::Helper::RSpecExampleHelpers)
.to receive(:search_object).once
.with(metadata, Dockerspec::Runner::Compose).and_return(compose)
expect(subject.container).to eq(container)
end
end

context '#to_s' do
it 'returns a description' do
expect(subject.to_s).to include(container)
expect(subject.to_s).to include(container_name)
end
end
end
Loading

0 comments on commit 8046c8a

Please sign in to comment.