Merge pull request #267 from travis-ci/rlh-are-remove-core

Move the travis-core dependancy into vendor
This commit is contained in:
Tyranja 2016-06-15 13:59:42 +02:00 committed by GitHub
commit 3cfac6a5c1
345 changed files with 20951 additions and 559 deletions

4
.gitignore vendored
View File

@ -6,8 +6,8 @@ config/skylight.yml
tmp/
log/
logs/
vendor
!vendor/travis-core/lib/travis/logs/
!vendor/travis-core/lib/travis/model/log/
.yardoc
.coverage

1
.rspec
View File

@ -1,3 +1,2 @@
--colour
--tty
--format documentation

View File

@ -1 +1 @@
2.1.5
2.2.3

View File

@ -2,7 +2,7 @@ language: ruby
sudo: false
rvm: 2.1.5
rvm: 2.2.3
env:
global:
@ -19,6 +19,3 @@ services:
before_script:
- 'RAILS_ENV=test bundle exec rake db:create --trace'
script:
- bundle exec rspec spec

View File

@ -3,11 +3,12 @@ gemspec
gem 's3', github: 'travis-ci/s3'
gem 'travis-core', github: 'travis-ci/travis-core'
gem 'travis-core', path: 'vendor'
gem 'travis-support', github: 'travis-ci/travis-support'
gem 'travis-amqp', github: 'travis-ci/travis-amqp'
gem 'travis-config', '~> 0.1.0'
gem 'travis-sidekiqs', github: 'travis-ci/travis-sidekiqs', require: nil
gem 'travis-sidekiqs', github: 'travis-ci/travis-sidekiqs'
gem 'travis-yaml', github: 'travis-ci/travis-yaml'
gem 'mustermann', github: 'rkh/mustermann'
gem 'sinatra'
@ -17,7 +18,7 @@ gem 'active_model_serializers'
gem 'unicorn'
gem 'sentry-raven'
gem 'yard-sinatra', github: 'rkh/yard-sinatra'
gem 'rack-contrib', github: 'rack/rack-contrib'
gem 'rack-contrib'
gem 'rack-cache', github: 'rtomayko/rack-cache'
gem 'rack-attack'
gem 'gh'

View File

@ -1,20 +1,13 @@
GIT
remote: git://github.com/eric/metriks-librato_metrics.git
revision: ccbeb751ec5fc4edfe446d8a67a423b96ebe86c7
revision: 2c124f024fd2e34378260cd24b0e8687ff9bd196
specs:
metriks-librato_metrics (1.0.2)
metriks-librato_metrics (1.0.5)
metriks (>= 0.9.9.6)
GIT
remote: git://github.com/rack/rack-contrib.git
revision: 1b11346d729efd88b274cd7f704e0bca9eb3de7a
specs:
rack-contrib (1.2.0)
rack (>= 0.9.1)
GIT
remote: git://github.com/rkh/mustermann.git
revision: fa22e2cf4cfdb57f452c366eac66f241827b7e9c
revision: 9611951c5c789ad8227a740ae142c157a8bf7401
specs:
mustermann (0.4.0)
tool (~> 0.2)
@ -42,13 +35,66 @@ GIT
GIT
remote: git://github.com/travis-ci/travis-amqp.git
revision: c388299757b7eda2cc0e33cdc7d90113cf283e6a
revision: 3966b3651adfd1c544f53d49d5986ac16cd9c4bc
specs:
travis-amqp (0.0.1)
GIT
remote: git://github.com/travis-ci/travis-core.git
revision: 13061d1474d7b4a71cf7889216410ec3c1d43304
remote: git://github.com/travis-ci/travis-migrations.git
revision: dc432e45354287c617c3ae07a72e9e3c4be012cd
specs:
travis-migrations (0.0.2)
GIT
remote: git://github.com/travis-ci/travis-sidekiqs.git
revision: c5d4a4abc6c3737f9c43d3333efb94daa18b9fbb
specs:
travis-sidekiqs (0.0.1)
redis-namespace
sidekiq
GIT
remote: git://github.com/travis-ci/travis-support.git
revision: 2cd02d2a06fdd1e2fc2f129148c168b56f7c190f
specs:
travis-support (0.0.1)
GIT
remote: git://github.com/travis-ci/travis-yaml.git
revision: 032caed23af8ed1ed55e9204bb91316f3ada2f74
specs:
travis-yaml (0.2.0)
PATH
remote: .
specs:
travis-api (0.0.1)
actionmailer (~> 3.2.19)
activerecord (~> 3.2.19)
coder (~> 0.4.0)
composite_primary_keys (~> 5.0)
google-api-client (~> 0.9.4)
hashr
memcachier
multi_json
mustermann (~> 0.4)
pg
pusher (~> 0.14.0)
rack-contrib (~> 1.1)
rack-ssl (~> 1.3, >= 1.3.3)
railties (~> 3.2.19)
redcarpet (~> 2.1)
redis (~> 3.0)
rollout (~> 1.1.0)
simple_states (~> 1.0.0)
sinatra (~> 1.3)
sinatra-contrib (~> 1.3)
travis-support
useragent
virtus (~> 1.0.0)
PATH
remote: vendor
specs:
travis-core (0.0.1)
actionmailer (~> 3.2.19)
@ -71,48 +117,6 @@ GIT
travis-config (~> 0.1.0)
virtus (~> 1.0.0)
GIT
remote: git://github.com/travis-ci/travis-migrations.git
revision: bf360857ef7830f7e3ff12de181ab58c33fb29f1
specs:
travis-migrations (0.0.1)
GIT
remote: git://github.com/travis-ci/travis-sidekiqs.git
revision: 21a2fee158e25252dd78f5fa31e81b4f6583be23
specs:
travis-sidekiqs (0.0.1)
sidekiq
GIT
remote: git://github.com/travis-ci/travis-support.git
revision: 2cd02d2a06fdd1e2fc2f129148c168b56f7c190f
specs:
travis-support (0.0.1)
GIT
remote: git://github.com/travis-ci/travis-yaml.git
revision: 032caed23af8ed1ed55e9204bb91316f3ada2f74
specs:
travis-yaml (0.2.0)
PATH
remote: .
specs:
travis-api (0.0.1)
composite_primary_keys (~> 5.0)
memcachier
mustermann (~> 0.4)
pg
rack-contrib (~> 1.1)
rack-ssl (~> 1.3, >= 1.3.3)
redcarpet (~> 2.1)
sinatra (~> 1.3)
sinatra-contrib (~> 1.3)
travis-core
travis-support
useragent
GEM
remote: https://rubygems.org/
specs:
@ -129,7 +133,7 @@ GEM
rack-cache (~> 1.2)
rack-test (~> 0.6.1)
sprockets (~> 2.2.1)
active_model_serializers (0.9.0)
active_model_serializers (0.9.5)
activemodel (>= 3.2)
activemodel (3.2.22.2)
activesupport (= 3.2.22.2)
@ -153,19 +157,17 @@ GEM
backports (3.6.8)
builder (3.0.4)
bunny (0.8.0)
celluloid (0.16.0)
timers (~> 4.0.0)
coder (0.4.0)
coderay (1.1.0)
coderay (1.1.1)
coercible (1.0.0)
descendants_tracker (~> 0.0.1)
composite_primary_keys (5.0.14)
activerecord (~> 3.2.0, >= 3.2.9)
connection_pool (2.1.1)
customerio (0.6.1)
httparty (>= 0.5, < 0.12)
concurrent-ruby (1.0.2)
connection_pool (2.2.0)
customerio (1.0.0)
multi_json (~> 1.0)
dalli (2.7.2)
dalli (2.7.6)
data_migrations (0.0.1)
activerecord
rake
@ -174,17 +176,15 @@ GEM
thread_safe (~> 0.3, >= 0.3.1)
diff-lcs (1.2.5)
docile (1.1.5)
dotenv (0.7.0)
equalizer (0.0.11)
erubis (2.7.0)
factory_girl (2.4.2)
activesupport
faraday (0.9.2)
multipart-post (>= 1.2, < 3)
ffi (1.9.6)
foreman (0.64.0)
dotenv (~> 0.7.0)
thor (>= 0.13.6)
ffi (1.9.10)
foreman (0.82.0)
thor (~> 0.19.1)
gh (0.14.0)
addressable
backports
@ -192,7 +192,8 @@ GEM
multi_json (~> 1.0)
net-http-persistent (>= 2.7)
net-http-pipeline
google-api-client (0.9.6)
git-version-bump (0.15.1)
google-api-client (0.9.8)
addressable (~> 2.3)
googleauth (~> 0.5)
httpclient (~> 2.7)
@ -213,9 +214,6 @@ GEM
hashr (0.0.22)
hike (1.2.3)
hitimes (1.2.4)
httparty (0.11.0)
multi_json (~> 1.0)
multi_xml (>= 0.5.2)
httpclient (2.8.0)
hurley (0.2)
i18n (0.7.0)
@ -224,11 +222,11 @@ GEM
journey (1.0.4)
json (1.8.3)
jwt (1.5.4)
kgio (2.9.2)
listen (1.0.3)
rb-fsevent (>= 0.9.3)
rb-inotify (>= 0.9)
rb-kqueue (>= 0.2)
kgio (2.10.0)
listen (3.1.5)
rb-fsevent (~> 0.9, >= 0.9.4)
rb-inotify (~> 0.9, >= 0.9.7)
ruby_dep (~> 1.2)
little-plugger (1.1.4)
logging (2.1.0)
little-plugger (~> 1.1)
@ -250,16 +248,15 @@ GEM
mime-types (1.25.1)
mocha (0.14.0)
metaclass (~> 0.0.1)
multi_json (1.12.0)
multi_xml (0.5.5)
multi_json (1.12.1)
multipart-post (2.0.0)
net-http-persistent (2.9.4)
net-http-pipeline (1.0.1)
os (0.9.6)
pg (0.18.2)
pg (0.18.4)
polyglot (0.3.5)
proxies (0.2.1)
pry (0.10.1)
pry (0.10.3)
coderay (~> 1.1.0)
method_source (~> 0.8.1)
slop (~> 3.4)
@ -269,8 +266,11 @@ GEM
pusher-signature (~> 0.1.8)
pusher-signature (0.1.8)
rack (1.4.7)
rack-attack (4.2.0)
rack-attack (4.4.1)
rack
rack-contrib (1.4.0)
git-version-bump (~> 0.15)
rack (~> 1.4)
rack-protection (1.5.3)
rack
rack-ssl (1.3.4)
@ -284,23 +284,21 @@ GEM
rake (>= 0.8.7)
rdoc (~> 3.4)
thor (>= 0.14.6, < 2.0)
raindrops (0.13.0)
raindrops (0.16.0)
rake (0.9.6)
rb-fsevent (0.9.4)
rb-inotify (0.9.5)
ffi (>= 0.5.0)
rb-kqueue (0.2.3)
rb-fsevent (0.9.7)
rb-inotify (0.9.7)
ffi (>= 0.5.0)
rdoc (3.12.2)
json (~> 1.4)
redcarpet (2.3.0)
redis (3.3.0)
redis-namespace (1.5.1)
redis-namespace (1.5.2)
redis (~> 3.0, >= 3.0.4)
representable (2.3.0)
uber (~> 0.0.7)
rerun (0.8.2)
listen (~> 1.0.3)
rerun (0.11.0)
listen (~> 3.0)
retriable (2.1.0)
rollout (1.1.0)
rspec (2.99.0)
@ -313,15 +311,14 @@ GEM
rspec-its (1.0.1)
rspec-core (>= 2.99.0.beta1)
rspec-expectations (>= 2.99.0.beta1)
rspec-mocks (2.99.2)
sentry-raven (0.15.3)
rspec-mocks (2.99.4)
ruby_dep (1.3.1)
sentry-raven (1.0.0)
faraday (>= 0.7.6)
sidekiq (3.3.0)
celluloid (>= 0.16.0)
connection_pool (>= 2.0.0)
json
redis (>= 3.0.6)
redis-namespace (>= 1.3.1)
sidekiq (4.1.2)
concurrent-ruby (~> 1.0)
connection_pool (~> 2.2, >= 2.2.0)
redis (~> 3.2, >= 3.2.1)
signet (0.7.2)
addressable (~> 2.3)
faraday (~> 0.9)
@ -330,23 +327,23 @@ GEM
simple_states (1.0.1)
activesupport
hashr (~> 0.0.10)
simplecov (0.9.1)
simplecov (0.11.2)
docile (~> 1.1.0)
multi_json (~> 1.0)
simplecov-html (~> 0.8.0)
simplecov-html (0.8.0)
json (~> 1.8)
simplecov-html (~> 0.10.0)
simplecov-html (0.10.0)
sinatra (1.4.6)
rack (~> 1.4)
rack-protection (~> 1.4)
tilt (>= 1.3, < 3)
sinatra-contrib (1.4.2)
sinatra-contrib (1.4.7)
backports (>= 2.0)
multi_json
rack-protection
rack-test
sinatra (~> 1.4.0)
tilt (~> 1.3)
skylight (0.6.0.beta.1)
tilt (>= 1.3, < 3)
skylight (0.6.2.beta.2)
activesupport (>= 3.0.0)
slop (3.6.0)
sprockets (2.2.3)
@ -354,13 +351,11 @@ GEM
multi_json (~> 1.0)
rack (~> 1.0)
tilt (~> 1.1, != 1.3.0)
stackprof (0.2.7)
stackprof (0.2.9)
thor (0.19.1)
thread_safe (0.3.5)
tilt (1.4.1)
timecop (0.8.0)
timers (4.0.1)
hitimes
timecop (0.8.1)
tool (0.2.3)
travis-config (0.1.4)
hashr (~> 0.0)
@ -369,11 +364,10 @@ GEM
polyglot (>= 0.3.1)
tzinfo (0.3.49)
uber (0.0.15)
unicorn (4.8.3)
unicorn (5.1.0)
kgio (~> 2.6)
rack
raindrops (~> 0.7)
useragent (0.13.3)
useragent (0.16.7)
virtus (1.0.5)
axiom-types (~> 0.1)
coercible (~> 1.0)
@ -402,7 +396,7 @@ DEPENDENCIES
pry
rack-attack
rack-cache!
rack-contrib!
rack-contrib
rake (~> 0.9.2)
rb-fsevent (~> 0.9.1)
rerun
@ -426,3 +420,6 @@ DEPENDENCIES
travis-yaml!
unicorn
yard-sinatra!
BUNDLED WITH
1.12.5

View File

@ -59,7 +59,7 @@ $ popd
### Run tests
```sh-session
$ bundle exec rspec
$ bundle exec rake
```
### Run the server
```sh-session

View File

@ -9,6 +9,20 @@ namespace :db do
end
end
begin
require 'rspec'
require 'rspec/core/rake_task'
RSpec::Core::RakeTask.new(:spec)
RSpec::Core::RakeTask.new(:spec_core) do |t|
t.pattern = 'spec_core/**{,/*/**}/*_spec.rb'
end
task :default => [:spec, :spec_core]
rescue LoadError => e
puts e.inspect
end
desc "generate gemspec"
task 'travis-api.gemspec' do
content = File.read 'travis-api.gemspec'

View File

@ -2,7 +2,7 @@ module Travis::API::V3
class Result
attr_accessor :access_control, :type, :resource, :status, :href, :meta_data, :warnings
def initialize(access_control, type, resource = [], status: 200, **meta_data)
def initialize(access_control, type, resource, status: 200, **meta_data)
@warnings = []
@access_control, @type, @resource, @status, @meta_data = access_control, type, resource, status, meta_data
end
@ -20,11 +20,6 @@ module Travis::API::V3
warn(message, warning_type: :ignored_parameter, parameter: param, **info)
end
def <<(value)
resource << value
self
end
def render(params, env)
href = self.href
href = V3.location(env) if href.nil? and env['REQUEST_METHOD'.freeze] == 'GET'.freeze

View File

@ -1,11 +0,0 @@
require 'spec_helper'
describe 'Branches' do
let(:repo) { Repository.by_slug('svenfuchs/minimal').first }
let(:headers) { { 'HTTP_ACCEPT' => 'application/vnd.travis-ci.1+json' } }
it 'GET /branches?repository_id=:repository_id' do
response = get '/branches', { repository_id: repo.id }, headers
response.should deliver_json_for(repo.last_finished_builds_by_branches, version: 'v1', type: 'branches')
end
end

View File

@ -1,32 +0,0 @@
require 'spec_helper'
describe 'Builds' do
let(:repo) { Repository.by_slug('svenfuchs/minimal').first }
let(:build) { repo.builds.first }
let(:headers) { { 'HTTP_ACCEPT' => 'application/vnd.travis-ci.1+json' } }
it 'GET /builds.json?repository_id=1' do
response = get '/builds.json', { repository_id: repo.id }, headers
response.should deliver_json_for(repo.builds.order('id DESC'), version: 'v1')
end
it 'GET /builds/1.json' do
response = get "/builds/#{build.id}.json", {}, headers
response.should deliver_json_for(build, version: 'v1')
end
it 'GET /builds/1?repository_id=1.json' do
response = get "/builds/#{build.id}.json", { repository_id: repo.id }, headers
response.should deliver_json_for(build, version: 'v1')
end
it 'GET /svenfuchs/minimal/builds.json' do
response = get '/svenfuchs/minimal/builds.json', {}, headers
response.should redirect_to('/repositories/svenfuchs/minimal/builds.json')
end
it 'GET /svenfuchs/minimal/builds/1.json' do
response = get "/svenfuchs/minimal/builds/#{build.id}.json", {}, headers
response.should redirect_to("/repositories/svenfuchs/minimal/builds/#{build.id}.json")
end
end

View File

@ -1,17 +0,0 @@
require 'spec_helper'
describe 'Hooks' do
before(:each) do
user.permissions.create repository: repo, admin: true
end
let(:user) { User.where(login: 'svenfuchs').first }
let(:repo) { Repository.first }
let(:token) { Travis::Api::App::AccessToken.create(user: user, app_id: -1) }
let(:headers) { { 'HTTP_ACCEPT' => 'application/vnd.travis-ci.1+json', 'HTTP_AUTHORIZATION' => "token #{token}" } }
it 'GET /hooks' do
response = get '/hooks', {}, headers
response.should deliver_json_for(user.service_hooks, version: 'v1', type: 'hooks')
end
end

View File

@ -1,20 +0,0 @@
require 'spec_helper'
describe 'Jobs' do
let!(:jobs) {[
FactoryGirl.create(:test, :number => '3.1', :queue => 'builds.common'),
FactoryGirl.create(:test, :number => '3.2', :queue => 'builds.common')
]}
let(:job) { jobs.first }
let(:headers) { { 'HTTP_ACCEPT' => 'application/vnd.travis-ci.1+json' } }
it '/jobs?queue=builds.common' do
response = get '/jobs', { queue: 'builds.common' }, headers
response.should deliver_json_for(Job.queued('builds.common'), version: 'v1')
end
it '/jobs/:job_id' do
response = get "/jobs/#{job.id}", {}, headers
response.should deliver_json_for(job, version: 'v1')
end
end

View File

@ -1,119 +0,0 @@
require 'spec_helper'
describe 'v1 repos' do
let(:repo) { Repository.by_slug('svenfuchs/minimal').first }
let(:headers) { { 'HTTP_ACCEPT' => 'application/vnd.travis-ci.1+json' } }
it 'GET /repositories.json' do
response = get '/repositories.json', {}, headers
response.should deliver_json_for(Repository.timeline, version: 'v1')
end
it 'GET /repositories.json?owner_name=svenfuchs' do
response = get '/repositories.json', { owner_name: 'svenfuchs' }, headers
response.should deliver_json_for(Repository.by_owner_name('svenfuchs'), version: 'v1')
end
it 'GET /repositories.json?member=svenfuchs' do
response = get '/repositories.json', { member: 'svenfuchs' }, headers
response.should deliver_json_for(Repository.by_member('svenfuchs'), version: 'v1')
end
it 'GET /repositories.json?slug=svenfuchs/name=minimal' do
response = get '/repositories.json', { slug: 'svenfuchs/minimal' }, headers
response.should deliver_json_for(Repository.by_slug('svenfuchs/minimal'), version: 'v1')
end
it 'GET /repositories/1.json' do
response = get "repositories/#{repo.id}.json", {}, headers
response.should deliver_json_for(Repository.by_slug('svenfuchs/minimal').first, version: 'v1')
end
it 'GET /svenfuchs/minimal.json' do
response = get '/svenfuchs/minimal.json', {}, headers
response.should redirect_to('/repositories/svenfuchs/minimal.json')
end
it 'GET /svenfuchs/minimal' do
response = get '/svenfuchs/minimal.json', {}, 'HTTP_ACCEPT' => 'application/json; version=2'
response.status.should == 404
end
it 'GET /svenfuchs/minimal/cc.xml' do
response = get '/svenfuchs/minimal/cc.xml'
response.should redirect_to('/repositories/svenfuchs/minimal/cc.xml')
end
describe 'GET /svenfuchs/minimal.png' do
it '"unknown" when it only has one build that is not finished' do
Build.delete_all
Factory(:build, repository: repo, state: :created)
repo.builds.update_all(state: 'started')
get('/svenfuchs/minimal.png').should deliver_result_image_for('unknown')
end
it '"failing" when the last build has failed' do
repo.builds.update_all(state: 'failed')
get('/svenfuchs/minimal.png').should deliver_result_image_for('failing')
end
it '"passing" when the last build has passed' do
repo.builds.update_all(state: 'passed')
get('/svenfuchs/minimal.png').should deliver_result_image_for('passing')
end
it '"passing" when there is a running build but the previous one has passed' do
Factory(:build, repository: repo, state: :passed, previous_state: :passed)
repo.update_attributes!(last_build_state: 'started')
get('/svenfuchs/minimal.png').should deliver_result_image_for('passing')
end
end
describe 'GET /svenfuchs/minimal.png?branch=foo,bar' do
let(:on_foo) { Factory(:commit, branch: 'foo') }
let(:on_bar) { Factory(:commit, branch: 'bar') }
it '"unknown" when it only has unfinished builds on the relevant branches' do
Build.delete_all
Factory(:build, repository: repo, state: :started, commit: on_foo)
Factory(:build, repository: repo, state: :started, commit: on_bar)
get('/svenfuchs/minimal.png?branch=foo,bar').should deliver_result_image_for('unknown')
end
it '"failing" when the last build has failed' do
Factory(:build, repository: repo, state: :failed, commit: on_foo)
Factory(:build, repository: repo, state: :failed, commit: on_bar)
get('/svenfuchs/minimal.png?branch=foo,bar').should deliver_result_image_for('failing')
end
it '"passing" when the last build has passed' do
Factory(:build, repository: repo, state: :failed, commit: on_foo)
Factory(:build, repository: repo, state: :passed, commit: on_bar)
get('/svenfuchs/minimal.png?branch=foo,bar').should deliver_result_image_for('passing')
end
it '"passing" when there is a running build but the previous one has passed' do
Factory(:build, repository: repo, state: :passed, commit: on_foo)
Factory(:build, repository: repo, state: :passed, commit: on_bar)
Factory(:build, repository: repo, state: :started, commit: on_bar)
repo.update_attributes!(last_build_state: 'started')
get('/svenfuchs/minimal.png?branch=foo,bar').should deliver_result_image_for('passing')
end
end
context 'with "Accept: application/atom+xml" header' do
let(:headers) { { 'HTTP_ACCEPT' => 'application/atom+xml' } }
it 'GET /repositories/svenfuchs/minimal/builds' do
response = get '/repositories/svenfuchs/minimal/builds', {}, headers
response.content_type.should =~ /^application\/atom\+xml/
end
end
context 'with .atom extension and "Accept: */*" header' do
let(:headers) { { 'HTTP_ACCEPT' => '*/*' } }
it 'GET /repositories/svenfuchs/minimal/builds.atom' do
response = get '/repositories/svenfuchs/minimal/builds.atom', {}, headers
response.content_type.should =~ /^application\/atom\+xml/
end
end
end

View File

@ -1,174 +0,0 @@
require 'spec_helper'
describe 'v1' do
let(:format) { :json }
let(:params) { { :controller => controller, :action => action, :format => format } }
describe 'GET to repositories.json' do
let(:controller) { 'v1/repositories' }
let(:action) { :index }
it 'routes to V1::RepositoriesController#index' do
{ :get => 'repositories.json' }.should route_to(params)
end
end
describe 'GET to repositories/1.json' do
let(:controller) { 'v1/repositories' }
let(:action) { :show }
it 'routes to V1::RepositoriesController#show' do
{ :get => 'repositories/1.json' }.should route_to(params.merge(:id => 1))
end
end
describe 'GET to builds.json' do
let(:controller) { 'v1/builds' }
let(:action) { :index }
it 'routes to V1::BuildsController#index' do
{ :get => 'builds.json' }.should route_to(params)
end
end
describe 'GET to builds/1.json' do
let(:controller) { 'v1/builds' }
let(:action) { :show }
it 'routes to V1::BuildsController#show' do
{ :get => 'builds/1.json' }.should route_to(params.merge(:id => 1))
end
end
describe 'GET to branches.json' do
let(:controller) { 'v1/branches' }
let(:action) { :index }
it 'routes to V1::BranchesController#index' do
{ :get => 'branches.json' }.should route_to(params)
end
end
describe 'GET to jobs.json' do
let(:controller) { 'v1/jobs' }
let(:action) { :index }
it 'routes to V1::JobsController#index' do
{ :get => 'jobs.json' }.should route_to(params)
end
end
describe 'GET to jobs/1.json' do
let(:controller) { 'v1/jobs' }
let(:action) { :show }
it 'routes to V1::JobsController#show' do
{ :get => 'jobs/1.json' }.should route_to(params.merge(:id => 1))
end
end
describe 'GET to workers.json' do
let(:controller) { 'v1/workers' }
let(:action) { :index }
it 'routes to V1::WorkersController#index' do
{ :get => 'workers.json' }.should route_to(params)
end
end
describe 'GET to service_hooks.json' do
let(:controller) { 'v1/service_hooks' }
let(:action) { :index }
it 'routes to V1::RepositoriesController#index' do
{ :get => 'service_hooks.json' }.should route_to(params)
end
end
describe 'PUT to service_hooks.json' do
let(:controller) { 'v1/service_hooks' }
let(:action) { :update }
it 'routes to V1::RepositoriesController#update' do
hook_params = params.merge(:id => 'svenfuchs:minimal')
hook_params.delete(:format)
{ :put => 'service_hooks/svenfuchs:minimal' }.should route_to(hook_params)
end
end
describe 'GET to :owner_name/:name.json' do
let(:controller) { 'v1/repositories' }
let(:action) { :show }
it 'routes to V1::RepositoriesController#show' do
{ :get => 'owner/name.json' }.should route_to(params.merge(:owner_name => 'owner', :name => 'name'))
end
it 'routes to V1::RepositoriesController#show when owner contains dots' do
{ :get => 'some.owner/name.json' }.should route_to(params.merge(:owner_name => 'some.owner', :name => 'name'))
end
it 'routes to V1::RepositoriesController#show when repository name contains dots' do
{ :get => 'owner/some.name.json' }.should route_to(params.merge(:owner_name => 'owner', :name => 'some.name'))
end
it 'routes to V1::RepositoriesController#show when owner name and repository name contains dots' do
{ :get => 'some.owner/some.name.json' }.should route_to(params.merge(:owner_name => 'some.owner', :name => 'some.name'))
end
end
describe 'GET to :owner_name/:name.png' do
let(:controller) { 'v1/repositories' }
let(:action) { :show }
let(:format) { :png }
it 'routes to V1::RepositoriesController#show' do
{ :get => 'owner/name.png' }.should route_to(params.merge(:owner_name => 'owner', :name => 'name'))
end
it 'routes to V1::RepositoriesController#show when owner contains dots' do
{ :get => 'some.owner/name.png' }.should route_to(params.merge(:owner_name => 'some.owner', :name => 'name'))
end
it 'routes to V1::RepositoriesController#show when repository name contains dots' do
{ :get => 'owner/some.name.png' }.should route_to(params.merge(:owner_name => 'owner', :name => 'some.name'))
end
it 'routes to V1::RepositoriesController#show when owner name and repository name contains dots' do
{ :get => 'some.owner/some.name.png' }.should route_to(params.merge(:owner_name => 'some.owner', :name => 'some.name'))
end
end
describe 'GET to :owner_name/:name/cc.xml' do
let(:controller) { 'v1/repositories' }
let(:action) { :show }
let(:format) { :xml }
it 'routes to V1::RepositoriesController#show in XML format with the cctray schema' do
{ :get => 'owner/name/cc.xml' }.should route_to(params.merge(:owner_name => 'owner', :name => 'name', :schema => 'cctray'))
end
it 'routes to V1::RepositoriesController#show in XML format with the cctray schema when owner and repository name contains dots' do
{ :get => 'some.owner/some.name/cc.xml' }.should route_to(params.merge(:owner_name => 'some.owner', :name => 'some.name', :schema => 'cctray'))
end
end
describe 'GET to :owner_name/:name/builds.json' do
let(:controller) { 'v1/builds' }
let(:action) { :index }
it 'routes to V1::BuildsController#index' do
{ :get => 'owner/name/builds.json' }.should route_to(params.merge(:owner_name => 'owner', :name => 'name'))
end
end
describe 'GET to :owner_name/:name/builds/:id.json' do
let(:controller) { 'v1/builds' }
let(:action) { :show }
it 'routes to V1::BuildsController#show' do
{ :get => 'owner/name/builds/1.json' }.should route_to(params.merge(:owner_name => 'owner', :name => 'name', :id => 1))
end
end
end

View File

@ -18,6 +18,8 @@ require 'travis/testing/matchers'
require 'support/matchers'
require 'support/formats'
require 'pry'
Travis.logger = Logger.new(StringIO.new)
Travis::Api::App.setup
Travis.config.client_domain = "www.example.com"
@ -53,7 +55,7 @@ end
RSpec.configure do |c|
c.mock_framework = :mocha
c.expect_with :rspec, :test_unit
c.expect_with :rspec
c.include TestHelpers
c.before :suite do

59
spec/spec_helper_core.rb Normal file
View File

@ -0,0 +1,59 @@
ENV['RAILS_ENV'] = ENV['ENV'] = 'test'
require 'simplecov' unless RUBY_ENGINE == 'jruby'
RSpec.configure do |c|
c.before(:each) { Time.now.utc.tap { | now| Time.stubs(:now).returns(now) } }
end
require 'support'
require 'travis'
require 'travis/model'
require 'travis/states_cache'
require 'travis/testing'
require 'travis/support'
require 'travis/testing/matchers'
require 'gh'
require 'mocha/api'
require 'stringio'
require 'logger'
require 'patches/rspec_hash_diff'
Travis.logger = Logger.new(StringIO.new)
Travis.services = Travis::Services
ActionMailer::Base.delivery_method = :test
include Mocha::API
RSpec.configure do |c|
c.mock_with :mocha
c.alias_example_to :fit, :focused => true
c.filter_run :focus => true
c.run_all_when_everything_filtered = true
# c.backtrace_clean_patterns.clear
c.before :each do
Travis.logger.level = Logger::INFO
Travis::Event.instance_variable_set(:@queues, nil)
Travis::Event.instance_variable_set(:@subscriptions, nil)
Travis::Event.stubs(:subscribers).returns []
Travis.config.oauth2 ||= {}
Travis.config.oauth2.scope = 'public_repo,user'
Travis.config.repository.ssl_key.size = 1024
Travis::Github.stubs(:scopes_for).returns(['public_repo', 'user'])
GH.reset
end
end
# this keeps Model.inspect from exploding which happens for
# expected method calls in tests that do not use a db connection
require 'active_record'
ActiveRecord::Base.class_eval do
def self.inspect
super
end
end

15
spec/support.rb Normal file
View File

@ -0,0 +1,15 @@
require 'support/matchers'
require 'support/payloads'
module Support
autoload :ActiveRecord, 'support/active_record'
autoload :Formats, 'support/formats'
autoload :GCS, 'support/gcs'
autoload :Log, 'support/log'
autoload :Mocks, 'support/mocks'
autoload :Notifications, 'support/notifications'
autoload :Redis, 'support/redis'
autoload :S3, 'support/s3'
autoload :Silence, 'support/silence'
end

View File

@ -0,0 +1,47 @@
require 'active_record'
require 'logger'
require 'fileutils'
require 'database_cleaner'
require 'travis/testing/factories'
FileUtils.mkdir_p('log')
# TODO why not make this use Travis::Database.connect ?
config = Travis.config.database.to_h
config.merge!('adapter' => 'jdbcpostgresql', 'username' => ENV['USER']) if RUBY_PLATFORM == 'java'
ActiveRecord::Base.default_timezone = :utc
ActiveRecord::Base.logger = Logger.new('log/test.db.log')
ActiveRecord::Base.configurations = { 'test' => config }
ActiveRecord::Base.establish_connection('test')
DatabaseCleaner.clean_with :truncation
DatabaseCleaner.strategy = :transaction
module Support
module ActiveRecord
extend ActiveSupport::Concern
included do
before :suite do
DatabaseCleaner.clean_with(:truncation)
end
before :each do
DatabaseCleaner.strategy = :transaction
end
before(:each, :truncation => true) do
DatabaseCleaner.strategy = :truncation
end
before :each do
DatabaseCleaner.start
end
after :each do
DatabaseCleaner.clean
end
end
end
end

42
spec/support/gcs.rb Normal file
View File

@ -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

624
spec/support/payloads.rb Normal file
View File

@ -0,0 +1,624 @@
API_PAYLOADS = {
'custom' => {
'repository' => {
'owner_id' => 2208,
'owner_type' => 'User',
'owner_name' => 'svenfuchs',
'name' => 'gem-release'
},
'branch' => 'master',
'config' => {
'env' => ['FOO=foo', 'BAR=bar']
},
'user' => {
'id' => 1
}
}
}
GITHUB_PAYLOADS = {
"private-repo" => %({
"repository": {
"url": "http://github.com/svenfuchs/gem-release",
"name": "gem-release",
"private":true,
"owner": {
"email": "svenfuchs@artweb-design.de",
"name": "svenfuchs"
}
},
"commits": [{
"id": "9854592",
"message": "Bump to 0.0.15",
"timestamp": "2010-10-27T04:32:37Z",
"committer": {
"name": "Sven Fuchs",
"email": "svenfuchs@artweb-design.de"
},
"author": {
"name": "Christopher Floess",
"email": "chris@flooose.de"
}
}],
"ref": "refs/heads/master"
}),
"gem-release" => %({
"repository": {
"id": 100,
"url": "http://github.com/svenfuchs/gem-release",
"name": "gem-release",
"description": "Release your gems with ease",
"owner": {
"id": "2208",
"email": "svenfuchs@artweb-design.de",
"name": "svenfuchs"
}
},
"commits": [{
"id": "586374eac43853e5542a2e2faafd48047127e4be",
"message": "Update the readme",
"timestamp": "2010-10-14T04:00:37Z",
"committer": {
"name": "Sven Fuchs",
"email": "svenfuchs@artweb-design.de"
},
"author": {
"name": "Christopher Floess",
"email": "chris@flooose.de"
}
},{
"id": "46ebe012ef3c0be5542a2e2faafd48047127e4be",
"message": "Bump to 0.0.15",
"timestamp": "2010-10-27T04:32:37Z",
"committer": {
"name": "Sven Fuchs",
"email": "svenfuchs@artweb-design.de"
},
"author": {
"name": "Christopher Floess",
"email": "chris@flooose.de"
}
}],
"ref": "refs/heads/master",
"compare": "https://github.com/svenfuchs/gem-release/compare/af674bd...9854592"
}),
"skip-last" => %({
"repository": {
"url": "http://github.com/svenfuchs/gem-release",
"name": "gem-release",
"description": "Release your gems with ease",
"owner": {
"email": "svenfuchs@artweb-design.de",
"name": "svenfuchs"
}
},
"commits": [{
"id": "60aaa2faaa5fdbd87719a10e308d396b828e5a01",
"message": "Bump to 0.0.14",
"timestamp": "2010-10-12T08:47:06Z",
"committer": {
"name": "Sven Fuchs",
"email": "svenfuchs@artweb-design.de"
},
"author": {
"name": "Sven Fuchs",
"email": "svenfuchs@artweb-design.de"
}
},{
"id": "586374eac43853e5542a2e2faafd48047127e4be",
"message": "Update the readme",
"timestamp": "2010-10-14T04:00:37Z",
"committer": {
"name": "Sven Fuchs",
"email": "svenfuchs@artweb-design.de"
},
"author": {
"name": "Christopher Floess",
"email": "chris@flooose.de"
}
},{
"id": "46ebe012ef3c0be5542a2e2faafd48047127e4be",
"message": "Bump to 0.0.15\\n\\n[ci skip]",
"timestamp": "2010-10-27T04:32:37Z",
"committer": {
"name": "Sven Fuchs",
"email": "svenfuchs@artweb-design.de"
},
"author": {
"name": "Christopher Floess",
"email": "chris@flooose.de"
}
}],
"ref": "refs/heads/master",
"compare": "https://github.com/svenfuchs/gem-release/compare/af674bd...9854592"
}),
"skip-all" => %({
"repository": {
"url": "http://github.com/svenfuchs/gem-release",
"name": "gem-release",
"description": "Release your gems with ease",
"owner": {
"email": "svenfuchs@artweb-design.de",
"name": "svenfuchs"
}
},
"commits": [{
"id": "60aaa2faaa5fdbd87719a10e308d396b828e5a01",
"message": "Bump to 0.0.14\\n\\n[ci skip]",
"timestamp": "2010-10-12T08:47:06Z",
"committer": {
"name": "Sven Fuchs",
"email": "svenfuchs@artweb-design.de"
},
"author": {
"name": "Sven Fuchs",
"email": "svenfuchs@artweb-design.de"
}
},{
"id": "586374eac43853e5542a2e2faafd48047127e4be",
"message": "Update the readme\\n\\n[ci skip]",
"timestamp": "2010-10-14T04:00:37Z",
"committer": {
"name": "Sven Fuchs",
"email": "svenfuchs@artweb-design.de"
},
"author": {
"name": "Christopher Floess",
"email": "chris@flooose.de"
}
},{
"id": "46ebe012ef3c0be5542a2e2faafd48047127e4be",
"message": "Bump to 0.0.15\\n\\n[ci skip]",
"timestamp": "2010-10-27T04:32:37Z",
"committer": {
"name": "Sven Fuchs",
"email": "svenfuchs@artweb-design.de"
},
"author": {
"name": "Christopher Floess",
"email": "chris@flooose.de"
}
}],
"ref": "refs/heads/master",
"compare": "https://github.com/svenfuchs/gem-release/compare/af674bd...9854592"
}),
"travis-core" => %({
"repository": {
"id": 111,
"url": "http://github.com/travis-ci/travis-core",
"name": "travis-core",
"description": "description for travis-core",
"organization": "travis-ci",
"owner": {
"email": "contact@travis-ci.org",
"name": "travis-ci"
}
},
"commits": [{
"id": "46ebe012ef3c0be5542a2e2faafd48047127e4be",
"message": "Bump to 0.0.15",
"timestamp": "2010-10-27T04:32:37Z",
"committer": {
"name": "Sven Fuchs",
"email": "svenfuchs@artweb-design.de"
},
"author": {
"name": "Josh Kalderimis",
"email": "josh@email.org"
}
}],
"ref": "refs/heads/master",
"compare": "https://github.com/travis-ci/travis-core/compare/af674bd...9854592"
}),
"travis-core-no-commit" => %({
"repository": {
"url": "http://github.com/travis-ci/travis-core",
"name": "travis-core",
"description": "description for travis-core",
"organization": "travis-ci",
"owner": {
"email": "contact@travis-ci.org",
"name": "travis-ci"
}
},
"commits":[],
"ref": "refs/heads/master",
"compare": "https://github.com/travis-ci/travis-core/compare/af674bd...9854592"
}),
"gh-pages-update" => %({
"repository": {
"url": "http://github.com/svenfuchs/gem-release",
"name": "gem-release",
"owner": {
"email": "svenfuchs@artweb-design.de",
"name": "svenfuchs"
}
},
"commits": [{
"id": "9854592",
"message": "Bump to 0.0.15",
"timestamp": "2010-10-27T04:32:37Z",
"committer": {
"name": "Sven Fuchs",
"email": "svenfuchs@artweb-design.de"
},
"author": {
"name": "Christopher Floess",
"email": "chris@flooose.de"
}
}],
"ref": "refs/heads/gh-pages"
}),
"gh_pages-update" => %({
"repository": {
"url": "http://github.com/svenfuchs/gem-release",
"name": "gem-release",
"owner": {
"email": "svenfuchs@artweb-design.de",
"name": "svenfuchs"
}
},
"commits": [{
"id": "46ebe012ef3c0be5542a2e2faafd48047127e4be",
"message": "Bump to 0.0.15",
"timestamp": "2010-10-27T04:32:37Z",
"committer": {
"name": "Sven Fuchs",
"email": "svenfuchs@artweb-design.de"
},
"author": {
"name": "Christopher Floess",
"email": "chris@flooose.de"
}
}],
"ref": "refs/heads/gh_pages"
}),
# it is unclear why this payload was send but it happened quite often. the force option
# seems to indicate something like $ git push --force
"force-no-commit" => %({
"pusher": { "name": "LTe", "email":"lite.88@gmail.com" },
"repository":{
"name":"acts-as-messageable",
"created_at":"2010/08/02 07:41:30 -0700",
"has_wiki":true,
"size":200,
"private":false,
"watchers":13,
"fork":false,
"url":"https://github.com/LTe/acts-as-messageable",
"language":"Ruby",
"pushed_at":"2011/05/31 04:16:01 -0700",
"open_issues":0,
"has_downloads":true,
"homepage":"http://github.com/LTe/acts-as-messageable",
"has_issues":true,
"forks":5,
"description":"ActsAsMessageable",
"owner": { "name":"LTe", "email":"lite.88@gmail.com" }
},
"ref_name":"v0.3.0",
"forced":true,
"after":"b842078c2f0084bb36cea76da3dad09129b3c26b",
"deleted":false,
"ref":"refs/tags/v0.3.0",
"commits":[],
"base_ref":"refs/heads/master",
"before":"0000000000000000000000000000000000000000",
"compare":"https://github.com/LTe/acts-as-messageable/compare/v0.3.0",
"created":true
}),
"pull-request" => %({
"action": "opened",
"number": 1,
"pull_request": {
"deletions": 1,
"merged_by": null,
"comments": 0,
"updated_at": "2012-04-12T17:02:33Z",
"state": "open",
"diff_url": "https://github.com/travis-repos/test-project-1/pull/1.diff",
"_links": {
"comments": {
"href": "https://api.github.com/repos/travis-repos/test-project-1/issues/1/comments"
},
"review_comments": {
"href": "https://api.github.com/repos/travis-repos/test-project-1/pulls/1/comments"
},
"self": {
"href": "https://api.github.com/repos/travis-repos/test-project-1/pulls/1"
},
"html": {
"href": "https://github.com/travis-repos/test-project-1/pull/1"
}
},
"merged_at": null,
"user": {
"gravatar_id": "5c2b452f6eea4a6d84c105ebd971d2a4",
"url": "https://api.github.com/users/rkh",
"avatar_url": "https://secure.gravatar.com/avatar/5c2b452f6eea4a6d84c105ebd971d2a4?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png",
"id": 30442,
"login": "rkh"
},
"issue_url": "https://github.com/travis-repos/test-project-1/issues/1",
"commits": 1,
"changed_files": 1,
"title": "You must enter a title to submit a Pull Request",
"merged": false,
"closed_at": null,
"created_at": "2012-02-14T14:00:48Z",
"patch_url": "https://github.com/travis-repos/test-project-1/pull/1.patch",
"url": "https://api.github.com/repos/travis-repos/test-project-1/pulls/1",
"base": {
"repo": {
"pushed_at": "2012-04-11T15:50:22Z",
"homepage": "http://travis-ci.org",
"svn_url": "https://github.com/travis-repos/test-project-1",
"has_issues": false,
"updated_at": "2012-04-11T15:50:22Z",
"forks": 6,
"has_downloads": true,
"ssh_url": "git@github.com:travis-repos/test-project-1.git",
"language": "Ruby",
"clone_url": "https://github.com/travis-repos/test-project-1.git",
"fork": false,
"git_url": "git://github.com/travis-repos/test-project-1.git",
"created_at": "2011-04-14T18:23:41Z",
"url": "https://api.github.com/repos/travis-repos/test-project-1",
"has_wiki": false,
"size": 140,
"private": false,
"description": "Test dummy repository for testing Travis CI",
"owner": {
"gravatar_id": "dad32d44d4850d2bc9485ee115ab4227",
"url": "https://api.github.com/users/travis-repos",
"avatar_url": "https://secure.gravatar.com/avatar/dad32d44d4850d2bc9485ee115ab4227?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-orgs.png",
"id": 864347,
"login": "travis-repos"
},
"name": "test-project-1",
"full_name": "travis-repos/test-project-1",
"watchers": 8,
"html_url": "https://github.com/travis-repos/test-project-1",
"id": 1615549,
"open_issues": 3,
"mirror_url": null
},
"sha": "4a90c0ad9187c8735e1bcbf39a0291a21284994a",
"label": "travis-repos:master",
"user": {
"gravatar_id": "dad32d44d4850d2bc9485ee115ab4227",
"url": "https://api.github.com/users/travis-repos",
"avatar_url": "https://secure.gravatar.com/avatar/dad32d44d4850d2bc9485ee115ab4227?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-orgs.png",
"id": 864347,
"login": "travis-repos"
},
"ref": "master"
},
"number": 1,
"review_comments": 0,
"head": {
"repo": {
"pushed_at": "2012-02-14T14:00:26Z",
"homepage": "http://travis-ci.org",
"svn_url": "https://github.com/rkh/test-project-1",
"has_issues": false,
"updated_at": "2012-02-14T14:00:27Z",
"forks": 0,
"has_downloads": true,
"ssh_url": "git@github.com:rkh/test-project-1.git",
"language": "Ruby",
"clone_url": "https://github.com/rkh/test-project-1.git",
"fork": true,
"git_url": "git://github.com/rkh/test-project-1.git",
"created_at": "2012-02-13T15:17:57Z",
"url": "https://api.github.com/repos/rkh/test-project-1",
"has_wiki": true,
"size": 108,
"private": false,
"description": "Test dummy repository for testing Travis CI",
"owner": {
"gravatar_id": "5c2b452f6eea4a6d84c105ebd971d2a4",
"url": "https://api.github.com/users/rkh",
"avatar_url": "https://secure.gravatar.com/avatar/5c2b452f6eea4a6d84c105ebd971d2a4?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png",
"id": 30442,
"login": "rkh"
},
"name": "test-project-1",
"full_name": "rkh/test-project-1",
"watchers": 1,
"html_url": "https://github.com/rkh/test-project-1",
"id": 3431064,
"open_issues": 0,
"mirror_url": null
},
"sha": "9b00989b1a0e7d9b609ad2e28338c060f79a71ac",
"label": "rkh:master",
"user": {
"gravatar_id": "5c2b452f6eea4a6d84c105ebd971d2a4",
"url": "https://api.github.com/users/rkh",
"avatar_url": "https://secure.gravatar.com/avatar/5c2b452f6eea4a6d84c105ebd971d2a4?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png",
"id": 30442,
"login": "rkh"
},
"ref": "master"
},
"body": "",
"html_url": "https://github.com/travis-repos/test-project-1/pull/1",
"id": 826379,
"mergeable": true,
"mergeable_state": "clean",
"additions": 1
},
"repository": {
"created_at": "2011-04-14T18:23:41Z",
"id": 1615549,
"name": "test-project-1",
"owner": {
"avatar_url": "https:\/\/secure.gravatar.com\/avatar\/dad32d44d4850d2bc9485ee115ab4227?d=https:\/\/a248.e.akamai.net\/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-orgs.png",
"gravatar_id": "dad32d44d4850d2bc9485ee115ab4227",
"id": 864347,
"login": "travis-repos",
"url": "https:\/\/api.github.com\/users\/travis-repos"
},
"pushed_at": "2011-12-12T06:38:20Z",
"updated_at": "2012-02-13T15:17:57Z",
"url": "https:\/\/api.github.com\/repos\/travis-repos\/test-project-1"
},
"sender": {
"avatar_url": "https:\/\/secure.gravatar.com\/avatar\/5c2b452f6eea4a6d84c105ebd971d2a4?d=https:\/\/a248.e.akamai.net\/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png",
"gravatar_id": "5c2b452f6eea4a6d84c105ebd971d2a4",
"id": 30442,
"login": "rkh",
"url": "https:\/\/api.github.com\/users\/rkh"
}
}),
'hook_inactive' => %({
"last_response": {
"status": "ok",
"message": "",
"code": 200
},
"config": {
"domain": "staging.travis-ci.org",
"user": "svenfuchs",
"token": "token"
},
"created_at": "2011-09-18T10:49:06Z",
"events": [
"push",
"pull_request",
"issue_comment",
"public",
"member"
],
"active": false,
"updated_at": "2012-08-09T09:32:42Z",
"name": "travis",
"_links": {
"self": {
"href": "https://api.github.com/repos/svenfuchs/minimal/hooks/77103"
}
},
"id": 77103
}),
'hook_active' => %({
"last_response": {
"status": "ok",
"message": "",
"code": 200
},
"config": {
"domain": "staging.travis-ci.org",
"user": "svenfuchs",
"token": "token"
},
"created_at": "2011-09-18T10:49:06Z",
"events": [
"push",
"pull_request",
"issue_comment",
"public",
"member"
],
"active": true,
"updated_at": "2012-08-09T09:32:42Z",
"name": "travis",
"_links": {
"self": {
"href": "https://api.github.com/repos/svenfuchs/minimal/hooks/77103"
}
},
"id": 77103
}),
'rkh' => %({
"user": {
"gravatar_id":"5c2b452f6eea4a6d84c105ebd971d2a4",
"company":"Travis GmbH",
"name":"Konstantin Haase",
"created_at":"2008/10/22 11:56:03 -0700",
"location":"Potsdam, Berlin, Portland",
"public_repo_count":108,
"public_gist_count":217,
"blog":"http://rkh.im",
"following_count":477,
"id":30442,
"type":"User",
"permission":null,
"followers_count":369,
"login":"rkh",
"email":"k.haase@finn.de"
}
}),
:oauth => {
"uid" => "234423",
"info" => {
"name" => "John",
"nickname" => "john",
"email" => "john@email.com"
},
"credentials" => {
"token" => "1234567890abcdefg"
},
"extra" => {
"raw_info" => {
"gravatar_id" => "41193cdbffbf06be0cdf231b28c54b18"
}
}
},
}
GITHUB_OAUTH_DATA = {
'name' => 'John',
'email' => 'john@email.com',
'login' => 'john',
'github_id' => 234423,
'github_oauth_token' => '1234567890abcdefg',
'gravatar_id' => '41193cdbffbf06be0cdf231b28c54b18'
}
WORKER_PAYLOADS = {
'job:test:receive' => { 'id' => 1, 'state' => 'received', 'received_at' => '2011-01-01 00:02:00 +0200', 'worker' => 'ruby3.worker.travis-ci.org:travis-ruby-4' },
'job:test:start' => { 'id' => 1, 'state' => 'started', 'started_at' => '2011-01-01 00:02:00 +0200', 'worker' => 'ruby3.worker.travis-ci.org:travis-ruby-4' },
'job:test:log' => { 'id' => 1, 'log' => '... appended' },
'job:test:log:1' => { 'id' => 1, 'log' => 'the ' },
'job:test:log:2' => { 'id' => 1, 'log' => 'full ' },
'job:test:log:3' => { 'id' => 1, 'log' => 'log' },
'job:test:finish' => { 'id' => 1, 'state' => 'passed', 'finished_at' => '2011-01-01 00:03:00 +0200', 'log' => 'the full log' },
'job:test:reset' => { 'id' => 1 }
}
WORKER_LEGACY_PAYLOADS = {
'job:test:finished' => { 'id' => 1, 'state' => 'finished', 'finished_at' => '2011-01-01 00:03:00 +0200', 'result' => 0, 'log' => 'the full log' }
}
QUEUE_PAYLOADS = {
'job:configure' => {
:type => 'configure',
:repository => { :slug => 'travis-ci/travis-ci' },
:build => { :id => 1, :commit => '313f61b', :config_url => 'https://raw.github.com/travis-ci/travis-ci/313f61b/.travis.yml' }
},
'job:test:1' => {
:build => { :id => 2, :number => '1.1', :commit => '9854592', :branch => 'master', :config => { :rvm => '1.8.7' } },
:repository => { :id => 1, :slug => 'svenfuchs/gem-release' },
:queue => 'builds.linux'
},
'job:test:2' => {
:build => { :id => 3, :number => '1.2', :commit => '9854592', :branch => 'master', :config => { :rvm => '1.9.2' } },
:repository => { :id => 1, :slug => 'svenfuchs/gem-release' },
:queue => 'builds.linux'
}
}

48
spec/support/s3.rb Normal file
View File

@ -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

View File

@ -28,7 +28,6 @@ describe Travis::Api::V2::Http::Branch do
'sha' => '62aae5f70ceee39123ef',
'branch' => 'master',
'message' => 'the commit message',
'compare_url' => 'https://github.com/svenfuchs/minimal/compare/master...develop',
'committed_at' => json_format_time(Time.now.utc - 1.hour),
'committer_email' => 'svenfuchs@artweb-design.de',
'committer_name' => 'Sven Fuchs',

View File

@ -28,7 +28,6 @@ describe Travis::Api::V2::Http::Branches do
'sha' => '62aae5f70ceee39123ef',
'branch' => 'master',
'message' => 'the commit message',
'compare_url' => 'https://github.com/svenfuchs/minimal/compare/master...develop',
'committed_at' => json_format_time(Time.now.utc - 1.hour),
'committer_email' => 'svenfuchs@artweb-design.de',
'committer_name' => 'Sven Fuchs',

View File

@ -31,7 +31,6 @@ describe Travis::Api::V2::Http::Build do
'branch' => 'master',
'branch_is_default' => true,
'message' => 'the commit message',
'compare_url' => 'https://github.com/svenfuchs/minimal/compare/master...develop',
'committed_at' => json_format_time(Time.now.utc - 1.hour),
'committer_email' => 'svenfuchs@artweb-design.de',
'committer_name' => 'Sven Fuchs',

View File

@ -30,7 +30,6 @@ describe Travis::Api::V2::Http::Builds do
'sha' => '62aae5f70ceee39123ef',
'branch' => 'master',
'message' => 'the commit message',
'compare_url' => 'https://github.com/svenfuchs/minimal/compare/master...develop',
'committed_at' => json_format_time(Time.now.utc - 1.hour),
'committer_email' => 'svenfuchs@artweb-design.de',
'committer_name' => 'Sven Fuchs',

View File

@ -32,7 +32,6 @@ describe Travis::Api::V2::Http::Job do
'message' => 'the commit message',
'branch' => 'master',
'branch_is_default' => true,
'message' => 'the commit message',
'committed_at' => json_format_time(Time.now.utc - 1.hour),
'committer_name' => 'Sven Fuchs',
'committer_email' => 'svenfuchs@artweb-design.de',

View File

@ -28,7 +28,6 @@ describe Travis::Api::V2::Http::Jobs do
data['commits'].first.should == {
'id' => 1,
'sha' => '62aae5f70ceee39123ef',
'message' => 'the commit message',
'branch' => 'master',
'message' => 'the commit message',
'committed_at' => json_format_time(Time.now.utc - 1.hour),

View File

@ -38,7 +38,6 @@ describe Travis::Api::V2::Http::Request do
'sha' => '62aae5f70ceee39123ef',
'branch' => 'master',
'message' => 'the commit message',
'compare_url' => 'https://github.com/svenfuchs/minimal/compare/master...develop',
'committed_at' => json_format_time(commit.committed_at),
'committer_email' => 'svenfuchs@artweb-design.de',
'committer_name' => 'Sven Fuchs',

View File

@ -44,7 +44,6 @@ describe Travis::Api::V2::Http::Requests do
'sha' => '62aae5f70ceee39123ef',
'branch' => 'master',
'message' => 'the commit message',
'compare_url' => 'https://github.com/svenfuchs/minimal/compare/master...develop',
'committed_at' => json_format_time(Time.now.utc - 1.hour),
'committer_email' => 'svenfuchs@artweb-design.de',
'committer_name' => 'Sven Fuchs',

View File

@ -2,15 +2,10 @@ require 'spec_helper'
describe Travis::API::V3::Result do
let(:access_control) { Object.new }
subject(:result) { described_class.new(access_control, :example) }
subject(:result) { described_class.new(access_control, :example, []) }
example { expect(result.type) .to be == :example }
example { expect(result.resource) .to be == [] }
example { expect(result.example) .to be == [] }
example { expect(result.access_control) .to be == access_control }
example do
result << 42
expect(result.example).to include(42)
end
end

View File

@ -0,0 +1,56 @@
require 'spec_helper_core'
describe AnnotationProvider do
include Support::ActiveRecord
let(:provider) { Factory(:annotation_provider) }
describe '.authenticate_provider', truncation: true do
context 'given a valid username and key' do
it 'authenticates the provider' do
described_class.authenticate_provider(provider.api_username, provider.api_key).should eq(provider)
end
end
context 'given an invalid username' do
it 'does not authenticate the provider' do
described_class.authenticate_provider('someone-else', provider.api_key).should be_nil
end
end
context 'given an invalid key' do
it 'does not authenticate the provider' do
described_class.authenticate_provider(provider.api_username, 'some-other-key').should be_nil
end
end
context 'with an encrypted key' do
it 'authenticates the provider' do
provider.update_column(:api_key, 'encrypted-key')
Travis::Model::EncryptedColumn.any_instance.stubs(encrypt?: true, key: 'abcd', load: '...')
Travis::Model::EncryptedColumn.any_instance.expects(:load).with('encrypted-key').returns('a-key')
described_class.authenticate_provider(provider.api_username, 'a-key').should eq(provider)
end
end
end
describe '#annotation_for_job' do
let(:job) { Factory(:test) }
context 'given an annotation already exists for the job' do
it 'returns the annotation' do
annotation = Factory(:annotation, annotation_provider: provider, job: job)
provider.annotation_for_job(job.id).should eq(annotation)
end
end
context 'given no annotation exists yet for the job' do
it 'returns a new annotation object' do
provider.annotation_for_job(job.id).new_record?.should be true
end
end
end
end

View File

@ -0,0 +1,21 @@
require 'spec_helper_core'
describe Annotation do
include Support::ActiveRecord
let(:annotation) { Factory.build(:annotation) }
describe 'validations' do
it 'only allows http or https URLs' do
annotation.url = 'ftp://travis-ci.org'
annotation.save.should be false
annotation.errors[:url].first.should match(/scheme/)
end
it 'only allows valid URLs' do
annotation.url = 'http://travis-ci.org:80b/'
annotation.save.should be false
annotation.errors[:url].first.should match(/invalid/)
end
end
end

View File

@ -0,0 +1,106 @@
require 'spec_helper_core'
describe Broadcast do
include Support::ActiveRecord
let(:org) { Factory(:org) }
let(:repo) { Factory(:repository) }
let(:user) { Factory(:user) }
before :each do
user.organizations << org
user.repositories << repo
end
describe 'by_user' do
let(:broadcasts) { Broadcast.by_user(user) }
it 'finds a global broadcast' do
global = Broadcast.create!
broadcasts.should include(global)
end
it 'finds a broadcast for the given user' do
to_user = Broadcast.create!(recipient: user)
broadcasts.should include(to_user)
end
it 'does not find a broadcast for a different user' do
to_user = Broadcast.create!(recipient: Factory(:user, login: 'rkh'))
broadcasts.should_not include(to_user)
end
it 'finds a broadcast for orgs where the given user is a member' do
to_org = Broadcast.create!(recipient: org)
broadcasts.should include(to_org)
end
it 'does not find a broadcast for a different org' do
to_org = Broadcast.create!(recipient: Factory(:org, login: 'sinatra'))
broadcasts.should_not include(to_org)
end
it 'finds a broadcast for a repo where the given user has any permissions' do
to_repo = Broadcast.create!(recipient: repo)
broadcasts.should include(to_repo)
end
it 'does not find a broadcast for a different repo' do
to_repo = Broadcast.create!(recipient: Factory(:repository, name: 'sinatra'))
broadcasts.should_not include(to_repo)
end
it 'does not find an expired broadcast' do
expired = Broadcast.create!(created_at: 4.weeks.ago, expired: true)
broadcasts.should_not include(expired)
end
it 'does not find broadcasts older than 2 weeks' do
too_old = Broadcast.create!(created_at: 4.weeks.ago)
broadcasts.should_not include(too_old)
end
end
describe 'by_repo' do
let(:broadcasts) { Broadcast.by_repo(repo) }
it 'finds a global broadcast' do
global = Broadcast.create!
broadcasts.should include(global)
end
it 'finds a broadcast for the given repo' do
to_repo = Broadcast.create!(recipient: repo)
broadcasts.should include(to_repo)
end
it 'does not find a broadcast for a different repo' do
to_repo = Broadcast.create!(recipient: Factory(:repository, name: 'sinatra'))
broadcasts.should_not include(to_repo)
end
it 'finds a broadcast for an org this repo belongs to' do
repo.update_attributes(owner: org)
to_org = Broadcast.create!(recipient: org)
broadcasts.should include(to_org)
end
it 'does not find a broadcast for a different org' do
repo.update_attributes(owner: org)
to_org = Broadcast.create!(recipient: Factory(:org, login: 'sinatra'))
broadcasts.should_not include(to_org)
end
it 'finds a broadcast for a user this repo belongs to' do
repo.update_attributes(owner: user)
to_org = Broadcast.create!(recipient: user)
broadcasts.should include(to_org)
end
it 'does not find a broadcast for a different user' do
repo.update_attributes(owner: org)
to_org = Broadcast.create!(recipient: Factory(:user, login: 'rkh'))
broadcasts.should_not include(to_org)
end
end
end

View File

@ -0,0 +1,7 @@
# require 'spec_helper_core'
#
# describe Build::Compat do
# include Support::ActiveRecord
#
# let(:build) { Factory(:build, :result => nil) }
# end

View File

@ -0,0 +1,124 @@
require 'travis/model/build/config/dist'
describe Build::Config::Dist do
subject { described_class.new(config, options) }
let(:config) { {} }
let(:options) { {} }
it 'sets dist to the default' do
subject.run[:dist].should eql(described_class::DEFAULT_DIST)
end
context 'with :dist' do
let(:config) { { dist: 'hambone' } }
it 'is a no-op' do
subject.run[:dist].should eql('hambone')
end
end
context "with 'dist'" do
let(:config) { { 'dist' => 'lentil' } }
it 'is a no-op' do
subject.run['dist'].should eql('lentil')
end
end
context 'with an override language' do
let(:config) { { language: language } }
let(:language) { described_class::DIST_LANGUAGE_MAP.keys.sample }
it 'sets the override for that language' do
subject.run[:dist].should eql(
described_class::DIST_LANGUAGE_MAP[language]
)
end
end
context 'with an override os' do
let(:config) { { os: os } }
let(:os) { described_class::DIST_OS_MAP.keys.sample }
it 'sets the override for that os' do
subject.run[:dist].should eql(described_class::DIST_OS_MAP[os])
end
end
context 'with an override language and os' do
let(:config) { { language: language, os: os } }
let(:language) { described_class::DIST_LANGUAGE_MAP.keys.sample }
let(:os) { described_class::DIST_OS_MAP.keys.sample }
it 'sets the override for that language' do
subject.run[:dist].should eql(
described_class::DIST_LANGUAGE_MAP[language]
)
end
end
context 'with multi_os option and override language set' do
let(:config) { { language: language } }
let(:options) { { multi_os: true } }
let(:language) { described_class::DIST_LANGUAGE_MAP.keys.sample }
it 'sets the override for that language' do
subject.run[:dist].should eql(
described_class::DIST_LANGUAGE_MAP[language]
)
end
end
context 'with multi_os option and non-override language set' do
let(:config) { { language: 'goober' } }
let(:options) { { multi_os: true } }
let(:language) { described_class::DIST_LANGUAGE_MAP.keys.sample }
it 'sets dist to the default' do
subject.run[:dist].should eql(described_class::DEFAULT_DIST)
end
end
context 'without multi_os and os array with override first entry' do
let(:config) { { os: %w(osx linux) } }
let(:options) { { multi_os: false } }
it 'sets the override for that os' do
subject.run[:dist].should eql(described_class::DIST_OS_MAP['osx'])
end
end
context 'without multi_os and os array without override first entry' do
let(:config) { { os: %w(freebsd osx linux) } }
let(:options) { { multi_os: false } }
it 'sets dist to the default' do
subject.run[:dist].should eql(described_class::DEFAULT_DIST)
end
end
context 'with docker in services' do
let(:config) { { services: %w(docker) } }
it 'sets the dist to trusty' do
subject.run[:dist].should eql('trusty')
end
end
context 'with docker in matrix include services' do
let(:config) do
{
matrix: { include: [{ services: %w(docker postgresql) }] },
services: %w(postgresql)
}
end
it 'sets the dist to trusty in the include hash' do
subject.run[:matrix][:include].first[:dist].should eql('trusty')
end
it 'sets the dist to the default at the top level' do
subject.run[:dist].should eql(described_class::DEFAULT_DIST)
end
end
end

View File

@ -0,0 +1,26 @@
require 'travis/model/build/config/group'
describe Build::Config::Group do
subject { described_class.new(config) }
let(:config) { { dist: 'bork' } }
it 'sets group to the default' do
subject.run[:group].should eql(described_class::DEFAULT_GROUP)
end
context 'with :group' do
let(:config) { { group: 'foo' } }
it 'is a no-op' do
subject.run[:group].should eql('foo')
end
end
context "with 'group'" do
let(:config) { { 'group' => 'bar' } }
it 'is a no-op' do
subject.run['group'].should eql('bar')
end
end
end

View File

@ -0,0 +1,14 @@
require 'spec_helper_core'
require 'core_ext/hash/deep_symbolize_keys'
describe Build::Config::Matrix do
include Support::ActiveRecord
it 'can handle nil values in exclude matrix' do
-> { Build::Config::Matrix.new(matrix: { exclude: [nil] }).expand }.should_not raise_error
end
it 'can handle list values in exclude matrix' do
-> { Build::Config::Matrix.new(matrix: []).expand }.should_not raise_error
end
end

View File

@ -0,0 +1,116 @@
require 'spec_helper_core'
describe Build::Config::Obfuscate do
include Support::ActiveRecord
let(:repo) { Factory(:repository) }
let(:build) { Build.new(repository: repo) }
it 'normalizes env vars which are hashes to strings' do
encrypted = repo.key.secure.encrypt('BAR=barbaz')
build.config = {
language: 'ruby',
env: [[encrypted, 'FOO=foo'], [{ ONE: 1, TWO: '2' }]]
}
build.obfuscated_config.should == {
language: 'ruby',
os: 'linux',
group: 'stable',
dist: 'precise',
env: ['BAR=[secure] FOO=foo', 'ONE=1 TWO=2']
}
end
it 'leaves regular vars untouched' do
build.config = {
rvm: ['1.8.7'], env: ['FOO=foo']
}
build.obfuscated_config.should == {
language: 'ruby',
os: 'linux',
group: 'stable',
dist: 'precise',
rvm: ['1.8.7'],
env: ['FOO=foo']
}
end
it 'obfuscates env vars' do
encrypted = build.repository.key.secure.encrypt('BAR=barbaz')
build.config = {
rvm: ['1.8.7'],
env: [[encrypted, 'FOO=foo'], 'BAR=baz']
}
build.obfuscated_config.should == {
language: 'ruby',
os: 'linux',
group: 'stable',
dist: 'precise',
rvm: ['1.8.7'],
env: ['BAR=[secure] FOO=foo', 'BAR=baz']
}
end
it 'obfuscates env vars which are not in nested array' do
build.config = {
rvm: ['1.8.7'],
env: [build.repository.key.secure.encrypt('BAR=barbaz')]
}
build.obfuscated_config.should == {
language: 'ruby',
os: 'linux',
group: 'stable',
dist: 'precise',
rvm: ['1.8.7'],
env: ['BAR=[secure]']
}
end
it 'works with nil values' do
build.config = {
rvm: ['1.8.7'],
env: [[nil, { secure: '' }]]
}
build.obfuscated_config.should == {
language: 'ruby',
os: 'linux',
group: 'stable',
dist: 'precise',
rvm: ['1.8.7'],
env: ['']
}
end
it 'does not make an empty env key an array but leaves it empty' do
build.config = {
rvm: ['1.8.7'],
env: nil
}
build.obfuscated_config.should == {
language: 'ruby',
os: 'linux',
group: 'stable',
dist: 'precise',
rvm: ['1.8.7'],
env: nil
}
end
it 'removes source key' do
build.config = {
rvm: ['1.8.7'],
source_key: '1234'
}
build.obfuscated_config.should == {
language: 'ruby',
os: 'linux',
group: 'stable',
dist: 'precise',
rvm: ['1.8.7']
}
end
end

View File

@ -0,0 +1,156 @@
require 'spec_helper_core'
describe Build::Config do
include Support::ActiveRecord
it 'keeps the given env if it is an array' do
config = YAML.load %(
env:
- FOO=foo
- BAR=bar
)
Build::Config.new(config).normalize.slice(:env, :global_env).should == {
env: [
'FOO=foo',
'BAR=bar'
]
}
end
# seems odd. is this on purpose?
it 'normalizes an env vars hash to an array of strings' do
config = YAML.load %(
env:
FOO: foo
BAR: bar
)
Build::Config.new(config).normalize.slice(:env, :global_env).should == {
env: [
'FOO=foo BAR=bar'
]
}
end
it 'keeps env vars global and matrix arrays' do
config = YAML.load %(
env:
global:
- FOO=foo
- BAR=bar
matrix:
- BAZ=baz
- BUZ=buz
)
Build::Config.new(config).normalize.slice(:env, :global_env).should == {
global_env: [
'FOO=foo',
'BAR=bar'
],
env: [
'BAZ=baz',
'BUZ=buz'
]
}
end
# seems odd. is this on purpose?
it 'normalizes env vars global and matrix which are hashes to an array of strings' do
config = YAML.load %(
env:
global:
FOO: foo
BAR: bar
matrix:
BAZ: baz
BUZ: buz
)
Build::Config.new(config).normalize.slice(:env, :global_env).should == {
global_env: [
'FOO=foo BAR=bar'
],
env: [
'BAZ=baz BUZ=buz'
]
}
end
it 'works fine if matrix part of env is undefined' do
config = YAML.load %(
env:
global: FOO=foo
)
Build::Config.new(config).normalize.slice(:env, :global_env).should == {
global_env: [
'FOO=foo'
]
}
end
it 'works fine if global part of env is undefined' do
config = YAML.load %(
env:
matrix: FOO=foo
)
Build::Config.new(config).normalize.slice(:env, :global_env).should == {
env: [
'FOO=foo'
]
}
end
# Seems odd. What's the usecase? Broken yaml?
it 'keeps matrix and global config as arrays, not hashes' do
config = YAML.load %(
env:
global: FOO=foo
matrix:
-
- BAR=bar
- BAZ=baz
- BUZ=buz
)
Build::Config.new(config).normalize.slice(:env, :global_env).should == {
global_env: [
'FOO=foo'
],
env: [
['BAR=bar', 'BAZ=baz'],
'BUZ=buz'
]
}
end
# Seems super odd. Do people actually pass such stuff?
it 'keeps wild nested array/hashes structure' do
config = YAML.load %(
env:
-
-
secure: encrypted-value
- FOO=foo
-
-
BAR: bar
BAZ: baz
)
Build::Config.new(config).normalize.slice(:env, :global_env).should == {
env: [
[{ secure: 'encrypted-value' }, 'FOO=foo'],
['BAR=bar BAZ=baz']
]
}
end
it 'sets the os value to osx for objective-c builds' do
config = YAML.load %(
language: objective-c
)
Build::Config.new(config).normalize[:os].should == 'osx'
end
it 'sets the os value to linux for other builds' do
config = YAML.load %(
)
Build::Config.new(config).normalize[:os].should == 'linux'
end
end

View File

@ -0,0 +1,59 @@
require 'spec_helper_core'
describe Build, 'denormalization' do
include Support::ActiveRecord
let(:build) { Factory(:build, state: :started, duration: 30) }
describe 'on build:started' do
before :each do
build.denormalize(:start)
build.reload
end
it 'denormalizes last_build_id to its repository' do
build.repository.last_build_id.should == build.id
end
it 'denormalizes last_build_state to its repository' do
build.repository.last_build_state.should == 'started'
end
it 'denormalizes last_build_number to its repository' do
build.repository.last_build_number.should == build.number
end
it 'denormalizes last_build_duration to its repository' do
build.repository.last_build_duration.should == build.duration
end
it 'denormalizes last_build_started_at to its repository' do
build.repository.last_build_started_at.should == build.started_at
end
it 'denormalizes last_build_finished_at to its repository' do
build.repository.last_build_finished_at.should == build.finished_at
end
end
describe 'on build:finished' do
before :each do
build.update_attributes(state: :errored)
build.denormalize(:finish)
build.reload
end
it 'denormalizes last_build_state to its repository' do
build.repository.last_build_state.should == 'errored'
end
it 'denormalizes last_build_duration to its repository' do
build.repository.last_build_duration.should == build.duration
end
it 'denormalizes last_build_finished_at to its repository' do
build.repository.last_build_finished_at.should == build.finished_at
end
end
end

View File

@ -0,0 +1,889 @@
require 'spec_helper_core'
describe Build, 'matrix' do
include Support::ActiveRecord
describe :matrix_finished? do
context 'if config[:matrix][:finish_fast] is not set' do
context 'if at least one job has not finished and is not allowed to fail' do
it 'returns false' do
build = Factory(:build, config: { rvm: ['1.8.7', '1.9.2'] })
build.matrix[0].update_attributes(state: :passed)
build.matrix[1].update_attributes(state: :started)
build.matrix_finished?.should_not be true
end
end
context 'if at least one job has not finished and is allowed to fail' do
it 'returns false' do
build = Factory(:build, config: { rvm: ['1.8.7', '1.9.2'] })
build.matrix[0].update_attributes(state: :passed)
build.matrix[1].update_attributes(state: :started, allow_failure: true)
build.matrix_finished?.should_not be true
end
end
context 'if all jobs have finished' do
it 'returns true' do
build = Factory(:build, config: { rvm: ['1.8.7', '1.9.2'] })
build.matrix[0].update_attributes!(state: :passed)
build.matrix[1].update_attributes!(state: :passed)
build.matrix_finished?.should be true
end
end
end
context 'if config[:matrix][:finish_fast] is set' do
context 'if at least one job has not finished and is not allowed to fail' do
it 'returns false' do
build = Factory(:build, config: { rvm: ['1.8.7', '1.9.2'], matrix: {fast_finish: true} })
build.matrix[0].update_attributes(state: :passed)
build.matrix[1].update_attributes(state: :started)
build.matrix_finished?.should be false
end
end
context 'if at least one job has not finished and is allowed to fail' do
it 'returns true' do
build = Factory(:build, config: { rvm: ['1.8.7', '1.9.2'], matrix: {fast_finish: true} })
build.matrix[0].update_attributes(state: :passed)
build.matrix[1].update_attributes(state: :started, allow_failure: true)
build.matrix_finished?.should be true
end
end
context 'if all jobs have finished' do
it 'returns true' do
build = Factory(:build, config: { rvm: ['1.8.7', '1.9.2'], matrix: {fast_finish: true} })
build.matrix[0].update_attributes!(state: :passed)
build.matrix[1].update_attributes!(state: :passed)
build.matrix_finished?.should be true
end
end
end
end
describe :matrix_state do
let(:build) { Factory(:build, config: { rvm: ['1.8.7', '1.9.2'] }) }
it 'returns :passed if all jobs have passed' do
build.matrix[0].update_attributes!(state: "passed")
build.matrix[1].update_attributes!(state: "passed")
build.matrix_state.should == :passed
end
it 'returns :failed if one job has failed' do
build.matrix[0].update_attributes!(state: "passed")
build.matrix[1].update_attributes!(state: "failed")
build.matrix_state.should == :failed
end
it 'returns :failed if one job has failed and one job has errored' do
build.matrix[0].update_attributes!(state: "errored")
build.matrix[1].update_attributes!(state: "failed")
build.matrix_state.should == :errored
end
it 'returns :errored if one job has errored' do
build.matrix[0].update_attributes!(state: "passed")
build.matrix[1].update_attributes!(state: "errored")
build.matrix_state.should == :errored
end
it 'returns :passed if a errored job is allowed to fail' do
build.matrix[0].update_attributes!(state: "passed")
build.matrix[1].update_attributes!(state: "errored", allow_failure: true)
build.matrix_state.should == :passed
end
it 'returns :passed if a failed job is allowed to fail' do
build.matrix[0].update_attributes!(state: "passed")
build.matrix[1].update_attributes!(state: "failed", allow_failure: true)
build.matrix_state.should == :passed
end
it 'returns :failed if all jobs have failed and only one is allowed to fail' do
build.matrix[0].update_attributes!(state: "failed")
build.matrix[1].update_attributes!(state: "failed", allow_failure: true)
build.matrix_state.should == :failed
end
it 'returns :failed if all jobs have failed and only one is allowed to fail' do
build.matrix[0].update_attributes!(state: "finished")
expect { build.matrix_state }.to raise_error(StandardError)
end
it 'returns :passed if all jobs have passed except a job that is allowed to fail, and config[:matrix][:finish_fast] is set' do
build.config.update(finish_fast: true)
build.matrix[0].update_attributes!(state: "passed")
build.matrix[1].update_attributes!(state: "failed", allow_failure: true)
build.matrix_state.should == :passed
end
end
context 'matrix with one allow_failure job' do
let(:build) { Factory(:build, config: { rvm: ['1.9.3'] }) }
it 'returns :passed' do
build.matrix[0].update_attributes!(state: "failed", allow_failure: true)
build.matrix_state.should == :passed
end
end
describe :matrix_duration do
let(:build) do
Build.new(matrix: [
Job::Test.new(started_at: 60.seconds.ago, finished_at: 40.seconds.ago),
Job::Test.new(started_at: 20.seconds.ago, finished_at: 10.seconds.ago)
])
end
context 'if the matrix is finished' do
it 'returns the sum of the matrix job durations' do
build.stubs(:matrix_finished?).returns(true)
build.matrix_duration.should == 30
end
end
context 'if the matrix is not finished' do
it 'returns nil' do
build.stubs(:matrix_finished?).returns(false)
build.matrix_duration.should be_nil
end
end
end
describe 'for Ruby projects' do
let(:no_matrix_config) {
YAML.load <<-yml
script: 'rake ci'
yml
}
let(:single_test_config) {
YAML.load <<-yml
script: 'rake ci'
rvm:
- 1.8.7
gemfile:
- gemfiles/rails-3.0.6
env:
- USE_GIT_REPOS=true
yml
}
let(:env_global_config) {
YAML.load <<-yml
script: 'rake ci'
rvm:
- 1.9.2
- 1.9.3
gemfile:
- gemfiles/rails-4.0.0
env:
global:
- TOKEN=abcdef
matrix:
- FOO=bar
- BAR=baz
yml
}
let(:multiple_tests_config) {
YAML.load <<-yml
script: 'rake ci'
rvm:
- 1.8.7
- 1.9.1
- 1.9.2
gemfile:
- gemfiles/rails-3.0.6
- gemfiles/rails-3.0.7
- gemfiles/rails-3-0-stable
- gemfiles/rails-master
env:
- USE_GIT_REPOS=true
yml
}
let(:multiple_tests_config_with_exculsion) {
YAML.load <<-yml
rvm:
- 1.8.7
- 1.9.2
gemfile:
- gemfiles/rails-2.3.x
- gemfiles/rails-3.0.x
- gemfiles/rails-3.1.x
matrix:
exclude:
- rvm: 1.8.7
gemfile: gemfiles/rails-3.1.x
- rvm: 1.9.2
gemfile: gemfiles/rails-2.3.x
yml
}
let(:multiple_tests_config_with_global_env_and_exclusion) {
YAML.load <<-yml
rvm:
- 1.9.2
- 2.0.0
gemfile:
- gemfiles/rails-3.1.x
- gemfiles/rails-4.0.x
env:
global:
- FOO=bar
matrix:
exclude:
- rvm: 1.9.2
gemfile: gemfiles/rails-4.0.x
yml
}
let(:multiple_tests_config_with_invalid_exculsion) {
YAML.load <<-yml
rvm:
- 1.8.7
- 1.9.2
gemfile:
- gemfiles/rails-3.0.x
- gemfiles/rails-3.1.x
env:
- FOO=bar
- BAR=baz
matrix:
exclude:
- rvm: 1.9.2
gemfile: gemfiles/rails-3.0.x
yml
}
let(:multiple_tests_config_with_inclusion) {
YAML.load <<-yml
rvm:
- 1.8.7
- 1.9.2
env:
- FOO=bar
- BAR=baz
matrix:
include:
- rvm: 1.9.2
env: BAR=xyzzy
yml
}
let(:matrix_with_inclusion_only) {
YAML.load <<-yml
language: ruby
matrix:
include:
- rvm: "2.1.0"
env: FOO=true
- rvm: "2.1.0"
env: BAR=true
- rvm: "1.9.3"
env: BAZ=true
yml
}
let(:matrix_with_empty_include) {
YAML.load <<-yml
language: ruby
matrix:
include:
yml
}
let(:multiple_tests_config_with_allow_failures) {
YAML.load <<-yml
language: objective-c
rvm:
- 1.8.7
- 1.9.2
xcode_sdk:
- iphonesimulator6.1
- iphonesimulator7.0
matrix:
allow_failures:
- rvm: 1.8.7
xcode_sdk: iphonesimulator7.0
yml
}
let(:allow_failures_with_global_env) {
YAML.load <<-yml
rvm:
- 1.9.3
- 2.0.0
env:
global:
- "GLOBAL=global NEXT_GLOBAL=next"
matrix:
- "FOO=bar"
- "FOO=baz"
matrix:
allow_failures:
- rvm: 1.9.3
env: "FOO=bar"
yml
}
let(:scalar_allow_failures) {
YAML.load <<-yml
env:
global:
- "GLOBAL=global NEXT_GLOBAL=next"
matrix:
- "FOO=bar"
- "FOO=baz"
matrix:
allow_failures:
"FOO=bar"
yml
}
let(:matrix_with_unwanted_expansion_ruby) {
YAML.load <<-yml
language: ruby
python:
- 3.3
- 2.7
rvm:
- 2.0.0
- 1.9.3
gemfile:
- 'gemfiles/rails-4'
yml
}
let(:matrix_with_unwanted_expansion_python) {
YAML.load <<-yml
language: python
python:
- "3.3"
- "2.7"
rvm:
- 2.0.0
- 1.9.3
gemfile:
- 'gemfiles/rails-4'
yml
}
let(:ruby_matrix_with_incorrect_allow_failures) {
YAML.load <<-yml
language: ruby
rvm:
- "1.9.3"
- "2.1.0"
matrix:
fast_finish: true
allow_failures:
- what: "ever"
yml
}
describe :expand_matrix, truncation: true do
it 'does not expand on :os' do
build = Factory.create(:build, config: { rvm: ['1.9.3', '2.0.0'], os: ['osx', 'linux']})
build.matrix.map(&:config).should == [
{ os: 'linux', language: 'ruby', group: 'stable', dist: 'precise', rvm: '1.9.3' },
{ os: 'linux', language: 'ruby', group: 'stable', dist: 'precise', rvm: '2.0.0' }
]
end
it 'does not clobber env and global_env vars' do
build = Factory(:build, config: env_global_config)
build.matrix.map(&:config).should == [
{ os: 'linux', language: 'ruby', group: 'stable', dist: 'precise', script: 'rake ci', rvm: '1.9.2', gemfile: 'gemfiles/rails-4.0.0', env: 'FOO=bar', global_env: ['TOKEN=abcdef'] },
{ os: 'linux', language: 'ruby', group: 'stable', dist: 'precise', script: 'rake ci', rvm: '1.9.2', gemfile: 'gemfiles/rails-4.0.0', env: 'BAR=baz', global_env: ['TOKEN=abcdef'] },
{ os: 'linux', language: 'ruby', group: 'stable', dist: 'precise', script: 'rake ci', rvm: '1.9.3', gemfile: 'gemfiles/rails-4.0.0', env: 'FOO=bar', global_env: ['TOKEN=abcdef'] },
{ os: 'linux', language: 'ruby', group: 'stable', dist: 'precise', script: 'rake ci', rvm: '1.9.3', gemfile: 'gemfiles/rails-4.0.0', env: 'BAR=baz', global_env: ['TOKEN=abcdef'] }
]
end
it 'sets the config to the jobs (no config)' do
build = Factory(:build, config: {})
build.matrix.map(&:config).should == [
{ os: 'linux', language: 'ruby', group: 'stable', dist: 'precise' }
]
end
it 'sets the config to the jobs (no matrix config)' do
build = Factory(:build, config: no_matrix_config)
build.matrix.map(&:config).should == [
{ os: 'linux', language: 'ruby', group: 'stable', dist: 'precise', script: 'rake ci' }
]
end
it 'sets the config to the jobs (single test config)' do
build = Factory(:build, config: single_test_config)
build.matrix.map(&:config).should == [
{ os: 'linux', language: 'ruby', group: 'stable', dist: 'precise', script: 'rake ci', rvm: '1.8.7', gemfile: 'gemfiles/rails-3.0.6', env: 'USE_GIT_REPOS=true' }
]
end
it 'sets the config to the jobs (multiple tests config)' do
build = Factory(:build, config: multiple_tests_config)
build.matrix.map(&:config).should == [
{ os: 'linux', language: 'ruby', group: 'stable', dist: 'precise', script: 'rake ci', rvm: '1.8.7', gemfile: 'gemfiles/rails-3.0.6', env: 'USE_GIT_REPOS=true' },
{ os: 'linux', language: 'ruby', group: 'stable', dist: 'precise', script: 'rake ci', rvm: '1.8.7', gemfile: 'gemfiles/rails-3.0.7', env: 'USE_GIT_REPOS=true' },
{ os: 'linux', language: 'ruby', group: 'stable', dist: 'precise', script: 'rake ci', rvm: '1.8.7', gemfile: 'gemfiles/rails-3-0-stable', env: 'USE_GIT_REPOS=true' },
{ os: 'linux', language: 'ruby', group: 'stable', dist: 'precise', script: 'rake ci', rvm: '1.8.7', gemfile: 'gemfiles/rails-master', env: 'USE_GIT_REPOS=true' },
{ os: 'linux', language: 'ruby', group: 'stable', dist: 'precise', script: 'rake ci', rvm: '1.9.1', gemfile: 'gemfiles/rails-3.0.6', env: 'USE_GIT_REPOS=true' },
{ os: 'linux', language: 'ruby', group: 'stable', dist: 'precise', script: 'rake ci', rvm: '1.9.1', gemfile: 'gemfiles/rails-3.0.7', env: 'USE_GIT_REPOS=true' },
{ os: 'linux', language: 'ruby', group: 'stable', dist: 'precise', script: 'rake ci', rvm: '1.9.1', gemfile: 'gemfiles/rails-3-0-stable', env: 'USE_GIT_REPOS=true' },
{ os: 'linux', language: 'ruby', group: 'stable', dist: 'precise', script: 'rake ci', rvm: '1.9.1', gemfile: 'gemfiles/rails-master', env: 'USE_GIT_REPOS=true' },
{ os: 'linux', language: 'ruby', group: 'stable', dist: 'precise', script: 'rake ci', rvm: '1.9.2', gemfile: 'gemfiles/rails-3.0.6', env: 'USE_GIT_REPOS=true' },
{ os: 'linux', language: 'ruby', group: 'stable', dist: 'precise', script: 'rake ci', rvm: '1.9.2', gemfile: 'gemfiles/rails-3.0.7', env: 'USE_GIT_REPOS=true' },
{ os: 'linux', language: 'ruby', group: 'stable', dist: 'precise', script: 'rake ci', rvm: '1.9.2', gemfile: 'gemfiles/rails-3-0-stable', env: 'USE_GIT_REPOS=true' },
{ os: 'linux', language: 'ruby', group: 'stable', dist: 'precise', script: 'rake ci', rvm: '1.9.2', gemfile: 'gemfiles/rails-master', env: 'USE_GIT_REPOS=true' }
]
end
it 'sets the config to the jobs (allow failures config)' do
build = Factory(:build, config: multiple_tests_config_with_allow_failures)
build.matrix.map(&:allow_failure).should == [false, true, false, false]
end
it 'ignores global env config when setting allow failures' do
build = Factory(:build, config: allow_failures_with_global_env)
build.matrix.map(&:allow_failure).should == [true, false, false, false]
end
context 'when matrix specifies incorrect allow_failures' do
before :each do
@build = Factory(:build, config: ruby_matrix_with_incorrect_allow_failures)
end
it 'excludes matrices correctly' do
@build.matrix.map(&:allow_failure).should == [false, false]
end
end
context 'when matrix specifies scalar allow_failures' do
before :each do
@build = Factory(:build, config: scalar_allow_failures)
end
it 'ignores allow_failures silently' do
@build.matrix.map(&:allow_failure).should == [false, false]
end
end
context 'when ruby project contains unwanted key' do
before :each do
@build_ruby = Factory(:build, config: matrix_with_unwanted_expansion_ruby)
end
it 'ignores irrelevant matrix dimensions' do
@build_ruby.matrix.map(&:config).should == [
{ os: 'linux', language: 'ruby', group: 'stable', dist: 'precise', rvm: '2.0.0', gemfile: 'gemfiles/rails-4' },
{ os: 'linux', language: 'ruby', group: 'stable', dist: 'precise', rvm: '1.9.3', gemfile: 'gemfiles/rails-4' }
]
end
it 'creates jobs whose config does not contain unwanted keys' do
configs = @build_ruby.matrix.map { |job| job.config[:python] }.flatten.compact
configs.should be_empty
end
# it 'does not touch config' do
# @build_ruby.config.keys.should include(:python)
# end
end
context 'when python project contains unwanted key' do
before :each do
@build_python = Factory(:build, config: matrix_with_unwanted_expansion_python)
end
it 'ignores irrelevant matrix dimensions' do
@build_python.matrix.map(&:config).should == [
{ os: 'linux', language: 'python', group: 'stable', dist: 'precise', python: '3.3' },
{ os: 'linux', language: 'python', group: 'stable', dist: 'precise', python: '2.7' }
]
end
# it 'does not touch config' do
# @build_python.config.keys.should include(:rvm)
# end
end
it 'copies build attributes' do
# TODO spec other attributes!
build = Factory(:build, config: multiple_tests_config)
build.matrix.map(&:commit_id).uniq.should == [build.commit_id]
end
it 'adds a sub-build number to the job number' do
build = Factory(:build, config: multiple_tests_config)
build.matrix.map(&:number)[0..3].should == ['1.1', '1.2', '1.3', '1.4']
end
describe :exclude_matrix_config do
it 'excludes a matrix config when all config items are defined in the exclusion' do
build = Factory(:build, config: multiple_tests_config_with_exculsion)
matrix_exclusion = {
exclude: [
{ rvm: '1.8.7', gemfile: 'gemfiles/rails-3.1.x' },
{ rvm: '1.9.2', gemfile: 'gemfiles/rails-2.3.x' }
]
}
build.matrix.map(&:config).should == [
{ os: 'linux', language: 'ruby', group: 'stable', dist: 'precise', rvm: '1.8.7', gemfile: 'gemfiles/rails-2.3.x' },
{ os: 'linux', language: 'ruby', group: 'stable', dist: 'precise', rvm: '1.8.7', gemfile: 'gemfiles/rails-3.0.x' },
{ os: 'linux', language: 'ruby', group: 'stable', dist: 'precise', rvm: '1.9.2', gemfile: 'gemfiles/rails-3.0.x' },
{ os: 'linux', language: 'ruby', group: 'stable', dist: 'precise', rvm: '1.9.2', gemfile: 'gemfiles/rails-3.1.x' }
]
end
it "excludes a matrix config without specifying global env vars in the exclusion" do
build = Factory(:build, config: multiple_tests_config_with_global_env_and_exclusion)
matrix_exclusion = { exclude: [{ rvm: "1.9.2", gemfile: "gemfiles/rails-4.0.x" }] }
build.matrix.map(&:config).should eq([
{ os: 'linux', language: 'ruby', group: 'stable', dist: 'precise', rvm: "1.9.2", gemfile: "gemfiles/rails-3.1.x", global_env: ["FOO=bar"] },
{ os: 'linux', language: 'ruby', group: 'stable', dist: 'precise', rvm: "2.0.0", gemfile: "gemfiles/rails-3.1.x", global_env: ["FOO=bar"] },
{ os: 'linux', language: 'ruby', group: 'stable', dist: 'precise', rvm: "2.0.0", gemfile: "gemfiles/rails-4.0.x", global_env: ["FOO=bar"] },
])
end
it 'excludes jobs from a matrix config when the matrix exclusion definition is incomplete' do
build = Factory(:build, config: multiple_tests_config_with_invalid_exculsion)
matrix_exclusion = { exclude: [{ rvm: '1.9.2', gemfile: 'gemfiles/rails-3.0.x' }] }
build.matrix.map(&:config).should == [
{ os: 'linux', language: 'ruby', group: 'stable', dist: 'precise', rvm: '1.8.7', gemfile: 'gemfiles/rails-3.0.x', env: 'FOO=bar' },
{ os: 'linux', language: 'ruby', group: 'stable', dist: 'precise', rvm: '1.8.7', gemfile: 'gemfiles/rails-3.0.x', env: 'BAR=baz' },
{ os: 'linux', language: 'ruby', group: 'stable', dist: 'precise', rvm: '1.8.7', gemfile: 'gemfiles/rails-3.1.x', env: 'FOO=bar' },
{ os: 'linux', language: 'ruby', group: 'stable', dist: 'precise', rvm: '1.8.7', gemfile: 'gemfiles/rails-3.1.x', env: 'BAR=baz' },
{ os: 'linux', language: 'ruby', group: 'stable', dist: 'precise', rvm: '1.9.2', gemfile: 'gemfiles/rails-3.1.x', env: 'FOO=bar' },
{ os: 'linux', language: 'ruby', group: 'stable', dist: 'precise', rvm: '1.9.2', gemfile: 'gemfiles/rails-3.1.x', env: 'BAR=baz' }
]
end
end
end
describe :include_matrix_config do
it 'includes a matrix config' do
build = Factory(:build, config: multiple_tests_config_with_inclusion)
matrix_inclusion = {
include: [
{ rvm: '1.9.2', env: 'BAR=xyzzy' }
]
}
build.matrix.map(&:config).should == [
{ os: 'linux', language: 'ruby', group: 'stable', dist: 'precise', rvm: '1.8.7', env: 'FOO=bar' },
{ os: 'linux', language: 'ruby', group: 'stable', dist: 'precise', rvm: '1.8.7', env: 'BAR=baz' },
{ os: 'linux', language: 'ruby', group: 'stable', dist: 'precise', rvm: '1.9.2', env: 'FOO=bar' },
{ os: 'linux', language: 'ruby', group: 'stable', dist: 'precise', rvm: '1.9.2', env: 'BAR=baz' },
{ os: 'linux', language: 'ruby', group: 'stable', dist: 'precise', rvm: '1.9.2', env: 'BAR=xyzzy' }
]
end
it 'does not include "empty" matrix config' do
build = Factory(:build, config: matrix_with_inclusion_only)
matrix_inclusion = {
include: [
{ rvm: '2.1.0', env: 'FOO=true' },
{ rvm: '2.1.0', env: 'BAR=true' },
{ rvm: '1.9.3', env: 'BAZ=true' }
]
}
build.matrix.map(&:config).should == [
{ os: 'linux', language: 'ruby', group: 'stable', dist: 'precise', rvm: '2.1.0', env: 'FOO=true' },
{ os: 'linux', language: 'ruby', group: 'stable', dist: 'precise', rvm: '2.1.0', env: 'BAR=true' },
{ os: 'linux', language: 'ruby', group: 'stable', dist: 'precise', rvm: '1.9.3', env: 'BAZ=true' }
]
end
it 'includes "empty" matrix config when matrix.include is null' do
build = Factory(:build, config: matrix_with_empty_include)
matrix_inclusion = {
include: nil
}
build.matrix.map(&:config).should == []
end
end
describe 'matrix expansion' do
let(:repository) { Factory(:repository) }
it 'with string values' do
build = Factory(:build, config: { rvm: '1.8.7', gemfile: 'gemfiles/rails-2.3.x', env: 'FOO=bar' })
build.matrix.map(&:config).should == [
{ os: 'linux', language: 'ruby', group: 'stable', dist: 'precise', rvm: '1.8.7', gemfile: 'gemfiles/rails-2.3.x', env: 'FOO=bar' }
]
end
it 'does not decrypt secure env vars' do
env = repository.key.secure.encrypt('FOO=bar').symbolize_keys
config = { rvm: '1.8.7', gemfile: 'gemfiles/rails-2.3.x', env: env }
build = Factory(:build, repository: repository, config: config)
build.matrix.map(&:config).should == [
{ os: 'linux', language: 'ruby', group: 'stable', dist: 'precise', rvm: '1.8.7', gemfile: 'gemfiles/rails-2.3.x', env: env }
]
end
it 'with two Rubies and Gemfiles' do
build = Factory(:build, config: { rvm: ['1.8.7', '1.9.2'], gemfile: ['gemfiles/rails-2.3.x', 'gemfiles/rails-3.0.x'] })
build.matrix.map(&:config).should == [
{ os: 'linux', language: 'ruby', group: 'stable', dist: 'precise', rvm: '1.8.7', gemfile: 'gemfiles/rails-2.3.x' },
{ os: 'linux', language: 'ruby', group: 'stable', dist: 'precise', rvm: '1.8.7', gemfile: 'gemfiles/rails-3.0.x' },
{ os: 'linux', language: 'ruby', group: 'stable', dist: 'precise', rvm: '1.9.2', gemfile: 'gemfiles/rails-2.3.x' },
{ os: 'linux', language: 'ruby', group: 'stable', dist: 'precise', rvm: '1.9.2', gemfile: 'gemfiles/rails-3.0.x' }
]
end
it 'with unequal number of Rubies, env variables and Gemfiles' do
build = Factory(:build, config: { rvm: ['1.8.7', '1.9.2', 'ree'], gemfile: ['gemfiles/rails-3.0.x'], env: ['DB=postgresql', 'DB=mysql'] })
build.matrix.map(&:config).should == [
{ os: 'linux', language: 'ruby', group: 'stable', dist: 'precise', rvm: '1.8.7', gemfile: 'gemfiles/rails-3.0.x', env: 'DB=postgresql' },
{ os: 'linux', language: 'ruby', group: 'stable', dist: 'precise', rvm: '1.8.7', gemfile: 'gemfiles/rails-3.0.x', env: 'DB=mysql' },
{ os: 'linux', language: 'ruby', group: 'stable', dist: 'precise', rvm: '1.9.2', gemfile: 'gemfiles/rails-3.0.x', env: 'DB=postgresql' },
{ os: 'linux', language: 'ruby', group: 'stable', dist: 'precise', rvm: '1.9.2', gemfile: 'gemfiles/rails-3.0.x', env: 'DB=mysql' },
{ os: 'linux', language: 'ruby', group: 'stable', dist: 'precise', rvm: 'ree', gemfile: 'gemfiles/rails-3.0.x', env: 'DB=postgresql' },
{ os: 'linux', language: 'ruby', group: 'stable', dist: 'precise', rvm: 'ree', gemfile: 'gemfiles/rails-3.0.x', env: 'DB=mysql' },
]
end
it 'with an array of Rubies and a single Gemfile' do
build = Factory(:build, config: { rvm: ['1.8.7', '1.9.2'], gemfile: 'gemfiles/rails-2.3.x' })
build.matrix.map(&:config).should == [
{ os: 'linux', language: 'ruby', group: 'stable', dist: 'precise', rvm: '1.8.7', gemfile: 'gemfiles/rails-2.3.x' },
{ os: 'linux', language: 'ruby', group: 'stable', dist: 'precise', rvm: '1.9.2', gemfile: 'gemfiles/rails-2.3.x' }
]
end
end
end
describe 'for Scala projects' do
it 'with a single Scala version given as a string' do
build = Factory(:build, config: { language: 'scala', scala: '2.8.2', env: 'NETWORK=false' })
build.matrix.map(&:config).should == [
{ os: 'linux', language: 'scala', group: 'stable', dist: 'precise', scala: '2.8.2', env: 'NETWORK=false' }
]
end
it 'with multiple Scala versions and no env variables' do
build = Factory(:build, config: { language: 'scala', scala: ['2.8.2', '2.9.1']})
build.matrix.map(&:config).should == [
{ os: 'linux', language: 'scala', group: 'stable', dist: 'precise', scala: '2.8.2' },
{ os: 'linux', language: 'scala', group: 'stable', dist: 'precise', scala: '2.9.1' }
]
end
it 'with a single Scala version passed in as array and two env variables' do
build = Factory(:build, config: { language: 'scala', scala: ['2.8.2'], env: ['STORE=postgresql', 'STORE=redis'] })
build.matrix.map(&:config).should == [
{ os: 'linux', language: 'scala', group: 'stable', dist: 'precise', scala: '2.8.2', env: 'STORE=postgresql' },
{ os: 'linux', language: 'scala', group: 'stable', dist: 'precise', scala: '2.8.2', env: 'STORE=redis' }
]
end
end
describe 'multi_os' do
let(:matrix_with_os_ruby) {
YAML.load(%(
language: ruby
os:
- osx
- linux
rvm:
- 2.0.0
- 1.9.3
gemfile:
- 'gemfiles/rails-4'
)).deep_symbolize_keys
}
let(:repository) { Factory(:repository)}
let(:test) { Factory(:test, repository: repository) }
context 'the feature is active' do
it 'expands on :os' do
repository.stubs(:multi_os_enabled?).returns(true)
build = Factory(:build, config: matrix_with_os_ruby, repository: repository)
build.matrix.map(&:config).should == [
{ os: 'osx', language: 'ruby', group: 'stable', dist: 'precise', rvm: '2.0.0', gemfile: 'gemfiles/rails-4' },
{ os: 'osx', language: 'ruby', group: 'stable', dist: 'precise', rvm: '1.9.3', gemfile: 'gemfiles/rails-4' },
{ os: 'linux', language: 'ruby', group: 'stable', dist: 'precise', rvm: '2.0.0', gemfile: 'gemfiles/rails-4' },
{ os: 'linux', language: 'ruby', group: 'stable', dist: 'precise', rvm: '1.9.3', gemfile: 'gemfiles/rails-4' },
]
end
end
context 'the feature is inactive' do
it 'does not expand on :os' do
repository.stubs(:multi_os_enabled?).returns(false)
build = Factory(:build, config: matrix_with_os_ruby, repository: repository)
build.matrix.map(&:config).should == [
{ os: 'linux', language: 'ruby', group: 'stable', dist: 'precise', rvm: '2.0.0', gemfile: 'gemfiles/rails-4' },
{ os: 'linux', language: 'ruby', group: 'stable', dist: 'precise', rvm: '1.9.3', gemfile: 'gemfiles/rails-4' }
]
end
end
end
describe 'os expansion' do
let(:matrix_with_includes_os_ruby) {
YAML.load(%(
language: ruby
matrix:
include:
- os: linux
compiler: gcc
- os: linux
compiler: clang
- os: osx
compiler: gcc
- os: osx
compiler: clang
)).deep_symbolize_keys
}
let(:repository) { Factory(:repository) }
let(:build) { Factory(:build, repository: repository, config: matrix_with_includes_os_ruby) }
it 'expands on :os if the feature is active' do
repository.stubs(:multi_os_enabled?).returns(true)
build.matrix.map(&:config).should == [
{ os: 'linux', language: 'ruby', group: 'stable', dist: 'precise', compiler: 'gcc' },
{ os: 'linux', language: 'ruby', group: 'stable', dist: 'precise', compiler: 'clang' },
{ os: 'osx', language: 'ruby', group: 'stable', dist: 'precise', compiler: 'gcc' },
{ os: 'osx', language: 'ruby', group: 'stable', dist: 'precise', compiler: 'clang' }
]
end
it 'ignores the os key if the feature is inactive' do
repository.stubs(:multi_os_enabled?).returns(false)
build.matrix.map(&:config).should == [
{ os: 'linux', language: 'ruby', group: 'stable', dist: 'precise', compiler: 'gcc' },
{ os: 'linux', language: 'ruby', group: 'stable', dist: 'precise', compiler: 'clang' }
]
end
end
describe 'dist_group_expansion' do
let(:matrix_with_dist_and_group_ruby) {
YAML.load(%(
language: ruby
dist:
- precise
- trusty
group:
- current
- update
rvm:
- 2.0.0
- 1.9.3
gemfile:
- 'gemfiles/rails-4'
)).deep_symbolize_keys
}
let(:repository) { Factory(:repository) }
context 'the feature is active' do
it 'expands on :dist and :group' do
repository.stubs(:dist_group_expansion_enabled?).returns(true)
build = Factory(:build, repository: repository, config: matrix_with_dist_and_group_ruby)
build.matrix.map(&:config).should == [
{ os: 'linux', language: 'ruby', dist: 'precise', group: 'current', rvm: '2.0.0', gemfile: 'gemfiles/rails-4' },
{ os: 'linux', language: 'ruby', dist: 'precise', group: 'current', rvm: '1.9.3', gemfile: 'gemfiles/rails-4' },
{ os: 'linux', language: 'ruby', dist: 'precise', group: 'update', rvm: '2.0.0', gemfile: 'gemfiles/rails-4' },
{ os: 'linux', language: 'ruby', dist: 'precise', group: 'update', rvm: '1.9.3', gemfile: 'gemfiles/rails-4' },
{ os: 'linux', language: 'ruby', dist: 'trusty', group: 'current', rvm: '2.0.0', gemfile: 'gemfiles/rails-4' },
{ os: 'linux', language: 'ruby', dist: 'trusty', group: 'current', rvm: '1.9.3', gemfile: 'gemfiles/rails-4' },
{ os: 'linux', language: 'ruby', dist: 'trusty', group: 'update', rvm: '2.0.0', gemfile: 'gemfiles/rails-4' },
{ os: 'linux', language: 'ruby', dist: 'trusty', group: 'update', rvm: '1.9.3', gemfile: 'gemfiles/rails-4' },
]
end
end
context 'the feature is inactive' do
it 'does not expand on :dist or :group' do
Build.any_instance.stubs(:dist_group_expansion_enabled?).returns(false)
build = Factory(:build, config: matrix_with_dist_and_group_ruby)
build.matrix.map(&:config).should == [
{ os: 'linux', language: 'ruby', dist: ['precise', 'trusty'], group: ['current', 'update'], rvm: '2.0.0', gemfile: 'gemfiles/rails-4' },
{ os: 'linux', language: 'ruby', dist: ['precise', 'trusty'], group: ['current', 'update'], rvm: '1.9.3', gemfile: 'gemfiles/rails-4' }
]
end
end
end
describe 'filter_matrix' do
it 'selects matching builds' do
build = Factory(:build, config: { rvm: ['1.8.7', '1.9.2'], env: ['DB=sqlite3', 'DB=postgresql'] })
build.filter_matrix({ rvm: '1.8.7', env: 'DB=sqlite3' }).should == [build.matrix[0]]
end
it 'does not select builds with non-matching values' do
build = Factory(:build, config: { rvm: ['1.8.7', '1.9.2'], env: ['DB=sqlite3', 'DB=postgresql'] })
build.filter_matrix({ rvm: 'nomatch', env: 'DB=sqlite3' }).should be_empty
end
it 'does not select builds with non-matching keys' do
build = Factory(:build, config: { rvm: ['1.8.7', '1.9.2'], env: ['DB=sqlite3', 'DB=postgresql'] })
build.filter_matrix({ rvm: '1.8.7', nomatch: 'DB=sqlite3' }).should == [build.matrix[0], build.matrix[1]]
end
end
describe 'does not explode' do
it 'on a config key that is `true`' do
config = { true => 'broken' }
build = Factory(:build, config: config, repository: Factory(:repository))
expect { build.expand_matrix }.to_not raise_error
end
it 'on bad matrix include values' do
config = { matrix: { include: ['broken'] } }
build = Factory(:build, config: config, repository: Factory(:repository))
expect { build.expand_matrix }.to_not raise_error
end
it 'on config[:matrix] being an array' do
config = { matrix: [{ foo: 'kaputt' }] }
build = Factory(:build, config: config, repository: Factory(:repository))
expect { build.expand_matrix }.to_not raise_error
end
end
# describe 'matrix_keys_for' do
# let(:config_default_lang) { { 'rvm' => ['1.8.7', '1.9.2'], 'env' => ['DB=sqlite3', 'DB=postgresql'] } }
# let(:config_non_def_lang) { { 'language' => 'scala', 'rvm' => ['1.8.7', '1.9.2'], 'env' => ['DB=sqlite3', 'DB=postgresql'] } }
# let(:config_lang_array) { { 'language' => ['scala'], 'rvm' => ['1.8.7', '1.9.2'], 'env' => ['DB=sqlite3', 'DB=postgresql'] } }
# let(:config_unrecognized) { { 'language' => 'bash', 'rvm' => ['1.8.7', '1.9.2'], 'env' => ['DB=sqlite3', 'DB=postgresql'] } }
# it 'only selects appropriate keys' do
# Build.matrix_keys_for(config_default_lang).should == [:rvm, :env]
# Build.matrix_keys_for(config_non_def_lang).should == [:env]
# Build.matrix_keys_for(config_lang_array).should == [:env]
# Build.matrix_keys_for(config_unrecognized).should == [:rvm, :env]
# end
# end
end

View File

@ -0,0 +1,42 @@
require 'spec_helper_core'
class BuildMetricsMock
include do
attr_accessor :state, :request
def initialize(request)
@request = request
end
def start(data = {})
self.state = :started
end
def started_at
Time.now
end
end
include Build::Metrics
end
describe Build::Metrics do
let(:request) { stub('request', created_at: Time.now - 60) }
let(:build) { BuildMetricsMock.new(request) }
let(:timer) { stub('meter', :update) }
before :each do
Metriks.stubs(:timer).returns(timer)
end
it 'measures on "travis.builds.start.delay"' do
Metriks.expects(:timer).with('travis.builds.start.delay').returns(timer)
build.start(started_at: Time.now)
end
it 'measures the time it takes from creating the request until starting the build' do
timer.expects(:update).with(60)
build.start(started_at: Time.now)
end
end

View File

@ -0,0 +1,148 @@
require 'spec_helper_core'
describe Build::ResultMessage do
def message(data)
described_class.new(data)
end
describe "short" do
it 'returns :pending if the build is pending' do
data = { state: :created, previous_state: nil }
message(data).short.should == 'Pending'
end
it 'returns :passed if the build has passed for the first time' do
data = { state: :passed, previous_state: nil }
message(data).short.should == 'Passed'
end
it 'returns :failed if the build has failed for the first time' do
data = { state: :failed, previous_state: nil }
message(data).short.should == 'Failed'
end
it 'returns :passed if the build has passed again' do
data = { state: :passed, previous_state: :passed }
message(data).short.should == 'Passed'
end
it 'returns :broken if the build was broken' do
data = { state: :failed, previous_state: :passed }
message(data).short.should == 'Broken'
end
it 'returns :fixed if the build was fixed' do
data = { state: :passed, previous_state: :failed }
message(data).short.should == 'Fixed'
end
it 'returns :failing if the build has failed again' do
data = { state: :failed, previous_state: :failed }
message(data).short.should == 'Still Failing'
end
it 'returns :errored if the build has errored' do
data = { state: :errored, previous_state: :failed }
message(data).short.should == 'Errored'
end
it 'returns :canceled if the build has canceled' do
data = { state: :canceled, previous_state: :failed }
message(data).short.should == 'Canceled'
end
end
describe "full" do
it 'returns :pending if the build is pending' do
data = { state: :created, previous_state: nil }
message(data).full.should == 'The build is pending.'
end
it 'returns :passed if the build has passed for the first time' do
data = { state: :passed, previous_state: nil }
message(data).full.should == 'The build passed.'
end
it 'returns :failed if the build has failed for the first time' do
data = { state: :failed, previous_state: nil }
message(data).full.should == 'The build failed.'
end
it 'returns :passed if the build has passed again' do
data = { state: :passed, previous_state: :passed }
message(data).full.should == 'The build passed.'
end
it 'returns :broken if the build was broken' do
data = { state: :failed, previous_state: :passed }
message(data).full.should == 'The build was broken.'
end
it 'returns :fixed if the build was fixed' do
data = { state: :passed, previous_state: :failed }
message(data).full.should == 'The build was fixed.'
end
it 'returns :failing if the build has failed again' do
data = { state: :failed, previous_state: :failed }
message(data).full.should == 'The build is still failing.'
end
it 'returns :errored if the build has errored' do
data = { state: :errored, previous_state: :failed }
message(data).full.should == 'The build has errored.'
end
it 'returns :canceled if the build has canceled' do
data = { state: :canceled, previous_state: :failed }
message(data).full.should == 'The build was canceled.'
end
end
describe "email" do
it 'returns :pending if the build is pending' do
data = { state: :created, previous_state: nil, number: 2 }
message(data).email.should == 'Build #2 is pending.'
end
it 'returns :passed if the build has passed for the first time' do
data = { state: :passed, previous_state: nil, number: 2 }
message(data).email.should == 'Build #2 passed.'
end
it 'returns :failed if the build has failed for the first time' do
data = { state: :failed, previous_state: nil, number: 2 }
message(data).email.should == 'Build #2 failed.'
end
it 'returns :passed if the build has passed again' do
data = { state: :passed, previous_state: :passed, number: 2 }
message(data).email.should == 'Build #2 passed.'
end
it 'returns :broken if the build was broken' do
data = { state: :failed, previous_state: :passed, number: 2 }
message(data).email.should == 'Build #2 was broken.'
end
it 'returns :fixed if the build was fixed' do
data = { state: :passed, previous_state: :failed, number: 2 }
message(data).email.should == 'Build #2 was fixed.'
end
it 'returns :failing if the build has failed again' do
data = { state: :failed, previous_state: :failed, number: 2 }
message(data).email.should == 'Build #2 is still failing.'
end
it 'returns :errored if the build has errored' do
data = { state: :errored, previous_state: :failed, number: 2 }
message(data).email.should == 'Build #2 has errored.'
end
it 'returns :canceled if the build has canceled' do
data = { state: :canceled, previous_state: :failed, number: 2 }
message(data).email.should == 'Build #2 was canceled.'
end
end
end

View File

@ -0,0 +1,249 @@
require 'spec_helper_core'
class BuildMock
include Build::States
class << self; def name; 'Build'; end; end
attr_accessor :state, :received_at, :started_at, :finished_at, :duration
def denormalize(*) end
end
describe Build::States do
include Support::ActiveRecord
let(:build) { BuildMock.new }
describe 'events' do
describe 'cancel' do
it 'cancels all the cancelable jobs' do
build = Factory(:build)
build.matrix.destroy_all
created_job = Factory(:test, source: build, state: :created)
finished_jobs = Job::Test::FINISHED_STATES.map do |state|
Factory(:test, source: build, state: state)
end
build.reload
expect {
build.cancel!
}.to change { created_job.reload.state }
created_job.state.should == 'canceled'
finished_jobs.map { |j| j.state.to_sym }.should == Job::Test::FINISHED_STATES
end
end
describe 'reset' do
before :each do
build.stubs(:write_attribute)
end
it 'does not set the state to created if any jobs in the matrix are running' do
build.stubs(matrix: [stub(state: :started)])
build.reset
build.state.should_not == :started
end
it 'sets the state to created if none of the jobs in the matrix are running' do
build.stubs(matrix: [stub(state: :passed)])
build.reset
build.state.should == :created
end
end
describe 'receive' do
let(:data) { WORKER_PAYLOADS['job:test:receive'] }
it 'does not denormalize attributes' do
build.denormalize?('job:test:receive').should be false
end
describe 'when the build is not already received' do
it 'sets the state to :received' do
build.receive(data)
build.state.should == :received
end
it 'notifies observers' do
Travis::Event.expects(:dispatch).with('build:received', build, data)
build.receive(data)
end
end
describe 'when the build is already received' do
before :each do
build.state = :received
end
it 'does not notify observers' do
Travis::Event.expects(:dispatch).never
build.receive(data)
end
end
describe 'when the build has failed' do
before :each do
build.state = :failed
end
it 'does not notify observers' do
Travis::Event.expects(:dispatch).never
build.receive(data)
end
end
describe 'when the build has errored' do
before :each do
build.state = :errored
end
it 'does not notify observers' do
Travis::Event.expects(:dispatch).never
build.receive(data)
end
end
end
describe 'start' do
let(:data) { WORKER_PAYLOADS['job:test:start'] }
describe 'when the build is not already started' do
it 'sets the state to :started' do
build.start(data)
build.state.should == :started
end
it 'denormalizes attributes' do
build.expects(:denormalize)
build.start(data)
end
it 'notifies observers' do
Travis::Event.expects(:dispatch).with('build:started', build, data)
build.start(data)
end
end
describe 'when the build is already started' do
before :each do
build.state = :started
end
it 'does not denormalize attributes' do
build.expects(:denormalize).never
build.start(data)
end
it 'does not notify observers' do
Travis::Event.expects(:dispatch).never
build.start(data)
end
end
describe 'when the build has failed' do
before :each do
build.state = :failed
end
it 'does not denormalize attributes' do
build.expects(:denormalize).never
build.start(data)
end
it 'does not notify observers' do
Travis::Event.expects(:dispatch).never
build.start(data)
end
end
describe 'when the build has errored' do
before :each do
build.state = :errored
end
it 'does not denormalize attributes' do
build.expects(:denormalize).never
build.start(data)
end
it 'does not notify observers' do
Travis::Event.expects(:dispatch).never
build.start(data)
end
end
end
describe 'finish' do
let(:data) { WORKER_PAYLOADS['job:test:finish'] }
describe 'when the matrix is not finished' do
before(:each) do
build.stubs(matrix_finished?: false)
end
describe 'when the build is already finished' do
before(:each) do
build.state = :finished
end
it 'does not denormalize attributes' do
build.expects(:denormalize).never
build.finish(data)
end
it 'does not notify observers' do
Travis::Event.expects(:dispatch).never
build.finish(data)
end
end
end
describe 'when the matrix is finished' do
before(:each) do
build.stubs(matrix_finished?: true, matrix_state: :passed, matrix_duration: 30)
end
describe 'when the build has not finished' do
before(:each) do
build.state = :started
build.expects(:save!)
end
it 'sets the state to the matrix state' do
build.finish(data)
build.state.should == :passed
end
it 'calculates the duration based on the matrix durations' do
build.finish(data)
build.duration.should == 30
end
it 'denormalizes attributes' do
build.expects(:denormalize).with(:finish, data)
build.finish(data)
end
it 'notifies observers' do
Travis::Event.expects(:dispatch).with('build:finished', build, data)
build.finish(data)
end
end
describe 'when the build has already finished' do
before(:each) do
build.state = :passed
end
it 'does not denormalize attributes' do
build.expects(:denormalize).never
build.finish(data)
end
it 'does not notify observers' do
Travis::Event.expects(:dispatch).never
build.finish(data)
end
end
end
end
end
end

View File

@ -0,0 +1,57 @@
require 'spec_helper_core'
describe Build::UpdateBranch, truncation: true do
include Support::ActiveRecord
let(:request) { Factory.create(:request, event_type: event_type) }
let(:build) { Factory.build(:build, request: request, state: :started, duration: 30, branch: 'master') }
let(:branch) { Branch.where(repository_id: build.repository_id, name: build.branch).first }
subject { described_class.new(build) }
shared_examples_for 'updates the branch' do
describe 'creates branch if missing' do
before { build.save! }
it { branch.should_not be_nil }
it { branch.last_build_id.should be == build.id }
end
describe 'updates an existing branch' do
before { Branch.create!(repository_id: build.repository_id, name: 'master', last_build_id: 0) }
before { build.save! }
it { branch.should_not be_nil }
it { branch.last_build_id.should be == build.id }
end
end
shared_examples_for 'does not update the branch' do
describe 'does not create a branch' do
before { build.save! }
it { branch.should be_nil }
end
describe 'does update existing branchs' do
before { Branch.create!(repository_id: build.repository_id, name: 'master', last_build_id: 0) }
before { build.save! }
it { branch.should_not be_nil }
it { branch.last_build_id.should be == 0 }
end
end
describe 'on build creation' do
describe 'for push events' do
let(:event_type) { 'push' }
include_examples 'updates the branch'
end
describe 'for api events' do
let(:event_type) { 'api' }
include_examples 'updates the branch'
end
describe 'for pull request events' do
let(:event_type) { 'pull_request' }
include_examples 'does not update the branch'
end
end
end

View File

@ -0,0 +1,337 @@
require 'spec_helper_core'
describe Build, truncation: true do
include Support::ActiveRecord
let(:repository) { Factory(:repository) }
it 'caches matrix ids' do
build = Factory.create(:build, config: { rvm: ['1.9.3', '2.0.0'] })
build.cached_matrix_ids.should == build.matrix_ids
end
it 'returns nil if cached_matrix_ids are not set' do
build = Factory.create(:build)
build.update_column(:cached_matrix_ids, nil)
build.reload.cached_matrix_ids.should be_nil
end
it 'is cancelable if at least one job is cancelable' do
jobs = [Factory.build(:test), Factory.build(:test)]
jobs.first.stubs(:cancelable?).returns(true)
jobs.second.stubs(:cancelable?).returns(false)
build = Factory.build(:build, matrix: jobs)
build.should be_cancelable
end
it 'is not cancelable if none of the jobs are cancelable' do
jobs = [Factory.build(:test), Factory.build(:test)]
jobs.first.stubs(:cancelable?).returns(false)
jobs.second.stubs(:cancelable?).returns(false)
build = Factory.build(:build, matrix: jobs)
build.should_not be_cancelable
end
describe '#secure_env_enabled?' do
it 'returns true if we\'re not dealing with pull request' do
build = Factory.build(:build)
build.stubs(:pull_request?).returns(false)
build.secure_env_enabled?.should be true
end
it 'returns true if pull request is from the same repository' do
build = Factory.build(:build)
build.stubs(:pull_request?).returns(true)
build.stubs(:same_repo_pull_request?).returns(true)
build.secure_env_enabled?.should be true
end
it 'returns false if pull request is not from the same repository' do
build = Factory.build(:build)
build.stubs(:pull_request?).returns(true)
build.stubs(:same_repo_pull_request?).returns(false)
build.secure_env_enabled?.should be false
end
end
describe 'class methods' do
describe 'recent' do
it 'returns recent finished builds ordered by id descending' do
Factory(:build, state: 'passed')
Factory(:build, state: 'failed')
Factory(:build, state: 'created')
Build.recent.all.map(&:state).should == ['failed', 'passed']
end
end
describe 'was_started' do
it 'returns builds that are either started or finished' do
Factory(:build, state: 'passed')
Factory(:build, state: 'started')
Factory(:build, state: 'created')
Build.was_started.map(&:state).sort.should == ['passed', 'started']
end
end
describe 'on_branch' do
it 'returns builds that are on any of the given branches' do
Factory(:build, commit: Factory(:commit, branch: 'master'))
Factory(:build, commit: Factory(:commit, branch: 'develop'))
Factory(:build, commit: Factory(:commit, branch: 'feature'))
Build.on_branch('master,develop').map(&:commit).map(&:branch).sort.should == ['develop', 'master']
end
it 'does not include pull requests' do
Factory(:build, commit: Factory(:commit, branch: 'no-pull'), request: Factory(:request, event_type: 'pull_request'))
Factory(:build, commit: Factory(:commit, branch: 'no-pull'), request: Factory(:request, event_type: 'push'))
Build.on_branch('no-pull').count.should be == 1
end
end
describe 'older_than' do
before do
5.times { |i| Factory(:build, number: i) }
Build.stubs(:per_page).returns(2)
end
context "when a Build is passed in" do
subject { Build.older_than(Build.new(number: 3)) }
it "should limit the results" do
expect(subject.size).to eq(2)
end
it "should return older than the passed build" do
subject.map(&:number).should == ['2', '1']
end
end
context "when a number is passed in" do
subject { Build.older_than(3) }
it "should limit the results" do
expect(subject.size).to eq(2)
end
it "should return older than the passed build" do
subject.map(&:number).should == ['2', '1']
end
end
context "when not passing a build" do
subject { Build.older_than() }
it "should limit the results" do
expect(subject.size).to eq(2)
end
end
end
describe 'paged' do
it 'limits the results to the `per_page` value' do
3.times { Factory(:build) }
Build.stubs(:per_page).returns(1)
expect(Build.descending.paged({}).size).to eq(1)
end
it 'uses an offset' do
3.times { |i| Factory(:build) }
Build.stubs(:per_page).returns(1)
builds = Build.descending.paged({page: 2})
expect(builds.size).to eq(1)
builds.first.number.should == '2'
end
end
describe 'pushes' do
before do
Factory(:build)
Factory(:build, request: Factory(:request, event_type: 'pull_request'))
end
it "returns only builds which have Requests with an event_type of push" do
Build.pushes.all.count.should == 1
end
end
describe 'pull_requests' do
before do
Factory(:build)
Factory(:build, request: Factory(:request, event_type: 'pull_request'))
end
it "returns only builds which have Requests with an event_type of pull_request" do
Build.pull_requests.all.count.should == 1
end
end
end
describe 'creation' do
describe 'previous_state' do
it 'is set to the last finished build state on the same branch' do
Factory(:build, state: 'failed')
Factory(:build).reload.previous_state.should == 'failed'
end
it 'is set to the last finished build state on the same branch (disregards non-finished builds)' do
Factory(:build, state: 'failed')
Factory(:build, state: 'started')
Factory(:build).reload.previous_state.should == 'failed'
end
it 'is set to the last finished build state on the same branch (disregards other branches)' do
Factory(:build, state: 'failed')
Factory(:build, state: 'passed', commit: Factory(:commit, branch: 'something'))
Factory(:build).reload.previous_state.should == 'failed'
end
end
it "updates the last_build on the build's branch" do
build = FactoryGirl.create(:build)
branch = Branch.where(repository_id: build.repository_id, name: build.branch).first
branch.last_build.should == build
end
end
describe 'instance methods' do
it 'sets its number to the next build number on creation' do
1.upto(3) do |number|
Factory(:build).reload.number.should == number.to_s
end
end
it 'sets previous_state to nil if no last build exists on the same branch' do
build = Factory(:build, commit: Factory(:commit, branch: 'master'))
build.reload.previous_state.should == nil
end
it 'sets previous_state to the result of the last build on the same branch if exists' do
build = Factory(:build, state: :canceled, commit: Factory(:commit, branch: 'master'))
build = Factory(:build, commit: Factory(:commit, branch: 'master'))
build.reload.previous_state.should == 'canceled'
end
describe 'config' do
it 'defaults to a hash with language and os set' do
build = Build.new(repository: Repository.new(owner: User.new))
build.config.should == { language: 'ruby', group: 'stable', dist: 'precise', os: 'linux' }
end
it 'deep_symbolizes keys on write' do
build = Factory(:build, config: { 'foo' => { 'bar' => 'bar' } })
build.read_attribute(:config)[:foo].should == { bar: 'bar' }
end
it 'downcases the language on config' do
build = Factory.create(:build, config: { language: "PYTHON" })
Build.last.config[:language].should == "python"
end
it 'sets ruby as default language' do
build = Factory.create(:build, config: { 'foo' => { 'bar' => 'bar' } })
Build.last.config[:language].should == "ruby"
end
end
describe :pending? do
it 'returns true if the build is finished' do
build = Factory(:build, state: :finished)
build.pending?.should be false
end
it 'returns true if the build is not finished' do
build = Factory(:build, state: :started)
build.pending?.should be true
end
end
describe :passed? do
it 'passed? returns true if state equals :passed' do
build = Factory(:build, state: :passed)
build.passed?.should be true
end
it 'passed? returns true if result does not equal :passed' do
build = Factory(:build, state: :failed)
build.passed?.should be false
end
end
describe :color do
it 'returns "green" if the build has passed' do
build = Factory(:build, state: :passed)
build.color.should == 'green'
end
it 'returns "red" if the build has failed' do
build = Factory(:build, state: :failed)
build.color.should == 'red'
end
it 'returns "yellow" if the build is pending' do
build = Factory(:build, state: :started)
build.color.should == 'yellow'
end
end
it 'saves event_type before create' do
build = Factory(:build, request: Factory(:request, event_type: 'pull_request'))
build.event_type.should == 'pull_request'
build = Factory(:build, request: Factory(:request, event_type: 'push'))
build.event_type.should == 'push'
end
it 'saves pull_request_title before create' do
payload = { 'pull_request' => { 'title' => 'A pull request' } }
build = Factory(:build, request: Factory(:request, event_type: 'pull_request', payload: payload))
build.pull_request_title.should == 'A pull request'
end
it 'saves branch before create' do
build = Factory(:build, commit: Factory(:commit, branch: 'development'))
build.branch.should == 'development'
end
describe 'reset' do
let(:build) { Factory(:build, state: 'finished') }
before :each do
build.matrix.each { |job| job.stubs(:reset) }
end
it 'sets the state to :created' do
build.reset
build.state.should == :created
end
it 'resets related attributes' do
build.reset
build.duration.should be_nil
build.finished_at.should be_nil
end
it 'resets each job if :reset_matrix is given' do
build.matrix.each { |job| job.expects(:reset) }
build.reset(reset_matrix: true)
end
it 'does not reset jobs if :reset_matrix is not given' do
build.matrix.each { |job| job.expects(:reset).never }
build.reset
end
it 'notifies obsevers' do
Travis::Event.expects(:dispatch).with('build:created', build)
build.reset
end
end
end
end

View File

@ -0,0 +1,92 @@
require 'spec_helper_core'
describe Commit do
include Support::ActiveRecord
let(:commit) { Commit.new(:commit => '12345678') }
describe 'pull_request_number' do
context 'when commit is from pull request' do
before { commit.ref = 'refs/pull/180/merge' }
it 'returns pull request\'s number' do
commit.pull_request_number.should == 180
end
end
context 'when commit is not from pull request' do
before { commit.ref = 'refs/branch/master' }
it 'returns nil' do
commit.pull_request_number.should be_nil
end
end
end
describe 'pull_request?' do
it 'is false for a nil ref' do
commit.ref = nil
commit.pull_request?.should be_falsey
end
it 'is false for a ref named ref/branch/master' do
commit.ref = 'refs/branch/master'
commit.pull_request?.should be_falsey
end
it 'is false for a ref named ref/pull/180/head' do
commit.ref = 'refs/pull/180/head'
commit.pull_request?.should be_falsey
end
it 'is true for a ref named ref/pull/180/merge' do
commit.ref = 'refs/pull/180/merge'
commit.pull_request?.should be_truthy
end
end
describe '#range' do
context 'with compare_url present' do
before { commit.compare_url = 'https://github.com/rails/rails/compare/ffaab2c4ffee...60790e852a4f' }
it 'returns range' do
commit.range.should == 'ffaab2c4ffee...60790e852a4f'
end
end
context 'with a compare_url with ^ in it' do
before { commit.compare_url = 'https://github.com/rails/rails/compare/ffaab2c4ffee^...60790e852a4f' }
it 'returns range' do
commit.range.should == 'ffaab2c4ffee^...60790e852a4f'
end
end
context 'with invalid compare_url' do
before { commit.compare_url = 'https://github.com/rails/rails/compare/ffaab2c4ffee.....60790e852a4f' }
it 'returns nil' do
commit.range.should be_nil
end
end
context 'without compare_url' do
before { commit.compare_url = nil }
it 'returns nil' do
commit.range.should be_nil
end
end
context 'for a pull request' do
before do
commit.ref = 'refs/pull/1/merge'
commit.request = Request.new(:base_commit => 'abcdef', :head_commit => '123456')
end
it 'returns range' do
commit.range.should == 'abcdef...123456'
end
end
end
end

View File

@ -0,0 +1,126 @@
require 'spec_helper_core'
class Travis::Model < ActiveRecord::Base
describe EncryptedColumn do
def encode str
Base64.strict_encode64 str
end
let(:options){ { key: 'secret-key' } }
let(:column) { EncryptedColumn.new(options) }
let(:iv) { 'a' * 16 }
let(:aes) { stub('aes', :final => '') }
describe '#encrypt?' do
it 'does not encrypt if given data is empty' do
column.encrypt?(nil).should be false
column.encrypt?('').should be false
end
context 'when disabled' do
let(:options) { { disable: true, key: 'secret-key' } }
it 'does not encrypt' do
column.encrypt?('--ENCR--abc').should be false
end
end
end
describe '#decrypt?' do
it 'does not decrypt if given data is empty' do
column.decrypt?(nil).should be false
column.decrypt?('').should be false
end
end
context 'when encryption is disabled' do
before { column.stubs :encrypt? => false }
describe '#dump' do
it 'does not encrypt data' do
column.dump('123qwe').should == '123qwe'
end
end
end
it 'allows to pass use_prefix as an option' do
EncryptedColumn.new(use_prefix: true).use_prefix?.should be true
end
it 'allows to pass key as an option' do
EncryptedColumn.new(key: 'foobarbaz').key.should == 'foobarbaz'
end
context 'when encryption is enabled' do
before { column.stubs :encrypt? => true }
context 'when prefix usage is disabled' do
before { column.stubs :use_prefix? => false }
describe '#load' do
it 'decrypts data even with no prefix' do
data = encode "to-decrypt#{iv}"
column.expects(:create_aes).with(:decrypt, 'secret-key', iv).returns(aes)
aes.expects(:update).with('to-decrypt').returns('decrypted')
column.load(data).should == 'decrypted'
end
it 'removes prefix if prefix is still used' do
data = encode "to-decrypt#{iv}"
data = "#{column.prefix}#{data}"
column.expects(:create_aes).with(:decrypt, 'secret-key', iv).returns(aes)
aes.expects(:update).with('to-decrypt').returns('decrypted')
column.load(data).should == 'decrypted'
end
end
describe '#dump' do
it 'attaches iv to encrypted string' do
column.stubs(:iv => iv)
column.expects(:create_aes).with(:encrypt, 'secret-key', iv).returns(aes)
aes.expects(:update).with('to-encrypt').returns('encrypted')
column.dump('to-encrypt').should == encode("encrypted#{iv}")
end
end
end
context 'when prefix usage is enabled' do
before { column.stubs :use_prefix? => true }
describe '#load' do
it 'does not decrypt data if prefix is not used' do
data = 'abc'
column.load(data).should == data
end
it 'decrypts data if prefix is used' do
data = encode "to-decrypt#{iv}"
data = "#{column.prefix}#{data}"
column.expects(:create_aes).with(:decrypt, 'secret-key', iv).returns(aes)
aes.expects(:update).with('to-decrypt').returns('decrypted')
column.load(data).should == 'decrypted'
end
end
describe '#dump' do
it 'attaches iv and prefix to encrypted string' do
column.stubs(:iv => iv)
column.expects(:create_aes).with(:encrypt, 'secret-key', iv).returns(aes)
aes.expects(:update).with('to-encrypt').returns('encrypted')
result = encode "encrypted#{iv}"
column.dump('to-encrypt').should == "#{column.prefix}#{result}"
end
end
end
end
end
end

View File

@ -0,0 +1,45 @@
# require 'spec_helper_core'
#
# describe Job::Cleanup do
# include Support::ActiveRecord
#
# let(:job) { Factory(:test) }
#
# describe 'scopes' do
# let! :jobs do
# [ Factory(:test, :state => :created, :created_at => Time.now.utc - Travis.config.jobs.retry.after - 60),
# Factory(:test, :state => :started, :created_at => Time.now.utc - Travis.config.jobs.retry.after - 120),
# Factory(:test, :state => :finished, :created_at => Time.now.utc - Travis.config.jobs.retry.after + 10) ]
# end
#
# describe :unfinished do
# it 'finds unfinished jobs' do
# # TODO fixme
# # Job.unfinished.should == jobs[0, 2]
# Job.unfinished.should include(jobs.first)
# Job.unfinished.should include(jobs.second)
# end
# end
#
# describe :stalled do
# it 'finds stalled jobs' do
# Job.stalled.order(:id).should == jobs[0, 2]
# end
# end
# end
#
# describe :force_finish do
# # TODO @flippingbits, could you look into this?
# xit 'appends a message to the log' do
# job.force_finish
# job.reload.log.content.should == "some log.\n#{Job::Requeueing::FORCE_FINISH_MESSAGE}"
# end
#
# it 'finishes the job' do
# job.force_finish
# job.finished?.should be_truthy
# end
# end
# end
#
#

View File

@ -0,0 +1,310 @@
require 'spec_helper_core'
describe 'Job::Queue' do
def queue(*args)
Job::Queue.new(*args)
end
let(:the_past) { Time.parse("1982-06-23") }
let(:recently) { 7.days.ago }
before do
Travis.config.queues = [
{ queue: 'builds.rails', slug: 'rails/rails' },
{ queue: 'builds.mac_osx', os: 'osx' },
{ queue: 'builds.docker', sudo: false },
{ queue: 'builds.gce', services: %w(docker) },
{ queue: 'builds.gce', dist: 'trusty' },
{ queue: 'builds.cloudfoundry', owner: 'cloudfoundry' },
{ queue: 'builds.clojure', language: 'clojure' },
{ queue: 'builds.erlang', language: 'erlang' },
]
Job::Queue.instance_variable_set(:@queues, nil)
Job::Queue.instance_variable_set(:@default, nil)
Travis::Features.stubs(:owner_active?).returns(true)
Travis::Github::Education.stubs(:education_queue?).returns(false)
end
after do
Travis.config.default_queue = 'builds.linux'
end
it 'returns builds.linux as the default queue' do
Job::Queue.default.name.should == 'builds.linux'
end
it 'returns builds.common as the default queue if configured to in Travis.config' do
Travis.config.default_queue = 'builds.common'
Job::Queue.default.name.should == 'builds.common'
end
describe 'Queue.sudo_detected?' do
[
[{ script: 'sudo echo' }, true],
[{ bogus: 'sudo echo' }, false],
[{ before_install: ['# no sudo', 'ping -c 1 google.com'] }, true],
[{ before_install: ['docker run busybox echo whatever'] }, true],
[{ before_script: ['echo ; echo ; echo ; sudo echo ; echo'] }, true],
[{ install: '# no sudo needed here' }, false],
[{ install: true }, false],
].each do |config, expected|
it "returns #{expected} for #{config}" do
Job::Queue.sudo_detected?(config).should == expected
end
end
end
describe 'Queue.for' do
it 'returns the default build queue when neither slug or language match the given configuration hash' do
job = stub('job', :config => {}, :repository => stub('repository', :owner_name => 'travis-ci', :name => 'travis-ci', :owner => stub, :created_at => the_past))
Job::Queue.for(job).name.should == 'builds.linux'
end
it 'returns the queue when slug matches the given configuration hash' do
job = stub('job', :config => {}, :repository => stub('repository', :owner_name => 'rails', :name => 'rails', :owner => stub, :created_at => the_past))
Job::Queue.for(job).name.should == 'builds.rails'
end
it 'returns the queue when language matches the given configuration hash' do
job = stub('job', :config => { :language => 'clojure' }, :repository => stub('repository', :owner_name => 'travis-ci', :name => 'travis-ci', :owner => stub, :created_at => the_past))
Job::Queue.for(job).name.should == 'builds.clojure'
end
it 'returns the queue when the owner matches the given configuration hash' do
job = stub('job', :config => {}, :repository => stub('repository', :owner_name => 'cloudfoundry', :name => 'bosh', :owner => stub, :created_at => the_past))
Job::Queue.for(job).name.should == 'builds.cloudfoundry'
end
it 'returns the queue when sudo requirements matches the given configuration hash' do
job = stub('job', :config => { sudo: false }, :repository => stub('repository', :owner_name => 'markronson', :name => 'recordcollection', :owner => stub, :created_at => the_past))
Job::Queue.for(job).name.should == 'builds.docker'
end
it 'returns the docker queue by default for educational repositories' do
Travis::Github::Education.stubs(:education_queue?).returns(true)
owner = stub('owner', :education => true)
job = stub('job', :config => { }, :repository => stub('repository', :owner_name => 'markronson', :name => 'recordcollection', :owner => owner, :created_at => the_past))
Job::Queue.for(job).name.should == 'builds.docker'
end
it 'returns the queue matching configuration for educational repository' do
Travis::Github::Education.stubs(:education_queue?).returns(true)
owner = stub('owner', :education => true)
job = stub('job', :config => { :os => 'osx' }, :repository => stub('repository', :owner_name => 'markronson', :name => 'recordcollection', :owner => owner, :created_at => the_past))
Job::Queue.for(job).name.should == 'builds.mac_osx'
end
it 'handles language being passed as an array gracefully' do
job = stub('job', :config => { :language => ['clojure'] }, :repository => stub('repository', :owner_name => 'travis-ci', :name => 'travis-ci', :owner => stub, :created_at => the_past))
Job::Queue.for(job).name.should == 'builds.clojure'
end
context 'when "os" value matches the given configuration hash' do
it 'returns the matching queue' do
job = stub('job', :config => { :os => 'osx'}, :repository => stub('travis-core', :owner_name => 'travis-ci', :name => 'bosh', :owner => stub, :created_at => the_past))
Job::Queue.for(job).name.should == 'builds.mac_osx'
end
it 'returns the matching queue when language is also given' do
job = stub('job', :config => {:language => 'clojure', :os => 'osx'}, :repository => stub('travis-core', :owner_name => 'travis-ci', :name => 'bosh', :owner => stub, :created_at => the_past))
Job::Queue.for(job).name.should == 'builds.mac_osx'
end
end
context 'when "services" value matches the given configuration hash' do
it 'returns the matching queue' do
job = stub('job', config: { services: %w(redis docker postgresql) }, repository: stub('travis-core', owner_name: 'travis-ci', name: 'bosh', owner: stub, created_at: the_past))
Job::Queue.for(job).name.should == 'builds.gce'
end
it 'returns the matching queue when language is also given' do
job = stub('job', config: { language: 'clojure', services: %w(redis docker postgresql) }, repository: stub('travis-core', owner_name: 'travis-ci', name: 'bosh', owner: stub, created_at: the_past))
Job::Queue.for(job).name.should == 'builds.gce'
end
end
context 'when "docker_default_queue" feature is active' do
before do
Travis::Features.stubs(:feature_active?).with(:docker_default_queue).returns(true)
Travis::Features.stubs(:feature_active?).with(:education).returns(true)
end
it 'returns "builds.docker" when sudo: nil and the repo created_at is nil' do
job = stub('job', :config => { }, :repository => stub('repository', :owner_name => 'travis-ci', :name => 'travis-core', :owner => stub, :created_at => nil))
Job::Queue.for(job).name.should == 'builds.docker'
end
it 'returns "builds.docker" when sudo: nil and the repo created_at is after cutoff' do
Travis.config.docker_default_queue_cutoff = recently.to_s
job = stub('job', :config => { }, :repository => stub('repository', :owner_name => 'travis-ci', :name => 'travis-core', :owner => stub, :created_at => Time.now))
Job::Queue.for(job).name.should == 'builds.docker'
end
it 'returns "builds.linux" when sudo: nil and the repo created_at is before cutoff' do
Travis.config.docker_default_queue_cutoff = recently.to_s
job = stub('job', :config => { }, :repository => stub('repository', :owner_name => 'travis-ci', :name => 'travis-core', :owner => stub, :created_at => recently - 7.days))
Job::Queue.for(job).name.should == 'builds.linux'
end
it 'returns "builds.linux" when sudo: nil and the repo created_at is after cutoff and sudo is detected' do
Travis.config.docker_default_queue_cutoff = recently.to_s
job = stub('job', :config => { script: 'sudo echo whatever' }, :repository => stub('repository', :owner_name => 'travis-ci', :name => 'travis-core', :owner => stub, :created_at => recently - 7.days))
Job::Queue.for(job).name.should == 'builds.linux'
end
it 'returns "builds.docker" when sudo: false and the repo created_at is nil' do
job = stub('job', :config => { sudo: false }, :repository => stub('repository', :owner_name => 'travis-ci', :name => 'travis-core', :owner => stub, :created_at => nil))
Job::Queue.for(job).name.should == 'builds.docker'
end
it 'returns "builds.docker" when sudo: false and the repo created_at is after cutoff' do
Travis.config.docker_default_queue_cutoff = recently.to_s
job = stub('job', :config => { sudo: false }, :repository => stub('repository', :owner_name => 'travis-ci', :name => 'travis-core', :owner => stub, :created_at => Time.now))
Job::Queue.for(job).name.should == 'builds.docker'
end
it 'returns "builds.docker" when sudo: false and the repo created_at is before cutoff' do
Travis.config.docker_default_queue_cutoff = recently.to_s
job = stub('job', :config => { sudo: false }, :repository => stub('repository', :owner_name => 'travis-ci', :name => 'travis-core', :owner => stub, :created_at => recently - 7.days))
Job::Queue.for(job).name.should == 'builds.docker'
end
[true, 'required'].each do |sudo|
it %{returns "builds.linux" when sudo: #{sudo} and the repo created_at is nil} do
Travis.config.docker_default_queue_cutoff = recently.to_s
job = stub('job', :config => { sudo: sudo }, :repository => stub('repository', :owner_name => 'travis-ci', :name => 'travis-core', :owner => stub, :created_at => nil))
Job::Queue.for(job).name.should == 'builds.linux'
end
it %{returns "builds.linux" when sudo: #{sudo} and the repo created_at is after cutoff} do
Travis.config.docker_default_queue_cutoff = recently.to_s
job = stub('job', :config => { sudo: sudo }, :repository => stub('repository', :owner_name => 'travis-ci', :name => 'travis-core', :owner => stub, :created_at => nil))
Job::Queue.for(job).name.should == 'builds.linux'
end
it %{returns "builds.linux" when sudo: #{sudo} and the repo created_at is before cutoff} do
Travis.config.docker_default_queue_cutoff = recently.to_s
job = stub('job', :config => { sudo: sudo }, :repository => stub('repository', :owner_name => 'travis-ci', :name => 'travis-core', :owner => stub, :created_at => nil))
Job::Queue.for(job).name.should == 'builds.linux'
end
end
end
end
context 'when "sudo" value matches the given configuration hash' do
it 'returns the matching queue' do
job = stub('job', config: { sudo: false }, repository: stub('travis-core', owner_name: 'travis-ci', name: 'travis-core', owner: stub, :created_at => the_past))
Job::Queue.for(job).name.should == 'builds.docker'
end
it 'returns the matching queue when language is also given' do
job = stub('job', config: { language: 'clojure', sudo: false }, repository: stub('travis-core', owner_name: 'travis-ci', name: 'travis-core', owner: stub, :created_at => the_past))
Job::Queue.for(job).name.should == 'builds.docker'
end
end
describe 'Queue.queues' do
it 'returns an array of Queues for the config hash' do
rails, _, docker, _, _, cloudfoundry, clojure, _ = Job::Queue.send(:queues)
rails.name.should == 'builds.rails'
rails.attrs[:slug].should == 'rails/rails'
docker.name.should == 'builds.docker'
docker.attrs[:sudo].should == false
cloudfoundry.name.should == 'builds.cloudfoundry'
cloudfoundry.attrs[:owner].should == 'cloudfoundry'
clojure.name.should == 'builds.clojure'
clojure.attrs[:language].should == 'clojure'
end
end
describe 'matches?' do
it "returns false when neither of slug or language match" do
queue = queue('builds.linux', {})
queue.matches?(stub('job', repository: stub('repository', owner_name: 'foo', name: 'bar', owner: nil), config: { language: 'COBOL' })).should be false
end
it "returns true when the given owner matches" do
queue = queue('builds.cloudfoundry', { owner: 'cloudfoundry' })
queue.matches?(stub('job', repository: stub('repository', owner_name: 'cloudfoundry', name: 'bosh', owner: nil), config: {})).should be true
end
it "returns true when the given slug matches" do
queue = queue('builds.rails', { slug: 'rails/rails' })
queue.matches?(stub('job', repository: stub('repository', owner_name: 'rails', name: 'rails', owner: nil), config: {})).should be true
end
it "returns true when the given language matches" do
queue = queue('builds.linux', { language: 'clojure' })
queue.matches?(stub('job', repository: stub('repository', owner_name: nil, name: nil, owner: nil), config: { language: 'clojure' })).should be true
end
it 'returns true when os is missing' do
queue = queue('builds.linux', { language: 'clojure' })
queue.matches?(stub('job', repository: stub('repository', owner_name: nil, name: nil, owner: nil), config: { language: 'clojure' })).should be true
end
it 'returns true when sudo is false' do
queue = queue('builds.docker', { sudo: false })
queue.matches?(stub('job', repository: stub('repository', owner_name: nil, name: nil, owner: nil), config: { sudo: false })).should be true
end
it 'returns false when sudo is true' do
queue = queue('builds.docker', { sudo: false })
queue.matches?(stub('job', repository: stub('repository', owner_name: nil, name: nil, owner: nil), config: { sudo: true })).should be false
end
it 'returns false when sudo is not specified' do
queue = queue('builds.docker', { sudo: false })
queue.matches?(stub('job', repository: stub('repository', owner_name: nil, name: nil, owner: nil), config: {})).should be false
end
it 'returns true when dist matches' do
queue = queue('builds.gce', { dist: 'trusty' })
queue.matches?(stub('job', repository: stub('repository', owner_name: nil, name: nil, owner: nil), config: { dist: 'trusty' })).should be true
end
it 'returns false when dist does not match' do
queue = queue('builds.docker', { dist: 'precise' })
queue.matches?(stub('job', repository: stub('repository', owner_name: nil, name: nil, owner: nil), config: { dist: 'trusty' })).should be false
end
it 'returns true when osx_image matches' do
queue = queue('builds.mac_beta', { osx_image: 'beta' })
queue.matches?(stub('job', repository: stub('repository', owner_name: nil, name: nil, owner: nil), config: { osx_image: 'beta' })).should be true
end
it 'returns false when osx_image does not match' do
queue = queue('builds.mac_stable', { osx_image: 'stable' })
queue.matches?(stub('job', repository: stub('repository', owner_name: nil, name: nil, owner: nil), config: { osx_image: 'beta' })).should be false
end
it 'returns true when services match' do
queue = queue('builds.gce', { services: %w(docker) })
queue.matches?(stub('job', repository: stub('repository', owner_name: nil, name: nil, owner: nil), config: { services: %w(redis docker postgresql) })).should be true
end
it 'returns false when services do not match' do
queue = queue('builds.gce', { services: %w(docker) })
queue.matches?(stub('job', repository: stub('repository', owner_name: nil, name: nil, owner: nil), config: { services: %w(redis postgresql) })).should be false
end
it 'returns false if no valid matchers are specified' do
queue = queue('builds.invalid', { foobar_donotmatch: true })
queue.matches?(stub('job', repository: stub('repository', owner_name: nil, name: nil, owner: nil), config: {})).should be false
end
it 'returns true for percentage: 100' do
queue = queue('builds.always', { percentage: 100 })
queue.matches?(stub('job', repository: stub('repository', owner_name: nil, name: nil, owner: nil), config: {})).should be true
end
it 'returns false for percentage: 0' do
queue = queue('builds.always', { percentage: 0 })
queue.matches?(stub('job', repository: stub('repository', owner_name: nil, name: nil, owner: nil), config: {})).should be false
end
end
end

View File

@ -0,0 +1,190 @@
require 'spec_helper_core'
describe Job::Test do
include Support::ActiveRecord
let(:job) { Factory(:test) }
before :each do
Travis::Event.stubs(:dispatch)
end
it 'is cancelable if the job has not finished yet' do
job = Factory(:test, state: :created)
job.should be_cancelable
job = Factory(:test, state: :started)
job.should be_cancelable
end
it 'is not cancelable if the job has already been finished' do
job = Factory(:test, state: :passed)
job.should_not be_cancelable
end
describe 'cancelling' do
it 'should not propagate cancel state to source' do
build = Factory(:build, state: :started)
build.matrix.destroy_all
job = Factory(:test, state: :created, source: build)
Factory(:test, state: :started, source: build)
build.reload
expect {
job.cancel!
}.to_not change { job.source.reload.state }
end
it 'should put a build into canceled state if all the jobs in matrix are in finished state' do
build = Factory(:build, state: :started)
build.matrix.destroy_all
job = Factory(:test, state: :created, source: build)
Job::Test::FINISHED_STATES.each do |state|
Factory(:test, source: build, state: state)
end
build.reload
expect {
expect {
expect {
job.cancel!
}.to change { build.state }
}.to change { build.canceled_at }
}.to change { build.repository.reload.last_build_state }
build.reload.state.should == 'canceled'
build.repository.last_build_state.should == 'canceled'
end
it 'should set canceled_at and finished_at on job' do
job = Factory(:test, state: :created)
expect {
expect {
job.cancel!
}.to change { job.canceled_at }
}.to change { job.finished_at }
end
end
describe 'events' do
describe 'receive' do
let(:data) { WORKER_PAYLOADS['job:test:receive'] }
it 'sets the state to :received' do
job.receive(data)
job.state.should == :received
end
it 'sets the worker from the payload' do
job.receive(data)
job.worker.should == 'ruby3.worker.travis-ci.org:travis-ruby-4'
end
it 'resets the log content' do
job.log.expects(:update_attributes!).with(content: '', removed_at: nil, removed_by: nil)
job.receive(data)
end
it 'notifies observers' do
Travis::Event.expects(:dispatch).with('job:test:received', job, data)
job.receive(data)
end
it 'propagates the event to the source' do
job.source.expects(:receive)
job.receive(data)
end
it 'sets log\'s removed_at and removed_by to nil' do
job.log.removed_at = Time.now
job.log.removed_by = job.repository.owner
job.receive(data)
job.log.removed_at.should be_nil
job.log.removed_by.should be_nil
end
end
describe 'start' do
let(:data) { WORKER_PAYLOADS['job:test:start'] }
it 'sets the state to :started' do
job.start(data)
job.state.should == :started
end
it 'notifies observers' do
Travis::Event.expects(:dispatch).with('job:test:started', job, data)
job.start(data)
end
it 'propagates the event to the source' do
job.source.expects(:start)
job.start(data)
end
end
describe 'finish' do
let(:data) { WORKER_PAYLOADS['job:test:finish'] }
it 'sets the state to the given result state' do
job.finish(data)
job.state.should == 'passed'
end
it 'notifies observers' do
Travis::Event.expects(:dispatch).with('job:test:finished', job, data)
job.finish(data)
end
it 'propagates the event to the source' do
job.source.expects(:finish).with(data)
job.finish(data)
end
end
describe 'reset' do
let(:job) { Factory(:test, state: 'finished', queued_at: Time.now, finished_at: Time.now) }
it 'sets the state to :created' do
job.reset!
job.reload.state.should == 'created'
end
it 'resets job attributes' do
job.reset!
job.reload.queued_at.should be_nil
job.reload.finished_at.should be_nil
end
it 'resets log attributes' do
job.log.update_attributes!(content: 'foo', aggregated_at: Time.now)
job.reset!
job.reload.log.aggregated_at.should be_nil
job.reload.log.content.should be_blank
end
it 'recreates log if it\'s removed' do
job.log.destroy
job.reload
job.reset!
job.reload.log.should_not be_nil
end
xit 'clears log parts' do
end
it 'destroys annotations' do
job.annotations << Factory(:annotation)
job.reload
job.reset!
job.reload.annotations.should be_empty
end
it 'triggers a :created event' do
job.expects(:notify).with(:reset)
job.reset
end
end
end
end

View File

@ -0,0 +1,471 @@
require 'spec_helper_core'
describe Job do
include Support::ActiveRecord
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 == 10
end
end
describe 'obfuscated config' do
it 'handles nil env' do
job = Job.new(repository: Factory(:repository))
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: Factory(:repository))
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: Factory(:repository))
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: Factory(:repository))
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: Factory(:repository))
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 whitelisted' do
job = Job.new(repository: Factory(:repository))
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: Factory(:repository))
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: Factory(:repository))
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
it 'handles nil env' do
job = Job.new(repository: Factory(:repository))
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: Factory(:repository))
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: Factory(:repository))
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: Factory(:repository))
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: Factory(:repository))
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: Factory(:repository))
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 whitelisted' 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: Factory(:repository))
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: Factory(:repository))
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

View File

@ -0,0 +1,22 @@
require 'spec_helper_core'
describe User do
include Support::ActiveRecord
let(:org) { Factory.create(:org, :login => 'travis-organization') }
describe 'educational_org' do
after do
Travis::Features.deactivate_owner(:educational_org, org)
end
it 'returns true if organization is flagged as educational_org' do
Travis::Features.activate_owner(:educational_org, org)
org.education?.should be true
end
it 'returns false if the organization has not been flagged as educational_org' do
org.education?.should be false
end
end
end

View File

@ -0,0 +1,23 @@
require 'spec_helper_core'
describe Permission do
include Support::ActiveRecord
describe 'by_roles' do
before :each do
Permission::ROLES.each { |role| Permission.create!(role => true) }
end
it 'returns matching permissions if two roles given as symbols' do
Permission.by_roles([:admin, :pull]).size.should == 2
end
it 'returns a single permission if one role given' do
Permission.by_roles('admin').size.should == 1
end
it 'returns an empty scope if no roles given' do
Permission.by_roles('').size.should == 0
end
end
end

View File

@ -0,0 +1,104 @@
# encoding: utf-8
require 'spec_helper_core'
describe Repository::Settings::SshKey do
let(:private_key) {
"-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEAuHVhiw/2qaGmIiRbKjO5bZmQI0UEQ1vQVVxujL5SFAUsEB3w
YvcsdK+EDrmFfhrvVde1PnWzuwdq4seftZvTni+5KIjpNJ6/YKfYVUEOQ5ORvuqb
zrYPWzJShXnqvrFpbF82unHs4ceb+XzV/2Tciy/p5Yv535yweLRJKtcwK+ANLz8O
wF+IZf20StugF7tZaXMCXD1ieGg6fv5eV7lohfxYCeRTVJsMUxnwcrLxeqi8IgAa
q44IsIayTn5jcBZMwir8W+PrlXq44WHyLWnErXCH0Pds1UrbL6HFz5+uU0xoMO0N
vG1T0er1KIOooQ2dnbcH8UoDnrYSsn5mWFq1fwIDAQABAoIBABB5Qz3lLhVWP30b
HB03w167cTkFJ+1QHNoSyDi/oprxH09NLTPZeVnudu/Nt9NcWnWjLyel4WhZsD0S
sPvKL+sXvgSVvaYaa2MZemOazMhSPJj9YO7kKZjudJpBGirvs0efdUbPd+VuK0rr
0Dzf6CZyIASFLMrAtq4BA+vUjhPM5tmQqwhuZVkrr+GstCvJa2W2K4hbpZ+1XyhX
++XX4QnvQ1HXjVxo4LSXV05oJ9OBbiCh+OEkME3X3vPuy62E0WngFyH67NlryR1O
AyqrDPALf0Fl/1IXwGOZNsjHUQr8j+lbAE3uxwS7KNwlvEmJ8Hc2LdRwmlvTim9f
xWRGaWkCgYEA6GJ/FBgSykBs0FvYqvAs8O2Y1Rh1YJrInwSI/nG4yXEUDvmM+rFB
7Cb2AhTamw4VlHbu2dvUuVh4I2u1GVESrIy2+SV2xfzieoEG6+HWvRAm5owqKq2u
HSM3GFN2VGZzYl1J1260J9wlWHoPZV8vpgzD/VulN7x/TAXXwv/u0FMCgYEAyzQU
rIlbqsomx3G4Nzi4Nr9nLaKkRiAmSITzEVojItRWJRAKZtrMPV57JlGfNIqzAzAS
MWkcZr4XLvn6gxks37rrl7NtgRTTyq9MLTx5opMqQalYUIpp8PHMJ0vevdqVGgmS
FOP17SEyO2Tnc0UYAezyS8VuQ30u2ReJx/PJ0KUCgYBI3vIok+/4ekFlCRglalE9
b9Q4JoZQN9lnfB2VZIXkrU/z7i9WQZWBfyovtuhiLQV5W95EdNn9ERADU3gjqzem
4i1SbXwUU9uVPLa160jSWqlILHXgkjwCKRPSzgFSMBpIoyZPpwhZY4BWgVgomrOv
Z1tiLIXft31XkpF5NZZmvwKBgGsBJu3geywJvbgDE13I+YCi9CNc5SKkZWSE1jbJ
/3yk0iQ8OS4Gg8zBRxpbmvmhHDlOhBYO4szbxvuO2bNVe4LpPIyrCLwTip/OBdBA
a1EILBVdpsrqyHT/72C2HDpfs2p9pbZogKV5eKk8LoFN3iGNc94gvjq93gCl24E2
yIydAoGBALrYhMbK+ljTaqn4IsC0CwG5S6dLA/uXLn4QosDGtCqi1iXWKb8ixRho
x9giBf4WDeH3Gb2TBF1QnB8sbhHJAzTW/CO3vOjRiFSSF7EjxjCFier/LfuDU1Kr
tFns8eTxHpZOYOftxpX91vS3tzKCKgkdPhnYBDrvFFWnGgRLXFpb
-----END RSA PRIVATE KEY-----
"
}
it 'validates correctness of private key' do
ssh_key = described_class.new(value: private_key)
ssh_key.should be_valid
ssh_key.value = 'foo'
ssh_key.should_not be_valid
ssh_key.errors[:value].should == [:not_a_private_key]
end
it 'allows only private key' do
public_key = OpenSSL::PKey::RSA.new(private_key).public_key.to_s
ssh_key = described_class.new(value: public_key)
ssh_key.should_not be_valid
ssh_key.errors[:value].should == [:not_a_private_key]
end
it 'does not check key if a value is nil' do
ssh_key = described_class.new({})
ssh_key.should_not be_valid
ssh_key.errors[:value].should == [:blank]
end
describe 'with a passphrase' do
let(:private_key) {
"-----BEGIN RSA PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: AES-128-CBC,A2EDFC4C1196A9F3A58F327650CA6A1A
hfJtbnTxMhnqNLbRi1KGKE1rX3ypef5tIQPC+2OyUH4MQDTpaz5INoMqk1AjOz+O
LfmPZcy+5g3w9HJCHkGFU2kNmseHWCyoPQ3t9BKzaVWun4IxoMn7K2hZaebsyPQx
sq7vyDKAflgUKlkZgHWIimJ1lJH0CJB/3mplc16NeNqv1AaICFJagYHwPGHfVxa/
CQLPd2nw4LGmxEvmfuVq4qiSsYTqUkBA4wqgEX4bWGbkDZF2mKJvL/5AM5Cei+cy
ZhB4zm0mORoNQnGHbolYstHfm6h7RjlYDV60WC7iBnRRNuWmktzA+oOjH8RqQ29Z
LDcjyg4Rl0BsRwjkppOSScO6QAt4h740ZYHv7I/m3UAIl33xHjlz7PJb8aOzYqgC
QDFNJOr+AJx3tamY0Hg7v2l77oWQX8hHeJEbIySbftzIX+UrpSWFlcTNm4xpffI0
yO0Id2wY1mMOSs3yzNQ0AlGJR9Ns3P+2RjbyAJnuKI2ZctcBdlZSEiz/aavNW3ql
mv5FAzP4tSkTaWCnJaf8RAL0CSr5ppycWYGnZYbem6Bh9Cwe4f7PmQL5RJ/Y2rMc
V5ir+CFiVPFZP7by0OVz1Hg8XjynyCXej6J21el2hUyTI7oLxh/CxHW2lmqgUwYA
SNGGqMrYVQKxs+yp6i62OhKTl93jmW+8mE9VX6jIKBQJ7GBf44YLALwgxLyO6lCP
yY3dXI2QTb1StBHZhyQHazoghs3/6vEAEC8kj2U2NLBxlk8+caSEIrNoWRNCOAo+
1p75ZHrPDuTirDsyascOJ0Yff+O+uzCiqf9aPxxVJllhC2l6LGCwU1dsZ+O2RuaI
anH8OvFpSUhQY97vEnRDuPei/jz/C2/oZJzIdXCUmPvVn7Ut23m9A/1x6Mq3FA1X
iN09gB3XzsJZpxQ3TEMg+pp6bV64O5yZghAAWmKzJOmZ2j2BxCbuX3H399EAW3hc
sB/TxCh6kjiMOltbHtKsclsNZ9YQmk5+x4LwX4BSCV6YnytS+I49eMx1ikLX5nJR
tVAVsF5oE31pgg8lUgIWRJdK7EjjX/cDfkJYf9NWSwFCYxUKB+2adX/ix0eI1NGH
oJ+AD9tUrMAnNDTgFM4n5rtYDwf0fuA1C9RjJP8NcPm7oNlpyP2VQbtr3rSwmx+c
xYQuIZxqYbO+iwlJDuAst1n7dDIzPtnea/KUEQy4u7jmONKQ1VdA9dyGvqy4y8ie
bVDfnAzAvO17Zbvmqk0zQRmYXRsLuIN6QyWsfi8e2O7FctcpRVgc4e5xmBTfztBL
Q5feJ50wqE+JNfL5dQp8N0NtWFA6d4RLMN1T8zLhYASO/NOOzQtR7X+NlMrtm0wQ
aNDoTrIrg7xpuQOlMOe9UCwfcHu+DoPxUzZrqzhPlCGYSbecBW6G4+S4FPL5LpsW
NrYx30C8A87A0eEUNzLxO3CoPv7XhN/b0xf7W2CA79gbZnhgPtF12/11VmRo8ckl
zkrhrvtsjexdwYje7xjngPXrZ9USh13CoYNlduTlWB72m+wN8W7zyCLn1Zl/grTI
76Z2FZiqBEuPEcoDRrNUmX6MeNcMRo8Zq1FRi8imYnKYC0YsJMU0N+kIsiuGQsOI
-----END RSA PRIVATE KEY-----
"
}
it 'returns key_with_a_passphrase validation error' do
ssh_key = described_class.new(value: private_key)
ssh_key.should_not be_valid
ssh_key.errors[:value].should == [:key_with_a_passphrase]
end
end
end

View File

@ -0,0 +1,140 @@
# encoding: utf-8
require 'spec_helper_core'
describe Repository::Settings do
describe 'env_vars' do
it 'can be filtered to get only public vars' do
settings = Repository::Settings.load(env_vars: [
{ name: 'PUBLIC_VAR', value: 'public var', public: true },
{ name: 'SECRET_VAR', value: 'secret var', public: false }
])
settings.env_vars.public.length.should == 1
settings.env_vars.public.first.name.should == 'PUBLIC_VAR'
end
end
describe '#maximum_number_of_builds' do
it 'defaults to 0' do
settings = Repository::Settings.new(maximum_number_of_builds: nil)
settings.maximum_number_of_builds.should == 0
end
end
describe '#restricts_number_of_builds?' do
it 'returns true if number of builds is restricted' do
settings = Repository::Settings.new(maximum_number_of_builds: 2)
settings.restricts_number_of_builds?.should be true
end
it 'returns false if builds are not restricted' do
settings = Repository::Settings.new(maximum_number_of_builds: 0)
settings.restricts_number_of_builds?.should be false
end
end
it 'validates maximum_number_of_builds' do
settings = Repository::Settings.new
settings.maximum_number_of_builds = nil
settings.should be_valid
settings.maximum_number_of_builds = 'foo'
settings.should_not be_valid
settings.errors[:maximum_number_of_builds].should == [:not_a_number]
settings.maximum_number_of_builds = 0
settings.should be_valid
end
describe '#api_builds_rate_limit' do
it 'saves new api_builds_rate_limit if rate is under 200' do
settings = Repository::Settings.new(api_builds_rate_limit: 2)
settings.should be_valid
end
it 'does not save new api_builds_rate_limit if rate is over 200' do
settings = Repository::Settings.new(api_builds_rate_limit: 201)
settings.should_not be_valid
end
it 'returns nil if no api_builds_rate_limit is set on settings' do
settings = Repository::Settings.new()
settings.api_builds_rate_limit.should eq(nil)
end
end
describe 'timeouts' do
MAX = {
off: { hard_limit: 50, log_silence: 10 },
on: { hard_limit: 180, log_silence: 60 }
}
[:hard_limit, :log_silence].each do |type|
describe type do
def settings(type, value)
Repository::Settings.load({ :"timeout_#{type}" => value }, repository_id: 1)
end
it 'defaults to nil' do
settings(type, nil).send(:"timeout_#{type}").should be_nil
end
it "is valid if #{type} is nil" do
settings(type, nil).should be_valid
end
it 'returns nil if set to 0' do
settings(type, 0).send(:"timeout_#{type}").should be_nil
end
it "is valid if #{type} is set to 0" do
settings(type, 0).should be_valid
end
[:off, :on].each do |status|
describe "with :custom_timeouts feature flag turned #{status}" do
max = MAX[status][type]
before :each do
Travis::Features.stubs(:repository_active?).with(:custom_timeouts, 1).returns true if status == :on
end
describe 'is valid' do
it "if #{type} is nil" do
settings(type, nil).should be_valid
end
it "if #{type} is > 0" do
settings(type, 1).should be_valid
end
it "if #{type} is < #{max}" do
settings(type, max - 1).should be_valid
end
it "if #{type} equals #{max}" do
settings(type, max).should be_valid
end
end
describe 'is invalid' do
it "if #{type} is < 0" do
settings(type, -1).should_not be_valid
end
it "if #{type} is > #{max}" do
settings(type, max + 1).should_not be_valid
end
end
it 'adds an error message if invalid' do
model = settings(type, - 1)
model.valid?
model.errors[:"timeout_#{type}"].should == ["Invalid #{type} timout value (allowed: 0 - #{max})"]
end
end
end
end
end
end
end

View File

@ -0,0 +1,105 @@
require 'spec_helper_core'
describe Repository::StatusImage do
include Support::ActiveRecord
let(:cache) { stub('states cache', fetch: nil, write: nil, fetch_state: nil) }
let!(:request) { Factory(:request, event_type: 'push', repository: repo) }
let!(:build) { Factory(:build, repository: repo, request: request, state: :passed) }
let(:repo) { Factory(:repository) }
before do
described_class.any_instance.stubs(cache: cache)
described_class.any_instance.stubs(:cache_enabled? => true)
end
describe('with cache') do
it 'tries to get state from cache first' do
image = described_class.new(repo, 'foobar')
cache.expects(:fetch_state).with(repo.id, 'foobar').returns(:passed)
image.result.should == :passing
end
it 'saves state to the cache if it needs to be fetched from the db' do
image = described_class.new(repo, 'master')
cache.expects(:fetch_state).with(repo.id, 'master').returns(nil)
cache.expects(:write).with(repo.id, 'master', build)
image.result.should == :passing
end
it 'saves state of the build to the cache with its branch even if brianch is not given' do
image = described_class.new(repo, nil)
cache.expects(:fetch_state).with(repo.id, nil).returns(nil)
cache.expects(:write).with(repo.id, 'master', build)
image.result.should == :passing
end
it 'handles cache failures gracefully' do
image = described_class.new(repo, nil)
cache.expects(:fetch_state).raises(Travis::StatesCache::CacheError)
expect {
image.result.should == :passing
}.to_not raise_error
end
end
describe 'given no branch' do
it 'returns the status of the last finished build' do
image = described_class.new(repo, nil)
image.result.should == :passing
end
it 'returns :failing if the status of the last finished build is failed' do
build.update_attributes(state: :failed)
image = described_class.new(repo, nil)
image.result.should == :failing
end
it 'returns :error if the status of the last finished build is errored' do
build.update_attributes(state: :errored)
image = described_class.new(repo, nil)
image.result.should == :error
end
it 'returns :canceled if the status of the last finished build is canceled' do
build.update_attributes(state: 'canceled')
image = described_class.new(repo, nil)
image.result.should == :canceled
end
it 'returns :unknown if the status of the last finished build is unknown' do
build.update_attributes(state: :created)
image = described_class.new(repo, nil)
image.result.should == :unknown
end
end
describe 'given a branch' do
it 'returns :passed if the last build on that branch has passed' do
build.update_attributes(state: :passed, branch: 'master')
image = described_class.new(repo, 'master')
image.result.should == :passing
end
it 'returns :failed if the last build on that branch has failed' do
build.update_attributes(state: :failed, branch: 'develop')
image = described_class.new(repo, 'develop')
image.result.should == :failing
end
it 'returns :error if the last build on that branch has errored' do
build.update_attributes(state: :errored, branch: 'develop')
image = described_class.new(repo, 'develop')
image.result.should == :error
end
it 'returns :canceled if the last build on that branch was canceled' do
build.update_attributes(state: :canceled, branch: 'develop')
image = described_class.new(repo, 'develop')
image.result.should == :canceled
end
end
end

View File

@ -0,0 +1,435 @@
require 'spec_helper_core'
describe Repository, truncation: true do
include Support::ActiveRecord
describe '#last_completed_build' do
let(:repo) { Factory(:repository, name: 'foobarbaz', builds: [build1, build2]) }
let(:build1) { Factory(:build, finished_at: 1.hour.ago, state: :passed) }
let(:build2) { Factory(:build, finished_at: Time.now, state: :failed) }
before do
build1.update_attributes(branch: 'master')
build2.update_attributes(branch: 'development')
end
it 'returns last completed build' do
repo.last_completed_build.should == build2
end
it 'returns last completed build for a branch' do
repo.last_completed_build('master').should == build1
end
end
describe '#regenerate_key!' do
it 'regenerates key' do
repo = Factory(:repository)
expect { repo.regenerate_key! }.to change { repo.key.private_key }
end
end
describe 'associations' do
describe 'owner' do
let(:user) { Factory(:user) }
let(:org) { Factory(:org) }
it 'can be a user' do
repo = Factory(:repository, owner: user)
repo.reload.owner.should == user
end
it 'can be an organization' do
repo = Factory(:repository, owner: org)
repo.reload.owner.should == org
end
end
end
describe 'class methods' do
describe 'find_by' do
let(:minimal) { Factory(:repository) }
it "should find a repository by it's github_id" do
Repository.find_by(github_id: minimal.github_id).should == minimal
end
it "should find a repository by it's id" do
Repository.find_by(id: minimal.id).id.should == minimal.id
end
it "should find a repository by it's name and owner_name" do
repo = Repository.find_by(name: minimal.name, owner_name: minimal.owner_name)
repo.owner_name.should == minimal.owner_name
repo.name.should == minimal.name
end
it "returns nil when a repository couldn't be found using params" do
Repository.find_by(name: 'emptiness').should be_nil
end
end
describe 'timeline' do
before do
Factory(:repository, name: 'unbuilt 1', active: true, last_build_started_at: nil, last_build_finished_at: nil)
Factory(:repository, name: 'unbuilt 2', active: true, last_build_started_at: nil, last_build_finished_at: nil)
Factory(:repository, name: 'finished 1', active: true, last_build_started_at: '2011-11-12 12:00:00', last_build_finished_at: '2011-11-12 12:00:05')
Factory(:repository, name: 'finished 2', active: true, last_build_started_at: '2011-11-12 12:00:01', last_build_finished_at: '2011-11-11 12:00:06')
Factory(:repository, name: 'started 1', active: true, last_build_started_at: '2011-11-11 12:00:00', last_build_finished_at: nil)
Factory(:repository, name: 'started 2', active: true, last_build_started_at: '2011-11-11 12:00:01', last_build_finished_at: nil)
Factory(:repository, name: 'invalidated', active: true, last_build_started_at: '2011-11-11 12:00:01', last_build_finished_at: nil, invalidated_at: '2012-11-11 12:00:06')
end
it 'sorts repositories with running builds to the top, most recent builds next, un-built repos last' do
repositories = Repository.timeline
repositories.map(&:name).should == ['started 2', 'started 1', 'finished 2', 'finished 1', 'unbuilt 2', 'unbuilt 1']
end
it 'does not include invalidated repos' do
repositories = Repository.timeline
repositories.map(&:name).should_not include('invalidated')
end
end
describe 'with_builds' do
it 'gets only projects with existing builds' do
one = Factory(:repository, name: 'one', last_build_started_at: '2011-11-11', last_build_id: nil)
two = Factory(:repository, name: 'two', last_build_started_at: '2011-11-12', last_build_id: 101)
three = Factory(:repository, name: 'three', last_build_started_at: nil, last_build_id: 100)
repositories = Repository.with_builds.all
repositories.map(&:id).sort.should == [two, three].map(&:id).sort
end
end
describe 'active' do
let(:active) { Factory(:repository, active: true) }
let(:inactive) { Factory(:repository, active: false) }
let(:invalidated) { Factory(:repository, invalidated_at: Time.now) }
it 'contains active repositories' do
Repository.active.should include(active)
end
it 'does not include inactive repositories' do
Repository.active.should_not include(inactive)
end
it 'does not include invalidated repositories' do
Repository.active.should_not include(invalidated)
end
end
describe 'search' do
before(:each) do
Factory(:repository, name: 'repo 1', last_build_started_at: '2011-11-11')
Factory(:repository, name: 'repo 2', last_build_started_at: '2011-11-12')
Factory(:repository, name: 'invalidated', invalidated_at: Time.now)
end
it 'performs searches case-insensitive' do
Repository.search('rEpO').to_a.count.should == 2
end
it 'performs searches with / entered' do
Repository.search('fuchs/').to_a.count.should == 2
end
it 'performs searches with \ entered' do
Repository.search('fuchs\\').to_a.count.should == 2
end
it 'does not find invalidated repos' do
Repository.search('fuchs').map(&:name).should_not include('invalidated')
end
end
describe 'by_member' do
let(:user) { Factory(:user) }
let(:org) { Factory(:org) }
let(:user_repo) { Factory(:repository, owner: user)}
let(:org_repo) { Factory(:repository, owner: org, name: 'globalize')}
let(:invalidated) { Factory(:repository, owner: org, name: 'invalidated', invalidated_at: Time.now)}
before do
Permission.create!(user: user, repository: user_repo, pull: true, push: true)
Permission.create!(user: user, repository: org_repo, pull: true)
Permission.create!(user: user, repository: invalidated, pull: true)
end
it 'returns all repositories a user has rights to' do
expect(Repository.by_member('svenfuchs').size).to eq(2)
end
it 'does not find invalidated repos' do
Repository.by_member('svenfuchs').map(&:name).should_not include('invalidated')
end
end
describe 'counts_by_owner_names' do
let!(:repositories) do
Factory(:repository, owner_name: 'svenfuchs', name: 'minimal')
Factory(:repository, owner_name: 'travis-ci', name: 'travis-ci')
Factory(:repository, owner_name: 'travis-ci', name: 'invalidated', invalidated_at: Time.now)
end
it 'returns repository counts per owner_name for the given owner_names' do
counts = Repository.counts_by_owner_names(%w(svenfuchs travis-ci))
counts.should == { 'svenfuchs' => 1, 'travis-ci' => 1 }
end
end
end
describe 'api_url' do
let(:repo) { Repository.new(owner_name: 'travis-ci', name: 'travis-ci') }
before :each do
Travis.config.github.api_url = 'https://api.github.com'
end
it 'returns the api url for the repository' do
repo.api_url.should == 'https://api.github.com/repos/travis-ci/travis-ci'
end
end
describe 'source_url' do
describe 'default source endpoint' do
let(:repo) { Repository.new(owner_name: 'travis-ci', name: 'travis-ci') }
before :each do
Travis.config.github.source_host = nil
end
it 'returns the public git source url for a public repository' do
repo.private = false
repo.source_url.should == 'git://github.com/travis-ci/travis-ci.git'
end
it 'returns the private git source url for a private repository' do
repo.private = true
repo.source_url.should == 'git@github.com:travis-ci/travis-ci.git'
end
end
describe 'custom source endpoint' do
let(:repo) { Repository.new(owner_name: 'travis-ci', name: 'travis-ci') }
before :each do
Travis.config.github.source_host = 'localhost'
end
it 'returns the private git source url for a public repository' do
repo.private = false
repo.source_url.should == 'git@localhost:travis-ci/travis-ci.git'
end
it 'returns the private git source url for a private repository' do
repo.private = true
repo.source_url.should == 'git@localhost:travis-ci/travis-ci.git'
end
end
end
describe 'source_host' do
before :each do
Travis.config.github.stubs(:source_host).returns('localhost')
end
it 'returns the source_host name from Travis.config' do
Repository.new.source_host.should == 'localhost'
end
end
describe "#last_build" do
let(:repo) { Factory(:repository) }
let(:attributes) { { repository: repo, state: 'finished' } }
let(:api_req) { Factory(:request, {event_type: 'api'}) }
before :each do
Factory(:build, attributes)
Factory(:build, attributes)
end
context 'when last build is a push build' do
before :each do
@build = Factory(:build, attributes)
end
it 'returns the most recent build' do
repo.last_build('master').id.should == @build.id
end
end
context 'when last build is an API build' do
before :each do
@build = Factory(:build, attributes.merge({request: api_req}))
end
it 'returns the most recent build' do
repo.last_build('master').id.should == @build.id
end
end
end
describe '#last_build_on' do
let(:repo) { Factory(:repository) }
let(:attributes) { { repository: repo, state: 'finished' } }
let(:api_req) { Factory(:request, {event_type: 'api'}) }
before :each do
Factory(:build, attributes)
end
context 'when last build is a push build' do
before :each do
@build = Factory(:build, attributes)
end
it 'returns the most recent build' do
repo.last_build_on('master').id.should == @build.id
end
end
context 'when last build is an API build' do
before :each do
@build = Factory(:build, attributes.merge({request: api_req}))
end
it 'returns the most recent build' do
repo.last_build_on('master').id.should == @build.id
end
end
end
describe "keys" do
let(:repo) { Factory(:repository) }
it "should return the public key" do
repo.public_key.should == repo.key.public_key
end
it "should create a new key when the repository is created" do
repo = Repository.create!(owner_name: 'travis-ci', name: 'travis-ci')
repo.key.should_not be_nil
end
end
describe 'branches' do
let(:repo) { Factory(:repository) }
it 'returns branches for the given repository' do
%w(master production).each do |branch|
2.times { Factory(:build, repository: repo, commit: Factory(:commit, branch: branch)) }
end
repo.branches.sort.should == %w(master production)
end
it 'is empty for empty repository' do
repo.branches.should eql []
end
end
describe 'settings' do
let(:repo) { Factory.build(:repository) }
it 'adds repository_id to collection records' do
repo.save
env_var = repo.settings.env_vars.create(name: 'FOO')
env_var.repository_id.should == repo.id
repo.settings.save
repo.reload
repo.settings.env_vars.first.repository_id.should == repo.id
end
it "is reset on reload" do
repo.save
repo.settings = {}
repo.update_column(:settings, { 'build_pushes' => false }.to_json)
repo.reload
repo.settings.build_pushes?.should be false
repo.update_column(:settings, { 'build_pushes' => true }.to_json)
repo.reload
repo.settings.build_pushes?.should be true
end
it "allows to set nil for settings" do
repo.settings = nil
repo.settings.to_hash.should == Repository::Settings.new.to_hash
end
it "allows to set settings as JSON string" do
repo.settings = '{"maximum_number_of_builds": 44}'
repo.settings.to_hash.should == Repository::Settings.new(maximum_number_of_builds: 44).to_hash
end
it "allows to set settings as a Hash" do
repo.settings = { maximum_number_of_builds: 44}
repo.settings.to_hash.should == Repository::Settings.new(maximum_number_of_builds: 44).to_hash
end
it 'updates settings in the DB' do
repo.settings = {'build_pushes' => false}
repo.save
repo.reload.settings.build_pushes?.should == false
repo.settings.merge('build_pushes' => true)
repo.settings.save
repo.reload.settings.build_pushes?.should == true
end
end
describe 'last_finished_builds_by_branches' do
let(:repo) { Factory(:repository) }
it 'properly orders branches by last build' do
Build.delete_all
one = Factory(:build, repository: repo, finished_at: 2.hours.ago, state: 'finished', commit: Factory(:commit, branch: '1one'))
two = Factory(:build, repository: repo, finished_at: 1.hours.ago, state: 'finished', commit: Factory(:commit, branch: '2two'))
builds = repo.last_finished_builds_by_branches(1)
builds.should == [two]
end
it 'retrieves last builds on all branches' do
Build.delete_all
old = Factory(:build, repository: repo, finished_at: 1.hour.ago, state: 'finished', commit: Factory(:commit, branch: 'one'))
one = Factory(:build, repository: repo, finished_at: 1.hour.from_now, state: 'finished', commit: Factory(:commit, branch: 'one'))
two = Factory(:build, repository: repo, finished_at: 1.hour.from_now, state: 'finished', commit: Factory(:commit, branch: 'two'))
three = Factory(:build, repository: repo, finished_at: 1.hour.from_now, state: 'finished', commit: Factory(:commit, branch: 'three'))
three.update_attribute(:event_type, 'pull_request')
builds = repo.last_finished_builds_by_branches
builds.size.should == 2
builds.should include(one)
builds.should include(two)
builds.should_not include(old)
end
end
describe '#users_with_permission' do
it 'returns users with the given permission linked to that repository' do
repo = Factory(:repository)
other_repo = Factory(:repository)
user_with_permission = Factory(:user)
user_with_permission.permissions.create!(repository: repo, admin: true)
user_wrong_repo = Factory(:user)
user_wrong_repo.permissions.create!(repository: other_repo, admin: true)
user_wrong_permission = Factory(:user)
user_wrong_permission.permissions.create!(repository: repo, push: true)
repo.users_with_permission(:admin).should include(user_with_permission)
repo.users_with_permission(:admin).should_not include(user_wrong_repo)
repo.users_with_permission(:admin).should_not include(user_wrong_permission)
end
end
end

View File

@ -0,0 +1,272 @@
require 'spec_helper_core'
describe Request::Approval do
include Travis::Testing::Stubs
let(:approval) { Request::Approval.new(request) }
before do
approval.stubs(:build_pull_requests?).returns(true)
approval.stubs(:build_pushes?).returns(true)
request.stubs(:creates_jobs?).returns(true)
end
describe 'config_accepted?' do
it 'approves the build when .travis.yml is missing, but builds with .travis.yml are allowed' do
request.config['.result'] = 'not_found'
approval.config_accepted?.should be true
end
it 'does not approve the build if .travis.yml is missing and builds without it are not allowed' do
request.repository.stubs(:builds_only_with_travis_yml?).returns(true)
request.config['.result'] = 'not_found'
approval.config_accepted?.should be false
approval.message.should == '.travis.yml is missing and builds without .travis.yml are disabled'
end
it 'approves the build when .travis.yml is present' do
request.config['.result'] = 'configured'
approval.config_accepted?.should be true
end
end
describe 'branch_accepted?' do
it 'does not accept a request that belongs to the github_pages branch' do
request.commit.stubs(:branch).returns('gh_pages')
approval.branch_accepted?.should be false
end
it 'accepts a request that belongs to the gh-pages branch if it\'s specified in branches:only' do
request.commit.stubs(:branch).returns('gh_pages')
request.config['branches'] = { 'only' => ['gh-pages'] }
approval.branch_accepted?.should be_truthy
end
it "doesn't fail when the branch configuration is an array" do
request.config['branches'] = [{ 'only' => ['gh-pages'] }]
approval.branch_accepted?.should be true
end
end
describe 'accepted?' do
it 'accepts a request that has a commit, belongs to a public repository, is not skipped and does not belong to the github_pages branch and it is not a rails fork' do
approval.should be_accepted
end
it 'does not accept a request that does not have a commit' do
approval.stubs(:commit).returns(nil)
approval.should_not be_accepted
end
it 'does not accept a request that belongs to a private repository' do
request.repository.stubs(:private?).returns(true)
approval.should_not be_accepted
end
it 'does not accept a request that belongs to an excluded repository' do
request.repository.stubs(:slug).returns('svenfuchs/rails')
approval.should_not be_accepted
end
it 'does not accept a request that is skipped (using the commit message)' do
request.commit.stubs(:message).returns('update README [ci:skip]')
approval.should_not be_accepted
end
it 'accepts a request that belongs to the github_pages branch and is explicitly set to build that branch (String)' do
request.commit.stubs(:branch).returns('gh_pages')
request.stubs(:config).returns('branches' => { 'only' => 'gh_pages' })
approval.should be_accepted
end
it 'accepts a request that belongs to the github_pages branch and is explicitly set to build that branch (Array)' do
request.commit.stubs(:branch).returns('gh_pages')
request.stubs(:config).returns('branches' => { 'only' => ['gh_pages'] })
approval.should be_accepted
end
it 'does not accept a request when it is disabled in settings' do
approval.stubs(:enabled_in_settings?).returns(false)
approval.should_not be_accepted
end
it 'does not accept a request when compare URL is too long' do
request.commit.stubs(:compare_url).returns('a'*256)
approval.should_not be_accepted
end
end
describe 'approved?' do
xit 'should be specified'
end
describe 'message' do
it 'returns "pull requests disabled" if pull requests are disabled' do
approval.stubs(:enabled_in_settings?).returns(false)
request.stubs(:pull_request?).returns(true)
approval.message.should == 'pull requests disabled'
end
it 'returns "pushes disabled" if pushes are disabled' do
approval.stubs(:enabled_in_settings?).returns(false)
request.stubs(:pull_request?).returns(false)
approval.message.should == 'pushes disabled'
end
it 'returns "missing commit" if the commit is missing' do
approval.stubs(:commit).returns(nil)
approval.message.should == 'missing commit'
end
it 'returns "private repository" if the repository is private' do
request.repository.stubs(:private?).returns(true)
request.stubs(:config).returns({key: 'value'})
approval.message.should == 'private repository'
end
it 'returns "excluded repository" if the repository is an excluded repository' do
request.repository.stubs(:slug).returns('svenfuchs/rails')
approval.message.should == 'excluded repository'
end
it 'returns "excluded repository" if the repository is an excluded repository and exclude rule is a string' do
Travis.config.repository_filter.stubs(:exclude).returns(["\\/rails$"])
request.repository.stubs(:slug).returns('svenfuchs/rails')
approval.message.should == 'excluded repository'
end
it 'returns "github pages branch" if the branch is a github pages branch' do
request.commit.stubs(:branch).returns('gh-pages')
approval.message.should == 'github pages branch'
end
it 'returns "config is missing or contains YAML syntax error" if the config is not present' do
request.stubs(:config).returns(nil)
approval.message.should == 'config is missing or contains YAML syntax error'
end
it 'returns "branch not included or excluded" if the branch was not approved' do
request.commit.stubs(:branch).returns('feature')
request.stubs(:config).returns('branches' => { 'only' => 'master' })
approval.message.should == 'branch not included or excluded'
end
it 'returns "compare URL too long; branch/tag names may be too long" if the compare URL is too long' do
request.stubs(:config).returns({key: 'value'})
request.commit.stubs(:compare_url).returns('a'*256)
approval.message.should == 'compare URL too long; branch/tag names may be too long'
end
end
describe 'skipped?' do
it 'returns true when the commit message contains [ci skip]' do
request.commit.stubs(:message).returns 'lets party like its 1999 [ci skip]'
approval.send(:skipped?).should be true
end
end
describe 'github_pages?' do
it 'returns true for a branch named gh-pages' do
request.commit.stubs(:branch).returns 'gh-pages'
approval.send(:github_pages?).should be_truthy
end
it 'returns true for a branch named gh_pages' do
request.commit.stubs(:branch).returns 'gh_pages'
approval.send(:github_pages?).should be_truthy
end
it 'returns true when a PR is for gh_pages' do
request.commit.stubs(:ref).returns 'refs/pulls/1/merge'
request.commit.stubs(:branch).returns 'gh_pages'
approval.send(:github_pages?).should be_truthy
end
it 'returns false for a branch named master' do
commit.stubs(:branch).returns 'master'
approval.send(:github_pages?).should be_falsy
end
end
describe 'included_repository?' do
it 'returns true if the repository is an included repository' do
request.repository.stubs(:slug).returns 'rails/rails'
approval.send(:included_repository?).should be true
end
it 'returns true if the repository is an included repository with rule as a string' do
Travis.config.repository_filter.stubs(:include).returns(["rails\\/rails"])
request.repository.stubs(:slug).returns 'rails/rails'
approval.send(:included_repository?).should be true
end
it 'returns false if the repository is not included' do
request.repository.stubs(:slug).returns 'josh/completeness-fu'
approval.send(:included_repository?).should be false
end
it 'returns false if the repository is not included with rule as a string' do
Travis.config.repository_filter.stubs(:include).returns(["rails\\/rails"])
request.repository.stubs(:slug).returns 'josh/completeness-fu'
approval.send(:included_repository?).should be false
end
end
describe 'excluded_repository?' do
it 'returns true if the repository is an excluded repository' do
request.repository.stubs(:slug).returns 'josh/rails'
approval.send(:excluded_repository?).should be true
end
it 'returns false if the repository is not excluded' do
request.repository.stubs(:slug).returns 'josh/completeness-fu'
approval.send(:excluded_repository?).should be false
end
it 'returns true if the repository is an excluded repository with rule as a string' do
Travis.config.repository_filter.stubs(:exclude).returns(["\\/rails$"])
request.repository.stubs(:slug).returns 'josh/rails'
approval.send(:excluded_repository?).should be true
end
it 'returns false if the repository is not excluded with rule as a string' do
Travis.config.repository_filter.stubs(:exclude).returns(["\\/rails$"])
request.repository.stubs(:slug).returns 'josh/completeness-fu'
approval.send(:excluded_repository?).should be false
end
end
describe 'enabled_in_settings?' do
it 'returns true if a request is an api request' do
request.stubs(:api_request?).returns(true)
approval.enabled_in_settings?.should be true
end
it 'returns true if pull requests are enabled and a request is a pull request' do
request.stubs(:pull_request?).returns(true)
approval.stubs(:build_pull_requests?).returns(true)
approval.enabled_in_settings?.should be true
end
it 'returns true if pushes are enabled and a request is a push' do
request.stubs(:pull_request?).returns(false)
approval.stubs(:build_pushes?).returns(true)
approval.enabled_in_settings?.should be true
end
it 'returns false if pull requests are disabled and a request is a pull request' do
request.stubs(:pull_request?).returns(true)
approval.stubs(:build_pull_requests?).returns(false)
approval.enabled_in_settings?.should be false
end
it 'returns false if pushes are disabled and a request is a push' do
request.stubs(:pull_request?).returns(false)
approval.stubs(:build_pushes?).returns(false)
approval.enabled_in_settings?.should be false
end
end
end

View File

@ -0,0 +1,159 @@
require 'spec_helper_core'
describe Request::Branches do
include Travis::Testing::Stubs
let(:branches) { Request::Branches.new(request) }
describe '#included?' do
it 'defaults to true if no branches are included' do
request.config['branches'] = { 'only' => nil }
branches.included?('feature').should be true
end
describe 'returns true if the included branches include the given branch' do
it 'given as a string' do
request.config['branches'] = { 'only' => 'feature' }
branches.included?('feature').should be true
end
it 'given as a comma separated list of branches' do
request.config['branches'] = { 'only' => 'feature, develop' }
branches.included?('feature').should be true
end
it 'given as an array of branches' do
request.config['branches'] = { 'only' => %w(feature develop) }
branches.included?('feature').should be true
end
end
describe 'returns true if the given branch matches a pattern from the included branches' do
it 'given as a string' do
request.config['branches'] = { 'only' => '/^feature-\d+$/' }
branches.included?('feature-42').should be true
end
it 'given as a comma separated list of patterns' do
request.config['branches'] = { 'only' => '/^feature-\d+$/,/^develop-\d+$/' }
branches.included?('feature-42').should be true
end
it 'given as an array of patterns' do
request.config['branches'] = { 'only' => %w(/^feature-\d+$/ /^develop-\d+$/) }
branches.included?('feature-42').should be true
end
end
describe 'returns false if the included branches do not include the given branch' do
it 'given as a string' do
request.config['branches'] = { 'only' => 'feature' }
branches.included?('master').should be false
end
it 'given as a comma separated list of branches' do
request.config['branches'] = { 'only' => 'feature, develop' }
branches.included?('master').should be false
end
it 'given as an array of branches' do
request.config['branches'] = { 'only' => %w(feature develop) }
branches.included?('master').should be false
end
end
describe 'returns false if the given branch does not match any pattern from the included branches' do
it 'given as a string' do
request.config['branches'] = { 'only' => '/^feature-\d+$/' }
branches.included?('master').should be false
end
it 'given as a comma separated list of patterns' do
request.config['branches'] = { 'only' => '/^feature-\d+$/,/^develop-\d+$/' }
branches.included?('master').should be false
end
it 'given as an array of patterns' do
request.config['branches'] = { 'only' => %w(/^feature-\d+$/ /^develop-\d+$/) }
branches.included?('master').should be false
end
end
end
describe '#excluded?' do
it 'defaults to false if no branches are excluded' do
request.config['branches'] = { 'except' => nil }
branches.excluded?('feature').should be_falsy
end
describe 'returns true if the excluded branches include the given branch' do
it 'given as a string' do
request.config['branches'] = { 'except' => 'feature' }
branches.excluded?('feature').should be true
end
it 'given as a comma separated list of branches' do
request.config['branches'] = { 'except' => 'feature, develop' }
branches.excluded?('feature').should be true
end
it 'given as an array of branches' do
request.config['branches'] = { 'except' => %w(feature develop) }
branches.excluded?('feature').should be true
end
end
describe 'returns true if the given branch matches a pattern from the excluded branches' do
it 'given as a string' do
request.config['branches'] = { 'except' => '/^feature-\d+$/' }
branches.excluded?('feature-42').should be true
end
it 'given as a comma separated list of patterns' do
request.config['branches'] = { 'except' => '/^feature-\d+$/,/^develop-\d+$/' }
branches.excluded?('feature-42').should be true
end
it 'given as an array of patterns' do
request.config['branches'] = { 'except' => %w(/^feature-\d+$/ /^develop-\d+$/) }
branches.excluded?('feature-42').should be true
end
end
describe 'returns false if the excluded branches do not include the given branch' do
it 'given as a string' do
request.config['branches'] = { 'except' => 'feature' }
branches.excluded?('master').should be false
end
it 'given as a comma separated list of branches' do
request.config['branches'] = { 'except' => 'feature, develop' }
branches.excluded?('master').should be false
end
it 'given as an array of branches' do
request.config['branches'] = { 'except' => %w(feature develop) }
branches.excluded?('master').should be false
end
end
describe 'returns false if the given branch does not match any pattern from the excluded branches' do
it 'given as a string' do
request.config['branches'] = { 'except' => '/^feature-\d+$/' }
branches.excluded?('master').should be false
end
it 'given as a comma separated list of patterns' do
request.config['branches'] = { 'except' => '/^feature-\d+$/,/^develop-\d+$/' }
branches.excluded?('master').should be false
end
it 'given as an array of patterns' do
request.config['branches'] = { 'except' => %w(/^feature-\d+$/ /^develop-\d+$/) }
branches.excluded?('master').should be false
end
end
end
end

View File

@ -0,0 +1,254 @@
require 'spec_helper_core'
describe Request::States do
include Support::ActiveRecord
let(:owner) { User.new(:login => 'joshk') }
let(:repository) { Repository.new(:name => 'travis-ci', :owner => owner, :owner_name => 'travis-ci') }
let(:commit) { Commit.new(:repository => repository, :commit => '12345', :branch => 'master', :message => 'message', :committed_at => Time.now, :compare_url => 'https://github.com/svenfuchs/minimal/compare/master...develop') }
let(:request) { Request.new(:repository => repository, :commit => commit) }
let(:approval) { Request::Approval.any_instance }
let(:config) { { :from => '.travis.yml' } }
before :each do
repository.save!
Travis.stubs(:run_service).with(:github_fetch_config, is_a(Hash)).returns(config)
request.stubs(:add_build)
request.stubs(:creates_jobs?).returns(true)
end
it 'has the state :created when just created' do
request.state.should == :created
end
describe 'start' do
describe 'with an accepted request' do
before :each do
approval.stubs(:accepted?).returns(true)
end
it 'configures the request' do
request.expects(:configure)
request.start
end
it 'finishes the request' do
request.expects(:finish)
request.start
end
it 'sets the state to started' do
request.start
request.was_started?.should be true
end
it 'sets the result to :accepted' do
request.start
request.result.should == :accepted
end
describe 'but rejected config' do
before :each do
approval.stubs(:config_accepted?).returns(false)
end
it 'does config, but resets it to nil' do
request.expects(:fetch_config).returns({})
request.start
request.config.should be_nil
end
end
describe 'but rejected branch' do
before :each do
approval.stubs(:branch_accepted?).returns(false)
end
it 'does config, but resets it to nil' do
request.expects(:fetch_config).returns({})
request.start
request.config.should be_nil
end
end
end
describe 'with a rejected request' do
before :each do
approval.stubs(:accepted?).returns(false)
end
it 'does not configure the request' do
request.expects(:fetch_config).never
request.start
end
it 'finishes the request' do
request.expects(:finish)
request.start
end
it 'sets the state to started' do
request.start
request.was_started?.should be true
end
it 'sets the result to :rejected' do
request.start
request.result.should == :rejected
end
end
end
describe 'configure' do
it 'fetches the .travis.yml config from Github' do
Travis.expects(:run_service).returns(config)
request.configure
end
it 'merges existing configuration (e.g. from an api request)' do
request.config = { env: 'FOO=foo' }
request.configure
request.config.should == config.merge(env: 'FOO=foo')
end
it 'stores the config on the request' do
request.configure
request.config.should == config
end
it 'sets the state to configured' do
request.configure
request.was_configured?.should be true
end
end
describe 'finish' do
before :each do
request.stubs(:config).returns('.configured' => true)
end
describe 'with an approved request' do
before :each do
approval.stubs(:approved?).returns(true)
end
it 'builds the build' do
request.expects(:add_build)
request.finish
end
it 'sets the state to finished' do
request.finish
request.should be_finished
end
end
describe 'with an unapproved request' do
before :each do
approval.stubs(:approved?).returns(false)
end
it 'does not build the build' do
request.expects(:add_build).never
request.finish
end
it 'sets the state to finished' do
request.finish
request.should be_finished
end
end
describe 'with a config parse error' do
let(:job) { stub(start!: nil, finish!: nil, :log_content= => nil) }
let(:build) { stub(matrix: [job], finish!: nil) }
before :each do
request.stubs(:add_build).returns(build)
request.stubs(:config).returns('.result' => 'parse_error')
end
it 'builds the build' do
request.expects(:add_build).returns(build)
request.finish
end
it 'prints an error to the log' do
job.expects(:log_content=)
request.finish
end
end
describe 'with a config server error' do
let(:job) { stub(start!: nil, finish!: nil, :log_content= => nil) }
let(:build) { stub(matrix: [job], finish!: nil) }
before :each do
request.stubs(:add_build).returns(build)
request.stubs(:config).returns('.result' => 'server_error')
end
it 'builds the build' do
request.expects(:add_build).returns(build)
request.finish
end
it 'prints an error to the log' do
job.expects(:log_content=)
request.finish
end
end
end
describe 'start!' do
before :each do
request.stubs(:config).returns('.configured' => true)
approval.stubs(:approved?).returns(true)
end
it 'finally sets the state to finished' do
request.repository.save!
request.repository_id = request.repository.id
request.save!
request.start!
request.reload.should be_finished
end
end
describe "adding a build" do
before do
request.unstub(:add_build)
Travis.config.notify_on_build_created = true
end
after do
request.stubs(:add_build)
Travis.config.notify_on_build_created = false
end
it "should create a build" do
request.save
request.add_build_and_notify.should be_a(Build)
end
it "should notify the build" do
request.save
Travis::Event.expects(:dispatch).with do |event, *args|
event.should == "build:created"
end
request.add_build_and_notify
end
it "shouldn't notify the build when the flag is disabled" do
Travis.config.notify_on_build_created = false
request.save
Travis::Event.expects(:dispatch).with { |e, *| e.should == "build:created" }.never
request.add_build_and_notify
end
end
end

View File

@ -0,0 +1,155 @@
require 'spec_helper_core'
describe Request do
include Support::ActiveRecord
let(:repo) { Repository.new(owner_name: 'travis-ci', name: 'travis-ci') }
let(:commit) { Commit.new(commit: '12345678') }
let(:request) { Request.new(repository: repo, commit: commit) }
describe 'config_url' do
before :each do
GH.options.delete(:api_url)
GH.current = nil
end
after :each do
GH.set api_url: nil
end
it 'returns the api url to the .travis.yml file on github' do
request.config_url.should == 'https://api.github.com/repos/travis-ci/travis-ci/contents/.travis.yml?ref=12345678'
end
it 'returns the api url to the .travis.yml file on github with a gh endpoint given' do
GH.set api_url: 'http://localhost/api/v3'
request.config_url.should == 'http://localhost/api/v3/repos/travis-ci/travis-ci/contents/.travis.yml?ref=12345678'
end
end
describe 'api_request?' do
it 'returns true if the event_type is api' do
request.event_type = 'api'
request.api_request?.should == true
end
it 'returns false if the event_type is not api' do
request.event_type = 'push'
request.api_request?.should == false
end
end
describe 'pull_request?' do
it 'returns true if the event_type is pull_request' do
request.event_type = 'pull_request'
request.pull_request?.should == true
end
it 'returns false if the event_type is not pull_request' do
request.event_type = 'push'
request.pull_request?.should == false
end
end
describe 'pull_request_title' do
it 'returns the title of the pull request from payload' do
request.event_type = 'pull_request'
request.payload = { 'pull_request' => { 'title' => 'A pull request' } }
request.pull_request_title.should == 'A pull request'
end
it 'returns nil for non pull request' do
request.event_type = 'build'
request.payload = { 'pull_request' => { 'title' => 'A pull request' } }
request.pull_request_title.should be_nil
end
end
describe 'tag_name' do
it 'returns a tag name if available' do
request.payload = { 'ref' => 'refs/tags/foo' }
request.tag_name.should == 'foo'
end
it 'returns nil if a tag name is not available' do
request.payload = { 'ref' => 'refs/heads/foo' }
request.tag_name.should be_nil
end
end
describe 'branch_name' do
it 'returns a branch name if available' do
request.payload = { 'ref' => 'refs/heads/foo' }
request.branch_name.should == 'foo'
end
it 'returns nil if a branch name is not available' do
request.payload = { 'ref' => 'refs/tags/foo' }
request.branch_name.should be_nil
end
end
describe '#head_repo' do
it 'returns a branch name if available' do
request.payload = { 'pull_request' => { 'head' => { 'repo' => { 'full_name' => 'foo/bar' } } } }
request.head_repo.should == 'foo/bar'
end
it 'returns nil if this is not a pull request' do
request.payload = { }
request.head_repo.should be_nil
end
end
describe '#head_branch' do
it 'returns a branch name if available' do
request.payload = { 'pull_request' => { 'head' => { 'ref' => 'foo' } } }
request.head_branch.should == 'foo'
end
it 'returns nil if this is not a pull request' do
request.payload = { }
request.head_branch.should be_nil
end
end
describe 'same_repo_pull_request?' do
it 'returns true if the base and head repos match' do
request.payload = {
'pull_request' => {
'base' => { 'repo' => { 'full_name' => 'travis-ci/travis-core' } },
'head' => { 'repo' => { 'full_name' => 'travis-ci/travis-core' } },
}
}
request.same_repo_pull_request?.should be true
end
it 'returns false if the base and head repos do not match' do
request.payload = {
'pull_request' => {
'base' => { 'repo' => { 'full_name' => 'travis-ci/travis-core' } },
'head' => { 'repo' => { 'full_name' => 'BanzaiMan/travis-core' } },
}
}
request.same_repo_pull_request?.should be false
end
it 'returns false if repo data is not available' do
request.payload = {}
request.same_repo_pull_request?.should be_falsy
end
end
end

View File

@ -0,0 +1,90 @@
require 'spec_helper_core'
describe SslKey do
include Support::ActiveRecord
let(:key) { SslKey.new }
before(:each) do
key.generate_keys
end
it "is a SslKey" do
key.should be_a(SslKey)
end
describe "generate_keys" do
it "generates the public key" do
key.public_key.should be_a(String)
end
it "generates the private key" do
key.private_key.should be_a(String)
end
it "does not generate a new public key if one already exists" do
public_key = key.public_key
key.generate_keys
key.public_key.should == public_key
end
it "does not generate a new private key if one already exists" do
private_key = key.private_key
key.generate_keys
key.private_key.should == private_key
end
end
describe "generate_keys!" do
it "generates a new public key even if one already exists" do
public_key = key.public_key
key.generate_keys!
key.public_key.should_not == public_key
end
it "generates a new private key even if one already exists" do
private_key = key.private_key
key.generate_keys!
key.private_key.should_not == private_key
end
end
describe "encrypt" do
it "encrypts something" do
key.encrypt("hello").should_not be_nil
key.encrypt("hello").should_not eql("hello")
end
it "is decryptable" do
encrypted = key.encrypt("hello")
key.decrypt(encrypted).should eql("hello")
end
end
describe "decrypt" do
it "decrypts something" do
encrypted_string = key.encrypt("hello world")
key.decrypt(encrypted_string).should_not be_nil
key.decrypt(encrypted_string).should_not eql("hello")
end
end
describe 'encoding' do
SSL_KEYS = {
:public_key => "-----BEGIN PUBLIC KEY-----\nMDwwDQYJKoZIhvcNAQEBBQADKwAwKAIhALlyZuHmCjZf8pGCUmqz1NESpeVMJoes\nWQblf1p2WhnZAgMBAAE=\n-----END PUBLIC KEY-----\n",
:private_key => "-----BEGIN RSA PRIVATE KEY-----\nMIGrAgEAAiEAuXJm4eYKNl/ykYJSarPU0RKl5Uwmh6xZBuV/WnZaGdkCAwEAAQIg\nVHk9Tjd4fW5VU1z25+4EyXQNnMvaJGr0vP/iG2xSRpECEQD0k/AbOvzsxT5KDXP9\nnsxNAhEAwhuFRSrB1ef6EIPEyLDZvQIRAMGkH4ZvvbD4uciHvj4fbEECEBAl0fRr\nFi0BW2A8VgaMD9ECEQCYSndvz+Vw6SnR9YqElWqc\n-----END RSA PRIVATE KEY-----\n",
:public_base64 => "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAIQC5cmbh5go2X/KRglJqs9TREqXlTCaHrFkG5X9adloZ2Q==",
:private_base64 => "LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUdyQWdFQUFpRUF1\nWEptNGVZS05sL3lrWUpTYXJQVTBSS2w1VXdtaDZ4WkJ1Vi9XblphR2RrQ0F3\nRUFBUUlnClZIazlUamQ0Zlc1VlUxejI1KzRFeVhRTm5NdmFKR3IwdlAvaUcy\neFNScEVDRVFEMGsvQWJPdnpzeFQ1S0RYUDkKbnN4TkFoRUF3aHVGUlNyQjFl\nZjZFSVBFeUxEWnZRSVJBTUdrSDRadnZiRDR1Y2lIdmo0ZmJFRUNFQkFsMGZS\ncgpGaTBCVzJBOFZnYU1EOUVDRVFDWVNuZHZ6K1Z3NlNuUjlZcUVsV3FjCi0t\nLS0tRU5EIFJTQSBQUklWQVRFIEtFWS0tLS0tCg=="
}
let(:key) { SslKey.new(SSL_KEYS.slice(:private_key, :public_key)) }
it 'generates the correct key format to export to github' do
key.encoded_public_key.should == SSL_KEYS[:public_base64]
end
it 'encodes the private key properly for the build' do
key.encoded_private_key.should == SSL_KEYS[:private_base64]
end
end
end

View File

@ -0,0 +1,17 @@
require 'spec_helper_core'
describe Token do
include Support::ActiveRecord
it 'generate_token sets the token to a 20 character value' do
Token.new.send(:generate_token).length.should == 20
end
it 'does not generate new token on save' do
token = Token.create!
expect {
token.save
}.to_not change { token.token }
end
end

View File

@ -0,0 +1,31 @@
require 'spec_helper_core'
describe Url do
include Support::ActiveRecord
subject { Url.create(:url => "http://example.com") }
describe ".shorten" do
it "creates a new Url object if the url has not been shortened" do
expect { Url.shorten("http://example.com") }.to change(Url, :count).from(0).to(1)
end
it "retrieves a Url which has already been shortened" do
Url.shorten("http://example.com")
expect { Url.shorten("http://example.com") }.not_to change(Url, :count)
end
end
describe "#code" do
it "sets the code automatically" do
subject.code.should_not be_nil
end
end
describe "#short_url" do
it "returns the full short url" do
subject.short_url.should match(%r(^http://trvs.io/\w{10}$))
end
end
end

View File

@ -0,0 +1,33 @@
require 'spec_helper_core'
describe User::Oauth do
include Support::ActiveRecord
let(:user) { Factory(:user, :github_oauth_token => 'token') }
let(:payload) { GITHUB_PAYLOADS[:oauth] }
describe 'find_or_create_by' do
def call(payload)
User::Oauth.find_or_create_by(payload)
end
it 'marks users as recently_signed_up' do
call(payload).should be_recently_signed_up
end
it 'does not mark existing users as recently_signed_up' do
call(payload)
call(payload).should_not be_recently_signed_up
end
it 'updates changed attributes' do
call(payload).attributes.slice(*GITHUB_OAUTH_DATA.keys).should == GITHUB_OAUTH_DATA
end
end
describe 'attributes_from' do
it 'returns required data' do
User::Oauth.attributes_from(payload).should == GITHUB_OAUTH_DATA
end
end
end

View File

@ -0,0 +1,236 @@
require 'spec_helper_core'
describe User, truncation: true do
include Support::ActiveRecord
let(:user) { Factory(:user, :github_oauth_token => 'token') }
let(:payload) { GITHUB_PAYLOADS[:oauth] }
describe 'find_or_create_for_oauth' do
def user(payload)
User.find_or_create_for_oauth(payload)
end
it 'marks new users as such' do
user(payload).should be_recently_signed_up
user(payload).should_not be_recently_signed_up
end
it 'updates changed attributes' do
user(payload).attributes.slice(*GITHUB_OAUTH_DATA.keys).should == GITHUB_OAUTH_DATA
end
end
describe '#to_json' do
it 'returns JSON representation of user' do
json = JSON.parse(user.to_json)
json['user']['login'].should == 'svenfuchs'
end
end
describe 'permission?' do
let!(:repo) { Factory(:org, :login => 'travis') }
it 'given roles and a condition it returns true if the user has a matching permission for this role' do
user.permissions.create!(push: true, repository_id: repo.id)
user.permission?(['push'], repository_id: repo.id).should be true
end
it 'given roles and a condition it returns false if the user does not have a matching permission for this role' do
user.permissions.create!(pull: true, repository_id: repo.id)
user.permission?(['push'], repository_id: repo.id).should be false
end
it 'given a condition it returns true if the user has a matching permission' do
user.permissions.create!(push: true, repository_id: repo.id)
user.permission?(repository_id: repo.id).should be true
end
it 'given a condition it returns true if the user has a matching permission' do
user.permission?(repository_id: repo.id).should be false
end
end
describe 'organization_ids' do
let!(:travis) { Factory(:org, :login => 'travis') }
let!(:sinatra) { Factory(:org, :login => 'sinatra') }
before :each do
user.organizations << travis
user.save!
end
it 'contains the ids of organizations that the user is a member of' do
user.organization_ids.should include(travis.id)
end
it 'does not contain the ids of organizations that the user is not a member of' do
user.organization_ids.should_not include(sinatra.id)
end
end
describe 'repository_ids' do
let!(:travis) { Factory(:repository, :name => 'travis', :owner => Factory(:org, :name => 'travis')) }
let!(:sinatra) { Factory(:repository, :name => 'sinatra', :owner => Factory(:org, :name => 'sinatra')) }
before :each do
user.repositories << travis
user.save!
user.reload
end
it 'contains the ids of repositories the user is permitted to see' do
user.repository_ids.should include(travis.id)
end
it 'does not contain the ids of repositories the user is not permitted to see' do
user.repository_ids.should_not include(sinatra.id)
end
end
describe 'profile_image_hash' do
it "returns gravatar_id if it's present" do
user.gravatar_id = '41193cdbffbf06be0cdf231b28c54b18'
user.profile_image_hash.should == '41193cdbffbf06be0cdf231b28c54b18'
end
it 'returns a MD5 hash of the email if no gravatar_id and an email is set' do
user.gravatar_id = nil
user.profile_image_hash.should == Digest::MD5.hexdigest(user.email)
end
it 'returns 32 zeros if no gravatar_id or email is set' do
user.gravatar_id = nil
user.email = nil
user.profile_image_hash.should == '0' * 32
end
end
describe 'authenticate_by' do
describe 'given a valid token and login' do
it 'authenticates the user' do
User.authenticate_by('login' => user.login, 'token' => user.tokens.first.token).should == user
end
end
describe 'given a wrong token' do
it 'does not authenticate the user' do
User.authenticate_by('login' => 'someone-else', 'token' => user.tokens.first.token).should be_nil
end
end
describe 'given a wrong login' do
it 'does not authenticate the user' do
User.authenticate_by('login' => user.login, 'token' => 'some-other-token').should be_nil
end
end
describe 'with encrypted token' do
it 'authenticates the user' do
user.tokens.first.update_column :token, 'encrypted-token'
Travis::Model::EncryptedColumn.any_instance.stubs(:encrypt? => true, :key => 'abcd', :load => '...')
Travis::Model::EncryptedColumn.any_instance.expects(:load).with('encrypted-token').returns('a-token')
User.authenticate_by('login' => user.login, 'token' => 'a-token').should == user
end
end
end
describe 'service_hooks' do
let(:own_repo) { Factory(:repository, :name => 'own-repo', :description => 'description', :active => true) }
let(:admin_repo) { Factory(:repository, :name => 'admin-repo') }
let(:other_repo) { Factory(:repository, :name => 'other-repo') }
let(:push_repo) { Factory(:repository, :name => 'push-repo') }
before :each do
user.permissions.create! :user => user, :repository => own_repo, :admin => true
user.permissions.create! :user => user, :repository => admin_repo, :admin => true
user.permissions.create! :user => user, :repository => push_repo, :push => true
other_repo
end
it "contains repositories where the user has an admin role" do
user.service_hooks.should include(own_repo)
end
it "does not contain repositories where the user does not have an admin role" do
user.service_hooks.should_not include(other_repo)
end
it "includes all repositories if :all options is passed" do
hooks = user.service_hooks(:all => true)
hooks.should include(own_repo)
hooks.should include(push_repo)
hooks.should include(admin_repo)
hooks.should_not include(other_repo)
end
end
describe 'track_github_scopes' do
before { user.save! }
it "does not resolve github scopes if the token didn't change" do
Travis::Github.expects(:scopes_for).never
user.save!
end
it "it resolves github scopes if the token did change" do
Travis::Github.expects(:scopes_for).with(user).returns(['foo', 'bar'])
user.github_oauth_token = 'new_token'
user.save!
user.github_scopes.should be == ['foo', 'bar']
end
it "it resolves github scopes if they haven't been resolved already" do
Travis::Github.expects(:scopes_for).with(user).returns(['foo', 'bar'])
user.github_scopes = nil
user.save!
user.github_scopes.should be == ['foo', 'bar']
end
it 'returns an empty list if the token is missing' do
user.github_scopes = ['foo']
user.github_oauth_token = nil
user.github_scopes.should be_empty
end
end
describe 'correct_scopes?' do
it "accepts correct scopes" do
user.should be_correct_scopes
end
it "complains about missing scopes" do
user.github_scopes.pop
user.should_not be_correct_scopes
end
it "accepts additional scopes" do
user.github_scopes << "foo"
user.should be_correct_scopes
end
end
describe 'inspect' do
context 'when user has GitHub OAuth token' do
before :each do
user.github_oauth_token = 'foobarbaz'
end
it 'does not include the user\'s GitHub OAuth token' do
user.inspect.should_not include('foobarbaz')
end
end
context 'when user has no GitHub OAuth token' do
before :each do
user.github_oauth_token = nil
end
it 'indicates nil GitHub OAuth token' do
user.inspect.should include('github_oauth_token: nil')
end
end
end
end

View File

@ -0,0 +1,54 @@
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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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_truthy
# end
# it 'returns false if the build is not finished' do
# build.update_attributes!(:state => :started)
# service.final?.should be false
# end
# end
end

View File

@ -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

View File

@ -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

View File

@ -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
expect(stats.size).to eq(1)
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
expect(stats.size).to eq(1)
stats.first['count'].to_i.should == 2
end
end

View File

@ -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
expect(stats.size).to eq(1)
stats.first['date'].should == Job.first.created_at.to_date.to_s(:date)
stats.first['count'].to_i.should == 13
end
end

View File

@ -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)
expect(hooks.size).to eq(2)
# 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

View File

@ -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_truthy
# end
# it 'returns false if the job is not finished' do
# job.update_attributes!(state: :started)
# service.final?.should be false
# end
# end
end

View File

@ -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
skip '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

View File

@ -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_truthy
# 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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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_falsey
end
end
end

View File

@ -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_truthy
result.should be_truthy
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 <Job id=#{job.id}> (svenfuchs)",
)
end
end

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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 #<Log id=#{log.id}> params=#{params}",
result: true
)
end
end
end

View File

@ -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

Some files were not shown because too many files have changed in this diff Show More