describe Job do describe '.result' do it 'returns 1 for failed builds' do job = Factory.build(:test, state: :failed) job.result.should == 1 end it 'returns 0 for passed builds' do job = Factory.build(:test, state: :passed) job.result.should == 0 end end describe ".queued" do let(:jobs) { [Factory.create(:test), Factory.create(:test), Factory.create(:test)] } it "returns jobs that are created but not started or finished" do jobs.first.start! jobs.third.start! jobs.third.finish!(state: 'passed') Job.queued.should include(jobs.second) Job.queued.should_not include(jobs.first) Job.queued.should_not include(jobs.third) end end describe 'before_create' do let(:job) { Job::Test.create!(owner: Factory(:user), repository: Factory(:repository), commit: Factory(:commit), source: Factory(:build)) } before :each do Job::Test.any_instance.stubs(:enqueueable?).returns(false) # prevent jobs to enqueue themselves on create end it 'instantiates the log' do job.reload.log.should be_instance_of(Log) end it 'sets the state attribute' do job.reload.should be_created end it 'sets the queue attribute' do job.reload.queue.should == 'builds.linux' end end describe 'duration' do it 'returns nil if both started_at is not populated' do job = Job.new(finished_at: Time.now) job.duration.should be_nil end it 'returns nil if both finished_at is not populated' do job = Job.new(started_at: Time.now) job.duration.should be_nil end it 'returns the duration if both started_at and finished_at are populated' do job = Job.new(started_at: 20.seconds.ago, finished_at: 10.seconds.ago) job.duration.should be_within(0.1).of(10) end end describe 'obfuscated config' do let(:repo) { Factory(:repository) } before { repo.regenerate_key! } it 'handles nil env' do job = Job.new(repository: repo) job.config = { rvm: '1.8.7', env: nil } job.obfuscated_config.should == { rvm: '1.8.7', env: nil } end it 'leaves regular vars untouched' do job = Job.new(repository: repo) job.expects(:secure_env_enabled?).at_least_once.returns(true) job.config = { rvm: '1.8.7', env: 'FOO=foo' } job.obfuscated_config.should == { rvm: '1.8.7', env: 'FOO=foo' } end it 'obfuscates env vars' do job = Job.new(repository: repo) job.expects(:secure_env_enabled?).at_least_once.returns(true) config = { rvm: '1.8.7', env: [job.repository.key.secure.encrypt('BAR=barbaz'), 'FOO=foo'] } job.config = config job.obfuscated_config.should == { rvm: '1.8.7', env: 'BAR=[secure] FOO=foo' } end it 'normalizes env vars which are hashes to strings' do job = Job.new(repository: repo) job.expects(:secure_env_enabled?).at_least_once.returns(true) config = { rvm: '1.8.7', env: [{FOO: 'bar', BAR: 'baz'}, job.repository.key.secure.encrypt('BAR=barbaz')] } job.config = config job.obfuscated_config.should == { rvm: '1.8.7', env: 'FOO=bar BAR=baz BAR=[secure]' } end it 'removes addons config if it is not a hash' do job = Job.new(repository: repo) config = { rvm: '1.8.7', addons: "foo", } job.config = config job.obfuscated_config.should == { rvm: '1.8.7' } end it 'removes addons items which are not safelisted' do job = Job.new(repository: repo) config = { rvm: '1.8.7', addons: { sauce_connect: true, firefox: '22.0' }, } job.config = config job.obfuscated_config.should == { rvm: '1.8.7', addons: { firefox: '22.0' } } end it 'removes source key' do job = Job.new(repository: repo) config = { rvm: '1.8.7', source_key: '1234' } job.config = config job.obfuscated_config.should == { rvm: '1.8.7', } end context 'when job has secure env disabled' do let :job do job = Job.new(repository: repo) job.expects(:secure_env_enabled?).returns(false).at_least_once job end it 'removes secure env vars' do config = { rvm: '1.8.7', env: [job.repository.key.secure.encrypt('BAR=barbaz'), 'FOO=foo'] } job.config = config job.obfuscated_config.should == { rvm: '1.8.7', env: 'FOO=foo' } end it 'works even if it removes all env vars' do config = { rvm: '1.8.7', env: [job.repository.key.secure.encrypt('BAR=barbaz')] } job.config = config job.obfuscated_config.should == { rvm: '1.8.7', env: nil } end it 'normalizes env vars which are hashes to strings' do config = { rvm: '1.8.7', env: [{FOO: 'bar', BAR: 'baz'}, job.repository.key.secure.encrypt('BAR=barbaz')] } job.config = config job.obfuscated_config.should == { rvm: '1.8.7', env: 'FOO=bar BAR=baz' } end end end describe '#pull_request?' do it 'is delegated to commit' do commit = Commit.new commit.expects(:pull_request?).returns(true) job = Job.new job.commit = commit job.pull_request?.should be true end end describe 'decrypted config' do let(:repo) { Factory(:repository) } before { repo.regenerate_key! } it 'handles nil env' do job = Job.new(repository: repo) job.config = { rvm: '1.8.7', env: nil, global_env: nil } job.decrypted_config.should == { rvm: '1.8.7', env: nil, global_env: nil } end it 'normalizes env vars which are hashes to strings' do job = Job.new(repository: repo) job.expects(:secure_env_enabled?).at_least_once.returns(true) config = { rvm: '1.8.7', env: [{FOO: 'bar', BAR: 'baz'}, job.repository.key.secure.encrypt('BAR=barbaz')], global_env: [{FOO: 'foo', BAR: 'bar'}, job.repository.key.secure.encrypt('BAZ=baz')] } job.config = config job.decrypted_config.should == { rvm: '1.8.7', env: ["FOO=bar BAR=baz", "SECURE BAR=barbaz"], global_env: ["FOO=foo BAR=bar", "SECURE BAZ=baz"] } end it 'does not change original config' do job = Job.new(repository: repo) job.expects(:secure_env_enabled?).at_least_once.returns(true) config = { env: [{secure: 'invalid'}], global_env: [{secure: 'invalid'}] } job.config = config job.decrypted_config job.config.should == { env: [{ secure: 'invalid' }], global_env: [{ secure: 'invalid' }] } end it 'leaves regular vars untouched' do job = Job.new(repository: repo) job.expects(:secure_env_enabled?).returns(true).at_least_once job.config = { rvm: '1.8.7', env: 'FOO=foo', global_env: 'BAR=bar' } job.decrypted_config.should == { rvm: '1.8.7', env: ['FOO=foo'], global_env: ['BAR=bar'] } end context 'when secure env is not enabled' do let :job do job = Job.new(repository: repo) job.expects(:secure_env_enabled?).returns(false).at_least_once job end it 'removes secure env vars' do config = { rvm: '1.8.7', env: [job.repository.key.secure.encrypt('BAR=barbaz'), 'FOO=foo'], global_env: [job.repository.key.secure.encrypt('BAR=barbaz'), 'BAR=bar'] } job.config = config job.decrypted_config.should == { rvm: '1.8.7', env: ['FOO=foo'], global_env: ['BAR=bar'] } end it 'removes only secured env vars' do config = { rvm: '1.8.7', env: [job.repository.key.secure.encrypt('BAR=barbaz'), 'FOO=foo'] } job.config = config job.decrypted_config.should == { rvm: '1.8.7', env: ['FOO=foo'] } end end context 'when addons are disabled' do let :job do job = Job.new(repository: repo) job.expects(:addons_enabled?).returns(false).at_least_once job end it 'removes addons if it is not a hash' do config = { rvm: '1.8.7', addons: [] } job.config = config job.decrypted_config.should == { rvm: '1.8.7' } end it 'removes addons items which are not safelisted' do config = { rvm: '1.8.7', addons: { sauce_connect: { username: 'johndoe', access_key: job.repository.key.secure.encrypt('foobar') }, firefox: '22.0', mariadb: '10.1', postgresql: '9.3', hosts: %w(travis.dev), apt_packages: %w(curl git), apt_sources: %w(deadsnakes) } } job.config = config job.decrypted_config.should == { rvm: '1.8.7', addons: { firefox: '22.0', mariadb: '10.1', postgresql: '9.3', hosts: %w(travis.dev), apt_packages: %w(curl git), apt_sources: %w(deadsnakes) } } end end context 'when job has secure env enabled' do let :job do job = Job.new(repository: repo) job.expects(:secure_env_enabled?).returns(true).at_least_once job end it 'decrypts env vars' do config = { rvm: '1.8.7', env: job.repository.key.secure.encrypt('BAR=barbaz'), global_env: job.repository.key.secure.encrypt('BAR=bazbar') } job.config = config job.decrypted_config.should == { rvm: '1.8.7', env: ['SECURE BAR=barbaz'], global_env: ['SECURE BAR=bazbar'] } end it 'decrypts only secure env vars' do config = { rvm: '1.8.7', env: [job.repository.key.secure.encrypt('BAR=bar'), 'FOO=foo'], global_env: [job.repository.key.secure.encrypt('BAZ=baz'), 'QUX=qux'] } job.config = config job.decrypted_config.should == { rvm: '1.8.7', env: ['SECURE BAR=bar', 'FOO=foo'], global_env: ['SECURE BAZ=baz', 'QUX=qux'] } end end context 'when job has addons enabled' do let :job do job = Job.new(repository: repo) job.expects(:addons_enabled?).returns(true).at_least_once job end it 'decrypts addons config' do config = { rvm: '1.8.7', addons: { sauce_connect: { username: 'johndoe', access_key: job.repository.key.secure.encrypt('foobar') } } } job.config = config job.decrypted_config.should == { rvm: '1.8.7', addons: { sauce_connect: { username: 'johndoe', access_key: 'foobar' } } } end it 'decrypts deploy addon config' do config = { rvm: '1.8.7', deploy: { foo: job.repository.key.secure.encrypt('foobar') } } job.config = config job.decrypted_config.should == { rvm: '1.8.7', addons: { deploy: { foo: 'foobar' } } } end it 'removes addons config if it is an array and deploy is present' do config = { rvm: '1.8.7', addons: ["foo"], deploy: { foo: 'bar'} } job.config = config job.decrypted_config.should == { rvm: '1.8.7', addons: { deploy: { foo: 'bar' } } } end end end describe 'log_content=' do let(:job) { Job::Test.create!(owner: Factory(:user), repository: Factory(:repository), commit: Factory(:commit), source: Factory(:build), log: Factory(:log)) } it 'sets the log content' do job.log_content = 'Hello, world' job.log.content.should == 'Hello, world' end it 'blanks out any old log content' do job.log_content = 'foo' job.log_content = 'bar' job.log.content.should == 'bar' end end end