squash rlh-are-remove-core

This commit is contained in:
Sven Fuchs 2016-06-08 15:26:51 +02:00
parent 05f926b0ec
commit a63b7efc83
380 changed files with 22588 additions and 676 deletions

4
.gitignore vendored
View File

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

1
.rspec
View File

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

View File

@ -1 +1 @@
2.1.5
2.2.3

View File

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

View File

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

View File

@ -1,20 +1,13 @@
GIT
remote: git://github.com/eric/metriks-librato_metrics.git
revision: ccbeb751ec5fc4edfe446d8a67a423b96ebe86c7
revision: 2c124f024fd2e34378260cd24b0e8687ff9bd196
specs:
metriks-librato_metrics (1.0.2)
metriks-librato_metrics (1.0.5)
metriks (>= 0.9.9.6)
GIT
remote: git://github.com/rack/rack-contrib.git
revision: 1b11346d729efd88b274cd7f704e0bca9eb3de7a
specs:
rack-contrib (1.2.0)
rack (>= 0.9.1)
GIT
remote: git://github.com/rkh/mustermann.git
revision: fa22e2cf4cfdb57f452c366eac66f241827b7e9c
revision: 9611951c5c789ad8227a740ae142c157a8bf7401
specs:
mustermann (0.4.0)
tool (~> 0.2)
@ -42,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

View File

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

View File

@ -9,6 +9,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'

View File

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

View File

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

View 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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

15
spec/support.rb Normal file
View File

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

View File

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

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

@ -0,0 +1,42 @@
require 'google/apis/storage_v1'
module Support
module GCS
class FakeObject
attr_accessor :key, :size
def initialize(key, options = {})
@key = key
@size = options[:size] || "0"
end
end
class FakeService
def authorization=(auth)
true
end
def list_objects(*args)
FakeObjects.new
end
end
class FakeObjects
def items
[]
end
end
class FakeAuthorization
end
extend ActiveSupport::Concern
included do
before :each do
::Google::Apis::StorageV1::StorageService.stubs(:new).returns(gcs_storage)
::Google::Auth::ServiceAccountCredentials.stubs(:make_creds).returns(FakeAuthorization.new)
end
let(:gcs_storage) { FakeService.new }
end
end
end

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

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

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

@ -0,0 +1,48 @@
require 's3'
module Support
module S3
class FakeObject
attr_accessor :key, :size
def initialize(key, options = {})
@key = key
@size = options[:size] || "0"
end
end
class FakeService
attr_reader :buckets
def initialize(bucket)
@buckets = [bucket]
@buckets.stubs(:find).returns(bucket)
end
end
class FakeBucket
def initialize(objects)
@objects = Array(objects)
end
def objects(params = {})
params.each_key { |key| raise "cannot fake #{key}" unless key == :prefix }
prefix = params[:prefix] || ""
@objects.select { |o| o.key.start_with? prefix }
end
def add(key, options = {})
@objects << FakeObject.new(key, options)
end
alias_method :<<, :add
end
extend ActiveSupport::Concern
included do
before(:each) { ::S3::Service.stubs(:new).returns(s3_service) }
let(:s3_service) { FakeService.new(s3_bucket) }
let(:s3_bucket) { FakeBucket.new(s3_objects) }
let(:s3_objects) { [] }
end
end
end

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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')
}
]
}}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -0,0 +1,54 @@
require 'spec_helper'
describe Travis::Services::CancelBuild do
include Support::ActiveRecord
let(:repo) { Factory(:repository) }
let!(:job) { Factory(:test, repository: repo, state: :created) }
let!(:passed_job) { Factory(:test, repository: repo, state: :passed) }
let(:build) { Factory(:build, repository: repo) }
let(:params) { { id: build.id, source: 'tests' } }
let(:user) { Factory(:user) }
let(:service) { described_class.new(user, params) }
before do
build.matrix.destroy_all
build.matrix << passed_job
build.matrix << job
end
describe 'run' do
it 'should cancel the build if it\'s cancelable' do
job.stubs(:cancelable?).returns(true)
service.stubs(:authorized?).returns(true)
publisher = mock('publisher')
service.stubs(:publisher).returns(publisher)
publisher.expects(:publish).with(type: 'cancel_job', job_id: job.id, source: 'tests')
publisher.expects(:publish).with(type: 'cancel_job', job_id: passed_job.id, source: 'tests')
expect {
expect {
service.run
}.to change { build.reload.state }
}.to change { job.reload.state }
job.state.should == 'canceled'
build.state.should == 'canceled'
end
it 'should not cancel the job if it\'s not cancelable' do
job.stubs(:cancelable?).returns(false)
expect {
service.run
}.to_not change { build.reload.state }
end
it 'should not be able to cancel job if user does not have any permissions' do
user.permissions.destroy_all
service.can_cancel?.should be false
end
end
end

View File

@ -0,0 +1,44 @@
require 'spec_helper'
describe Travis::Services::CancelJob do
include Support::ActiveRecord
let(:repo) { Factory(:repository) }
let!(:job) { Factory(:test, repository: repo, state: :created) }
let(:params) { { id: job.id, source: 'tests' } }
let(:user) { Factory(:user) }
let(:service) { described_class.new(user, params) }
describe 'run' do
it 'should cancel the job if it\'s cancelable' do
job.stubs(:cancelable?).returns(true)
service.stubs(:authorized?).returns(true)
publisher = mock('publisher')
service.stubs(:publisher).returns(publisher)
publisher.expects(:publish).with(type: 'cancel_job', job_id: job.id, source: 'tests')
expect {
service.run
}.to change { job.reload.state }
job.state.should == 'canceled'
end
it 'should not cancel the job if it\'s not cancelable' do
job.state.should == :created
job.stubs(:cancelable?).returns(false)
expect {
service.run
}.to_not change { job.state }
end
it 'should not be able to cancel job if user does not have pull permission' do
user.permissions.destroy_all
service.can_cancel?.should be false
end
end
end

View File

@ -0,0 +1,90 @@
require 'spec_helper'
describe Travis::Services::FindAdmin do
include Travis::Testing::Stubs
describe 'find' do
let(:result) { described_class.new(nil, repository: repository).run }
before :each do
User.stubs(:with_permissions).with(:repository_id => repository.id, :admin => true).returns [user]
end
describe 'given a user has admin access to a repository (as seen by github)' do
before :each do
GH.stubs(:[]).with("repos/#{repository.slug}").returns('permissions' => { 'admin' => true })
end
it 'returns that user' do
result.should == user
end
end
describe 'given a user does not have access to a repository' do
before :each do
GH.stubs(:[]).with("repos/#{repository.slug}").returns('permissions' => { 'admin' => false })
user.stubs(:update_attributes!)
end
xit 'raises an exception' do
lambda { result }.should raise_error(Travis::AdminMissing, 'no admin available for svenfuchs/minimal')
end
xit 'revokes admin permissions for that user on our side' do
user.expects(:update_attributes!).with(:permissions => { 'admin' => false })
ignore_exception { result }
end
end
describe 'given an error occurs while retrieving the repository info' do
let(:error) { stub('error', :backtrace => [], :response => stub('reponse')) }
before :each do
GH.stubs(:[]).with("repos/#{repository.slug}").raises(GH::Error.new(error))
end
xit 'raises an exception' do
lambda { result }.should raise_error(Travis::AdminMissing, 'no admin available for svenfuchs/minimal')
end
it 'does not revoke permissions' do
user.expects(:update_permissions!).never
ignore_exception { result }
end
end
describe 'missing repository' do
it 'raises Travis::RepositoryMissing' do
expect { described_class.new.run }.to raise_error(Travis::RepositoryMissing)
end
end
def ignore_exception(&block)
block.call
rescue Travis::AdminMissing
end
end
end
describe Travis::Services::FindAdmin::Instrument do
include Travis::Testing::Stubs
let(:publisher) { Travis::Notification::Publisher::Memory.new }
let(:event) { publisher.events[1] }
let(:service) { Travis::Services::FindAdmin.new(nil, repository: repository) }
before :each do
Travis::Notification.publishers.replace([publisher])
User.stubs(:with_permissions).with(repository_id: repository.id, admin: true).returns [user]
GH.stubs(:[]).with("repos/#{repository.slug}").returns('permissions' => { 'admin' => true })
service.run
end
it 'publishes a event' do
event.should publish_instrumentation_event(
event: 'travis.services.find_admin.run:completed',
message: 'Travis::Services::FindAdmin#run:completed for svenfuchs/minimal: svenfuchs',
result: user,
)
end
end

View File

@ -0,0 +1,23 @@
require 'spec_helper'
describe Travis::Services::FindAnnotations do
include Support::ActiveRecord
let(:job) { Factory(:test) }
let!(:annotation) { Factory(:annotation, job: job) }
let(:service) { described_class.new(params) }
attr_reader :params
describe 'run' do
it 'finds annotations by a given list of ids' do
@params = { ids: [annotation.id] }
service.run.should eq([annotation])
end
it 'finds annotations by job_id' do
@params = { job_id: job.id }
service.run.should eq([annotation])
end
end
end

View File

@ -0,0 +1,32 @@
require 'spec_helper'
describe Travis::Services::FindBranch do
include Support::ActiveRecord
let(:repo) { Factory(:repository, :owner_name => 'travis-ci', :name => 'travis-core') }
let!(:build) { Factory(:build, :repository => repo, :state => :finished) }
let(:service) { described_class.new(stub('user'), params) }
attr_reader :params
it 'finds the last builds of the given repository and branch' do
@params = { :repository_id => repo.id, :branch => 'master' }
service.run.should be == build
end
it 'scopes to the given repository' do
@params = { :repository_id => repo.id, :branch => 'master' }
build = Factory(:build, :repository => Factory(:repository), :state => :finished)
service.run.should_not be == build
end
it 'returns an empty build scope when the repository could not be found' do
@params = { :repository_id => repo.id + 1, :branch => 'master' }
service.run.should be_nil
end
it 'finds branches by a given id' do
@params = { :id => build.id }
service.run.should be == build
end
end

View File

@ -0,0 +1,32 @@
require 'spec_helper'
describe Travis::Services::FindBranches do
include Support::ActiveRecord
let(:repo) { Factory(:repository, :owner_name => 'travis-ci', :name => 'travis-core') }
let!(:build) { Factory(:build, :repository => repo, :state => :finished) }
let(:service) { described_class.new(stub('user'), params) }
attr_reader :params
it 'finds the last builds of the given repository grouped per branch' do
@params = { :repository_id => repo.id }
service.run.should include(build)
end
it 'scopes to the given repository' do
@params = { :repository_id => repo.id }
build = Factory(:build, :repository => Factory(:repository), :state => :finished)
service.run.should_not include(build)
end
it 'returns an empty build scope when the repository could not be found' do
@params = { :repository_id => repo.id + 1 }
service.run.should == Build.none
end
it 'finds branches by a given list of ids' do
@params = { :ids => [build.id] }
service.run.should == [build]
end
end

View File

@ -0,0 +1,67 @@
require 'spec_helper'
describe Travis::Services::FindBuild do
include Support::ActiveRecord
let(:repo) { Factory(:repository, owner_name: 'travis-ci', name: 'travis-core') }
let!(:build) { Factory(:build, repository: repo, state: :finished, number: 1, config: {'sudo' => false}) }
let(:params) { { id: build.id } }
let(:service) { described_class.new(stub('user'), params) }
describe 'run' do
it 'finds a build by the given id' do
service.run.should == build
end
it 'does not raise if the build could not be found' do
@params = { :id => build.id + 1 }
lambda { service.run }.should_not raise_error
end
it 'includes config by default' do
service.run.config.should include(:sudo)
end
it 'excludes config when requested' do
params[:exclude_config] = '1'
service.run.config.should_not include(:sudo)
end
end
describe 'updated_at' do
it 'returns builds updated_at attribute' do
service.updated_at.to_s.should == build.updated_at.to_s
end
end
describe 'with newer associated record' do
it 'returns updated_at of newest result' do
build.update_attribute(:updated_at, 5.minutes.ago)
build.reload.updated_at.should < build.matrix.first.updated_at
service.updated_at.to_s.should == build.matrix.first.updated_at.to_s
end
end
describe 'without updated_at in one of the resources' do
it 'returns updated_at of newest result' do
Build.any_instance.stubs(updated_at: nil)
expect {
service.updated_at
}.to_not raise_error
end
end
# TODO builds can be requeued, so finished builds are no more final
#
# describe 'final?' do
# it 'returns true if the build is finished' do
# build.update_attributes!(:state => :errored)
# service.final?.should be_truthy
# end
# it 'returns false if the build is not finished' do
# build.update_attributes!(:state => :started)
# service.final?.should be false
# end
# end
end

View File

@ -0,0 +1,87 @@
require 'spec_helper'
describe Travis::Services::FindBuilds do
include Support::ActiveRecord
let(:repo) { Factory(:repository, owner_name: 'travis-ci', name: 'travis-core') }
let!(:push) { Factory(:build, repository: repo, event_type: 'push', state: :failed, number: 1) }
let(:service) { described_class.new(stub('user'), params) }
attr_reader :params
describe 'run' do
it 'finds recent builds when empty params given' do
@params = { :repository_id => repo.id }
service.run.should == [push]
end
it 'finds running builds when running param is passed' do
running = Factory(:build, repository: repo, event_type: 'push', state: 'started', number: 2)
@params = { :running => true }
service.run.should == [running]
end
it 'finds recent builds when no repo given' do
@params = nil
service.run.should == [push]
end
it 'finds builds older than the given number' do
@params = { :repository_id => repo.id, :after_number => 2 }
service.run.should == [push]
end
it 'finds builds with a given number, scoped by repository' do
@params = { :repository_id => repo.id, :number => 1 }
Factory(:build, :repository => Factory(:repository), :state => :finished, :number => 1)
Factory(:build, :repository => repo, :state => :finished, :number => 2)
service.run.should == [push]
end
it 'does not find by number if repository_id is missing' do
@params = { :number => 1 }
service.run.should == Build.none
end
it 'scopes to the given repository_id' do
@params = { :repository_id => repo.id }
Factory(:build, :repository => Factory(:repository), :state => :finished)
service.run.should == [push]
end
it 'returns an empty build scope when the repository could not be found' do
@params = { :repository_id => repo.id + 1 }
service.run.should == Build.none
end
it 'finds builds by a given list of ids' do
@params = { :ids => [push.id] }
service.run.should == [push]
end
describe 'finds recent builds when event_type' do
let!(:pull_request) { Factory(:build, repository: repo, state: :finished, number: 2, request: Factory(:request, :event_type => 'pull_request')) }
let!(:api) { Factory(:build, repository: repo, state: :finished, number: 2, request: Factory(:request, :event_type => 'api')) }
it 'given as push' do
@params = { repository_id: repo.id, event_type: 'push' }
service.run.should == [push]
end
it 'given as pull_request' do
@params = { repository_id: repo.id, event_type: 'pull_request' }
service.run.should == [pull_request]
end
it 'given as api' do
@params = { repository_id: repo.id, event_type: 'api' }
service.run.should == [api]
end
it 'given as [push, api]' do
@params = { repository_id: repo.id, event_type: ['push', 'api'] }
service.run.sort.should == [push, api]
end
end
end
end

View File

@ -0,0 +1,76 @@
require 'spec_helper'
describe Travis::Services::FindCaches do
include Support::ActiveRecord, Support::S3, Support::GCS
let(:user) { User.first || Factory(:user) }
let(:service) { described_class.new(user, params) }
let(:repo) { Factory(:repository, :owner_name => 'travis-ci', :name => 'travis-core') }
let(:cache_options) {{ s3: { bucket_name: '' , access_key_id: '', secret_access_key: ''} }}
let(:has_access) { true }
let(:result) { service.run }
subject { result }
before :each do
Travis.config.roles = {}
Travis.config.cache_options = cache_options
user.stubs(:permission?).returns(has_access)
end
describe 'given a repository_id' do
let(:params) {{ repository_id: repo.id }}
describe 'without any caches' do
it { should be == [] }
end
describe 'with caches' do
before do
s3_bucket << "#{repo.github_id}/master/cache--example1.tbz"
s3_bucket << "#{repo.github_id}/other/cache--example2.tbz"
s3_bucket << "#{repo.github_id.succ}/master/cache--example3.tbz"
end
its(:size) { should be == 2 }
describe 'the cache instances' do
subject { result.first }
its(:slug) { should be == 'cache--example1' }
its(:branch) { should be == 'master' }
its(:repository) { should be == repo }
its(:size) { should be == 0 }
end
describe 'with branch' do
let(:params) {{ repository_id: repo.id, branch: 'other' }}
its(:size) { should be == 1 }
end
describe 'with match' do
let(:params) {{ repository_id: repo.id, match: 'example1' }}
its(:size) { should be == 1 }
end
describe 'without access' do
let(:has_access) { false }
its(:size) { should be == 0 }
end
describe 'without s3 credentials' do
let(:cache_options) {{ }}
before { service.logger.expects(:warn).with("[services:find-caches] cache settings incomplete") }
it { should be == [] }
end
describe 'with multiple buckets' do
let(:cache_options) {[{ s3: { bucket_name: '', access_key_id: '', secret_access_key: '' } }, { s3: { bucket_name: '', access_key_id: '', secret_access_key: '' } }]}
its(:size) { should be == 4 }
end
end
context 'with GCS configuration' do
let(:cache_options) { { gcs: { bucket_name: '', json_key: '' } } }
its(:size) { should be == 0 }
end
end
end

View File

@ -0,0 +1,22 @@
require 'spec_helper'
require 'travis/testing/scenario'
describe Travis::Services::FindDailyReposStats do
include Support::ActiveRecord
let(:service) { described_class.new(stub('user'), {}) }
before { Scenario.default }
it 'should include the date' do
stats = service.run
expect(stats.size).to eq(1)
stats.first['date'].should == Repository.first.created_at.to_date.to_s(:date)
end
it 'should include the number per day' do
stats = service.run
expect(stats.size).to eq(1)
stats.first['count'].to_i.should == 2
end
end

View File

@ -0,0 +1,17 @@
require 'spec_helper'
require 'travis/testing/scenario'
describe Travis::Services::FindDailyTestsStats do
include Support::ActiveRecord
let(:service) { described_class.new(stub('user'), {}) }
before { Scenario.default }
it 'should return the jobs per day' do
stats = service.run
expect(stats.size).to eq(1)
stats.first['date'].should == Job.first.created_at.to_date.to_s(:date)
stats.first['count'].to_i.should == 13
end
end

View File

@ -0,0 +1,49 @@
require 'spec_helper'
describe Travis::Services::FindHooks do
include Support::ActiveRecord
let(:user) { User.first || Factory(:user) }
let(:repo) { Factory(:repository) }
let(:push_repo) { Factory(:repository, name: 'push-repo') }
let(:service) { described_class.new(user, params) }
before :each do
user.permissions.create!(:repository => repo, :admin => true)
user.permissions.create!(:repository => push_repo, :push => true)
end
attr_reader :params
it 'finds repositories where the current user has access with :all option' do
@params = { all: true }
hooks = service.run
hooks.should include(repo)
hooks.should include(push_repo)
expect(hooks.size).to eq(2)
# hooks should include admin information
hooks.sort_by(&:id).map(&:admin?).should == [true, false]
end
it 'finds repositories where the current user has admin access' do
@params = {}
service.run.should include(repo)
end
it 'does not find repositories where the current user does not have admin access' do
@params = {}
user.permissions.delete_all
service.run.should_not include(repo)
end
it 'finds repositories by a given owner_name where the current user has admin access' do
@params = { :owner_name => repo.owner_name }
service.run.should include(repo)
end
it 'does not find repositories by a given owner_name where the current user does not have admin access' do
@params = { :owner_name => 'rails' }
service.run.should_not include(repo)
end
end

View File

@ -0,0 +1,61 @@
require 'spec_helper'
describe Travis::Services::FindJob do
include Support::ActiveRecord
let(:repo) { Factory(:repository) }
let!(:job) { Factory(:test, repository: repo, state: :created, queue: 'builds.linux', config: {'sudo' => false}) }
let(:params) { { id: job.id } }
let(:service) { described_class.new(stub('user'), params) }
describe 'run' do
it 'finds the job with the given id' do
@params = { id: job.id }
service.run.should == job
end
it 'does not raise if the job could not be found' do
@params = { id: job.id + 1 }
lambda { service.run }.should_not raise_error
end
it 'raises RecordNotFound if a SubclassNotFound error is raised during find' do
find_by_id = stub.tap do |s|
s.stubs(:column_names).returns(%w(id config))
s.stubs(:select).returns(s)
s.stubs(:find_by_id).raises(ActiveRecord::SubclassNotFound)
end
service.stubs(:scope).returns(find_by_id)
lambda { service.run }.should raise_error(ActiveRecord::RecordNotFound)
end
it 'includes config by default' do
service.run.config.should include(:sudo)
end
it 'excludes config when requested' do
params[:exclude_config] = '1'
service.run.config.should_not include(:sudo)
end
end
describe 'updated_at' do
it 'returns jobs updated_at attribute' do
service.updated_at.to_s.should == job.updated_at.to_s
end
end
# TODO jobs can be requeued, so finished jobs are no more final
#
# describe 'final?' do
# it 'returns true if the job is finished' do
# job.update_attributes!(state: :errored)
# service.final?.should be_truthy
# end
# it 'returns false if the job is not finished' do
# job.update_attributes!(state: :started)
# service.final?.should be false
# end
# end
end

View File

@ -0,0 +1,68 @@
require 'spec_helper'
describe Travis::Services::FindJobs do
include Support::ActiveRecord
let(:repo) { Factory(:repository) }
let!(:job) { Factory(:test, :repository => repo, :state => :created, :queue => 'builds.linux') }
let(:service) { described_class.new(stub('user'), params) }
attr_reader :params
describe 'run' do
it 'finds jobs on the given queue' do
@params = { :queue => 'builds.linux' }
service.run.should include(job)
end
it 'does not find jobs on other queues' do
@params = { :queue => 'builds.nodejs' }
service.run.should_not include(job)
end
it 'finds jobs by a given list of ids' do
@params = { :ids => [job.id] }
service.run.should == [job]
end
it 'finds jobs by state' do
build = Factory(:build)
Job::Test.destroy_all
started = Factory(:test, :state => :started, :source => build)
passed = Factory(:test, :state => :passed, :source => build)
created = Factory(:test, :state => :created, :source => build)
@params = { :state => ['created', 'passed'] }
service.run.sort_by(&:id).should == [created, passed].sort_by(&:id)
end
it 'finds jobs that are about to run without any args' do
build = Factory(:build)
Job::Test.destroy_all
started = Factory(:test, :state => :started, :source => build)
queued = Factory(:test, :state => :queued, :source => build)
passed = Factory(:test, :state => :passed, :source => build)
created = Factory(:test, :state => :created, :source => build)
received = Factory(:test, :state => :received, :source => build)
@params = {}
service.run.sort_by(&:id).should == [started, queued, created, received].sort_by(&:id)
end
end
describe 'updated_at' do
it 'returns the latest updated_at time' do
skip 'rack cache is disabled, so not much need for caching now'
@params = { :queue => 'builds.linux' }
Job.delete_all
Factory(:test, :repository => repo, :state => :queued, :queue => 'build.common', :updated_at => Time.now - 1.hour)
Factory(:test, :repository => repo, :state => :queued, :queue => 'build.common', :updated_at => Time.now)
service.updated_at.to_s.should == Time.now.to_s
end
end
end

View File

@ -0,0 +1,42 @@
require 'spec_helper'
describe Travis::Services::FindLog do
include Support::ActiveRecord
let!(:job) { Factory(:test) }
let(:log) { job.log }
let(:service) { described_class.new(stub('user'), params) }
attr_reader :params
describe 'run' do
it 'finds the log with the given id' do
@params = { id: log.id }
service.run.should == log
end
it 'finds the log with the given job_id' do
@params = { job_id: job.id }
service.run.should == log
end
it 'does not raise if the log could not be found' do
@params = { id: log.id + 1 }
lambda { service.run }.should_not raise_error
end
end
# TODO jobs can be requeued, so finished jobs are no more final
#
# describe 'final?' do
# it 'returns true if the job is finished' do
# log.job.update_attributes!(:state => :finished)
# service.final?.should be_truthy
# end
# it 'returns false if the job is not finished' do
# log.job.update_attributes!(:state => :started)
# service.final?.should be false
# end
# end
end

View File

@ -0,0 +1,29 @@
require 'spec_helper'
describe Travis::Services::FindRepoKey do
include Support::ActiveRecord
let!(:repo) { Factory(:repository, :owner_name => 'travis-ci', :name => 'travis-core') }
let(:service) { described_class.new(stub('user'), params) }
attr_reader :params
describe 'run' do
it 'finds a key by the given repository id' do
@params = { :id => repo.id }
service.run.should == repo.key
end
it 'finds a key by the given owner_name and name' do
@params = { :owner_name => repo.owner_name, :name => repo.name }
service.run.should == repo.key
end
end
describe 'updated_at' do
it 'returns key\'s updated_at attribute' do
@params = { :id => repo.id }
service.updated_at.to_s.should == repo.key.updated_at.to_s
end
end
end

View File

@ -0,0 +1,43 @@
require 'spec_helper'
describe Travis::Services::FindRepoSettings do
include Support::ActiveRecord
let(:repo) { Factory(:repository) }
let(:params) { { id: repo.id } }
let(:user) { Factory(:user) }
let(:service) { described_class.new(user, params) }
before do
repo.settings.merge('build_pushes' => false)
repo.settings.save
repo.save
end
describe 'authorized?' do
let(:service) { described_class.new(nil, params) }
it 'should be unauthorized with current_user' do
service.should_not be_authorized
end
end
describe 'run' do
it 'should return nil without a repo' do
repo.destroy
service.run.should be_nil
end
it 'should return repo settings' do
user.permissions.create(repository_id: repo.id, push: true)
service.run.to_hash.should == repo.settings.to_hash
end
it 'should not be able to get settings if user does not have push permission' do
user.permissions.create(repository_id: repo.id, push: false)
service.run.should be_nil
end
end
end

View File

@ -0,0 +1,34 @@
require 'spec_helper'
describe Travis::Services::FindRepo do
include Support::ActiveRecord
let!(:repo) { Factory(:repository, :owner_name => 'travis-ci', :name => 'travis-core') }
let(:service) { described_class.new(stub('user'), params) }
attr_reader :params
describe 'run' do
it 'finds a repository by the given id' do
@params = { :id => repo.id }
service.run.should == repo
end
it 'finds a repository by the given owner_name and name' do
@params = { :owner_name => repo.owner_name, :name => repo.name }
service.run.should == repo
end
it 'does not raise if the repository could not be found' do
@params = { :id => repo.id + 1 }
lambda { service.run }.should_not raise_error
end
end
describe 'updated_at' do
it 'returns jobs updated_at attribute' do
@params = { :id => repo.id }
service.updated_at.to_s.should == repo.updated_at.to_s
end
end
end

View File

@ -0,0 +1,127 @@
require 'spec_helper'
describe Travis::Services::FindRepos do
include Support::ActiveRecord
let!(:repo) { Factory(:repository, :owner_name => 'travis-ci', :name => 'travis-core', :active => true) }
let(:service) { described_class.new(stub('user'), params) }
attr_reader :params
it 'limits the repositories list' do
Factory(:repository)
@params = { :limit => 1 }
service.run.length.should == 1
end
it 'ignores the limit if it is not a number' do
Factory(:repository)
@params = { :limit => 'a' }
service.run.length.should == 2
end
it 'does not allow for limit higher than 50' do
@params = { :limit => 60 }
service.send(:limit).should == 50
end
it 'finds repositories by a given list of ids' do
@params = { :ids => [repo.id] }
service.run.should == [repo]
end
it 'returns the recent timeline when given empty params' do
@params = {}
service.run.should include(repo)
end
it 'applies timeline only if no other params are given' do
repo = Factory(:repository, :owner_name => 'foo', :name => 'bar', :last_build_started_at => nil, :active => true)
@params = { slug: 'foo/bar' }
service.run.should include(repo)
end
describe 'given a member name' do
it 'finds a repository where that member has permissions' do
@params = { :member => 'joshk' }
repo.users << Factory(:user, :login => 'joshk')
service.run.should include(repo)
end
it 'does not find a repository where the member does not have permissions' do
@params = { :member => 'joshk' }
service.run.should_not include(repo)
end
# TODO ... we now include all :active repos (i.e. including those that haven't built yet)
# and last_build_started_at is nil for them, too. since there's no easy way to detect
# queued builds on the repo timeline i'm just disabling this for now.
#
# it 'sorts by latest build, putting queued (no last_build_started_at) at the front' do
# repo.update_column(:last_build_started_at, Time.now - 10)
# queued = Factory(:repository, name: 'queued', last_build_started_at: nil, :active => true)
# just_started = Factory(:repository, name: 'just-started',last_build_started_at: Time.now, :active => true)
# josh = Factory(:user, :login => 'joshk')
# [repo, queued, just_started].each { |r| r.users << josh }
# @params = { :member => 'joshk' }
# service.run.map(&:name).should == [queued, just_started, repo].map(&:name)
# end
end
describe 'given an owner_name name' do
it 'finds a repository with that owner_name' do
@params = { :owner_name => 'travis-ci' }
service.run.should include(repo)
end
it 'does not find a repository with another owner name' do
@params = { :owner_name => 'sinatra' }
service.run.should_not include(repo)
end
end
describe 'given an owner_name name and active param' do
it 'finds a repository with that owner_name even if it does not have any builds' do
repo.update_column(:last_build_id, nil)
repo.update_column(:active, true)
@params = { :owner_name => 'travis-ci', :active => true }
service.run.should include(repo)
end
end
describe 'given a slug name' do
it 'finds a repository with that slug' do
@params = { :slug => 'travis-ci/travis-core' }
service.run.should include(repo)
end
it 'does not find a repository with a different slug' do
@params = { :slug => 'travis-ci/travis-hub' }
service.run.should_not include(repo)
end
end
describe 'given a search phrase' do
it 'finds a repository matching that phrase' do
@params = { :search => 'travis' }
service.run.should include(repo)
end
it 'does not find a repository that does not match that phrase' do
@params = { :search => 'sinatra' }
service.run.should_not include(repo)
end
end
describe 'given a list of ids' do
it 'finds included repositories' do
@params = { :ids => [repo.id] }
service.run.should include(repo)
end
it 'does not find a repositories that are not included' do
@params = { :ids => [repo.id + 1] }
service.run.should_not include(repo)
end
end
end

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