squash rlh-are-remove-core
This commit is contained in:
parent
05f926b0ec
commit
a63b7efc83
4
.gitignore
vendored
4
.gitignore
vendored
|
@ -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 +1 @@
|
|||
2.1.5
|
||||
2.2.3
|
||||
|
|
|
@ -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
|
||||
|
|
9
Gemfile
9
Gemfile
|
@ -1,13 +1,16 @@
|
|||
source 'https://rubygems.org'
|
||||
gemspec
|
||||
|
||||
ruby '2.2.3' if ENV.key?('DYNO')
|
||||
|
||||
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 +20,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'
|
||||
|
|
180
Gemfile.lock
180
Gemfile.lock
|
@ -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,46 +35,22 @@ 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
|
||||
specs:
|
||||
travis-core (0.0.1)
|
||||
actionmailer (~> 3.2.19)
|
||||
activerecord (~> 3.2.19)
|
||||
coder (~> 0.4.0)
|
||||
data_migrations (~> 0.0.1)
|
||||
gh
|
||||
google-api-client (~> 0.9.4)
|
||||
hashr
|
||||
metriks (~> 0.9.7)
|
||||
multi_json
|
||||
pusher (~> 0.14.0)
|
||||
railties (~> 3.2.19)
|
||||
rake
|
||||
redis (~> 3.0)
|
||||
rollout (~> 1.1.0)
|
||||
s3 (~> 0.3)
|
||||
simple_states (~> 1.0.0)
|
||||
thor
|
||||
travis-config (~> 0.1.0)
|
||||
virtus (~> 1.0.0)
|
||||
|
||||
GIT
|
||||
remote: git://github.com/travis-ci/travis-migrations.git
|
||||
revision: bf360857ef7830f7e3ff12de181ab58c33fb29f1
|
||||
revision: dc432e45354287c617c3ae07a72e9e3c4be012cd
|
||||
specs:
|
||||
travis-migrations (0.0.1)
|
||||
travis-migrations (0.0.2)
|
||||
|
||||
GIT
|
||||
remote: git://github.com/travis-ci/travis-sidekiqs.git
|
||||
revision: 21a2fee158e25252dd78f5fa31e81b4f6583be23
|
||||
revision: c5d4a4abc6c3737f9c43d3333efb94daa18b9fbb
|
||||
specs:
|
||||
travis-sidekiqs (0.0.1)
|
||||
redis-namespace
|
||||
sidekiq
|
||||
|
||||
GIT
|
||||
|
@ -113,6 +82,30 @@ PATH
|
|||
travis-support
|
||||
useragent
|
||||
|
||||
PATH
|
||||
remote: vendor
|
||||
specs:
|
||||
travis-core (0.0.1)
|
||||
actionmailer (~> 3.2.19)
|
||||
activerecord (~> 3.2.19)
|
||||
coder (~> 0.4.0)
|
||||
data_migrations (~> 0.0.1)
|
||||
gh
|
||||
google-api-client (~> 0.9.4)
|
||||
hashr
|
||||
metriks (~> 0.9.7)
|
||||
multi_json
|
||||
pusher (~> 0.14.0)
|
||||
railties (~> 3.2.19)
|
||||
rake
|
||||
redis (~> 3.0)
|
||||
rollout (~> 1.1.0)
|
||||
s3 (~> 0.3)
|
||||
simple_states (~> 1.0.0)
|
||||
thor
|
||||
travis-config (~> 0.1.0)
|
||||
virtus (~> 1.0.0)
|
||||
|
||||
GEM
|
||||
remote: https://rubygems.org/
|
||||
specs:
|
||||
|
@ -129,7 +122,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 +146,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 +165,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 +181,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 +203,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 +211,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 +237,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 +255,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 +273,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 +300,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 +316,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 +340,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 +353,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 +385,7 @@ DEPENDENCIES
|
|||
pry
|
||||
rack-attack
|
||||
rack-cache!
|
||||
rack-contrib!
|
||||
rack-contrib
|
||||
rake (~> 0.9.2)
|
||||
rb-fsevent (~> 0.9.1)
|
||||
rerun
|
||||
|
@ -426,3 +409,6 @@ DEPENDENCIES
|
|||
travis-yaml!
|
||||
unicorn
|
||||
yard-sinatra!
|
||||
|
||||
BUNDLED WITH
|
||||
1.12.5
|
||||
|
|
|
@ -59,7 +59,7 @@ $ popd
|
|||
|
||||
### Run tests
|
||||
```sh-session
|
||||
$ bundle exec rspec
|
||||
$ bundle exec rake
|
||||
```
|
||||
### Run the server
|
||||
```sh-session
|
||||
|
|
17
Rakefile
17
Rakefile
|
@ -9,6 +9,23 @@ namespace :db do
|
|||
end
|
||||
end
|
||||
|
||||
begin
|
||||
require 'rspec'
|
||||
require 'rspec/core/rake_task'
|
||||
RSpec::Core::RakeTask.new(:spec)
|
||||
|
||||
task :core_specs do
|
||||
RSpec::Core::RakeTask.new(:core_spec) do |t|
|
||||
t.pattern = 'core_specs/**{,/*/**}/*_spec.rb'
|
||||
end
|
||||
Rake::Task["core_spec"].execute
|
||||
end
|
||||
|
||||
task :default => [:spec, :core_specs]
|
||||
rescue LoadError => e
|
||||
puts e.inspect
|
||||
end
|
||||
|
||||
desc "generate gemspec"
|
||||
task 'travis-api.gemspec' do
|
||||
content = File.read 'travis-api.gemspec'
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
require 'travis/api/app'
|
||||
require 'travis/api/workers/build_cancellation'
|
||||
require 'travis/api/workers/build_restart'
|
||||
require 'travis/api/enqueue/services/restart_model'
|
||||
require 'travis/api/enqueue/services/enqueue_build'
|
||||
|
||||
class Travis::Api::App
|
||||
class Endpoint
|
||||
|
@ -49,24 +49,27 @@ class Travis::Api::App
|
|||
|
||||
post '/:id/restart' do
|
||||
Metriks.meter("api.request.restart_build").mark
|
||||
service = if Travis::Features.owner_active?(:enqueue_to_hub, current_user)
|
||||
Travis::Enqueue::Services::RestartModel.new(current_user, build_id: params[:id])
|
||||
if Travis::Features.owner_active?(:enqueue_to_hub, current_user)
|
||||
service = Travis::Enqueue::Services::EnqueueBuild.new(current_user, params[:id])
|
||||
if !service.accept?
|
||||
status 400
|
||||
result = false
|
||||
else
|
||||
payload = {id: params[:id], user_id: current_user.id}
|
||||
service.push("build:restart", payload)
|
||||
status 202
|
||||
result = true
|
||||
end
|
||||
else
|
||||
self.service(:reset_model, build_id: params[:id])
|
||||
end
|
||||
|
||||
result = if !service.accept?
|
||||
status 400
|
||||
false
|
||||
elsif service.respond_to?(:push)
|
||||
payload = { id: params[:id], user_id: current_user.id }
|
||||
service.push("build:restart", payload)
|
||||
status 202
|
||||
true
|
||||
else
|
||||
Travis::Sidekiq::BuildRestart.perform_async(id: params[:id], user_id: current_user.id)
|
||||
status 202
|
||||
true
|
||||
service = self.service(:reset_model, build_id: params[:id])
|
||||
if !service.accept?
|
||||
status 400
|
||||
result = false
|
||||
else
|
||||
Travis::Sidekiq::BuildRestart.perform_async(id: params[:id], user_id: current_user.id)
|
||||
status 202
|
||||
result = true
|
||||
end
|
||||
end
|
||||
|
||||
respond_with(result: result, flash: service.messages)
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
require 'travis/api/app'
|
||||
require 'travis/api/workers/job_cancellation'
|
||||
require 'travis/api/workers/job_restart'
|
||||
require 'travis/api/enqueue/services/restart_model'
|
||||
|
||||
class Travis::Api::App
|
||||
class Endpoint
|
||||
|
@ -56,26 +55,16 @@ class Travis::Api::App
|
|||
|
||||
post '/:id/restart' do
|
||||
Metriks.meter("api.request.restart_job").mark
|
||||
service = if Travis::Features.owner_active?(:enqueue_to_hub, current_user)
|
||||
Travis::Enqueue::Services::RestartModel.new(current_user, { job_id: params[:id] })
|
||||
else
|
||||
self.service(:reset_model, job_id: params[:id])
|
||||
end
|
||||
|
||||
result = if !service.accept?
|
||||
service = self.service(:reset_model, job_id: params[:id])
|
||||
if !service.accept?
|
||||
status 400
|
||||
false
|
||||
elsif service.respond_to?(:push)
|
||||
payload = {id: params[:id], user_id: current_user.id}
|
||||
service.push("job:restart", payload)
|
||||
status 202
|
||||
true
|
||||
result = false
|
||||
else
|
||||
Travis::Sidekiq::JobRestart.perform_async(id: params[:id], user_id: current_user.id)
|
||||
status 202
|
||||
result = true
|
||||
end
|
||||
|
||||
respond_with(result: result, flash: service.messages)
|
||||
end
|
||||
|
||||
|
|
49
lib/travis/api/enqueue/services/enqueue_build.rb
Normal file
49
lib/travis/api/enqueue/services/enqueue_build.rb
Normal file
|
@ -0,0 +1,49 @@
|
|||
module Travis
|
||||
module Enqueue
|
||||
module Services
|
||||
|
||||
class EnqueueBuild
|
||||
attr_reader :current_user, :build
|
||||
|
||||
def initialize(current_user, build_id)
|
||||
@current_user = current_user
|
||||
@build = Build.find(build_id)
|
||||
end
|
||||
|
||||
def push(event, payload)
|
||||
::Sidekiq::Client.push(
|
||||
'queue' => 'hub',
|
||||
'class' => 'Travis::Hub::Sidekiq::Worker',
|
||||
'args' => [event, payload]
|
||||
)
|
||||
end
|
||||
|
||||
def accept?
|
||||
current_user && permission? && resetable?
|
||||
end
|
||||
|
||||
def messages
|
||||
messages = []
|
||||
messages << { notice: "The build was successfully restarted." } if accept?
|
||||
messages << { error: 'You do not seem to have sufficient permissions.' } unless permission?
|
||||
messages << { error: "This build currently can not be restarted." } unless resetable?
|
||||
messages
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def permission?
|
||||
current_user.permission?(required_role, repository_id: build.repository_id)
|
||||
end
|
||||
|
||||
def resetable?
|
||||
build.resetable?
|
||||
end
|
||||
|
||||
def required_role
|
||||
Travis.config.roles.reset_model
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,64 +0,0 @@
|
|||
module Travis
|
||||
module Enqueue
|
||||
module Services
|
||||
|
||||
class RestartModel
|
||||
attr_reader :current_user, :target
|
||||
|
||||
def initialize(current_user, params)
|
||||
@current_user = current_user
|
||||
@params = params
|
||||
target
|
||||
end
|
||||
|
||||
def push(event, payload)
|
||||
if current_user && target && accept?
|
||||
::Sidekiq::Client.push(
|
||||
'queue' => 'hub',
|
||||
'class' => 'Travis::Hub::Sidekiq::Worker',
|
||||
'args' => [event, payload]
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
def accept?
|
||||
current_user && permission? && resetable?
|
||||
end
|
||||
|
||||
def messages
|
||||
messages = []
|
||||
messages << { notice: "The #{type} was successfully restarted." } if accept?
|
||||
messages << { error: 'You do not seem to have sufficient permissions.' } unless permission?
|
||||
messages << { error: "This #{type} currently can not be restarted." } unless resetable?
|
||||
messages
|
||||
end
|
||||
|
||||
def type
|
||||
@type ||= @params[:build_id] ? :build : :job
|
||||
end
|
||||
|
||||
def target
|
||||
if type == :build
|
||||
@target = Build.find(@params[:build_id])
|
||||
else
|
||||
@target = Job.find(@params[:job_id])
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def permission?
|
||||
current_user.permission?(required_role, repository_id: target.repository_id)
|
||||
end
|
||||
|
||||
def resetable?
|
||||
target.resetable?
|
||||
end
|
||||
|
||||
def required_role
|
||||
Travis.config.roles.reset_model
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,9 +1,10 @@
|
|||
module Travis::API::V3
|
||||
class Queries::Repositories < Query
|
||||
params :active, :private, :starred, prefix: :repository
|
||||
sortable_by :id, :github_id, :owner_name, :name, active: sort_condition(:active), :'default_branch.last_build' => 'builds.started_at'
|
||||
sortable_by :id, :github_id, :owner_name, :name, active: sort_condition(:active),
|
||||
:'default_branch.last_build' => 'builds.started_at',
|
||||
:current_build_id => "repositories.current_build_id %{order} NULLS LAST"
|
||||
:current_build => "current_build_id %{order} NULLS LAST"
|
||||
|
||||
def for_member(user, **options)
|
||||
all(user: user, **options).joins(:users).where(users: user_condition(user), invalidated_at: nil)
|
||||
|
|
|
@ -3,7 +3,7 @@ require 'travis/api/v3/renderer/model_renderer'
|
|||
module Travis::API::V3
|
||||
class Renderer::Cron < Renderer::ModelRenderer
|
||||
representation(:minimal, :id)
|
||||
representation(:standard, :id, :repository, :branch, :interval, :disable_by_build, :next_enqueuing, :created_at)
|
||||
representation(:standard, :id, :repository, :branch, :interval, :disable_by_build, :next_enqueuing)
|
||||
|
||||
def repository
|
||||
model.branch.repository
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -117,7 +117,7 @@ describe 'Builds' do
|
|||
build.update_attribute(:state, 'passed')
|
||||
end
|
||||
|
||||
describe 'Enqueues restart event for the Hub' do
|
||||
describe 'Enqueues restart event to the Hub' do
|
||||
before { Travis::Features.activate_owner(:enqueue_to_hub, repo.owner) }
|
||||
|
||||
it 'restarts the build' do
|
||||
|
|
|
@ -274,51 +274,24 @@ describe 'Jobs' do
|
|||
response = post "/jobs/#{job.id}/restart", {}, headers
|
||||
response.status.should == 400
|
||||
end
|
||||
|
||||
context 'when enqueueing for the Hub' do
|
||||
before { Travis::Features.activate_owner(:enqueue_to_hub, job.repository.owner) }
|
||||
|
||||
it 'responds with 400' do
|
||||
response = post "/jobs/#{job.id}/restart", {}, headers
|
||||
response.status.should == 400
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when job passed' do
|
||||
before { job.update_attribute(:state, 'passed') }
|
||||
|
||||
context 'Restart from travis-core' do
|
||||
before { Travis::Sidekiq::JobCancellation.stubs(:perform_async) }
|
||||
|
||||
it 'restarts the job' do
|
||||
Travis::Sidekiq::JobRestart.expects(:perform_async).with(id: job.id.to_s, user_id: user.id)
|
||||
response = post "/jobs/#{job.id}/restart", {}, headers
|
||||
response.status.should == 202
|
||||
end
|
||||
it 'sends the correct response body' do
|
||||
Travis::Sidekiq::JobRestart.expects(:perform_async).with(id: job.id.to_s, user_id: user.id)
|
||||
response = post "/jobs/#{job.id}/restart", {}, headers
|
||||
body = JSON.parse(response.body)
|
||||
body.should == {"result"=>true, "flash"=>[{"notice"=>"The job was successfully restarted."}]}
|
||||
end
|
||||
before do
|
||||
Travis::Sidekiq::JobCancellation.stubs(:perform_async)
|
||||
job.update_attribute(:state, 'passed')
|
||||
end
|
||||
|
||||
context 'Enqueues restart event for the Hub' do
|
||||
before { Travis::Features.activate_owner(:enqueue_to_hub, job.repository.owner) }
|
||||
|
||||
it 'restarts the job' do
|
||||
::Sidekiq::Client.expects(:push)
|
||||
response = post "/jobs/#{job.id}/restart", {}, headers
|
||||
response.status.should == 202
|
||||
end
|
||||
it 'sends the correct response body' do
|
||||
::Sidekiq::Client.expects(:push)
|
||||
response = post "/jobs/#{job.id}/restart", {}, headers
|
||||
body = JSON.parse(response.body)
|
||||
body.should == {"result"=>true, "flash"=>[{"notice"=>"The job was successfully restarted."}]}
|
||||
end
|
||||
|
||||
it 'restarts the job' do
|
||||
Travis::Sidekiq::JobRestart.expects(:perform_async).with(id: job.id.to_s, user_id: user.id)
|
||||
response = post "/jobs/#{job.id}/restart", {}, headers
|
||||
response.status.should == 202
|
||||
end
|
||||
it 'sends the correct response body' do
|
||||
Travis::Sidekiq::JobRestart.expects(:perform_async).with(id: job.id.to_s, user_id: user.id)
|
||||
response = post "/jobs/#{job.id}/restart", {}, headers
|
||||
body = JSON.parse(response.body)
|
||||
body.should == {"result"=>true, "flash"=>[{"notice"=>"The job was successfully restarted."}]}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -53,7 +53,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
59
spec/spec_helper_core.rb
Normal 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
15
spec/support.rb
Normal 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
|
||||
|
47
spec/support/active_record.rb
Normal file
47
spec/support/active_record.rb
Normal 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
42
spec/support/gcs.rb
Normal 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
624
spec/support/payloads.rb
Normal 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
48
spec/support/s3.rb
Normal 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
|
|
@ -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',
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -64,8 +64,7 @@ describe Travis::API::V3::Services::Cron::Create do
|
|||
"name" => "#{branch.name}" },
|
||||
"interval" => "monthly",
|
||||
"disable_by_build" => false,
|
||||
"next_enqueuing" => current_cron.next_enqueuing.strftime('%Y-%m-%dT%H:%M:%SZ'),
|
||||
"created_at" => current_cron.created_at.strftime('%Y-%m-%dT%H:%M:%SZ')
|
||||
"next_enqueuing" => current_cron.next_enqueuing.strftime('%Y-%m-%dT%H:%M:%SZ')
|
||||
}}
|
||||
end
|
||||
|
||||
|
|
|
@ -52,8 +52,7 @@ describe Travis::API::V3::Services::Cron::Delete do
|
|||
"name" => branch.name },
|
||||
"interval" => "daily",
|
||||
"disable_by_build" => true,
|
||||
"next_enqueuing" => cron.next_enqueuing.strftime('%Y-%m-%dT%H:%M:%SZ'),
|
||||
"created_at" => cron.created_at.strftime('%Y-%m-%dT%H:%M:%SZ')
|
||||
"next_enqueuing" => cron.next_enqueuing.strftime('%Y-%m-%dT%H:%M:%SZ')
|
||||
}}
|
||||
end
|
||||
|
||||
|
|
|
@ -46,9 +46,8 @@ describe Travis::API::V3::Services::Cron::Find do
|
|||
"@representation" => "minimal",
|
||||
"name" => branch.name },
|
||||
"interval" => "daily",
|
||||
"disable_by_build" => true,
|
||||
"next_enqueuing" => cron.next_enqueuing.strftime('%Y-%m-%dT%H:%M:%SZ'),
|
||||
"created_at" => cron.created_at.strftime('%Y-%m-%dT%H:%M:%SZ')
|
||||
"disable_by_build" => true,
|
||||
"next_enqueuing" => cron.next_enqueuing.strftime('%Y-%m-%dT%H:%M:%SZ')
|
||||
}}
|
||||
end
|
||||
|
||||
|
@ -106,9 +105,8 @@ describe Travis::API::V3::Services::Cron::Find do
|
|||
"@representation" => "minimal",
|
||||
"name" => branch.name },
|
||||
"interval" => "daily",
|
||||
"disable_by_build" => true,
|
||||
"next_enqueuing" => cron.next_enqueuing.strftime('%Y-%m-%dT%H:%M:%SZ'),
|
||||
"created_at" => cron.created_at.strftime('%Y-%m-%dT%H:%M:%SZ')
|
||||
"disable_by_build" => true,
|
||||
"next_enqueuing" => cron.next_enqueuing.strftime('%Y-%m-%dT%H:%M:%SZ')
|
||||
}}
|
||||
end
|
||||
|
||||
|
|
|
@ -47,9 +47,8 @@ describe Travis::API::V3::Services::Cron::ForBranch do
|
|||
"@representation" => "minimal",
|
||||
"name" => branch.name },
|
||||
"interval" => "daily",
|
||||
"disable_by_build" => true,
|
||||
"next_enqueuing" => cron.next_enqueuing.strftime('%Y-%m-%dT%H:%M:%SZ'),
|
||||
"created_at" => cron.created_at.strftime('%Y-%m-%dT%H:%M:%SZ')
|
||||
"disable_by_build" => true,
|
||||
"next_enqueuing" => cron.next_enqueuing.strftime('%Y-%m-%dT%H:%M:%SZ')
|
||||
}}
|
||||
end
|
||||
|
||||
|
|
|
@ -68,8 +68,7 @@ describe Travis::API::V3::Services::Crons::ForRepository do
|
|||
"name" => "#{branch.name}" },
|
||||
"interval" => "daily",
|
||||
"disable_by_build" => true,
|
||||
"next_enqueuing" => cron.next_enqueuing.strftime('%Y-%m-%dT%H:%M:%SZ'),
|
||||
"created_at" => cron.created_at.strftime('%Y-%m-%dT%H:%M:%SZ')
|
||||
"next_enqueuing" => cron.next_enqueuing.strftime('%Y-%m-%dT%H:%M:%SZ')
|
||||
}
|
||||
]
|
||||
}}
|
||||
|
|
56
spec_core/core/model/annotation_provider_spec.rb
Normal file
56
spec_core/core/model/annotation_provider_spec.rb
Normal 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
|
21
spec_core/core/model/annotation_spec.rb
Normal file
21
spec_core/core/model/annotation_spec.rb
Normal 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
|
106
spec_core/core/model/broadcast_spec.rb
Normal file
106
spec_core/core/model/broadcast_spec.rb
Normal 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
|
7
spec_core/core/model/build/compat_spec.rb
Normal file
7
spec_core/core/model/build/compat_spec.rb
Normal file
|
@ -0,0 +1,7 @@
|
|||
# require 'spec_helper_core'
|
||||
#
|
||||
# describe Build::Compat do
|
||||
# include Support::ActiveRecord
|
||||
#
|
||||
# let(:build) { Factory(:build, :result => nil) }
|
||||
# end
|
124
spec_core/core/model/build/config/dist_spec.rb
Normal file
124
spec_core/core/model/build/config/dist_spec.rb
Normal 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
|
26
spec_core/core/model/build/config/group_spec.rb
Normal file
26
spec_core/core/model/build/config/group_spec.rb
Normal 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
|
14
spec_core/core/model/build/config/matrix_spec.rb
Normal file
14
spec_core/core/model/build/config/matrix_spec.rb
Normal 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
|
116
spec_core/core/model/build/config/obfuscate_spec.rb
Normal file
116
spec_core/core/model/build/config/obfuscate_spec.rb
Normal 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
|
156
spec_core/core/model/build/config_spec.rb
Normal file
156
spec_core/core/model/build/config_spec.rb
Normal 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
|
59
spec_core/core/model/build/denormalize_spec.rb
Normal file
59
spec_core/core/model/build/denormalize_spec.rb
Normal 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
|
||||
|
889
spec_core/core/model/build/matrix_spec.rb
Normal file
889
spec_core/core/model/build/matrix_spec.rb
Normal 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
|
42
spec_core/core/model/build/metrics_spec.rb
Normal file
42
spec_core/core/model/build/metrics_spec.rb
Normal 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
|
148
spec_core/core/model/build/result_message_spec.rb
Normal file
148
spec_core/core/model/build/result_message_spec.rb
Normal 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
|
249
spec_core/core/model/build/states_spec.rb
Normal file
249
spec_core/core/model/build/states_spec.rb
Normal 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
|
57
spec_core/core/model/build/update_branch_spec.rb
Normal file
57
spec_core/core/model/build/update_branch_spec.rb
Normal 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
|
337
spec_core/core/model/build_spec.rb
Normal file
337
spec_core/core/model/build_spec.rb
Normal 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
|
92
spec_core/core/model/commit_spec.rb
Normal file
92
spec_core/core/model/commit_spec.rb
Normal 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
|
126
spec_core/core/model/encrypted_column_spec.rb
Normal file
126
spec_core/core/model/encrypted_column_spec.rb
Normal 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
|
45
spec_core/core/model/job/cleanup_spec.rb
Normal file
45
spec_core/core/model/job/cleanup_spec.rb
Normal 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
|
||||
#
|
||||
#
|
310
spec_core/core/model/job/queue_spec.rb
Normal file
310
spec_core/core/model/job/queue_spec.rb
Normal 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
|
190
spec_core/core/model/job/test_spec.rb
Normal file
190
spec_core/core/model/job/test_spec.rb
Normal 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
|
471
spec_core/core/model/job_spec.rb
Normal file
471
spec_core/core/model/job_spec.rb
Normal 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
|
22
spec_core/core/model/organization_spec.rb
Normal file
22
spec_core/core/model/organization_spec.rb
Normal 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
|
23
spec_core/core/model/permission_spec.rb
Normal file
23
spec_core/core/model/permission_spec.rb
Normal 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
|
104
spec_core/core/model/repository/settings/ssh_key_spec.rb
Normal file
104
spec_core/core/model/repository/settings/ssh_key_spec.rb
Normal 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
|
140
spec_core/core/model/repository/settings_spec.rb
Normal file
140
spec_core/core/model/repository/settings_spec.rb
Normal 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
|
105
spec_core/core/model/repository/status_image_spec.rb
Normal file
105
spec_core/core/model/repository/status_image_spec.rb
Normal 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
|
435
spec_core/core/model/repository_spec.rb
Normal file
435
spec_core/core/model/repository_spec.rb
Normal 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
|
272
spec_core/core/model/request/approval_spec.rb
Normal file
272
spec_core/core/model/request/approval_spec.rb
Normal 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
|
159
spec_core/core/model/request/branches_spec.rb
Normal file
159
spec_core/core/model/request/branches_spec.rb
Normal 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
|
254
spec_core/core/model/request/states_spec.rb
Normal file
254
spec_core/core/model/request/states_spec.rb
Normal 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
|
155
spec_core/core/model/request_spec.rb
Normal file
155
spec_core/core/model/request_spec.rb
Normal 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
|
90
spec_core/core/model/ssl_key_spec.rb
Normal file
90
spec_core/core/model/ssl_key_spec.rb
Normal 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
|
17
spec_core/core/model/token_spec.rb
Normal file
17
spec_core/core/model/token_spec.rb
Normal 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
|
31
spec_core/core/model/url_spec.rb
Normal file
31
spec_core/core/model/url_spec.rb
Normal 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
|
33
spec_core/core/model/user/oauth_spec.rb
Normal file
33
spec_core/core/model/user/oauth_spec.rb
Normal 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
|
236
spec_core/core/model/user_spec.rb
Normal file
236
spec_core/core/model/user_spec.rb
Normal 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
|
54
spec_core/core/services/cancel_build_spec.rb
Normal file
54
spec_core/core/services/cancel_build_spec.rb
Normal 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
|
44
spec_core/core/services/cancel_job_spec.rb
Normal file
44
spec_core/core/services/cancel_job_spec.rb
Normal 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
|
||||
|
90
spec_core/core/services/find_admin_spec.rb
Normal file
90
spec_core/core/services/find_admin_spec.rb
Normal 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
|
23
spec_core/core/services/find_annotation_spec.rb
Normal file
23
spec_core/core/services/find_annotation_spec.rb
Normal 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
|
32
spec_core/core/services/find_branch_spec.rb
Normal file
32
spec_core/core/services/find_branch_spec.rb
Normal 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
|
32
spec_core/core/services/find_branches_spec.rb
Normal file
32
spec_core/core/services/find_branches_spec.rb
Normal 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
|
67
spec_core/core/services/find_build_spec.rb
Normal file
67
spec_core/core/services/find_build_spec.rb
Normal 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
|
87
spec_core/core/services/find_builds_spec.rb
Normal file
87
spec_core/core/services/find_builds_spec.rb
Normal 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
|
76
spec_core/core/services/find_caches_spec.rb
Normal file
76
spec_core/core/services/find_caches_spec.rb
Normal 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
|
22
spec_core/core/services/find_daily_repos_stats_spec.rb
Normal file
22
spec_core/core/services/find_daily_repos_stats_spec.rb
Normal 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
|
17
spec_core/core/services/find_daily_tests_stats_spec.rb
Normal file
17
spec_core/core/services/find_daily_tests_stats_spec.rb
Normal 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
|
49
spec_core/core/services/find_hooks_spec.rb
Normal file
49
spec_core/core/services/find_hooks_spec.rb
Normal 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
|
61
spec_core/core/services/find_job_spec.rb
Normal file
61
spec_core/core/services/find_job_spec.rb
Normal 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
|
68
spec_core/core/services/find_jobs_spec.rb
Normal file
68
spec_core/core/services/find_jobs_spec.rb
Normal 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
|
42
spec_core/core/services/find_log_spec.rb
Normal file
42
spec_core/core/services/find_log_spec.rb
Normal 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
|
29
spec_core/core/services/find_repo_key_spec.rb
Normal file
29
spec_core/core/services/find_repo_key_spec.rb
Normal 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
|
43
spec_core/core/services/find_repo_settings_spec.rb
Normal file
43
spec_core/core/services/find_repo_settings_spec.rb
Normal 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
|
||||
|
34
spec_core/core/services/find_repo_spec.rb
Normal file
34
spec_core/core/services/find_repo_spec.rb
Normal 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
|
127
spec_core/core/services/find_repos_spec.rb
Normal file
127
spec_core/core/services/find_repos_spec.rb
Normal 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
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user