Merge commit 'bddd9952a74d1eddad5d5a255419e07c836b286d' into addCron

Conflicts:
	spec/v3/services/owner/find_spec.rb
	spec/v3/services/repositories/for_current_user_spec.rb
	spec/v3/services/repositories/for_owner_spec.rb
	spec/v3/services/repository/find_spec.rb
This commit is contained in:
Steffen Kötte 2015-12-17 10:02:21 +01:00
commit e4aec173ee
28 changed files with 577 additions and 47 deletions

View File

@ -33,6 +33,7 @@ gem 'skylight', '~> 0.6.0.beta.1'
gem 'stackprof'
gem 'jemalloc'
gem 'customerio'
group :test do
gem 'rspec', '~> 2.13'

View File

@ -36,9 +36,9 @@ GIT
GIT
remote: git://github.com/rtomayko/rack-cache.git
revision: d00e6e491fcc7bdca9c27d735abde5c4fdb48cd9
revision: f96febebed7700337e8c362403b081e45b8e4f13
specs:
rack-cache (1.2)
rack-cache (1.5.1)
rack (>= 0.4)
GIT
@ -50,7 +50,7 @@ GIT
GIT
remote: git://github.com/travis-ci/travis-core.git
revision: 21793fc8b01f965b93cf98b7ab1458ee359a5a62
revision: 3a9f6e4c14bb1eacb93609e1864c9c547a13c1a4
specs:
travis-core (0.0.1)
actionmailer (~> 3.2.19)
@ -137,7 +137,7 @@ GEM
activesupport (3.2.22)
i18n (~> 0.6, >= 0.6.4)
multi_json (~> 1.0)
addressable (2.3.8)
addressable (2.4.0)
arel (3.0.3)
atomic (1.1.99)
avl_tree (1.1.3)
@ -145,7 +145,7 @@ GEM
descendants_tracker (~> 0.0.4)
ice_nine (~> 0.11.0)
thread_safe (~> 0.3, >= 0.3.1)
backports (3.6.6)
backports (3.6.7)
builder (3.0.4)
bunny (0.8.0)
celluloid (0.16.0)
@ -157,6 +157,9 @@ GEM
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)
multi_json (~> 1.0)
dalli (2.7.2)
data_migrations (0.0.1)
activerecord
@ -171,7 +174,7 @@ GEM
erubis (2.7.0)
factory_girl (2.4.2)
activesupport
faraday (0.9.1)
faraday (0.9.2)
multipart-post (>= 1.2, < 3)
ffi (1.9.6)
foreman (0.64.0)
@ -187,7 +190,10 @@ GEM
hashr (0.0.22)
hike (1.2.3)
hitimes (1.2.3)
httpclient (2.6.0.1)
httparty (0.11.0)
multi_json (~> 1.0)
multi_xml (>= 0.5.2)
httpclient (2.7.0.1)
i18n (0.7.0)
ice_nine (0.11.1)
jemalloc (1.0.1)
@ -215,6 +221,7 @@ GEM
mocha (0.14.0)
metaclass (~> 0.0.1)
multi_json (1.11.2)
multi_xml (0.5.5)
multipart-post (2.0.0)
net-http-persistent (2.9.4)
net-http-pipeline (1.0.1)
@ -225,10 +232,11 @@ GEM
coderay (~> 1.1.0)
method_source (~> 0.8.1)
slop (~> 3.4)
pusher (0.14.5)
pusher (0.14.6)
httpclient (~> 2.5)
multi_json (~> 1.0)
signature (~> 0.1.8)
pusher-signature (~> 0.1.8)
pusher-signature (0.1.8)
rack (1.4.7)
rack-attack (4.2.0)
rack
@ -255,7 +263,7 @@ GEM
rdoc (3.12.2)
json (~> 1.4)
redcarpet (2.3.0)
redis (3.2.1)
redis (3.2.2)
redis-namespace (1.5.1)
redis (~> 3.0, >= 3.0.4)
rerun (0.8.2)
@ -275,7 +283,6 @@ GEM
json
redis (>= 3.0.6)
redis-namespace (>= 1.3.1)
signature (0.1.8)
simple_states (1.0.1)
activesupport
hashr (~> 0.0.10)
@ -315,7 +322,7 @@ GEM
treetop (1.4.15)
polyglot
polyglot (>= 0.3.1)
tzinfo (0.3.44)
tzinfo (0.3.46)
unicorn (4.8.3)
kgio (~> 2.6)
rack
@ -335,6 +342,7 @@ PLATFORMS
DEPENDENCIES
active_model_serializers
bunny (~> 0.8.0)
customerio
dalli
database_cleaner (~> 0.8.0)
factory_girl (~> 2.4.0)

View File

@ -17,7 +17,8 @@ This is the app running on https://api.travis-ci.org/
### Database setup
1. `rake db:create db:structure:load`
1. `rake db:create db:migrate`
2. for testing 'RAILS_ENV=test bundle exec rake db:create db:migrate --trace'
1. Clone `travis-logs` and copy the `logs` database (assume the PostgreSQL user is `postgres`):
```sh-session
cd ..

View File

@ -2,6 +2,7 @@ require 'travis/api/app'
require 'addressable/uri'
require 'faraday'
require 'securerandom'
require 'customerio'
class Travis::Api::App
class Endpoint
@ -78,6 +79,7 @@ class Travis::Api::App
#
# * **github_token**: GitHub token for checking authorization (required)
post '/github' do
check_agent
unless params[:github_token]
halt 422, { "error" => "Must pass 'github_token' parameter" }
end
@ -94,6 +96,7 @@ class Travis::Api::App
# * **redirect_uri**: URI to redirect to after handshake.
get '/handshake' do
handshake do |user, token, redirect_uri|
if target_ok? redirect_uri
content_type :html
data = { user: user, token: token, uri: redirect_uri }
@ -145,6 +148,43 @@ class Travis::Api::App
private
def allowed_agents
@allowed_agents ||= redis.smembers('auth_agents')
end
def check_agent
return if settings.test? or allowed_agents.empty?
return if allowed_agents.any? { |a| request.user_agent.to_s.start_with? a }
halt 403, "you are currently not allowed to perform this request. please contact support@travis-ci.com."
end
# update first login date if not set
def update_first_login(user)
unless user.first_logged_in_at
user.update_attributes(first_logged_in_at: Time.now)
end
end
def update_customerio(user)
return unless Travis.config.customerio.site_id
# send event to customer.io
payload = {
:id => user.id,
:name => user.name,
:login => user.login,
:email => primary_email_for_user(user.github_oauth_token),
:created_at => user.created_at.to_i,
:github_id => user.github_id,
:education => user.education,
:first_logged_in_at => user.first_logged_in_at.to_i
}
customerio.identify(payload)
rescue StandardError => e
Travis.logger.error "Could not update Customer.io for User: #{user.id}:#{user.login} with message:#{e.message}"
end
def serialize_user(user)
rendered = Travis::Api.data(user, version: :v2)
rendered['user'].merge('token' => user.tokens.first.try(:token).to_s)
@ -174,6 +214,8 @@ class Travis::Api::App
user = user_for_github_token(github_token)
token = generate_token(user: user, app_id: 0)
payload = params[:state].split(":::", 2)[1]
update_first_login(user)
update_customerio(user)
yield serialize_user(user), token, payload
else
values[:state] = create_state
@ -183,6 +225,7 @@ class Travis::Api::App
end
end
def create_state
state = SecureRandom.urlsafe_base64(16)
redis.sadd('github:states', state)
@ -212,6 +255,7 @@ class Travis::Api::App
super
@user = ::User.find_by_github_id(data['id'])
end
def info(attributes = {})
@ -342,6 +386,15 @@ class Travis::Api::App
def allowed_https_targets
@allowed_https_targets ||= Travis.config.auth.target_origin.to_s.split(',')
end
def primary_email_for_user(oauth_token)
# check for the users primary email address (we don't store this info)
GH.with(token: oauth_token, client_id: nil) { GH['user/emails'] }.select { |e| e['primary'] }.first['email']
end
def customerio
Customerio::Client.new(Travis.config.customerio.site_id, Travis.config.customerio.api_key, :json => true)
end
end
end
end

View File

@ -53,6 +53,8 @@ class Travis::Api::App
if params
params = Hash[*params.split(';').map { |p| p.scan /(#{TOKEN})=(#{TOKEN})/ }.flatten]
quality = params.delete('q').to_f if params['q']
else
params = {}
end
if subtype =~ HEADER_FORMAT

View File

@ -37,7 +37,7 @@ class Travis::Api::App
end
def mark_travis(agent)
command = agent.application.comment.detect { |c| c.start_with? "command " }
command = agent.application.comment.detect { |c| c.start_with? "command " } if agent.application.comment
if command
mark(:cli, :version, agent.version)

View File

@ -31,6 +31,12 @@ class Rack::Attack
"/auth/post_message/iframe"
]
####
# Whitelisted IP addresses
whitelist('whitelist client requesting from redis') do |request|
Travis.redis.sismember(:api_whitelisted_ips, request.ip)
end
####
# Ban based on: IP address
# Ban time: indefinite
@ -44,7 +50,7 @@ class Rack::Attack
# Ban time: 5 hours
# Ban after: 10 POST requests within five minutes to /auth/github
blacklist('hammering /auth/github') do |request|
Rack::Attack::Allow2Ban.filter(request.identifier, maxretry: 10, findtime: 5.minutes, bantime: bantime(5.hours)) do
Rack::Attack::Allow2Ban.filter(request.identifier, maxretry: 2, findtime: 5.minutes, bantime: bantime(5.hours)) do
request.post? and request.path == '/auth/github'
end
end
@ -59,6 +65,14 @@ class Rack::Attack
end
end
###
# Throttle: unauthenticated requests to /auth/github - 1 per minute
# Scoped by: IP address
throttle('req/ip/1min', limit: 1, period: 1.minute) do |request|
request.ip unless request.authenticated? and request.path == '/auth/github'
end
###
# Throttle: unauthenticated requests - 500 per minute
# Scoped by: IP address

View File

@ -6,6 +6,7 @@ module Travis::API::V3
has_many :builds, dependent: :delete_all, order: 'builds.id DESC'.freeze
has_many :permissions, dependent: :delete_all
has_many :users, through: :permissions
has_many :stars
belongs_to :owner, polymorphic: true
belongs_to :last_build, class_name: 'Travis::API::V3::Models::Build'.freeze
@ -49,5 +50,17 @@ module Travis::API::V3
rescue ActiveRecord::RecordNotUnique
branches.where(name: name).first
end
def id_default_branch
[id, default_branch_name]
end
def send(name, *args, &block)
if name == [:id, :default_branch]
name = :id_default_branch
end
__send__(name, *args, &block)
end
end
end

View File

@ -0,0 +1,6 @@
module Travis::API::V3
class Models::Star < Model
belongs_to :user
belongs_to :repository
end
end

View File

@ -6,6 +6,7 @@ module Travis::API::V3
has_many :tokens, dependent: :destroy
has_many :organizations, through: :memberships
has_many :repositories, as: :owner
has_many :stars
has_one :subscription, as: :owner
serialize :github_oauth_token, Extensions::EncryptedColumn.new(disable: true)
@ -17,5 +18,9 @@ module Travis::API::V3
def subscription
super if Features.use_subscriptions?
end
def starred_repository_ids
@starred_repository_ids ||= stars.map(&:repository_id)
end
end
end

View File

@ -10,6 +10,14 @@ module Travis::API::V3
write?
end
def star?
write?
end
def unstar?
write?
end
def create_request?
write?
end

View File

@ -1,26 +1,34 @@
module Travis::API::V3
class Queries::Repositories < Query
params :active, :private, prefix: :repository
params :active, :private, :starred, prefix: :repository
sortable_by :id, :github_id, :owner_name, :name, active: sort_condition(:active)
def for_member(user)
all.joins(:users).where(users: user_condition(user), invalidated_at: nil)
def for_member(user, **options)
all(user: user, **options).joins(:users).where(users: user_condition(user), invalidated_at: nil)
end
def for_owner(owner)
filter(owner.repositories)
def for_owner(owner, **options)
filter(owner.repositories, **options)
end
def all
@all ||= filter(Models::Repository)
def all(**options)
filter(Models::Repository, **options)
end
def filter(list)
def filter(list, user: nil)
list = list.where(invalidated_at: nil)
list = list.where(active: bool(active)) unless active.nil?
list = list.where(private: bool(private)) unless private.nil?
list = list.includes(:owner) if includes? 'repository.owner'.freeze
if user and not starred.nil?
if bool(starred)
list = list.joins(:stars).where(stars: { user_id: user.id })
elsif user.starred_repository_ids.any?
list = list.where("repositories.id NOT IN (?)", user.starred_repository_ids)
end
end
if includes? 'repository.last_build'.freeze or includes? 'build'.freeze
list = list.includes(:last_build)
list = list.includes(last_build: :commit) if includes? 'build.commit'.freeze

View File

@ -3,13 +3,31 @@ module Travis::API::V3
params :id, :slug
def find
@find ||= find!
end
def star(current_user)
repository = find
starred = Models::Star.where(repository_id: repository.id, user_id: current_user.id).first
Models::Star.create(repository_id: repository.id, user_id: current_user.id) unless starred
repository
end
def unstar(current_user)
repository = find
starred = Models::Star.where(repository_id: repository.id, user_id: current_user.id).first
starred.delete if starred
repository
end
private
def find!
return by_slug if slug
return Models::Repository.find_by_id(id) if id
raise WrongParams, 'missing repository.id'.freeze
end
private
def by_slug
owner_name, name = slug.split('/')
Models::Repository.where(owner_name: owner_name, name: name, invalidated_at: nil).first

View File

@ -3,7 +3,7 @@ require 'travis/api/v3/renderer/model_renderer'
module Travis::API::V3
class Renderer::Repository < Renderer::ModelRenderer
representation(:minimal, :id, :name, :slug)
representation(:standard, :id, :name, :slug, :description, :github_language, :active, :private, :owner, :default_branch)
representation(:standard, :id, :name, :slug, :description, :github_language, :active, :private, :owner, :default_branch, :starred)
def active
!!model.active
@ -19,6 +19,11 @@ module Travis::API::V3
}
end
def starred
return false unless user = access_control.user
user.starred_repository_ids.include? id
end
def include_default_branch?
return true if include? 'repository.default_branch'.freeze
return true if include.any? { |i| i.start_with? 'branch'.freeze }

View File

@ -78,6 +78,8 @@ module Travis::API::V3
post :enable, '/enable'
post :disable, '/disable'
post :star, '/star'
post :unstar, '/unstar'
resource :branch do
route '/branch/{branch.name}'

View File

@ -1,6 +1,6 @@
module Travis::API::V3
class Services::Repositories::ForCurrentUser < Service
params :active, :private, prefix: :repository
params :active, :private, :starred, prefix: :repository
paginate(default_limit: 100)
def run!

View File

@ -1,10 +1,10 @@
module Travis::API::V3
class Services::Repositories::ForOwner < Service
params :active, :private, prefix: :repository
params :active, :private, :starred, prefix: :repository
paginate(default_limit: 100)
def run!
unfiltered = query.for_owner(find(:owner))
unfiltered = query.for_owner(find(:owner), user: access_control.user)
access_control.visible_repositories(unfiltered)
end
end

View File

@ -0,0 +1,15 @@
module Travis::API::V3
class Services::Repository::Star < Service
def run!
raise LoginRequired unless access_control.logged_in? or access_control.full_access?
raise NotFound unless repository = find(:repository)
check_access(repository)
current_user = access_control.user
query.star(current_user)
end
def check_access(repository)
access_control.permissions(repository).star!
end
end
end

View File

@ -0,0 +1,15 @@
module Travis::API::V3
class Services::Repository::Unstar < Service
def run!
raise LoginRequired unless access_control.logged_in? or access_control.full_access?
raise NotFound unless repository = find(:repository)
check_access(repository)
current_user = access_control.user
query.unstar(current_user)
end
def check_access(repository)
access_control.permissions(repository).unstar!
end
end
end

View File

@ -64,7 +64,7 @@ describe Travis::API::V3::ServiceIndex do
describe "for_current_user action" do
let(:action) { resource.fetch("actions").fetch("for_current_user") }
specify { expect(action).to include("@type"=>"template", "request_method"=>"GET", "uri_template"=>"#{path}repos{?active,include,limit,offset,private,repository.active,repository.private,sort_by}") }
specify { expect(action).to include("@type"=>"template", "request_method"=>"GET", "uri_template"=>"#{path}repos{?active,include,limit,offset,private,repository.active,repository.private,repository.starred,sort_by,starred}") }
end
end

View File

@ -64,8 +64,10 @@ describe Travis::API::V3::Services::Owner::Find do
"read" => true,
"enable" => false,
"disable" => false,
"star" => false,
"unstar" => false,
"create_request" => false,
"create_cron" => false },
"create_cron" => false},
"id" => repo.id,
"name" => "example-repo",
"slug" => "example-org/example-repo",
@ -78,7 +80,8 @@ describe Travis::API::V3::Services::Owner::Find do
"@type" => "branch",
"@href" => "/v3/repo/#{repo.id}/branch/master",
"@representation" => "minimal",
"name" => "master"}
"name" => "master"},
"starred" => false
}]
}}
end
@ -109,8 +112,10 @@ describe Travis::API::V3::Services::Owner::Find do
"read" => true,
"enable" => false,
"disable" => false,
"star" => false,
"unstar" => false,
"create_request"=> false,
"create_cron" => false },
"create_cron" => false},
"id" => repo.id,
"name" => "example-repo",
"slug" => "example-org/example-repo",
@ -123,7 +128,8 @@ describe Travis::API::V3::Services::Owner::Find do
"@type" => "branch",
"@href" => "/v3/repo/#{repo.id}/branch/master",
"@representation"=> "minimal",
"name" => "master"}
"name" => "master"},
"starred" => false
}]
}}
end

View File

@ -1,7 +1,7 @@
require 'spec_helper'
describe Travis::API::V3::Services::Repositories::ForCurrentUser do
let(:repo) { Travis::API::V3::Models::Repository.where(owner_name: 'svenfuchs', name: 'minimal').first }
let(:repo) { Travis::API::V3::Models::Repository.where(owner_name: 'svenfuchs', name: 'minimal').first }
let(:build) { repo.builds.first }
let(:jobs) { Travis::API::V3::Models::Build.find(build.id).jobs }
@ -42,8 +42,10 @@ describe Travis::API::V3::Services::Repositories::ForCurrentUser do
"read" => true,
"enable" => true,
"disable" => true,
"star" => true,
"unstar" => true,
"create_request" => true,
"create_cron" => true },
"create_cron" => true},
"id" => repo.id,
"name" => "minimal",
"slug" => "svenfuchs/minimal",
@ -60,7 +62,9 @@ describe Travis::API::V3::Services::Repositories::ForCurrentUser do
"@type" => "branch",
"@href" => "/v3/repo/#{repo.id}/branch/master",
"@representation" => "minimal",
"name" => "master"}}]
"name" => "master"},
"starred" => false
}]
}}
end
@ -82,4 +86,29 @@ describe Travis::API::V3::Services::Repositories::ForCurrentUser do
example { expect(last_response) .to be_ok }
example { expect(JSON.load(body)['repositories']) .to be == [] }
end
describe "filter: starred=true" do
before { Travis::API::V3::Models::Star.create(user: repo.owner, repository: repo) }
before { get("/v3/repos", {"starred" => "true"}, headers) }
after { repo.owner.stars.each(&:destroy) }
example { expect(last_response) .to be_ok }
example { expect(JSON.load(body)['@href']) .to be == "/v3/repos?starred=true" }
example { expect(JSON.load(body)['repositories']) .not_to be_empty }
end
describe "filter: starred=false" do
before { get("/v3/repos", {"starred" => "false"}, headers) }
example { expect(last_response) .to be_ok }
example { expect(JSON.load(body)['@href']) .to be == "/v3/repos?starred=false" }
example { expect(JSON.load(body)['repositories']) .not_to be_empty }
end
describe "filter: starred=false but no unstarred repos" do
before { Travis::API::V3::Models::Star.create(user: repo.owner, repository: repo) }
after { repo.owner.stars.each(&:destroy) }
before { get("/v3/repos", {"starred" => "false"}, headers) }
example { expect(last_response) .to be_ok }
example { expect(JSON.load(body)['@href']) .to be == "/v3/repos?starred=false" }
example { expect(JSON.load(body)['repositories']) .to be_empty }
end
end

View File

@ -42,8 +42,10 @@ describe Travis::API::V3::Services::Repositories::ForOwner do
"read" => true,
"enable" => false,
"disable" => false,
"star" => false,
"unstar" => false,
"create_request" => false,
"create_cron" => false },
"create_cron" => false},
"id" => repo.id,
"name" => "minimal",
"slug" => "svenfuchs/minimal",
@ -60,7 +62,9 @@ describe Travis::API::V3::Services::Repositories::ForOwner do
"@type" => "branch",
"@href" => "/v3/repo/#{repo.id}/branch/master",
"@representation" => "minimal",
"name" => "master"}}]}}
"name" => "master"},
"starred" => false
}]}}
end
describe "filter: private=false" do
@ -75,4 +79,29 @@ describe Travis::API::V3::Services::Repositories::ForOwner do
example { expect(last_response) .to be_ok }
example { expect(JSON.load(body)['repositories']) .to be == [] }
end
describe "filter: starred=true" do
before { Travis::API::V3::Models::Star.create(user: repo.owner, repository: repo) }
before { get("/v3/repos", {"starred" => "true"}, headers) }
after { repo.owner.stars.each(&:destroy) }
example { expect(last_response) .to be_ok }
example { expect(JSON.load(body)['@href']) .to be == "/v3/repos?starred=true" }
example { expect(JSON.load(body)['repositories']) .not_to be_empty }
end
describe "filter: starred=false" do
before { get("/v3/repos", {"starred" => "false"}, headers) }
example { expect(last_response) .to be_ok }
example { expect(JSON.load(body)['@href']) .to be == "/v3/repos?starred=false" }
example { expect(JSON.load(body)['repositories']) .not_to be_empty }
end
describe "filter: starred=false but no unstarred repos" do
before { Travis::API::V3::Models::Star.create(user: repo.owner, repository: repo) }
after { repo.owner.stars.each(&:destroy) }
before { get("/v3/repos", {"starred" => "false"}, headers) }
example { expect(last_response) .to be_ok }
example { expect(JSON.load(body)['@href']) .to be == "/v3/repos?starred=false" }
example { expect(JSON.load(body)['repositories']) .to be_empty }
end
end

View File

@ -0,0 +1,72 @@
require 'spec_helper'
describe Travis::API::V3::Services::Repository::Disable do
let(:repo) { Travis::API::V3::Models::Repository.where(owner_name: 'svenfuchs', name: 'minimal').first }
before do
repo.update_attributes!(active: true)
end
describe "not authenticated" do
before { post("/v3/repo/#{repo.id}/disable") }
example { expect(last_response.status).to be == 403 }
example { expect(JSON.load(body)).to be == {
"@type" => "error",
"error_type" => "login_required",
"error_message" => "login required"
}}
end
describe "missing repo, authenticated" do
let(:token) { Travis::Api::App::AccessToken.create(user: repo.owner, app_id: 1) }
let(:headers) {{ 'HTTP_AUTHORIZATION' => "token #{token}" }}
before { post("/v3/repo/9999999999/disable", {}, headers) }
example { expect(last_response.status).to be == 404 }
example { expect(JSON.load(body)).to be == {
"@type" => "error",
"error_type" => "not_found",
"error_message" => "repository not found (or insufficient access)",
"resource_type" => "repository"
}}
end
describe "existing repository, no push access" do
let(:token) { Travis::Api::App::AccessToken.create(user: repo.owner, app_id: 1) }
let(:headers) {{ 'HTTP_AUTHORIZATION' => "token #{token}" }}
before { post("/v3/repo/#{repo.id}/disable", {}, headers) }
example { expect(last_response.status).to be == 403 }
example { expect(JSON.load(body).to_s).to include(
"@type",
"error_type",
"insufficient_access",
"error_message",
"operation requires disable access to repository",
"resource_type",
"repository",
"permission",
"disable")
}
end
describe "private repository, no access" do
let(:token) { Travis::Api::App::AccessToken.create(user: repo.owner, app_id: 1) }
let(:headers) {{ 'HTTP_AUTHORIZATION' => "token #{token}" }}
before { repo.update_attribute(:private, true) }
before { post("/v3/repo/#{repo.id}/disable", {}, headers) }
after { repo.update_attribute(:private, false) }
example { expect(last_response.status).to be == 404 }
example { expect(JSON.load(body)).to be == {
"@type" => "error",
"error_type" => "not_found",
"error_message" => "repository not found (or insufficient access)",
"resource_type" => "repository"
}}
end
describe "existing repository, push access"
# as this reqires a call to github, and stubbing this request has proven difficult,
# this test has been omitted for now
end

View File

@ -0,0 +1,72 @@
require 'spec_helper'
describe Travis::API::V3::Services::Repository::Enable do
let(:repo) { Travis::API::V3::Models::Repository.where(owner_name: 'svenfuchs', name: 'minimal').first }
before do
repo.update_attributes!(active: false)
end
describe "not authenticated" do
before { post("/v3/repo/#{repo.id}/enable") }
example { expect(last_response.status).to be == 403 }
example { expect(JSON.load(body)).to be == {
"@type" => "error",
"error_type" => "login_required",
"error_message" => "login required"
}}
end
describe "missing repo, authenticated" do
let(:token) { Travis::Api::App::AccessToken.create(user: repo.owner, app_id: 1) }
let(:headers) {{ 'HTTP_AUTHORIZATION' => "token #{token}" }}
before { post("/v3/repo/9999999999/enable", {}, headers) }
example { expect(last_response.status).to be == 404 }
example { expect(JSON.load(body)).to be == {
"@type" => "error",
"error_type" => "not_found",
"error_message" => "repository not found (or insufficient access)",
"resource_type" => "repository"
}}
end
describe "existing repository, no push access" do
let(:token) { Travis::Api::App::AccessToken.create(user: repo.owner, app_id: 1) }
let(:headers) {{ 'HTTP_AUTHORIZATION' => "token #{token}" }}
before { post("/v3/repo/#{repo.id}/enable", {}, headers) }
example { expect(last_response.status).to be == 403 }
example { expect(JSON.load(body).to_s).to include(
"@type",
"error_type",
"insufficient_access",
"error_message",
"operation requires enable access to repository",
"resource_type",
"repository",
"permission",
"enable")
}
end
describe "private repository, no access" do
let(:token) { Travis::Api::App::AccessToken.create(user: repo.owner, app_id: 1) }
let(:headers) {{ 'HTTP_AUTHORIZATION' => "token #{token}" }}
before { repo.update_attribute(:private, true) }
before { post("/v3/repo/#{repo.id}/enable", {}, headers) }
after { repo.update_attribute(:private, false) }
example { expect(last_response.status).to be == 404 }
example { expect(JSON.load(body)).to be == {
"@type" => "error",
"error_type" => "not_found",
"error_message" => "repository not found (or insufficient access)",
"resource_type" => "repository"
}}
end
describe "existing repository, push access"
# as this reqires a call to github, and stubbing this request has proven difficult,
# this test has been omitted for now
end

View File

@ -34,8 +34,10 @@ describe Travis::API::V3::Services::Repository::Find do
"read" => true,
"enable" => false,
"disable" => false,
"star" => false,
"unstar" => false,
"create_request" => false,
"create_cron" => false },
"create_cron" => false},
"id" => repo.id,
"name" => "minimal",
"slug" => "svenfuchs/minimal",
@ -52,7 +54,8 @@ describe Travis::API::V3::Services::Repository::Find do
"@type" => "branch",
"@href" => "/v3/repo/#{repo.id}/branch/master",
"@representation" => "minimal",
"name" => "master"}
"name" => "master"},
"starred" => false
}}
end
@ -109,8 +112,10 @@ describe Travis::API::V3::Services::Repository::Find do
"read" => true,
"enable" => false,
"disable" => false,
"star" => false,
"unstar" => false,
"create_request" => false,
"create_cron" => false },
"create_cron" => false},
"id" => repo.id,
"name" => "minimal",
"slug" => "svenfuchs/minimal",
@ -127,7 +132,8 @@ describe Travis::API::V3::Services::Repository::Find do
"@type" => "branch",
"@href" => "/v3/repo/#{repo.id}/branch/master",
"@representation" => "minimal",
"name" => "master"}
"name" => "master"},
"starred" => false
}}
end
@ -169,8 +175,10 @@ describe Travis::API::V3::Services::Repository::Find do
"read" => true,
"enable" => true,
"disable" => true,
"star" => true,
"unstar" => true,
"create_request" => true,
"create_cron" => true },
"create_cron" => true},
"id" => repo.id,
"name" => "minimal",
"slug" => "svenfuchs/minimal",
@ -187,7 +195,8 @@ describe Travis::API::V3::Services::Repository::Find do
"@type" => "branch",
"@href" => "/v3/repo/#{repo.id}/branch/master",
"@representation" => "minimal",
"name" => "master"}
"name" => "master"},
"starred" => false
}}
end
@ -235,8 +244,10 @@ describe Travis::API::V3::Services::Repository::Find do
"read" => true,
"enable" => true,
"disable" => true,
"star" => true,
"unstar" => true,
"create_request" => true,
"create_cron" => true },
"create_cron" => true},
"id" => repo.id,
"name" => "minimal",
"slug" => "svenfuchs/minimal",
@ -253,7 +264,8 @@ describe Travis::API::V3::Services::Repository::Find do
"@type" => "branch",
"@href" => "/v3/repo/#{repo.id}/branch/master",
"@representation" => "minimal",
"name" => "master"}
"name" => "master"},
"starred" => false
}}
end

View File

@ -0,0 +1,68 @@
require 'spec_helper'
describe Travis::API::V3::Services::Repository::Star do
let(:repo) { Travis::API::V3::Models::Repository.where(owner_name: 'svenfuchs', name: 'minimal').first }
describe "not authenticated" do
before { post("/v3/repo/#{repo.id}/star") }
example { expect(last_response.status).to be == 403 }
example { expect(JSON.load(body)).to be == {
"@type" => "error",
"error_type" => "login_required",
"error_message" => "login required"
}}
end
describe "missing repo, authenticated" do
let(:token) { Travis::Api::App::AccessToken.create(user: repo.owner, app_id: 1) }
let(:headers) {{ 'HTTP_AUTHORIZATION' => "token #{token}" }}
before { post("/v3/repo/9999999999/star", {}, headers) }
example { expect(last_response.status).to be == 404 }
example { expect(JSON.load(body)).to be == {
"@type" => "error",
"error_type" => "not_found",
"error_message" => "repository not found (or insufficient access)",
"resource_type" => "repository"
}}
end
describe "existing repository, no push access" do
let(:token) { Travis::Api::App::AccessToken.create(user: repo.owner, app_id: 1) }
let(:headers) {{ 'HTTP_AUTHORIZATION' => "token #{token}" }}
before { post("/v3/repo/#{repo.id}/star", {}, headers) }
example { expect(last_response.status).to be == 403 }
example { expect(JSON.load(body).to_s).to include(
"@type",
"error_type",
"insufficient_access",
"error_message",
"operation requires star access to repository",
"resource_type",
"repository",
"permission",
"star")
}
end
describe "private repository, no access" do
let(:token) { Travis::Api::App::AccessToken.create(user: repo.owner, app_id: 1) }
let(:headers) {{ 'HTTP_AUTHORIZATION' => "token #{token}" }}
before { repo.update_attribute(:private, true) }
before { post("/v3/repo/#{repo.id}/star", {}, headers) }
after { repo.update_attribute(:private, false) }
example { expect(last_response.status).to be == 404 }
example { expect(JSON.load(body)).to be == {
"@type" => "error",
"error_type" => "not_found",
"error_message" => "repository not found (or insufficient access)",
"resource_type" => "repository"
}}
end
describe "existing repository, push access"
# this requires stubing a github request, which is difficult, so has been omitted for now
end

View File

@ -0,0 +1,68 @@
require 'spec_helper'
describe Travis::API::V3::Services::Repository::Unstar do
let(:repo) { Travis::API::V3::Models::Repository.where(owner_name: 'svenfuchs', name: 'minimal').first }
describe "not authenticated" do
before { post("/v3/repo/#{repo.id}/unstar") }
example { expect(last_response.status).to be == 403 }
example { expect(JSON.load(body)).to be == {
"@type" => "error",
"error_type" => "login_required",
"error_message" => "login required"
}}
end
describe "missing repo, authenticated" do
let(:token) { Travis::Api::App::AccessToken.create(user: repo.owner, app_id: 1) }
let(:headers) {{ 'HTTP_AUTHORIZATION' => "token #{token}" }}
before { post("/v3/repo/9999999999/unstar", {}, headers) }
example { expect(last_response.status).to be == 404 }
example { expect(JSON.load(body)).to be == {
"@type" => "error",
"error_type" => "not_found",
"error_message" => "repository not found (or insufficient access)",
"resource_type" => "repository"
}}
end
describe "existing repository, no push access" do
let(:token) { Travis::Api::App::AccessToken.create(user: repo.owner, app_id: 1) }
let(:headers) {{ 'HTTP_AUTHORIZATION' => "token #{token}" }}
before { post("/v3/repo/#{repo.id}/unstar", {}, headers) }
example { expect(last_response.status).to be == 403 }
example { expect(JSON.load(body).to_s).to include(
"@type",
"error_type",
"insufficient_access",
"error_message",
"operation requires unstar access to repository",
"resource_type",
"repository",
"permission",
"unstar")
}
end
describe "private repository, no access" do
let(:token) { Travis::Api::App::AccessToken.create(user: repo.owner, app_id: 1) }
let(:headers) {{ 'HTTP_AUTHORIZATION' => "token #{token}" }}
before { repo.update_attribute(:private, true) }
before { post("/v3/repo/#{repo.id}/unstar", {}, headers) }
after { repo.update_attribute(:private, false) }
example { expect(last_response.status).to be == 404 }
example { expect(JSON.load(body)).to be == {
"@type" => "error",
"error_type" => "not_found",
"error_message" => "repository not found (or insufficient access)",
"resource_type" => "repository"
}}
end
describe "existing repository, push access"
# this requires stubing a github request, which is difficult, so has been omitted for now
end