From cecc501255167a47b96cb33687965e3337465e00 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9e=20Hendricksen?= Date: Thu, 26 May 2016 18:14:12 +0200 Subject: [PATCH] working service specs --- core_specs/core/services/cancel_build_spec.rb | 55 ++++ core_specs/core/services/cancel_job_spec.rb | 44 ++++ core_specs/core/services/find_admin_spec.rb | 90 +++++++ .../core/services/find_annotation_spec.rb | 23 ++ core_specs/core/services/find_branch_spec.rb | 32 +++ .../core/services/find_branches_spec.rb | 32 +++ core_specs/core/services/find_build_spec.rb | 67 +++++ core_specs/core/services/find_builds_spec.rb | 87 +++++++ core_specs/core/services/find_caches_spec.rb | 76 ++++++ .../services/find_daily_repos_stats_spec.rb | 22 ++ .../services/find_daily_tests_stats_spec.rb | 17 ++ core_specs/core/services/find_hooks_spec.rb | 49 ++++ core_specs/core/services/find_job_spec.rb | 61 +++++ core_specs/core/services/find_jobs_spec.rb | 68 +++++ core_specs/core/services/find_log_spec.rb | 42 +++ .../core/services/find_repo_key_spec.rb | 29 +++ .../core/services/find_repo_settings_spec.rb | 43 +++ core_specs/core/services/find_repo_spec.rb | 34 +++ core_specs/core/services/find_repos_spec.rb | 127 +++++++++ core_specs/core/services/find_request_spec.rb | 27 ++ .../core/services/find_requests_spec.rb | 61 +++++ .../core/services/find_user_accounts_spec.rb | 50 ++++ .../core/services/next_build_number_spec.rb | 51 ++++ .../core/services/regenerate_repo_key_spec.rb | 29 +++ core_specs/core/services/remove_log_spec.rb | 113 ++++++++ core_specs/core/services/reset_model_spec.rb | 96 +++++++ core_specs/core/services/sync_user_spec.rb | 36 +++ .../core/services/update_annotation_spec.rb | 74 ++++++ core_specs/core/services/update_hook_spec.rb | 76 ++++++ core_specs/core/services/update_job_spec.rb | 244 ++++++++++++++++++ core_specs/core/services/update_log_spec.rb | 37 +++ core_specs/core/services/update_user_spec.rb | 25 ++ core_specs/core/services_spec.rb | 58 +++++ spec/support/gcs.rb | 42 +++ spec/support/s3.rb | 48 ++++ 35 files changed, 2065 insertions(+) create mode 100644 core_specs/core/services/cancel_build_spec.rb create mode 100644 core_specs/core/services/cancel_job_spec.rb create mode 100644 core_specs/core/services/find_admin_spec.rb create mode 100644 core_specs/core/services/find_annotation_spec.rb create mode 100644 core_specs/core/services/find_branch_spec.rb create mode 100644 core_specs/core/services/find_branches_spec.rb create mode 100644 core_specs/core/services/find_build_spec.rb create mode 100644 core_specs/core/services/find_builds_spec.rb create mode 100644 core_specs/core/services/find_caches_spec.rb create mode 100644 core_specs/core/services/find_daily_repos_stats_spec.rb create mode 100644 core_specs/core/services/find_daily_tests_stats_spec.rb create mode 100644 core_specs/core/services/find_hooks_spec.rb create mode 100644 core_specs/core/services/find_job_spec.rb create mode 100644 core_specs/core/services/find_jobs_spec.rb create mode 100644 core_specs/core/services/find_log_spec.rb create mode 100644 core_specs/core/services/find_repo_key_spec.rb create mode 100644 core_specs/core/services/find_repo_settings_spec.rb create mode 100644 core_specs/core/services/find_repo_spec.rb create mode 100644 core_specs/core/services/find_repos_spec.rb create mode 100644 core_specs/core/services/find_request_spec.rb create mode 100644 core_specs/core/services/find_requests_spec.rb create mode 100644 core_specs/core/services/find_user_accounts_spec.rb create mode 100644 core_specs/core/services/next_build_number_spec.rb create mode 100644 core_specs/core/services/regenerate_repo_key_spec.rb create mode 100644 core_specs/core/services/remove_log_spec.rb create mode 100644 core_specs/core/services/reset_model_spec.rb create mode 100644 core_specs/core/services/sync_user_spec.rb create mode 100644 core_specs/core/services/update_annotation_spec.rb create mode 100644 core_specs/core/services/update_hook_spec.rb create mode 100644 core_specs/core/services/update_job_spec.rb create mode 100644 core_specs/core/services/update_log_spec.rb create mode 100644 core_specs/core/services/update_user_spec.rb create mode 100644 core_specs/core/services_spec.rb create mode 100644 spec/support/gcs.rb create mode 100644 spec/support/s3.rb diff --git a/core_specs/core/services/cancel_build_spec.rb b/core_specs/core/services/cancel_build_spec.rb new file mode 100644 index 00000000..087ae1ed --- /dev/null +++ b/core_specs/core/services/cancel_build_spec.rb @@ -0,0 +1,55 @@ +require 'spec_helper' + +describe Travis::Services::CancelBuild do + include Support::ActiveRecord + + let(:repo) { Factory(:repository) } + let!(:job) { Factory(:test, repository: repo, state: :created) } + let!(:passed_job) { Factory(:test, repository: repo, state: :passed) } + let(:build) { Factory(:build, repository: repo) } + let(:params) { { id: build.id, source: 'tests' } } + let(:user) { Factory(:user) } + let(:service) { described_class.new(user, params) } + + before do + build.matrix.destroy_all + build.matrix << passed_job + build.matrix << job + end + + describe 'run' do + it 'should cancel the build if it\'s cancelable' do + job.stubs(:cancelable?).returns(true) + service.stubs(:authorized?).returns(true) + + publisher = mock('publisher') + service.stubs(:publisher).returns(publisher) + publisher.expects(:publish).with(type: 'cancel_job', job_id: job.id, source: 'tests') + publisher.expects(:publish).with(type: 'cancel_job', job_id: passed_job.id, source: 'tests') + + expect { + expect { + service.run + }.to change { build.reload.state } + }.to change { job.reload.state } + + job.state.should == 'canceled' + build.state.should == 'canceled' + end + + it 'should not cancel the job if it\'s not cancelable' do + job.stubs(:cancelable?).returns(false) + + expect { + service.run + }.to_not change { build.reload.state } + end + + it 'should not be able to cancel job if user does not have any permissions' do + user.permissions.destroy_all + + service.can_cancel?.should be_false + end + end +end + diff --git a/core_specs/core/services/cancel_job_spec.rb b/core_specs/core/services/cancel_job_spec.rb new file mode 100644 index 00000000..d076250d --- /dev/null +++ b/core_specs/core/services/cancel_job_spec.rb @@ -0,0 +1,44 @@ +require 'spec_helper' + +describe Travis::Services::CancelJob do + include Support::ActiveRecord + + let(:repo) { Factory(:repository) } + let!(:job) { Factory(:test, repository: repo, state: :created) } + let(:params) { { id: job.id, source: 'tests' } } + let(:user) { Factory(:user) } + let(:service) { described_class.new(user, params) } + + describe 'run' do + it 'should cancel the job if it\'s cancelable' do + job.stubs(:cancelable?).returns(true) + service.stubs(:authorized?).returns(true) + + publisher = mock('publisher') + service.stubs(:publisher).returns(publisher) + publisher.expects(:publish).with(type: 'cancel_job', job_id: job.id, source: 'tests') + + expect { + service.run + }.to change { job.reload.state } + + job.state.should == 'canceled' + end + + it 'should not cancel the job if it\'s not cancelable' do + job.state.should == :created + job.stubs(:cancelable?).returns(false) + + expect { + service.run + }.to_not change { job.state } + end + + it 'should not be able to cancel job if user does not have pull permission' do + user.permissions.destroy_all + + service.can_cancel?.should be_false + end + end +end + diff --git a/core_specs/core/services/find_admin_spec.rb b/core_specs/core/services/find_admin_spec.rb new file mode 100644 index 00000000..e4e20aa2 --- /dev/null +++ b/core_specs/core/services/find_admin_spec.rb @@ -0,0 +1,90 @@ +require 'spec_helper' + +describe Travis::Services::FindAdmin do + include Travis::Testing::Stubs + + describe 'find' do + let(:result) { described_class.new(nil, repository: repository).run } + + before :each do + User.stubs(:with_permissions).with(:repository_id => repository.id, :admin => true).returns [user] + end + + describe 'given a user has admin access to a repository (as seen by github)' do + before :each do + GH.stubs(:[]).with("repos/#{repository.slug}").returns('permissions' => { 'admin' => true }) + end + + it 'returns that user' do + result.should == user + end + end + + describe 'given a user does not have access to a repository' do + before :each do + GH.stubs(:[]).with("repos/#{repository.slug}").returns('permissions' => { 'admin' => false }) + user.stubs(:update_attributes!) + end + + xit 'raises an exception' do + lambda { result }.should raise_error(Travis::AdminMissing, 'no admin available for svenfuchs/minimal') + end + + xit 'revokes admin permissions for that user on our side' do + user.expects(:update_attributes!).with(:permissions => { 'admin' => false }) + ignore_exception { result } + end + end + + describe 'given an error occurs while retrieving the repository info' do + let(:error) { stub('error', :backtrace => [], :response => stub('reponse')) } + + before :each do + GH.stubs(:[]).with("repos/#{repository.slug}").raises(GH::Error.new(error)) + end + + xit 'raises an exception' do + lambda { result }.should raise_error(Travis::AdminMissing, 'no admin available for svenfuchs/minimal') + end + + it 'does not revoke permissions' do + user.expects(:update_permissions!).never + ignore_exception { result } + end + end + + describe 'missing repository' do + it 'raises Travis::RepositoryMissing' do + expect { described_class.new.run }.to raise_error(Travis::RepositoryMissing) + end + end + + def ignore_exception(&block) + block.call + rescue Travis::AdminMissing + end + end +end + +describe Travis::Services::FindAdmin::Instrument do + include Travis::Testing::Stubs + + let(:publisher) { Travis::Notification::Publisher::Memory.new } + let(:event) { publisher.events[1] } + let(:service) { Travis::Services::FindAdmin.new(nil, repository: repository) } + + before :each do + Travis::Notification.publishers.replace([publisher]) + User.stubs(:with_permissions).with(repository_id: repository.id, admin: true).returns [user] + GH.stubs(:[]).with("repos/#{repository.slug}").returns('permissions' => { 'admin' => true }) + service.run + end + + it 'publishes a event' do + event.should publish_instrumentation_event( + event: 'travis.services.find_admin.run:completed', + message: 'Travis::Services::FindAdmin#run:completed for svenfuchs/minimal: svenfuchs', + result: user, + ) + end +end diff --git a/core_specs/core/services/find_annotation_spec.rb b/core_specs/core/services/find_annotation_spec.rb new file mode 100644 index 00000000..9b9fa77c --- /dev/null +++ b/core_specs/core/services/find_annotation_spec.rb @@ -0,0 +1,23 @@ +require 'spec_helper' + +describe Travis::Services::FindAnnotations do + include Support::ActiveRecord + + let(:job) { Factory(:test) } + let!(:annotation) { Factory(:annotation, job: job) } + let(:service) { described_class.new(params) } + + attr_reader :params + + describe 'run' do + it 'finds annotations by a given list of ids' do + @params = { ids: [annotation.id] } + service.run.should eq([annotation]) + end + + it 'finds annotations by job_id' do + @params = { job_id: job.id } + service.run.should eq([annotation]) + end + end +end diff --git a/core_specs/core/services/find_branch_spec.rb b/core_specs/core/services/find_branch_spec.rb new file mode 100644 index 00000000..0394e074 --- /dev/null +++ b/core_specs/core/services/find_branch_spec.rb @@ -0,0 +1,32 @@ +require 'spec_helper' + +describe Travis::Services::FindBranch do + include Support::ActiveRecord + + let(:repo) { Factory(:repository, :owner_name => 'travis-ci', :name => 'travis-core') } + let!(:build) { Factory(:build, :repository => repo, :state => :finished) } + let(:service) { described_class.new(stub('user'), params) } + + attr_reader :params + + it 'finds the last builds of the given repository and branch' do + @params = { :repository_id => repo.id, :branch => 'master' } + service.run.should be == build + end + + it 'scopes to the given repository' do + @params = { :repository_id => repo.id, :branch => 'master' } + build = Factory(:build, :repository => Factory(:repository), :state => :finished) + service.run.should_not be == build + end + + it 'returns an empty build scope when the repository could not be found' do + @params = { :repository_id => repo.id + 1, :branch => 'master' } + service.run.should be_nil + end + + it 'finds branches by a given id' do + @params = { :id => build.id } + service.run.should be == build + end +end diff --git a/core_specs/core/services/find_branches_spec.rb b/core_specs/core/services/find_branches_spec.rb new file mode 100644 index 00000000..d9ca70f2 --- /dev/null +++ b/core_specs/core/services/find_branches_spec.rb @@ -0,0 +1,32 @@ +require 'spec_helper' + +describe Travis::Services::FindBranches do + include Support::ActiveRecord + + let(:repo) { Factory(:repository, :owner_name => 'travis-ci', :name => 'travis-core') } + let!(:build) { Factory(:build, :repository => repo, :state => :finished) } + let(:service) { described_class.new(stub('user'), params) } + + attr_reader :params + + it 'finds the last builds of the given repository grouped per branch' do + @params = { :repository_id => repo.id } + service.run.should include(build) + end + + it 'scopes to the given repository' do + @params = { :repository_id => repo.id } + build = Factory(:build, :repository => Factory(:repository), :state => :finished) + service.run.should_not include(build) + end + + it 'returns an empty build scope when the repository could not be found' do + @params = { :repository_id => repo.id + 1 } + service.run.should == Build.none + end + + it 'finds branches by a given list of ids' do + @params = { :ids => [build.id] } + service.run.should == [build] + end +end diff --git a/core_specs/core/services/find_build_spec.rb b/core_specs/core/services/find_build_spec.rb new file mode 100644 index 00000000..b1a13861 --- /dev/null +++ b/core_specs/core/services/find_build_spec.rb @@ -0,0 +1,67 @@ +require 'spec_helper' + +describe Travis::Services::FindBuild do + include Support::ActiveRecord + + let(:repo) { Factory(:repository, owner_name: 'travis-ci', name: 'travis-core') } + let!(:build) { Factory(:build, repository: repo, state: :finished, number: 1, config: {'sudo' => false}) } + let(:params) { { id: build.id } } + let(:service) { described_class.new(stub('user'), params) } + + describe 'run' do + it 'finds a build by the given id' do + service.run.should == build + end + + it 'does not raise if the build could not be found' do + @params = { :id => build.id + 1 } + lambda { service.run }.should_not raise_error + end + + it 'includes config by default' do + service.run.config.should include(:sudo) + end + + it 'excludes config when requested' do + params[:exclude_config] = '1' + service.run.config.should_not include(:sudo) + end + end + + describe 'updated_at' do + it 'returns builds updated_at attribute' do + service.updated_at.to_s.should == build.updated_at.to_s + end + end + + describe 'with newer associated record' do + it 'returns updated_at of newest result' do + build.update_attribute(:updated_at, 5.minutes.ago) + build.reload.updated_at.should < build.matrix.first.updated_at + service.updated_at.to_s.should == build.matrix.first.updated_at.to_s + end + end + + describe 'without updated_at in one of the resources' do + it 'returns updated_at of newest result' do + Build.any_instance.stubs(updated_at: nil) + expect { + service.updated_at + }.to_not raise_error + end + end + + # TODO builds can be requeued, so finished builds are no more final + # + # describe 'final?' do + # it 'returns true if the build is finished' do + # build.update_attributes!(:state => :errored) + # service.final?.should be_true + # end + + # it 'returns false if the build is not finished' do + # build.update_attributes!(:state => :started) + # service.final?.should be_false + # end + # end +end diff --git a/core_specs/core/services/find_builds_spec.rb b/core_specs/core/services/find_builds_spec.rb new file mode 100644 index 00000000..4f7e74c5 --- /dev/null +++ b/core_specs/core/services/find_builds_spec.rb @@ -0,0 +1,87 @@ +require 'spec_helper' + +describe Travis::Services::FindBuilds do + include Support::ActiveRecord + + let(:repo) { Factory(:repository, owner_name: 'travis-ci', name: 'travis-core') } + let!(:push) { Factory(:build, repository: repo, event_type: 'push', state: :failed, number: 1) } + let(:service) { described_class.new(stub('user'), params) } + + attr_reader :params + + describe 'run' do + it 'finds recent builds when empty params given' do + @params = { :repository_id => repo.id } + service.run.should == [push] + end + + it 'finds running builds when running param is passed' do + running = Factory(:build, repository: repo, event_type: 'push', state: 'started', number: 2) + @params = { :running => true } + service.run.should == [running] + end + + it 'finds recent builds when no repo given' do + @params = nil + service.run.should == [push] + end + + it 'finds builds older than the given number' do + @params = { :repository_id => repo.id, :after_number => 2 } + service.run.should == [push] + end + + it 'finds builds with a given number, scoped by repository' do + @params = { :repository_id => repo.id, :number => 1 } + Factory(:build, :repository => Factory(:repository), :state => :finished, :number => 1) + Factory(:build, :repository => repo, :state => :finished, :number => 2) + service.run.should == [push] + end + + it 'does not find by number if repository_id is missing' do + @params = { :number => 1 } + service.run.should == Build.none + end + + it 'scopes to the given repository_id' do + @params = { :repository_id => repo.id } + Factory(:build, :repository => Factory(:repository), :state => :finished) + service.run.should == [push] + end + + it 'returns an empty build scope when the repository could not be found' do + @params = { :repository_id => repo.id + 1 } + service.run.should == Build.none + end + + it 'finds builds by a given list of ids' do + @params = { :ids => [push.id] } + service.run.should == [push] + end + + describe 'finds recent builds when event_type' do + let!(:pull_request) { Factory(:build, repository: repo, state: :finished, number: 2, request: Factory(:request, :event_type => 'pull_request')) } + let!(:api) { Factory(:build, repository: repo, state: :finished, number: 2, request: Factory(:request, :event_type => 'api')) } + + it 'given as push' do + @params = { repository_id: repo.id, event_type: 'push' } + service.run.should == [push] + end + + it 'given as pull_request' do + @params = { repository_id: repo.id, event_type: 'pull_request' } + service.run.should == [pull_request] + end + + it 'given as api' do + @params = { repository_id: repo.id, event_type: 'api' } + service.run.should == [api] + end + + it 'given as [push, api]' do + @params = { repository_id: repo.id, event_type: ['push', 'api'] } + service.run.sort.should == [push, api] + end + end + end +end diff --git a/core_specs/core/services/find_caches_spec.rb b/core_specs/core/services/find_caches_spec.rb new file mode 100644 index 00000000..a6bc5dc0 --- /dev/null +++ b/core_specs/core/services/find_caches_spec.rb @@ -0,0 +1,76 @@ +require 'spec_helper' + +describe Travis::Services::FindCaches do + include Support::ActiveRecord, Support::S3, Support::GCS + + let(:user) { User.first || Factory(:user) } + let(:service) { described_class.new(user, params) } + let(:repo) { Factory(:repository, :owner_name => 'travis-ci', :name => 'travis-core') } + let(:cache_options) {{ s3: { bucket_name: '' , access_key_id: '', secret_access_key: ''} }} + let(:has_access) { true } + let(:result) { service.run } + subject { result } + + before :each do + Travis.config.roles = {} + Travis.config.cache_options = cache_options + user.stubs(:permission?).returns(has_access) + end + + describe 'given a repository_id' do + let(:params) {{ repository_id: repo.id }} + + describe 'without any caches' do + it { should be == [] } + end + + describe 'with caches' do + before do + s3_bucket << "#{repo.github_id}/master/cache--example1.tbz" + s3_bucket << "#{repo.github_id}/other/cache--example2.tbz" + s3_bucket << "#{repo.github_id.succ}/master/cache--example3.tbz" + end + + its(:size) { should be == 2 } + + describe 'the cache instances' do + subject { result.first } + its(:slug) { should be == 'cache--example1' } + its(:branch) { should be == 'master' } + its(:repository) { should be == repo } + its(:size) { should be == 0 } + end + + describe 'with branch' do + let(:params) {{ repository_id: repo.id, branch: 'other' }} + its(:size) { should be == 1 } + end + + describe 'with match' do + let(:params) {{ repository_id: repo.id, match: 'example1' }} + its(:size) { should be == 1 } + end + + describe 'without access' do + let(:has_access) { false } + its(:size) { should be == 0 } + end + + describe 'without s3 credentials' do + let(:cache_options) {{ }} + before { service.logger.expects(:warn).with("[services:find-caches] cache settings incomplete") } + it { should be == [] } + end + + describe 'with multiple buckets' do + let(:cache_options) {[{ s3: { bucket_name: '', access_key_id: '', secret_access_key: '' } }, { s3: { bucket_name: '', access_key_id: '', secret_access_key: '' } }]} + its(:size) { should be == 4 } + end + end + + context 'with GCS configuration' do + let(:cache_options) { { gcs: { bucket_name: '', json_key: '' } } } + its(:size) { should be == 0 } + end + end +end \ No newline at end of file diff --git a/core_specs/core/services/find_daily_repos_stats_spec.rb b/core_specs/core/services/find_daily_repos_stats_spec.rb new file mode 100644 index 00000000..1fc35a00 --- /dev/null +++ b/core_specs/core/services/find_daily_repos_stats_spec.rb @@ -0,0 +1,22 @@ +require 'spec_helper' +require 'travis/testing/scenario' + +describe Travis::Services::FindDailyReposStats do + include Support::ActiveRecord + + let(:service) { described_class.new(stub('user'), {}) } + + before { Scenario.default } + + it 'should include the date' do + stats = service.run + stats.should have(1).items + stats.first['date'].should == Repository.first.created_at.to_date.to_s(:date) + end + + it 'should include the number per day' do + stats = service.run + stats.should have(1).items + stats.first['count'].to_i.should == 2 + end +end diff --git a/core_specs/core/services/find_daily_tests_stats_spec.rb b/core_specs/core/services/find_daily_tests_stats_spec.rb new file mode 100644 index 00000000..fe1fc7da --- /dev/null +++ b/core_specs/core/services/find_daily_tests_stats_spec.rb @@ -0,0 +1,17 @@ +require 'spec_helper' +require 'travis/testing/scenario' + +describe Travis::Services::FindDailyTestsStats do + include Support::ActiveRecord + + let(:service) { described_class.new(stub('user'), {}) } + + before { Scenario.default } + + it 'should return the jobs per day' do + stats = service.run + stats.should have(1).item + stats.first['date'].should == Job.first.created_at.to_date.to_s(:date) + stats.first['count'].to_i.should == 13 + end +end diff --git a/core_specs/core/services/find_hooks_spec.rb b/core_specs/core/services/find_hooks_spec.rb new file mode 100644 index 00000000..af972d71 --- /dev/null +++ b/core_specs/core/services/find_hooks_spec.rb @@ -0,0 +1,49 @@ +require 'spec_helper' + +describe Travis::Services::FindHooks do + include Support::ActiveRecord + + let(:user) { User.first || Factory(:user) } + let(:repo) { Factory(:repository) } + let(:push_repo) { Factory(:repository, name: 'push-repo') } + let(:service) { described_class.new(user, params) } + + before :each do + user.permissions.create!(:repository => repo, :admin => true) + user.permissions.create!(:repository => push_repo, :push => true) + end + + attr_reader :params + + it 'finds repositories where the current user has access with :all option' do + @params = { all: true } + hooks = service.run + hooks.should include(repo) + hooks.should include(push_repo) + hooks.should have(2).items + + # hooks should include admin information + hooks.sort_by(&:id).map(&:admin?).should == [true, false] + end + + it 'finds repositories where the current user has admin access' do + @params = {} + service.run.should include(repo) + end + + it 'does not find repositories where the current user does not have admin access' do + @params = {} + user.permissions.delete_all + service.run.should_not include(repo) + end + + it 'finds repositories by a given owner_name where the current user has admin access' do + @params = { :owner_name => repo.owner_name } + service.run.should include(repo) + end + + it 'does not find repositories by a given owner_name where the current user does not have admin access' do + @params = { :owner_name => 'rails' } + service.run.should_not include(repo) + end +end diff --git a/core_specs/core/services/find_job_spec.rb b/core_specs/core/services/find_job_spec.rb new file mode 100644 index 00000000..93206fca --- /dev/null +++ b/core_specs/core/services/find_job_spec.rb @@ -0,0 +1,61 @@ +require 'spec_helper' + +describe Travis::Services::FindJob do + include Support::ActiveRecord + + let(:repo) { Factory(:repository) } + let!(:job) { Factory(:test, repository: repo, state: :created, queue: 'builds.linux', config: {'sudo' => false}) } + let(:params) { { id: job.id } } + let(:service) { described_class.new(stub('user'), params) } + + describe 'run' do + it 'finds the job with the given id' do + @params = { id: job.id } + service.run.should == job + end + + it 'does not raise if the job could not be found' do + @params = { id: job.id + 1 } + lambda { service.run }.should_not raise_error + end + + it 'raises RecordNotFound if a SubclassNotFound error is raised during find' do + find_by_id = stub.tap do |s| + s.stubs(:column_names).returns(%w(id config)) + s.stubs(:select).returns(s) + s.stubs(:find_by_id).raises(ActiveRecord::SubclassNotFound) + end + service.stubs(:scope).returns(find_by_id) + lambda { service.run }.should raise_error(ActiveRecord::RecordNotFound) + end + + it 'includes config by default' do + service.run.config.should include(:sudo) + end + + it 'excludes config when requested' do + params[:exclude_config] = '1' + service.run.config.should_not include(:sudo) + end + end + + describe 'updated_at' do + it 'returns jobs updated_at attribute' do + service.updated_at.to_s.should == job.updated_at.to_s + end + end + + # TODO jobs can be requeued, so finished jobs are no more final + # + # describe 'final?' do + # it 'returns true if the job is finished' do + # job.update_attributes!(state: :errored) + # service.final?.should be_true + # end + + # it 'returns false if the job is not finished' do + # job.update_attributes!(state: :started) + # service.final?.should be_false + # end + # end +end diff --git a/core_specs/core/services/find_jobs_spec.rb b/core_specs/core/services/find_jobs_spec.rb new file mode 100644 index 00000000..76c41b0d --- /dev/null +++ b/core_specs/core/services/find_jobs_spec.rb @@ -0,0 +1,68 @@ +require 'spec_helper' + +describe Travis::Services::FindJobs do + include Support::ActiveRecord + + let(:repo) { Factory(:repository) } + let!(:job) { Factory(:test, :repository => repo, :state => :created, :queue => 'builds.linux') } + let(:service) { described_class.new(stub('user'), params) } + + attr_reader :params + + describe 'run' do + it 'finds jobs on the given queue' do + @params = { :queue => 'builds.linux' } + service.run.should include(job) + end + + it 'does not find jobs on other queues' do + @params = { :queue => 'builds.nodejs' } + service.run.should_not include(job) + end + + it 'finds jobs by a given list of ids' do + @params = { :ids => [job.id] } + service.run.should == [job] + end + + it 'finds jobs by state' do + build = Factory(:build) + + Job::Test.destroy_all + + started = Factory(:test, :state => :started, :source => build) + passed = Factory(:test, :state => :passed, :source => build) + created = Factory(:test, :state => :created, :source => build) + + @params = { :state => ['created', 'passed'] } + service.run.sort_by(&:id).should == [created, passed].sort_by(&:id) + end + + it 'finds jobs that are about to run without any args' do + build = Factory(:build) + + Job::Test.destroy_all + + started = Factory(:test, :state => :started, :source => build) + queued = Factory(:test, :state => :queued, :source => build) + passed = Factory(:test, :state => :passed, :source => build) + created = Factory(:test, :state => :created, :source => build) + received = Factory(:test, :state => :received, :source => build) + + @params = {} + service.run.sort_by(&:id).should == [started, queued, created, received].sort_by(&:id) + end + end + + describe 'updated_at' do + it 'returns the latest updated_at time' do + pending 'rack cache is disabled, so not much need for caching now' + + @params = { :queue => 'builds.linux' } + Job.delete_all + Factory(:test, :repository => repo, :state => :queued, :queue => 'build.common', :updated_at => Time.now - 1.hour) + Factory(:test, :repository => repo, :state => :queued, :queue => 'build.common', :updated_at => Time.now) + service.updated_at.to_s.should == Time.now.to_s + end + end +end diff --git a/core_specs/core/services/find_log_spec.rb b/core_specs/core/services/find_log_spec.rb new file mode 100644 index 00000000..c3ca32ce --- /dev/null +++ b/core_specs/core/services/find_log_spec.rb @@ -0,0 +1,42 @@ +require 'spec_helper' + +describe Travis::Services::FindLog do + include Support::ActiveRecord + + let!(:job) { Factory(:test) } + let(:log) { job.log } + let(:service) { described_class.new(stub('user'), params) } + + attr_reader :params + + describe 'run' do + it 'finds the log with the given id' do + @params = { id: log.id } + service.run.should == log + end + + it 'finds the log with the given job_id' do + @params = { job_id: job.id } + service.run.should == log + end + + it 'does not raise if the log could not be found' do + @params = { id: log.id + 1 } + lambda { service.run }.should_not raise_error + end + end + + # TODO jobs can be requeued, so finished jobs are no more final + # + # describe 'final?' do + # it 'returns true if the job is finished' do + # log.job.update_attributes!(:state => :finished) + # service.final?.should be_true + # end + + # it 'returns false if the job is not finished' do + # log.job.update_attributes!(:state => :started) + # service.final?.should be_false + # end + # end +end diff --git a/core_specs/core/services/find_repo_key_spec.rb b/core_specs/core/services/find_repo_key_spec.rb new file mode 100644 index 00000000..70c60fa1 --- /dev/null +++ b/core_specs/core/services/find_repo_key_spec.rb @@ -0,0 +1,29 @@ +require 'spec_helper' + +describe Travis::Services::FindRepoKey do + include Support::ActiveRecord + + let!(:repo) { Factory(:repository, :owner_name => 'travis-ci', :name => 'travis-core') } + let(:service) { described_class.new(stub('user'), params) } + + attr_reader :params + + describe 'run' do + it 'finds a key by the given repository id' do + @params = { :id => repo.id } + service.run.should == repo.key + end + + it 'finds a key by the given owner_name and name' do + @params = { :owner_name => repo.owner_name, :name => repo.name } + service.run.should == repo.key + end + end + + describe 'updated_at' do + it 'returns key\'s updated_at attribute' do + @params = { :id => repo.id } + service.updated_at.to_s.should == repo.key.updated_at.to_s + end + end +end diff --git a/core_specs/core/services/find_repo_settings_spec.rb b/core_specs/core/services/find_repo_settings_spec.rb new file mode 100644 index 00000000..17842406 --- /dev/null +++ b/core_specs/core/services/find_repo_settings_spec.rb @@ -0,0 +1,43 @@ +require 'spec_helper' + +describe Travis::Services::FindRepoSettings do + include Support::ActiveRecord + + let(:repo) { Factory(:repository) } + let(:params) { { id: repo.id } } + let(:user) { Factory(:user) } + let(:service) { described_class.new(user, params) } + + before do + repo.settings.merge('build_pushes' => false) + repo.settings.save + repo.save + end + + describe 'authorized?' do + let(:service) { described_class.new(nil, params) } + + it 'should be unauthorized with current_user' do + service.should_not be_authorized + end + end + + describe 'run' do + it 'should return nil without a repo' do + repo.destroy + service.run.should be_nil + end + + it 'should return repo settings' do + user.permissions.create(repository_id: repo.id, push: true) + service.run.to_hash.should == repo.settings.to_hash + end + + it 'should not be able to get settings if user does not have push permission' do + user.permissions.create(repository_id: repo.id, push: false) + + service.run.should be_nil + end + end +end + diff --git a/core_specs/core/services/find_repo_spec.rb b/core_specs/core/services/find_repo_spec.rb new file mode 100644 index 00000000..d13c8372 --- /dev/null +++ b/core_specs/core/services/find_repo_spec.rb @@ -0,0 +1,34 @@ +require 'spec_helper' + +describe Travis::Services::FindRepo do + include Support::ActiveRecord + + let!(:repo) { Factory(:repository, :owner_name => 'travis-ci', :name => 'travis-core') } + let(:service) { described_class.new(stub('user'), params) } + + attr_reader :params + + describe 'run' do + it 'finds a repository by the given id' do + @params = { :id => repo.id } + service.run.should == repo + end + + it 'finds a repository by the given owner_name and name' do + @params = { :owner_name => repo.owner_name, :name => repo.name } + service.run.should == repo + end + + it 'does not raise if the repository could not be found' do + @params = { :id => repo.id + 1 } + lambda { service.run }.should_not raise_error + end + end + + describe 'updated_at' do + it 'returns jobs updated_at attribute' do + @params = { :id => repo.id } + service.updated_at.to_s.should == repo.updated_at.to_s + end + end +end diff --git a/core_specs/core/services/find_repos_spec.rb b/core_specs/core/services/find_repos_spec.rb new file mode 100644 index 00000000..6033f71e --- /dev/null +++ b/core_specs/core/services/find_repos_spec.rb @@ -0,0 +1,127 @@ +require 'spec_helper' + +describe Travis::Services::FindRepos do + include Support::ActiveRecord + + let!(:repo) { Factory(:repository, :owner_name => 'travis-ci', :name => 'travis-core', :active => true) } + let(:service) { described_class.new(stub('user'), params) } + + attr_reader :params + + it 'limits the repositories list' do + Factory(:repository) + @params = { :limit => 1 } + service.run.length.should == 1 + end + + it 'ignores the limit if it is not a number' do + Factory(:repository) + @params = { :limit => 'a' } + service.run.length.should == 2 + end + + it 'does not allow for limit higher than 50' do + @params = { :limit => 60 } + service.send(:limit).should == 50 + end + + it 'finds repositories by a given list of ids' do + @params = { :ids => [repo.id] } + service.run.should == [repo] + end + + it 'returns the recent timeline when given empty params' do + @params = {} + service.run.should include(repo) + end + + it 'applies timeline only if no other params are given' do + repo = Factory(:repository, :owner_name => 'foo', :name => 'bar', :last_build_started_at => nil, :active => true) + @params = { slug: 'foo/bar' } + service.run.should include(repo) + end + + describe 'given a member name' do + it 'finds a repository where that member has permissions' do + @params = { :member => 'joshk' } + repo.users << Factory(:user, :login => 'joshk') + service.run.should include(repo) + end + + it 'does not find a repository where the member does not have permissions' do + @params = { :member => 'joshk' } + service.run.should_not include(repo) + end + + # TODO ... we now include all :active repos (i.e. including those that haven't built yet) + # and last_build_started_at is nil for them, too. since there's no easy way to detect + # queued builds on the repo timeline i'm just disabling this for now. + # + # it 'sorts by latest build, putting queued (no last_build_started_at) at the front' do + # repo.update_column(:last_build_started_at, Time.now - 10) + # queued = Factory(:repository, name: 'queued', last_build_started_at: nil, :active => true) + # just_started = Factory(:repository, name: 'just-started',last_build_started_at: Time.now, :active => true) + # josh = Factory(:user, :login => 'joshk') + # [repo, queued, just_started].each { |r| r.users << josh } + # @params = { :member => 'joshk' } + # service.run.map(&:name).should == [queued, just_started, repo].map(&:name) + # end + end + + describe 'given an owner_name name' do + it 'finds a repository with that owner_name' do + @params = { :owner_name => 'travis-ci' } + service.run.should include(repo) + end + + it 'does not find a repository with another owner name' do + @params = { :owner_name => 'sinatra' } + service.run.should_not include(repo) + end + end + + describe 'given an owner_name name and active param' do + it 'finds a repository with that owner_name even if it does not have any builds' do + repo.update_column(:last_build_id, nil) + repo.update_column(:active, true) + @params = { :owner_name => 'travis-ci', :active => true } + service.run.should include(repo) + end + end + + describe 'given a slug name' do + it 'finds a repository with that slug' do + @params = { :slug => 'travis-ci/travis-core' } + service.run.should include(repo) + end + + it 'does not find a repository with a different slug' do + @params = { :slug => 'travis-ci/travis-hub' } + service.run.should_not include(repo) + end + end + + describe 'given a search phrase' do + it 'finds a repository matching that phrase' do + @params = { :search => 'travis' } + service.run.should include(repo) + end + + it 'does not find a repository that does not match that phrase' do + @params = { :search => 'sinatra' } + service.run.should_not include(repo) + end + end + + describe 'given a list of ids' do + it 'finds included repositories' do + @params = { :ids => [repo.id] } + service.run.should include(repo) + end + + it 'does not find a repositories that are not included' do + @params = { :ids => [repo.id + 1] } + service.run.should_not include(repo) + end + end +end diff --git a/core_specs/core/services/find_request_spec.rb b/core_specs/core/services/find_request_spec.rb new file mode 100644 index 00000000..559c7989 --- /dev/null +++ b/core_specs/core/services/find_request_spec.rb @@ -0,0 +1,27 @@ +require 'spec_helper' + +describe Travis::Services::FindRequest do + include Support::ActiveRecord + + let(:repo) { Factory(:repository, :owner_name => 'travis-ci', :name => 'travis-core') } + let!(:request) { Factory(:request, :repository => repo) } + let(:params) { { :id => request.id } } + let(:service) { described_class.new(stub('user'), params) } + + describe 'run' do + it 'finds a request by the given id' do + service.run.should == request + end + + it 'does not raise if the request could not be found' do + @params = { :id => request.id + 1 } + lambda { service.run }.should_not raise_error + end + end + + describe 'updated_at' do + it 'returns request\'s updated_at attribute' do + service.updated_at.to_s.should == request.updated_at.to_s + end + end +end diff --git a/core_specs/core/services/find_requests_spec.rb b/core_specs/core/services/find_requests_spec.rb new file mode 100644 index 00000000..d680c9f1 --- /dev/null +++ b/core_specs/core/services/find_requests_spec.rb @@ -0,0 +1,61 @@ +require 'spec_helper' + +describe Travis::Services::FindRequests do + include Support::ActiveRecord + + let(:repo) { Factory(:repository, :owner_name => 'travis-ci', :name => 'travis-core') } + let!(:request) { Factory(:request, :repository => repo) } + let!(:newer_request) { Factory(:request, :repository => repo) } + let(:service) { described_class.new(stub('user'), params) } + + attr_reader :params + + describe 'run' do + it 'finds recent requests when older_than is not given' do + @params = { :repository_id => repo.id } + service.run.should == [newer_request, request] + end + + it 'finds requests older than the given id' do + @params = { :repository_id => repo.id, :older_than => newer_request.id } + service.run.should == [request] + end + + it 'raises an error if repository params are missing' do + @params = { } + expect { + service.run + }.to raise_error(Travis::RepositoryNotFoundError, "Repository could not be found") + end + + it 'scopes to the given repository_id' do + @params = { :repository_id => repo.id } + Factory(:request, :repository => Factory(:repository)) + service.run.should == [newer_request, request] + end + + it 'raises when the repository could not be found' do + @params = { :repository_id => repo.id + 1 } + expect { + service.run + }.to raise_error(Travis::RepositoryNotFoundError, "Repository with id=#{repo.id + 1} could not be found") + end + + it 'limits requests if limit is passed' do + @params = { :repository_id => repo.id, :limit => 1 } + service.run.should == [newer_request] + end + + it 'limits requests to Travis.config.services.find_requests.max_limit if limit is higher' do + Travis.config.services.find_requests.expects(:max_limit).returns(1) + @params = { :repository_id => repo.id, :limit => 2 } + service.run.should == [newer_request] + end + + it 'limits requests to Travis.config.services.find_requests.default_limit if limit is not given' do + Travis.config.services.find_requests.expects(:default_limit).returns(1) + @params = { :repository_id => repo.id } + service.run.should == [newer_request] + end + end +end diff --git a/core_specs/core/services/find_user_accounts_spec.rb b/core_specs/core/services/find_user_accounts_spec.rb new file mode 100644 index 00000000..44cb3fcf --- /dev/null +++ b/core_specs/core/services/find_user_accounts_spec.rb @@ -0,0 +1,50 @@ +require 'spec_helper' + +describe Travis::Services::FindUserAccounts do + include Support::ActiveRecord + + let!(:sven) { Factory(:user, :login => 'sven') } + let!(:travis) { Factory(:org, :login => 'travis-ci') } + let!(:sinatra) { Factory(:org, :login => 'sinatra') } + + let!(:repos) do + Factory(:repository, :owner => sven, :owner_name => 'sven', :name => 'minimal') + Factory(:repository, :owner => travis, :owner_name => 'travis-ci', :name => 'travis-ci') + Factory(:repository, :owner => travis, :owner_name => 'travis-ci', :name => 'travis-core') + Factory(:repository, :owner => sinatra, :owner_name => 'sinatra', :name => 'sinatra') + end + + let(:service) { described_class.new(sven, params || {}) } + + attr_reader :params + + before :each do + Repository.all.each do |repo| + permissions = repo.name == 'sinatra' ? { :push => true } : { :admin => true } + sven.permissions.create!(permissions.merge :repository => repo) + end + + sven.organizations << travis + end + + it 'includes all repositories with :all param' do + @params = { all: true } + service.run.should include(Account.from(sven), Account.from(travis), Account.from(sinatra)) + end + + it 'includes the user' do + service.run.should include(Account.from(sven)) + end + + it 'includes accounts where the user has admin access' do + service.run.should include(Account.from(travis)) + end + + it 'does not include accounts where the user does not have admin access' do + service.run.should_not include(Account.from(sinatra)) + end + + it 'includes repository counts' do + service.run.map(&:repos_count).should == [1, 2] + end +end diff --git a/core_specs/core/services/next_build_number_spec.rb b/core_specs/core/services/next_build_number_spec.rb new file mode 100644 index 00000000..887a958b --- /dev/null +++ b/core_specs/core/services/next_build_number_spec.rb @@ -0,0 +1,51 @@ +require 'spec_helper' +require 'travis/services/next_build_number' + +describe Travis::Services::NextBuildNumber do + include Support::ActiveRecord + + let(:service) { described_class.new(user, params) } + let!(:user) { Factory(:user) } + let(:result) { service.run } + let(:params) { { repository_id: 1234 } } + let(:repo) do + Factory(:repository, owner_name: 'travis-ci', name: 'travis-core') + end + + subject { result } + + before do + Repository.expects(:find).with(1234).returns(repo) + end + + context 'with a new repository' do + before(:each) { repo.next_build_number = nil } + + it 'returns 1' do + subject.should == 1 + end + + it 'initializes the next_build_number' do + repo.next_build_number.should be_nil + subject + repo.next_build_number.should == 2 + end + end + + context 'with an existing repository' do + let(:repo) do + Factory(:repository, + owner_name: 'travis-ci', name: 'travis-core', next_build_number: 4 + ) + end + + it 'returns the next_build_number' do + subject.should == 4 + end + + it 'increments the next_build_number' do + subject + repo.next_build_number.should == 5 + end + end +end diff --git a/core_specs/core/services/regenerate_repo_key_spec.rb b/core_specs/core/services/regenerate_repo_key_spec.rb new file mode 100644 index 00000000..61c983bc --- /dev/null +++ b/core_specs/core/services/regenerate_repo_key_spec.rb @@ -0,0 +1,29 @@ +require 'spec_helper' + +describe Travis::Services::RegenerateRepoKey do + include Support::ActiveRecord + + let(:user) { User.first || Factory(:user) } + let!(:repo) { Factory(:repository, :owner_name => 'travis-ci', :name => 'travis-core') } + let(:service) { described_class.new(user, :id => repo.id) } + + before :each do + service.expects(:service).with(:find_repo, :id => repo.id).returns(stub(:run => repo)) + user.permissions.create!(:repository_id => repo.id, :admin => true) + end + + describe 'given the request is authorized' do + it 'regenerates the key' do + repo.expects(:regenerate_key!) + service.run.should == repo.reload.key + end + end + + describe 'given the request is not authorized' do + it 'does not regenerate key' do + user.permissions.destroy_all + repo.expects(:regenerate_key!).never + service.run.should be_false + end + end +end diff --git a/core_specs/core/services/remove_log_spec.rb b/core_specs/core/services/remove_log_spec.rb new file mode 100644 index 00000000..40e128cf --- /dev/null +++ b/core_specs/core/services/remove_log_spec.rb @@ -0,0 +1,113 @@ +require 'spec_helper' + +describe Travis::Services::RemoveLog do + include Support::ActiveRecord + + let(:repo) { Factory(:repository) } + let(:job) { Factory(:test, repository: repo, state: :created) } + let(:user) { Factory(:user) } + let(:service) { described_class.new(user, params) } + let(:params) { { id: job.id, reason: 'Because reason!'} } + + context 'when job is not finished' do + before :each do + job.stubs(:finished?).returns false + user.stubs(:permission?).with(:push, anything).returns true + end + + it 'raises JobUnfinished error' do + lambda { + service.run + }.should raise_error Travis::JobUnfinished + end + end + + context 'when user does not have push permissions' do + before :each do + user.stubs(:permission?).with(:push, anything).returns false + end + + it 'raises AuthorizationDenied' do + lambda { + service.run + }.should raise_error Travis::AuthorizationDenied + end + end + + context 'when a job is found' do + before do + find_by_id = stub + find_by_id.stubs(:find_by_id).returns job + job.stubs(:finished?).returns true + service.stubs(:scope).returns find_by_id + user.stubs(:permission?).with(:push, anything).returns true + end + + it 'runs successfully' do + result = service.run + result.removed_by.should == user + result.removed_at.should be_true + result.should be_true + end + + it "updates logs with desired information" do + service.run + service.log.content.should =~ Regexp.new(user.name) + service.log.content.should =~ Regexp.new(params[:reason]) + end + + it "uses a log part for storing the content" do + service.run + service.log.parts.first.content.should =~ Regexp.new(user.name) + service.log.parts.first.content.should =~ Regexp.new(params[:reason]) + end + + context 'when log is already removed' do + it 'raises LogAlreadyRemoved error' do + service.run + lambda { + service.run + }.should raise_error Travis::LogAlreadyRemoved + end + end + end + + context 'when a job is not found' do + before :each do + find_by_id = stub + find_by_id.stubs(:find_by_id).raises(ActiveRecord::SubclassNotFound) + service.stubs(:scope).returns(find_by_id) + end + + it 'raises ActiveRecord::RecordNotFound exception' do + lambda { service.run }.should raise_error(ActiveRecord::RecordNotFound) + end + end + +end + +describe Travis::Services::RemoveLog::Instrument do + include Support::ActiveRecord + + let(:service) { Travis::Services::RemoveLog.new(user, params) } + let(:repo) { Factory(:repository) } + let(:user) { Factory(:user) } + let(:job) { Factory(:test, repository: repo, state: :passed) } + let(:params) { { id: job.id, reason: 'Because Science!' } } + let(:publisher) { Travis::Notification::Publisher::Memory.new } + let(:event) { publisher.events.last } + + before :each do + Travis::Notification.publishers.replace([publisher]) + service.stubs(:run_service) + user.stubs(:permission?).with(:push, anything).returns true + end + + it 'publishes a event' do + service.run + event.should publish_instrumentation_event( + event: 'travis.services.remove_log.run:completed', + message: "Travis::Services::RemoveLog#run:completed for (svenfuchs)", + ) + end +end diff --git a/core_specs/core/services/reset_model_spec.rb b/core_specs/core/services/reset_model_spec.rb new file mode 100644 index 00000000..c738ec49 --- /dev/null +++ b/core_specs/core/services/reset_model_spec.rb @@ -0,0 +1,96 @@ +require 'spec_helper' + +describe Travis::Services::ResetModel do + include Support::ActiveRecord + + let(:user) { User.first || Factory(:user) } + + before :each do + Travis.config.roles = {} + end + + describe 'given a job_id' do + let(:service) { described_class.new(user, job_id: job.id, token: 'token') } + let(:job) { Factory(:test, state: :passed) } + + before :each do + service.stubs(:service).with(:find_job, id: job.id).returns(stub(run: job)) + end + + it 'resets the job' do + user.permissions.create!(repository_id: job.repository_id, pull: true) + job.expects(:reset!) + service.run + end + + it 'has message: all cool' do + user.permissions.create!(repository_id: job.repository_id, pull: true) + service.run + service.messages.should == [{ notice: 'The job was successfully restarted.' }] + end + + it 'has message: missing permissions and can not be enqueued' do + job.stubs(:resetable?).returns(false) + service.run + service.messages.should == [ + { error: 'You do not seem to have sufficient permissions.' }, + { error: 'This job currently can not be restarted.' } + ] + end + end + + describe 'given a build_id' do + let(:service) { described_class.new(user, build_id: build.id, token: 'token') } + let(:build) { Factory(:build, state: 'passed') } + + before :each do + service.stubs(:service).with(:find_build, id: build.id).returns(stub(run: build)) + end + + it 'resets the build (given no roles configuration and the user has permissions)' do + user.permissions.create!(repository_id: build.repository_id, pull: true) + build.expects(:reset!) + service.run + end + + it 'resets the build (given roles configuration and the user has permissions)' do + Travis.config.roles.reset_model = 'push' + user.permissions.create!(repository_id: build.repository_id, push: true) + build.expects(:reset!) + service.run + end + + it 'does not reset the build (given no roles configuration and the user does not have permissions)' do + build.expects(:reset!).never + service.run + end + + it 'does not reset the build (given roles configuration and the user does not have permissions)' do + Travis.config.roles.reset_model = 'push' + build.expects(:reset!).never + service.run + end + + describe 'Instrument' do + let(:publisher) { Travis::Notification::Publisher::Memory.new } + let(:event) { publisher.events.last } + + before :each do + Travis::Notification.publishers.replace([publisher]) + end + + it 'publishes a event' do + service.run + event.should publish_instrumentation_event( + event: 'travis.services.reset_model.run:completed', + message: "Travis::Services::ResetModel#run:completed build_id=#{build.id} not accepted", + data: { + type: :build, + id: build.id, + accept?: false + } + ) + end + end + end +end diff --git a/core_specs/core/services/sync_user_spec.rb b/core_specs/core/services/sync_user_spec.rb new file mode 100644 index 00000000..2c8a284f --- /dev/null +++ b/core_specs/core/services/sync_user_spec.rb @@ -0,0 +1,36 @@ +require 'spec_helper' + +describe Travis::Services::SyncUser do + include Travis::Testing::Stubs + + let(:publisher) { stub('publisher', :publish => true) } + let(:service) { described_class.new(user, {}) } + + describe 'given the user is not currently syncing' do + before :each do + user.stubs(:update_column) + user.stubs(:syncing?).returns(false) + end + + it 'enqueues a sync job' do + Travis::Sidekiq::SynchronizeUser.expects(:perform_async).with(user.id) + service.run + end + + it 'sets the user to syncing' do + user.expects(:update_column).with(:is_syncing, true) + service.run + end + end + + describe 'given the user is currently syncing' do + before :each do + user.stubs(:syncing?).returns(true) + end + + it 'does not set the user to syncing' do + user.expects(:update_column).never + service.run + end + end +end diff --git a/core_specs/core/services/update_annotation_spec.rb b/core_specs/core/services/update_annotation_spec.rb new file mode 100644 index 00000000..fe94a23b --- /dev/null +++ b/core_specs/core/services/update_annotation_spec.rb @@ -0,0 +1,74 @@ +require 'spec_helper' + +describe Travis::Services::UpdateAnnotation do + include Support::ActiveRecord + + let(:annotation_provider) { Factory(:annotation_provider) } + let(:job) { Factory(:test) } + let(:service) { described_class.new(params) } + let(:repository) { Factory(:repository) } + + attr_reader :params + + context 'when annotation is enabled' do + before :each do + job.stubs(:repository).returns(repository) + Travis::Features.stubs(:active?).returns(true) + end + + it 'creates the annotation if it doesn\'t exist already' do + @params = { + username: annotation_provider.api_username, + key: annotation_provider.api_key, + job_id: job.id, + description: 'Foo bar baz', + } + + expect { + @annotation = service.run + }.to change(Annotation, :count).by(1) + @annotation.description.should eq(params[:description]) + end + + it 'updates an existing annotation if one exists' do + @params = { + username: annotation_provider.api_username, + key: annotation_provider.api_key, + job_id: job.id, + description: 'Foo bar baz', + } + + annotation = Factory(:annotation, annotation_provider: annotation_provider, job: job) + service.run.id.should eq(annotation.id) + end + end + + context 'when annotation is disabled' do + before :each do + job.stubs(:repository).returns(repository) + Travis::Features.stubs(:active?).returns(false) + end + + it 'returns nil' do + @params = { + username: annotation_provider.api_username, + key: annotation_provider.api_key, + job_id: job.id, + description: 'Foo bar baz', + } + + service.run.should be_nil + end + end + + it 'returns nil when given invalid provider credentials' do + @params = { + username: 'some-invalid-provider', + key: 'some-invalid-key', + job_id: job.id, + description: 'Foo bar baz', + } + + service.run.should be_nil + end +end diff --git a/core_specs/core/services/update_hook_spec.rb b/core_specs/core/services/update_hook_spec.rb new file mode 100644 index 00000000..a5dac014 --- /dev/null +++ b/core_specs/core/services/update_hook_spec.rb @@ -0,0 +1,76 @@ +require 'spec_helper' + +describe Travis::Services::UpdateHook do + include Travis::Testing::Stubs + + let(:service) { described_class.new(user, params) } + let(:params) { { id: repo.id, active: true } } + + before :each do + repo.stubs(:update_column) + service.stubs(:run_service) + user.stubs(:service_hook).returns(repo) + end + + it 'finds the repo by the given params' do + user.expects(:service_hook).with(id: repo.id).returns(repo) + service.run + end + + it 'sets the given :active param to the hook' do + service.expects(:run_service).with(:github_set_hook, is_a(Hash)) + service.run + end + + describe 'sets the repo to the active param' do + it 'given true' do + service.params.update(active: true) + repo.expects(:update_column).with(:active, true) + service.run + end + + it 'given false' do + service.params.update(active: false) + repo.expects(:update_column).with(:active, false) + service.run + end + + it 'given "true"' do + service.params.update(active: 'true') + repo.expects(:update_column).with(:active, true) + service.run + end + + it 'given "false"' do + service.params.update(active: 'false') + repo.expects(:update_column).with(:active, false) + service.run + end + end +end + +describe Travis::Services::UpdateHook::Instrument do + include Travis::Testing::Stubs + + let(:service) { Travis::Services::UpdateHook.new(user, params) } + let(:params) { { id: repository.id, active: 'true' } } + let(:publisher) { Travis::Notification::Publisher::Memory.new } + let(:event) { publisher.events.last } + + before :each do + Travis::Notification.publishers.replace([publisher]) + service.stubs(:run_service) + user.stubs(:service_hook).returns(repo) + repo.stubs(:update_column).returns(true) + end + + it 'publishes a event' do + service.run + event.should publish_instrumentation_event( + event: 'travis.services.update_hook.run:completed', + message: 'Travis::Services::UpdateHook#run:completed for svenfuchs/minimal active=true (svenfuchs)', + result: true + ) + end +end + diff --git a/core_specs/core/services/update_job_spec.rb b/core_specs/core/services/update_job_spec.rb new file mode 100644 index 00000000..5a49db5b --- /dev/null +++ b/core_specs/core/services/update_job_spec.rb @@ -0,0 +1,244 @@ +require 'spec_helper' + +describe Travis::Services::UpdateJob do + include Support::ActiveRecord + + let(:service) { described_class.new(event: event, data: payload) } + let(:payload) { WORKER_PAYLOADS["job:test:#{event}"].merge('id' => job.id) } + let(:build) { Factory(:build, state: :created, started_at: nil, finished_at: nil) } + let(:job) { Factory(:test, source: build, state: :started, started_at: nil, finished_at: nil) } + + before :each do + build.matrix.delete_all + end + + describe '#cancel_job_in_worker' do + let(:event) { :start } + + it 'sends cancel event to the worker' do + publisher = mock('publisher') + service.stubs(:publisher).returns(publisher) + + publisher.expects(:publish).with(type: 'cancel_job', job_id: job.id, source: 'update_job_service') + + service.cancel_job_in_worker + end + end + + describe 'event: receive' do + let(:event) { :receive } + + before :each do + job.repository.update_attributes(last_build_state: :passed) + end + + context 'when job is canceled' do + before { job.update_attribute(:state, :canceled) } + + it 'does not update state' do + service.expects(:cancel_job_in_worker) + + service.run + job.reload.state.should == 'canceled' + end + end + + it 'sets the job state to received' do + service.run + job.reload.state.should == 'received' + end + + it 'sets the job received_at' do + service.run + job.reload.received_at.to_s.should == '2011-01-01 00:02:00 UTC' + end + + it 'sets the job worker name' do + service.run + job.reload.worker.should == 'ruby3.worker.travis-ci.org:travis-ruby-4' + end + + it 'sets the build state to received' do + service.run + job.reload.source.state.should == 'received' + end + + it 'sets the build received_at' do + service.run + job.reload.source.received_at.to_s.should == '2011-01-01 00:02:00 UTC' + end + + it 'sets the build state to received' do + service.run + job.reload.source.state.should == 'received' + end + end + + + describe 'event: start' do + let(:event) { :start } + + before :each do + job.repository.update_attributes(last_build_state: :passed) + end + + context 'when job is canceled' do + before { job.update_attribute(:state, :canceled) } + + it 'does not update state' do + service.expects(:cancel_job_in_worker) + + service.run + job.reload.state.should == 'canceled' + end + end + + it 'sets the job state to started' do + service.run + job.reload.state.should == 'started' + end + + it 'sets the job started_at' do + service.run + job.reload.started_at.to_s.should == '2011-01-01 00:02:00 UTC' + end + + it 'sets the build state to started' do + service.run + job.reload.source.state.should == 'started' + end + + it 'sets the build started_at' do + service.run + job.reload.source.started_at.to_s.should == '2011-01-01 00:02:00 UTC' + end + + it 'sets the build state to started' do + service.run + job.reload.source.state.should == 'started' + end + + it 'sets the repository last_build_state to started' do + service.run + job.reload.repository.last_build_state.should == 'started' + end + + it 'sets the repository last_build_started_at' do + service.run + job.reload.repository.last_build_started_at.to_s.should == '2011-01-01 00:02:00 UTC' + end + end + + describe 'event: finish' do + let(:event) { :finish } + + before :each do + job.repository.update_attributes(last_build_state: :started) + end + + context 'when job is canceled' do + before { job.update_attribute(:state, :canceled) } + + it 'does not update state' do + service.expects(:cancel_job_in_worker) + + service.run + job.reload.state.should == 'canceled' + end + end + + it 'sets the job state to passed' do + service.run + job.reload.state.should == 'passed' + end + + it 'sets the job finished_at' do + service.run + job.reload.finished_at.to_s.should == '2011-01-01 00:03:00 UTC' + end + + it 'sets the build state to passed' do + service.run + job.reload.source.state.should == 'passed' + end + + it 'sets the build finished_at' do + service.run + job.reload.source.finished_at.to_s.should == '2011-01-01 00:03:00 UTC' + end + + it 'sets the repository last_build_state to passed' do + service.run + job.reload.repository.last_build_state.should == 'passed' + end + + it 'sets the repository last_build_finished_at' do + service.run + job.reload.repository.last_build_finished_at.to_s.should == '2011-01-01 00:03:00 UTC' + end + end + + describe 'compat' do + let(:event) { :finish } + + it 'swaps :result for :state (passed) if present' do + payload.delete(:state) + payload.merge!(result: 0) + service.data[:state].should == :passed + end + + it 'swaps :result for :state (failed) if present' do + payload.delete(:state) + payload.merge!(result: 1) + service.data[:state].should == :failed + end + end + + describe 'event: reset' do + let(:event) { :reset } + + before :each do + job.repository.update_attributes(last_build_state: :passed) + end + + it 'sets the job state to created' do + service.run + job.reload.state.should == 'created' + end + + it 'resets the job started_at' do + service.run + job.reload.started_at.should be_nil + end + + it 'resets the job worker name' do + service.run + job.reload.worker.should be_nil + end + + it 'resets the build state to started' do + service.run + job.reload.source.state.should == 'created' + end + + it 'resets the build started_at' do + service.run + job.reload.source.started_at.should be_nil + end + + it 'resets the build state to started' do + service.run + job.reload.source.state.should == 'created' + end + + it 'resets the repository last_build_state to started' do + service.run + job.reload.repository.last_build_state.should == 'created' + end + + it 'resets the repository last_build_started_at' do + service.run + job.reload.repository.last_build_started_at.should be_nil + end + end +end diff --git a/core_specs/core/services/update_log_spec.rb b/core_specs/core/services/update_log_spec.rb new file mode 100644 index 00000000..ebc0a5cd --- /dev/null +++ b/core_specs/core/services/update_log_spec.rb @@ -0,0 +1,37 @@ +require 'spec_helper' + +describe Travis::Services::UpdateLog do + include Travis::Testing::Stubs + + let(:service) { described_class.new(user, params) } + let(:params) { { id: log.id, archived_at: Time.now, archive_verified: true } } + + before :each do + log.stubs(:update_attributes).returns(true) + service.stubs(:run_service).with(:find_log, id: log.id).returns(log) + end + + it 'updates the log' do + log.expects(:update_attributes).with(archived_at: params[:archived_at], archive_verified: true) + service.run + end + + + describe 'the instrument' do + let(:publisher) { Travis::Notification::Publisher::Memory.new } + let(:event) { publisher.events.last } + + before :each do + Travis::Notification.publishers.replace([publisher]) + end + + it 'publishes a event' do + service.run + event.should publish_instrumentation_event( + event: 'travis.services.update_log.run:completed', + message: "Travis::Services::UpdateLog#run:completed for # params=#{params}", + result: true + ) + end + end +end diff --git a/core_specs/core/services/update_user_spec.rb b/core_specs/core/services/update_user_spec.rb new file mode 100644 index 00000000..9b31107b --- /dev/null +++ b/core_specs/core/services/update_user_spec.rb @@ -0,0 +1,25 @@ +require 'spec_helper' + +describe Travis::Services::UpdateUser do + include Travis::Testing::Stubs + + let(:service) { described_class.new(user, params) } + + before :each do + user.stubs(:update_attributes!) + end + + attr_reader :params + + it 'updates the locale if valid' do + @params = { :locale => 'en' } + user.expects(:update_attributes!).with(params) + service.run + end + + it 'does not update the locale if invalid' do + @params = { :locale => 'foo' } + user.expects(:update_attributes!).never + service.run + end +end diff --git a/core_specs/core/services_spec.rb b/core_specs/core/services_spec.rb new file mode 100644 index 00000000..55cb5124 --- /dev/null +++ b/core_specs/core/services_spec.rb @@ -0,0 +1,58 @@ +require 'spec_helper' + +module Test + module Services + extend Travis::Services::Registry + + class DoStuff < Travis::Services::Base + attr_reader :current_user, :params + + def initialize(current_user, params) + @current_user, @params = current_user, params + end + end + end + + class Foo + include Travis::Services::Helpers + end +end + +describe Travis::Services::Helpers do + include Travis::Testing::Stubs + + let(:object) { Test::Foo.new } + + before :each do + Travis.stubs(:services).returns(Test::Services) + Test::Services.add(:do_stuff, Test::Services::DoStuff) + end + + describe 'service' do + it 'given :foo as a type and :stuff as a name it returns an instance of Foo::Stuff' do + object.service(:do_stuff, {}).should be_instance_of(Test::Services::DoStuff) + end + + it 'passes the given user' do + object.service(:do_stuff, user).current_user.should == user + end + + it 'passes the given params' do + params = { some: :thing } + object.service(:do_stuff, params).params.should == params + end + + it 'defaults params to {}' do + object.service(:do_stuff).params.should == {} + end + + it 'defaults the user to the current user if the object responds to :current_user' do + object.stubs(:current_user).returns(user) + object.service(:do_stuff, {}).current_user.should == user + end + + it 'defaults the user to nil if the object does not respond to :current_user' do + object.service(:do_stuff, {}).current_user.should be_nil + end + end +end diff --git a/spec/support/gcs.rb b/spec/support/gcs.rb new file mode 100644 index 00000000..11ff98d6 --- /dev/null +++ b/spec/support/gcs.rb @@ -0,0 +1,42 @@ +require 'google/apis/storage_v1' + +module Support + module GCS + class FakeObject + attr_accessor :key, :size + def initialize(key, options = {}) + @key = key + @size = options[:size] || "0" + end + end + + class FakeService + def authorization=(auth) + true + end + + def list_objects(*args) + FakeObjects.new + end + end + + class FakeObjects + def items + [] + end + end + + class FakeAuthorization + end + + extend ActiveSupport::Concern + + included do + before :each do + ::Google::Apis::StorageV1::StorageService.stubs(:new).returns(gcs_storage) + ::Google::Auth::ServiceAccountCredentials.stubs(:make_creds).returns(FakeAuthorization.new) + end + let(:gcs_storage) { FakeService.new } + end + end +end \ No newline at end of file diff --git a/spec/support/s3.rb b/spec/support/s3.rb new file mode 100644 index 00000000..2439edf5 --- /dev/null +++ b/spec/support/s3.rb @@ -0,0 +1,48 @@ +require 's3' + +module Support + module S3 + class FakeObject + attr_accessor :key, :size + def initialize(key, options = {}) + @key = key + @size = options[:size] || "0" + end + end + + class FakeService + attr_reader :buckets + def initialize(bucket) + @buckets = [bucket] + @buckets.stubs(:find).returns(bucket) + end + end + + class FakeBucket + def initialize(objects) + @objects = Array(objects) + end + + def objects(params = {}) + params.each_key { |key| raise "cannot fake #{key}" unless key == :prefix } + prefix = params[:prefix] || "" + @objects.select { |o| o.key.start_with? prefix } + end + + def add(key, options = {}) + @objects << FakeObject.new(key, options) + end + + alias_method :<<, :add + end + + extend ActiveSupport::Concern + + included do + before(:each) { ::S3::Service.stubs(:new).returns(s3_service) } + let(:s3_service) { FakeService.new(s3_bucket) } + let(:s3_bucket) { FakeBucket.new(s3_objects) } + let(:s3_objects) { [] } + end + end +end \ No newline at end of file