diff --git a/spec/aws_spec_helper.rb b/spec/aws_spec_helper.rb index f1fff72..bea388d 100644 --- a/spec/aws_spec_helper.rb +++ b/spec/aws_spec_helper.rb @@ -1,10 +1,82 @@ require 'spec_helper' require 'aws-sdk' -::AWS.config({ - :access_key_id => '1234', - :secret_access_key => '5678', - :region => 'us-east-1', - :logger => nil, - :stub_requests => true -}) +shared_context 'Not an EC2 Instance' do + before(:each) do + allow(Net::HTTP).to receive(:get_response).and_return(false) + end +end + +shared_context 'Amazon Web Services' do + # Preload AWS library + AWS.eager_autoload! AWS::Core # Make sure to load Core first. + AWS.eager_autoload! AWS::EC2 # Load the EC2 class + + ::AWS.config({ + :access_key_id => '1234', + :secret_access_key => '5678', + :logger => nil, + :stub_requests => true + }) + + let(:region) {'us-east-1'} + + let(:fake_instances) {::AWS::Core::Data.new([ + {id: 'i-abcdefg1', status: :running, tags: tags}, + {id: 'i-abcdefg2', status: :terminated, tags: tags}, + {id: 'i-abcdefg3', status: :running, tags: {}}, + {id: 'i-abcdefg4', status: :terminated, tags: {}}, + ])} + + let(:tags) {{ + 'Name' => 'TestName-1.TestDomain.tld', + 'Domain' => 'TestDomainValue.tld', + 'TestTagKey' => 'TestTagValue', + }} + + before(:each) do + allow_any_instance_of(VScripts::AWS::EC2) + .to receive_message_chain('ec2.instances.tagged') + .and_return(fake_instances) + allow_any_instance_of(VScripts::AWS::EC2) + .to receive_message_chain(:named_instances) + .and_return(fake_instances[0..1]) + end +end + +shared_context 'EC2 Instance' do + include_context 'Amazon Web Services' + + before(:each) do + allow(Net::HTTP).to receive(:get_response).and_return(true) + allow(VScripts::AWS::Metadata).to receive_message_chain('open.read') + .and_return('Remote server response') + allow_any_instance_of(VScripts::AWS::Metadata).to receive(:zone) + allow_any_instance_of(VScripts::AWS::Metadata).to receive(:region) + .and_return('us-east-1') + allow_any_instance_of(VScripts::AWS::Metadata).to receive(:instance_id) + .and_return(fake_instances[1].id) + allow_any_instance_of(VScripts::AWS::EC2) + .to receive_message_chain('ec2.tags.create') + end +end + +shared_context 'EC2 Instance without tags' do + include_context 'EC2 Instance' + + before(:each) do + allow_any_instance_of(VScripts::AWS::EC2) + .to receive_message_chain('instance.tags') + .and_return({}) + end +end + +shared_context 'EC2 Instance with tags' do + include_context 'EC2 Instance' + + before(:each) do + allow_any_instance_of(VScripts::AWS::EC2) + .to receive_message_chain('instance.tags') + .and_return(tags) + end +end diff --git a/spec/integration/commands/identify_spec.rb b/spec/integration/commands/identify_spec.rb new file mode 100644 index 0000000..768bdd8 --- /dev/null +++ b/spec/integration/commands/identify_spec.rb @@ -0,0 +1,85 @@ +require 'integration/spec_helper' + +describe 'Command: Identify' do + include_context 'System files' + include_context 'Suppressed output' + + subject { VScripts } + + before(:each) do + allow_any_instance_of(VScripts::Commands::Identify) + .to receive('`') + end + + context 'when \'--help\'' do + it 'returns command specific help' do + expect{subject.run(['identify', '--help'])}.to raise_error(SystemExit) + expect($stdout.string).to match(/USAGE:/) + expect($stdout.string).to match(/OPTIONS:/) + end + end + + context 'when unknown argument' do + it 'returns error with message' do + expect{subject.run(['identify', '--xyz'])}.to raise_error(SystemExit) + expect($stderr.string).to match(/Error: unknown argument/) + end + end + + context 'when not an EC2 instance' do + include_context 'Not an EC2 Instance' + it 'returns error with message' do + expect{subject.run(['identify'])}.to raise_error(SystemExit) + expect($stderr.string) + .to match('FATAL: NOT an EC2 instance or could not connect to Metadata') + end + end + + context 'when EC2 instance' do + include_context 'EC2 Instance without tags' + + context 'without tags' do + it 'writes default hostname' do + allow_any_instance_of(VScripts::Commands::Identify).to receive(:tag) + subject.run(['identify']) + expect(IO.read(hostname_file)).to eq('1') + expect($stdout.string).to match('Done.') + end + end + + context 'and when \'--ec2-tag-theme\'' do + it 'returns the themed host' do + allow_any_instance_of(VScripts::Commands::Identify).to receive(:tag) + .and_return('TestValue') + subject.run(['identify', '--ec2-tag-theme=Test-#']) + expect(IO.read(hostname_file)).to eq('TestValue-1') + end + end + + context 'and when \'--host\'' do + it 'returns the new host' do + subject.run(['identify', '--host=test-host']) + expect(IO.read(hostname_file)).to eq('test-host') + end + end + + context 'and when \'--domain\'' do + it 'returns the new domain' do + subject.run(['identify', '--domain=example.tld']) + expect(IO.read(hosts_file)).to match('example.tld') + end + end + + context 'and when similar found' do + it 'returns the incremented host' do + allow_any_instance_of(VScripts::Commands::Identify).to receive(:tag) + .and_return('TestName') + allow_any_instance_of(VScripts::Commands::Identify).to receive(:domain) + .and_return('TestDomain.tld') + subject.run(['identify', '--ec2-tag-theme=Test-#']) + expect(IO.read(hostname_file)).to eq('TestName-2') + expect(IO.read(hosts_file)).to match('TestName-2.TestDomain.tld') + end + end + end +end diff --git a/spec/integration/commands/tags2facts_spec.rb b/spec/integration/commands/tags2facts_spec.rb new file mode 100644 index 0000000..67eae2d --- /dev/null +++ b/spec/integration/commands/tags2facts_spec.rb @@ -0,0 +1,87 @@ +require 'integration/spec_helper' + +describe 'Command: Tags2Facts' do + include_context 'Suppressed output' + include_context 'Temporary' + + subject { VScripts } + + context 'when \'--help\'' do + it 'returns command specific help' do + expect{subject.run(['tags2facts', '--help'])}.to raise_error(SystemExit) + expect($stdout.string).to match(/USAGE:/) + expect($stdout.string).to match(/OPTIONS:/) + end + end + + context 'when unknown argument' do + it 'returns error with message' do + expect{subject.run(['tags2facts', '--xyz'])}.to raise_error(SystemExit) + expect($stderr.string).to match(/Error: unknown argument/) + end + end + + context 'when not an EC2 instance' do + include_context 'Not an EC2 Instance' + it 'returns error with message' do + expect{subject.run(['tags2facts'])}.to raise_error(SystemExit) + expect($stderr.string) + .to match('FATAL: NOT an EC2 instance or could not connect to Metadata') + end + end + + context 'when EC2 instance' do + context 'without tags' do + include_context 'EC2 Instance without tags' + it 'returns error with message' do + expect{subject.run(['tags2facts'])}.to raise_error(SystemExit) + expect($stderr.string).to match(/No tags were found/) + end + end + + context 'with tags' do + before(:each) do + allow_any_instance_of(VScripts::Commands::Tags2facts) + .to receive_message_chain('cli.file') + .and_return(test_file) + allow_any_instance_of(VScripts::Commands::Tags2facts) + .to receive_message_chain('cli.all').and_return(false) + end + + context '--all specified' do + include_context 'EC2 Instance with tags' + it 'creates file' do + allow_any_instance_of(VScripts::Commands::Tags2facts) + .to receive_message_chain('cli.all').and_return(true) + subject.run(['tags2facts', '--all']) + expect(IO.read(test_file)) + .to match(tags['Name']) + end + end + + context '--all not specified' do + include_context 'EC2 Instance with tags' + it 'creates file' do + subject.run(['tags2facts']) + expect(IO.read(test_file)).to match(tags.keys.last) + end + end + + context '--file specified' do + include_context 'EC2 Instance with tags' + it 'creates file' do + subject.run(['tags2facts', "--file #{test_file}"]) + expect(IO.read(test_file)).to match(tags.keys.last) + end + end + + context '--file not specified' do + include_context 'EC2 Instance with tags' + it 'creates file' do + subject.run(['tags2facts']) + expect(IO.read(test_file)).to match(tags.keys.last) + end + end + end + end +end diff --git a/spec/integration/commands_spec.rb b/spec/integration/commands_spec.rb new file mode 100644 index 0000000..fb435b1 --- /dev/null +++ b/spec/integration/commands_spec.rb @@ -0,0 +1,12 @@ +require 'integration/spec_helper' + +describe 'Command: Unknown' do + include_context 'Suppressed output' + + subject { VScripts } + + it 'returns error with message' do + expect{subject.run(['xyz'])}.to raise_error(SystemExit) + expect($stderr.string).to match(/Error: unknown subcommand/) + end +end diff --git a/spec/integration/global_opts_spec.rb b/spec/integration/global_opts_spec.rb new file mode 100644 index 0000000..8acbe2a --- /dev/null +++ b/spec/integration/global_opts_spec.rb @@ -0,0 +1,62 @@ +require 'integration/spec_helper' + +describe 'Global Options' do + subject { VScripts } + + before(:all) do + $stdout = StringIO.new + $stderr = StringIO.new + end + + after(:all) do + $stdout = STDOUT + $stderr = STDERR + end + + describe 'Help' do + context 'when \'-h\'' do + it 'returns help and exits' do + expect{subject.run(['-h'])}.to raise_error(SystemExit) + expect($stdout.string).to match(/Available commands/) + end + end + + context 'when \'--help\'' do + it 'returns help and exits' do + expect{subject.run(['--help'])}.to raise_error(SystemExit) + expect($stdout.string).to match(/Available commands/) + end + end + end + + describe 'Version' do + context 'when \'-v\'' do + it 'returns version and exits' do + expect{subject.run(['-v'])}.to raise_error(SystemExit) + expect($stdout.string).to match(/VScripts.*(c)/) + end + end + + context 'when \'--version\'' do + it 'returns version and exits' do + expect{subject.run(['--version'])}.to raise_error(SystemExit) + expect($stdout.string).to match(/VScripts.*(c)/) + end + end + end + describe 'Unknown argument' do + context 'when short' do + it 'returns error with message' do + expect{subject.run(['-z'])}.to raise_error(SystemExit) + expect($stderr.string).to match(/Error: unknown argument/) + end + end + + context 'when long' do + it 'returns error with message' do + expect{subject.run(['--xyz'])}.to raise_error(SystemExit) + expect($stderr.string).to match(/Error: unknown argument/) + end + end + end +end diff --git a/spec/integration/spec_helper.rb b/spec/integration/spec_helper.rb new file mode 100644 index 0000000..01a6dea --- /dev/null +++ b/spec/integration/spec_helper.rb @@ -0,0 +1,2 @@ +require 'aws_spec_helper' +require 'vscripts' diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index e32603f..865771d 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -2,6 +2,8 @@ require 'coveralls' Coveralls.wear! +TEMP_DIR = File.join(File.dirname(__FILE__),'..','tmp') + # Development code coverage SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter[ Coveralls::SimpleCov::Formatter, @@ -19,13 +21,60 @@ # Use color not only in STDOUT but also in pagers and files config.tty = true + # Show test times + config.profile_examples = true + # Use the specified formatter config.formatter = :documentation # :progress, :html, :textmate + # Create temporary files + config.before(:suite) { + FileUtils.mkdir_p TEMP_DIR + FileUtils.touch "#{TEMP_DIR}/test-file" + File.open("#{TEMP_DIR}/test-hostname", 'w') do |newfile| + newfile.write('TestHostname') + end + File.open("#{TEMP_DIR}/test-hosts", 'w') do |newfile| + newfile.write('127.0.0.1 TestHostname') + end + } + # Clean up config.after(:suite) { - unless ENV['TRAVIS'] - `ls /tmp/vscripts-test-* &> /dev/null && rm -r /tmp/vscripts-test-*` - end + FileUtils.rm_rf TEMP_DIR if Dir[TEMP_DIR] } } + +shared_context 'Suppressed output' do + before(:each) do + $stdout = StringIO.new + $stderr = StringIO.new + end + + after(:each) do + $stdout = STDOUT + $stderr = STDERR + end +end + +shared_context 'Temporary' do + let(:temp_dir) { TEMP_DIR } + let(:test_dir) { "#{TEMP_DIR}/test-dir" } + let(:test_file) { "#{TEMP_DIR}/test-file" } + let(:test_missing_file) { "#{test_dir}/test-file" } + let(:test_cont) { 'VScripts Test Content.' } +end + +shared_context 'System files' do + include_context 'Temporary' + + let(:hostname_file) { "#{TEMP_DIR}/test-hostname" } + let(:hosts_file) { "#{TEMP_DIR}/test-hosts" } + + before(:each) do + allow_any_instance_of(VScripts::Util::LocalSystem) + .to receive(:hostname_path).and_return(hostname_file) + allow_any_instance_of(VScripts::Util::LocalSystem) + .to receive(:hosts_path).and_return(hosts_file) + end +end diff --git a/spec/unit/vscripts/commands/identify_spec.rb b/spec/unit/vscripts/commands/identify_spec.rb index 409b22b..e656f14 100644 --- a/spec/unit/vscripts/commands/identify_spec.rb +++ b/spec/unit/vscripts/commands/identify_spec.rb @@ -171,8 +171,8 @@ allow(subject).to receive(:set_name_tag) allow(subject).to receive(:set_hostname) allow(subject).to receive(:update_hosts) - expect(STDOUT).to receive(:puts).with('Done.') - subject.execute + allow(STDOUT).to receive(:puts) + expect(subject.execute).to be_nil end end end diff --git a/spec/unit/vscripts/util/local_system_spec.rb b/spec/unit/vscripts/util/local_system_spec.rb index 7ea7ad2..f2a3f4d 100644 --- a/spec/unit/vscripts/util/local_system_spec.rb +++ b/spec/unit/vscripts/util/local_system_spec.rb @@ -5,9 +5,7 @@ describe VScripts::Util::LocalSystem do subject { Object.new.extend VScripts::Util } - let(:test_dir) { Dir::Tmpname.make_tmpname('/tmp/vscripts-test-', nil) } - let(:test_file) { "#{test_dir}/test_file" } - let(:test_cont) { 'testing content' } + include_context 'Temporary' describe '#hosts_path' do it 'returns the path to hosts file' do @@ -21,13 +19,6 @@ end end - describe '#hosts_file' do - it 'returns the an array' do - allow(File).to receive(:read).and_return(test_cont) - expect(subject.hosts_file).to be_a String - end - end - describe '#local_fqdn' do it 'returns the local FQDN' do expect(subject.local_fqdn).to be_a String @@ -59,7 +50,7 @@ describe '#ensure_file_dir' do it 'create a directory for the specified files' do - subject.ensure_file_dir(test_file) + subject.ensure_file_dir(test_missing_file) expect(Dir.exists?(test_dir)).to be true end end