diff --git a/lib/travis/api/app/endpoint/authorization.rb b/lib/travis/api/app/endpoint/authorization.rb index b158572a12..df7cc56072 100644 --- a/lib/travis/api/app/endpoint/authorization.rb +++ b/lib/travis/api/app/endpoint/authorization.rb @@ -232,7 +232,8 @@ def vcs_handshake vcs_data = remote_vcs_user.authenticate( provider: params[:provider], code: params[:code], - redirect_uri: oauth_endpoint + redirect_uri: oauth_endpoint, + cluster: params[:cluster] ) if vcs_data['redirect_uri'].present? diff --git a/lib/travis/api/v3/models/repository.rb b/lib/travis/api/v3/models/repository.rb index 6b1ece977c..fdb15a676d 100644 --- a/lib/travis/api/v3/models/repository.rb +++ b/lib/travis/api/v3/models/repository.rb @@ -21,6 +21,8 @@ class Models::Repository < Model primary_key: [:id, :default_branch], class_name: 'Travis::API::V3::Models::Branch'.freeze + scope :by_server_type, ->(server_type) { where(server_type: server_type) } + alias last_started_build current_build after_initialize do @@ -198,5 +200,13 @@ def admin def allow_migration? Travis::Features.owner_active?(:allow_migration, self.owner) end + + def perforce? + server_type == 'perforce' + end + + def subversion? + server_type == 'subversion' + end end end diff --git a/lib/travis/api/v3/queries/repository.rb b/lib/travis/api/v3/queries/repository.rb index 6bb931391f..3f04741af4 100644 --- a/lib/travis/api/v3/queries/repository.rb +++ b/lib/travis/api/v3/queries/repository.rb @@ -1,7 +1,7 @@ module Travis::API::V3 class Queries::Repository < Query setup_sidekiq(:repo_sync, queue: :sync, class_name: "Travis::GithubSync::Worker") - params :id, :slug + params :id, :slug, :server_type def find @find ||= find! @@ -37,13 +37,13 @@ def update(attrs) def find! return by_slug if slug - return Models::Repository.find_by_id(id) if id + return Models::Repository.find_by_id(id) if id && !id.match(/\D/) raise WrongParams, 'missing repository.id'.freeze end def by_slug owner_name, repo_name = slug.split('/') - Models::Repository.where( + repos = Models::Repository.where( "(lower(repositories.vcs_slug) = ? "\ "or (lower(repositories.owner_name) = ? and lower(repositories.name) = ?)) "\ "and lower(repositories.vcs_type) = ? "\ @@ -51,13 +51,16 @@ def by_slug slug.downcase, owner_name.downcase, repo_name.downcase, - provider.downcase + 'repository' - ).order("updated_at desc, vcs_slug asc, owner_name asc, name asc, vcs_type asc").first + "#{provider.downcase}repository" + ) + repos = repos.by_server_type(server_type) if server_type && provider == 'assembla' + + repos.order("updated_at desc, vcs_slug asc, owner_name asc, name asc, vcs_type asc") + .first end def provider params['provider'] || 'github' end - end end diff --git a/lib/travis/api/v3/queries/repository_vcs.rb b/lib/travis/api/v3/queries/repository_vcs.rb new file mode 100644 index 0000000000..53c8e7273a --- /dev/null +++ b/lib/travis/api/v3/queries/repository_vcs.rb @@ -0,0 +1,32 @@ +module Travis::API::V3 + class Queries::RepositoryVcs < Query + params :vcs_id + + def find + @find ||= find! + end + + private + + def find! + return by_vcs_id if vcs_id + raise WrongParams, 'missing repository_vcs.vcs_id'.freeze + end + + def by_vcs_id + Models::Repository.where( + "repositories.vcs_id = ? "\ + "and lower(repositories.vcs_type) = ? "\ + "and repositories.invalidated_at is null", + vcs_id, + "#{provider.downcase}repository" + ) + .order("updated_at desc, vcs_slug asc, owner_name asc, name asc, vcs_type asc") + .first + end + + def provider + params['provider'] || 'github' + end + end +end diff --git a/lib/travis/api/v3/result.rb b/lib/travis/api/v3/result.rb index 6152a2b6bc..1a0e4fd65c 100644 --- a/lib/travis/api/v3/result.rb +++ b/lib/travis/api/v3/result.rb @@ -29,14 +29,19 @@ def render(params, env) href = self.href href = V3.location(env) if href.nil? and env['REQUEST_METHOD'.freeze] == 'GET'.freeze include = params.to_h['include'.freeze].to_s.split(?,.freeze) - add_info Renderer[type].render(resource, + result = Renderer[type].render( + resource, href: href, script_name: env['SCRIPT_NAME'.freeze], params: params, include: include, access_control: access_control, meta_data: meta_data, - accept: env.fetch('HTTP_ACCEPT'.freeze, 'application/json'.freeze)) + accept: env.fetch('HTTP_ACCEPT'.freeze, 'application/json'.freeze) + ) + Travis.logger.error("#{self.class}#render: Render failed! type=#{type.inspect} resource=#{resource.inspect} href=#{href} params=#{params.inspect} env=#{env}") if result.nil? + + add_info(result) end def add_info(payload) diff --git a/lib/travis/api/v3/routes.rb b/lib/travis/api/v3/routes.rb index b1604bfa52..ea84051342 100644 --- a/lib/travis/api/v3/routes.rb +++ b/lib/travis/api/v3/routes.rb @@ -282,6 +282,11 @@ module Routes end end + hidden_resource :repository_vcs do + route '/repo_vcs/{provider}/{repository_vcs.vcs_id}' + get :find + end + resource :scan_results do route '/scan_results' get :all diff --git a/lib/travis/api/v3/services.rb b/lib/travis/api/v3/services.rb index e3565ec493..97cd9b3d95 100644 --- a/lib/travis/api/v3/services.rb +++ b/lib/travis/api/v3/services.rb @@ -53,6 +53,7 @@ module Services Queues = Module.new { extend Services } Repositories = Module.new { extend Services } Repository = Module.new { extend Services } + RepositoryVcs = Module.new { extend Services } Request = Module.new { extend Services } Requests = Module.new { extend Services } SslKey = Module.new { extend Services } diff --git a/lib/travis/api/v3/services/repository/activate.rb b/lib/travis/api/v3/services/repository/activate.rb index 2f3e57318a..f913d5f5a8 100644 --- a/lib/travis/api/v3/services/repository/activate.rb +++ b/lib/travis/api/v3/services/repository/activate.rb @@ -16,15 +16,26 @@ def run! repository.update_attributes(active: true) - if repository.private? || access_control.enterprise? + if repository.perforce? + remote_vcs_repository.create_perforce_group( + repository_id: repository.id, + user_id: admin.id + ) + + remote_vcs_repository.set_perforce_ticket( + repository_id: repository.id, + user_id: admin.id + ) + elsif repository.private? || access_control.enterprise? remote_vcs_repository.upload_key( repository_id: repository.id, user_id: admin.id, - read_only: !Travis::Features.owner_active?(:read_write_github_keys, repository.owner) + read_only: !repository.subversion? && !Travis::Features.owner_active?(:read_write_github_keys, repository.owner) ) end query.sync(access_control.user || access_control.admin_for(repository)) + save_audit(repository) result repository end @@ -33,7 +44,21 @@ def check_access(repository) end def check_repo_key(repository) + if repository.subversion? && repository.key.nil? + key = Travis::API::V3::Models::SslKey.new(repository: repository) + key.generate_keys! + key.save! + + return + end + raise RepoSshKeyMissing if repository.key.nil? end + + def save_audit(repository) + app_id = Travis::Api::App::AccessToken.find_by_token(access_control.token)&.app_id + change_source = (app_id.nil? || app_id == 2) ? 'admin-v2' : 'travis-api' + Travis::API::V3::Models::Audit.create!(owner: access_control.user, change_source: change_source, source: repository, source_changes: { active: [false, true] }) + end end end diff --git a/lib/travis/api/v3/services/repository/deactivate.rb b/lib/travis/api/v3/services/repository/deactivate.rb index 3a89dc4820..3c28d3c919 100644 --- a/lib/travis/api/v3/services/repository/deactivate.rb +++ b/lib/travis/api/v3/services/repository/deactivate.rb @@ -12,12 +12,49 @@ def run!(activate = false) user_id: admin.id, activate: activate ) + + if repository.perforce? + begin + remote_vcs_repository.delete_perforce_group( + repository_id: repository.id, + user_id: admin.id + ) + rescue Travis::RemoteVCS::ResponseError + # Do nothing, the group is already removed + end + + if repository.key.present? + repository.key.generate_keys! + repository.key.save! + end + elsif repository.key.present? + keys = remote_vcs_repository.keys( + repository_id: repository.id, + user_id: admin.id + ) + fingerprint = PrivateKey.new(repository.key.private_key).fingerprint.gsub(':', '') + matched_key = keys.detect { |key| key['fingerprint'] == fingerprint } + remote_vcs_repository.delete_key( + repository_id: repository.id, + user_id: admin.id, + id: matched_key['id'] + ) if matched_key.present? + repository.key.destroy if repository.subversion? + end + repository.update_attributes(active: activate) + save_audit(repository) result repository end def check_access(repository) access_control.permissions(repository).deactivate! end + + def save_audit(repository) + app_id = Travis::Api::App::AccessToken.find_by_token(access_control.token)&.app_id + change_source = (app_id.nil? || app_id == 2) ? 'admin-v2' : 'travis-api' + Travis::API::V3::Models::Audit.create!(owner: access_control.user, change_source: change_source, source: repository, source_changes: { active: [true, false] }) + end end end diff --git a/lib/travis/api/v3/services/repository/find.rb b/lib/travis/api/v3/services/repository/find.rb index 68e4f65277..619c162f3c 100644 --- a/lib/travis/api/v3/services/repository/find.rb +++ b/lib/travis/api/v3/services/repository/find.rb @@ -1,5 +1,7 @@ module Travis::API::V3 class Services::Repository::Find < Service + params :server_type + def run! result find end diff --git a/lib/travis/api/v3/services/repository_vcs/find.rb b/lib/travis/api/v3/services/repository_vcs/find.rb new file mode 100644 index 0000000000..3d38ba1df1 --- /dev/null +++ b/lib/travis/api/v3/services/repository_vcs/find.rb @@ -0,0 +1,7 @@ +module Travis::API::V3 + class Services::RepositoryVcs::Find < Service + def run! + result find, type: :repository + end + end +end diff --git a/lib/travis/remote_vcs/repository.rb b/lib/travis/remote_vcs/repository.rb index ab805ea0a2..4d36680e89 100644 --- a/lib/travis/remote_vcs/repository.rb +++ b/lib/travis/remote_vcs/repository.rb @@ -15,7 +15,7 @@ def set_hook(repository_id:, user_id:, activate: true) end def keys(repository_id:, user_id:) - request(:get, __method__) do |req| + request(:get, __method__, false) do |req| req.url "repos/#{repository_id}/keys" req.params['user_id'] = user_id end @@ -50,6 +50,29 @@ def show(repository_id:, admin_id: nil) rescue ResponseError nil end + + def create_perforce_group(repository_id:, user_id:) + request(:post, __method__) do |req| + req.url "repos/#{repository_id}/perforce_groups" + req.params['user_id'] = user_id + end + end + + def delete_perforce_group(repository_id:, user_id:) + request(:delete, __method__) do |req| + req.url "repos/#{repository_id}/perforce_groups" + req.params['user_id'] = user_id + end + end + + def set_perforce_ticket(repository_id:, user_id:) + request(:post, __method__, false) do |req| + req.url "repos/#{repository_id}/perforce_ticket" + req.params['user_id'] = user_id + end + rescue ResponseError + {} + end end end end diff --git a/lib/travis/remote_vcs/user.rb b/lib/travis/remote_vcs/user.rb index 3b342b5846..f95b6e0ebd 100644 --- a/lib/travis/remote_vcs/user.rb +++ b/lib/travis/remote_vcs/user.rb @@ -16,12 +16,13 @@ def auth_request(provider: :github, redirect_uri:, state:, signup:) end end - def authenticate(provider: :github, code:, redirect_uri:) + def authenticate(provider: :github, code:, redirect_uri:, cluster: nil) request(:post, __method__) do |req| req.url 'users/session' req.params['provider'] = provider req.params['code'] = code req.params['redirect_uri'] = redirect_uri + req.params['cluster'] = cluster unless cluster.nil? end end diff --git a/spec/lib/github/services/set_key_spec.rb b/spec/lib/github/services/set_key_spec.rb index 5662fa0ac0..b219296a2f 100644 --- a/spec/lib/github/services/set_key_spec.rb +++ b/spec/lib/github/services/set_key_spec.rb @@ -14,9 +14,7 @@ WebMock.stub_request(:get, "http://vcsfake.travis-ci.com/repos/#{repo.id}/keys?user_id=#{owner.id}") .to_return( status: 200, - body: JSON.dump( - data: keys, - ) + body: JSON.dump(keys) ) end let!(:delete_request) do diff --git a/spec/travis/remote_vcs/repository_spec.rb b/spec/travis/remote_vcs/repository_spec.rb new file mode 100644 index 0000000000..0c0dba6426 --- /dev/null +++ b/spec/travis/remote_vcs/repository_spec.rb @@ -0,0 +1,82 @@ +# frozen_string_literal: true +require 'spec_helper' + +describe Travis::RemoteVCS::Repository do + let(:repository) { described_class.new } + let(:id) { 2 } + let(:user_id) { 1 } + + before { Travis.config.vcs = { url: 'http://vcs:3000', token: 'token' } } + + describe '#keys' do + let(:key) do + { id: 1 } + end + let!(:request) do + stub_request(:get, /\/repos\/#{id}\/keys\?user_id=#{user_id}/) + .to_return( + status: 200, + body: JSON.dump([key]), + ) + end + + subject { repository.keys(repository_id: id, user_id: user_id) } + + it 'performs a proper request' do + expect(subject).to eq([key.stringify_keys]) + expect(request).to have_been_made + end + end + + describe '#create_perforce_group' do + let!(:request) do + stub_request(:post, /\/repos\/#{id}\/perforce_groups\?user_id=#{user_id}/) + .to_return( + status: 200, + body: nil, + ) + end + + subject { repository.create_perforce_group(repository_id: id, user_id: user_id) } + + it 'performs a proper request' do + subject + expect(request).to have_been_made + end + end + + describe '#delete_perforce_group' do + let!(:request) do + stub_request(:delete, /\/repos\/#{id}\/perforce_groups\?user_id=#{user_id}/) + .to_return( + status: 200, + body: nil, + ) + end + + subject { repository.delete_perforce_group(repository_id: id, user_id: user_id) } + + it 'performs a proper request' do + subject + expect(request).to have_been_made + end + end + + describe '#set_perforce_ticket' do + let(:token) { '1235' } + let!(:request) do + stub_request(:post, /\/repos\/#{id}\/perforce_ticket\?user_id=#{user_id}/) + .to_return( + status: 200, + body: JSON.dump(token: token), + ) + end + + subject { repository.set_perforce_ticket(repository_id: id, user_id: user_id) } + + it 'performs a proper request' do + expect(subject).to eq('token' => token) + expect(request).to have_been_made + end + end +end diff --git a/spec/travis/remote_vcs/user_spec.rb b/spec/travis/remote_vcs/user_spec.rb index ab0eea9972..9c3c5f5237 100644 --- a/spec/travis/remote_vcs/user_spec.rb +++ b/spec/travis/remote_vcs/user_spec.rb @@ -48,4 +48,28 @@ subject end end + + describe '#authenticate' do + let(:user) { described_class.new } + let(:provider) { 'assembla' } + let(:code) { '1234' } + let(:redirect_uri) { 'test' } + let(:cluster) { 'special' } + let!(:request) do + stub_request(:post, /\/users\/session\?cluster=#{cluster}&code=#{code}&provider=#{provider}&redirect_uri=#{redirect_uri}/) + .to_return( + status: 200, + body: nil, + ) + end + + before { Travis.config.vcs = { url: 'http://vcs:3000', token: 'token' } } + + subject { user.authenticate(provider: provider, code: code, redirect_uri: redirect_uri, cluster: cluster) } + + it 'performs a proper request' do + subject + expect(request).to have_been_made + end + end end diff --git a/spec/unit/endpoint/authorization_spec.rb b/spec/unit/endpoint/authorization_spec.rb index 67ac2f9af2..f9686dde07 100644 --- a/spec/unit/endpoint/authorization_spec.rb +++ b/spec/unit/endpoint/authorization_spec.rb @@ -197,6 +197,30 @@ end end end + + describe 'when cluster param is passed' do + let(:user) { FactoryBot.create :user } + let(:cluster) { 'awscluster' } + + before do + ENV['TRAVIS_SITE'] = 'com' + cookie_jar['travis.state-assembla'] = state + Travis.redis.sadd('github:states', state) + WebMock.stub_request(:post, /\/users\/session\?cluster=#{cluster}&code=1234&provider=assembla&redirect_uri=http:\/\/example.org\/auth\/handshake\/assembla/) + .to_return(status: 200, body: JSON.dump(data: { user: { id: '1' } })) + end + + after do + ENV['TRAVIS_SITE'] = nil + end + + let(:state) { '1234' } + + it 'passes the cluster param' do + response = get "/auth/handshake/assembla?code=1234&state=#{URI.encode(state)}&cluster=#{cluster}" + expect(response.status).to eq(302) + end + end describe 'with insufficient oauth permissions' do before do diff --git a/spec/v3/models/repository_spec.rb b/spec/v3/models/repository_spec.rb new file mode 100644 index 0000000000..c623a2c677 --- /dev/null +++ b/spec/v3/models/repository_spec.rb @@ -0,0 +1,37 @@ +# frozen_string_literal: true +describe Travis::API::V3::Models::Repository do + let(:repo) { FactoryBot.build :repository } + let(:v3_repo) { described_class.new(repo.attributes) } + + describe '#perforce?' do + subject { v3_repo.perforce? } + + context 'when server_type is perforce' do + before { v3_repo.server_type = 'perforce' } + + it { is_expected.to be true } + end + + context 'when server_type is not' do + before { v3_repo.server_type = 'git' } + + it { is_expected.to be false } + end + end + + describe '#subversion?' do + subject { v3_repo.subversion? } + + context 'when server_type is subversion' do + before { v3_repo.server_type = 'subversion' } + + it { is_expected.to be true } + end + + context 'when server_type is not' do + before { v3_repo.server_type = 'git' } + + it { is_expected.to be false } + end + end +end \ No newline at end of file diff --git a/spec/v3/service_index_spec.rb b/spec/v3/service_index_spec.rb index 2cb9af0631..e6c0ee02be 100644 --- a/spec/v3/service_index_spec.rb +++ b/spec/v3/service_index_spec.rb @@ -124,7 +124,7 @@ module Routes describe "find action" do let(:action) { resource.fetch("actions").fetch("find") } - specify { expect(action).to include("@type"=>"template", "request_method"=>"GET", "uri_template"=>"#{path}repo/{repository.id}{?include}") } + specify { expect(action).to include("@type"=>"template", "request_method"=>"GET", "uri_template"=>"#{path}repo/{repository.id}{?include,repository.server_type,server_type}") } end describe "activate action" do diff --git a/spec/v3/services/repository/activate_spec.rb b/spec/v3/services/repository/activate_spec.rb index 863b249d53..eac221eb95 100644 --- a/spec/v3/services/repository/activate_spec.rb +++ b/spec/v3/services/repository/activate_spec.rb @@ -78,6 +78,78 @@ "resource_type" => "repository" }} end + + context 'when activating a perforce repo' do + let!(:permission) { FactoryBot.create :permission, user_id: repo.owner_id, repository_id: repo.id, admin: true, pull: true, push: true } + let!(:hook_request) do + stub_request(:post, "http://vcsfake.travis-ci.com/repos/#{repo.id}/hook?user_id=#{repo.owner_id}") + .to_return( + status: 200, + body: nil, + ) + end + let!(:group_request) do + stub_request(:post, "http://vcsfake.travis-ci.com/repos/#{repo.id}/perforce_groups?user_id=#{repo.owner_id}") + .to_return( + status: 200, + body: nil, + ) + end + let!(:ticket_request) do + stub_request(:post, "http://vcsfake.travis-ci.com/repos/#{repo.id}/perforce_ticket?user_id=#{repo.owner_id}") + .to_return( + status: 200, + body: nil, + ) + end + + before { repo.update(server_type: 'perforce') } + + it 'creates a perforce group and sets a perforce ticket' do + post("/v3/repo/#{repo.id}/activate", {}, headers) + expect(last_response.status).to eq(200) + expect(group_request).to have_been_made + expect(ticket_request).to have_been_made + end + end + + context 'when activating a private subversion repo' do + let!(:permission) { FactoryBot.create :permission, user_id: repo.owner_id, repository_id: repo.id, admin: true, pull: true, push: true } + let!(:hook_request) do + stub_request(:post, "http://vcsfake.travis-ci.com/repos/#{repo.id}/hook?user_id=#{repo.owner_id}") + .to_return( + status: 200, + body: nil, + ) + end + let!(:key_request) do + stub_request(:post, "http://vcsfake.travis-ci.com/repos/#{repo.id}/keys?read_only=false&user_id=#{repo.owner_id}") + .to_return( + status: 200, + body: nil, + ) + end + + before { repo.update(private: true, server_type: 'subversion') } + + it 'activates repository' do + expect do + post("/v3/repo/#{repo.id}/activate", {}, headers) + end.to change(Travis::API::V3::Models::Audit, :count).by(1) + expect(Travis::API::V3::Models::Audit.last.source_changes).to eq('active' => [false, true]) + expect(last_response.status).to eq(200) + end + + context 'when repository does not have a key' do + before { repo.key.delete } + + it 'generates a key' do + post("/v3/repo/#{repo.id}/activate", {}, headers) + expect(repo.reload.key).to be_present + expect(last_response.status).to eq(200) + end + end + end end context 'with internal auth' do let(:internal_token) { 'FOO' } diff --git a/spec/v3/services/repository/deactivate_spec.rb b/spec/v3/services/repository/deactivate_spec.rb index c978dd1c0d..5665f27db0 100644 --- a/spec/v3/services/repository/deactivate_spec.rb +++ b/spec/v3/services/repository/deactivate_spec.rb @@ -1,5 +1,13 @@ describe Travis::API::V3::Services::Repository::Deactivate, set_app: true do let(:repo) { Travis::API::V3::Models::Repository.where(owner_name: 'svenfuchs', name: 'minimal').first } + let(:keys) { [] } + let!(:keys_request) do + stub_request(:get, "http://vcsfake.travis-ci.com/repos/#{repo.id}/keys?user_id=#{repo.owner_id}") + .to_return( + status: 200, + body: JSON.dump(keys), + ) + end before do Travis.config.vcs.url = 'http://vcsfake.travis-ci.com' Travis.config.vcs.token = 'vcs-token' @@ -94,6 +102,68 @@ "resource_type" => "repository" }} end + + context 'when deactivating a perforce repo' do + let!(:permission) { FactoryBot.create :permission, user_id: repo.owner_id, repository_id: repo.id, admin: true, pull: true, push: true } + let!(:hook_request) do + stub_request(:delete, "http://vcsfake.travis-ci.com/repos/#{repo.id}/hook?user_id=#{repo.owner_id}") + .to_return( + status: 200, + body: nil, + ) + end + let!(:group_request) do + stub_request(:delete, "http://vcsfake.travis-ci.com/repos/#{repo.id}/perforce_groups?user_id=#{repo.owner_id}") + .to_return( + status: 200, + body: nil, + ) + end + + before { repo.update(server_type: 'perforce') } + + it 'deletes the perforce group' do + post("/v3/repo/#{repo.id}/deactivate", {}, headers) + expect(last_response.status).to eq(200) + expect(group_request).to have_been_made + end + end + + context 'when deactivating a private subversion repo' do + let!(:permission) { FactoryBot.create :permission, user_id: repo.owner_id, repository_id: repo.id, admin: true, pull: true, push: true } + let(:fingerprint) { PrivateKey.new(repo.key.private_key).fingerprint.gsub(':', '') } + let(:key) do + { + id: 1, + fingerprint: fingerprint + } + end + let!(:hook_request) do + stub_request(:delete, "http://vcsfake.travis-ci.com/repos/#{repo.id}/hook?user_id=#{repo.owner_id}") + .to_return( + status: 200, + body: nil, + ) + end + let(:keys) { [key] } + let!(:key_request) do + stub_request(:delete, "http://vcsfake.travis-ci.com/repos/#{repo.id}/keys/#{key[:id]}?user_id=#{repo.owner_id}") + .to_return( + status: 200, + body: nil, + ) + end + + before { repo.update(private: true, server_type: 'subversion') } + + it 'deactivates repository' do + expect do + post("/v3/repo/#{repo.id}/deactivate", {}, headers) + end.to change(Travis::API::V3::Models::Audit, :count).by(1) + expect(Travis::API::V3::Models::Audit.last.source_changes).to eq('active' => [true, false]) + expect(last_response.status).to eq(200) + end + end end context 'internal auth' do let(:internal_token) { 'FOO' }