...@@ -64,6 +64,46 @@ describe ProjectsController do ...@@ -64,6 +64,46 @@ describe ProjectsController do
end end
end end
describe "GET #activity as JSON" do
render_views
let(:project) { create(:project, :public, issues_access_level: ProjectFeature::PRIVATE) }
before do
create(:event, :created, project: project, target: create(:issue))
sign_in(user)
request.cookies[:event_filter] = 'all'
end
context 'when user has permission to see the event' do
before do
project.add_developer(user)
end
it 'returns count' do
get :activity, params: { namespace_id: project.namespace, id: project, format: :json }
expect(json_response['count']).to eq(1)
end
end
context 'when user has no permission to see the event' do
it 'filters out invisible event' do
get :activity, params: { namespace_id: project.namespace, id: project, format: :json }
expect(json_response['html']).to eq("\n")
end
it 'filters out invisible event when calculating the count' do
get :activity, params: { namespace_id: project.namespace, id: project, format: :json }
expect(json_response['count']).to eq(0)
end
end
end
describe "GET show" do describe "GET show" do
context "user not project member" do context "user not project member" do
before do before do
... ...
......
...@@ -928,14 +928,14 @@ describe ProjectsHelper do ...@@ -928,14 +928,14 @@ describe ProjectsHelper do
helper.instance_variable_set(:@project, project) helper.instance_variable_set(:@project, project)
end end
subject { helper.grafana_integration_token } subject { helper.grafana_integration_masked_token }
it { is_expected.to eq(nil) } it { is_expected.to eq(nil) }
context 'grafana integration exists' do context 'grafana integration exists' do
let!(:grafana_integration) { create(:grafana_integration, project: project) } let!(:grafana_integration) { create(:grafana_integration, project: project) }
it { is_expected.to eq(grafana_integration.token) } it { is_expected.to eq(grafana_integration.masked_token) }
end end
end end
... ...
......
import bp from '~/breakpoints'; import bp from '~/breakpoints';
import { isMobile, getTopFrequentItems, updateExistingFrequentItem } from '~/frequent_items/utils'; import {
isMobile,
getTopFrequentItems,
updateExistingFrequentItem,
sanitizeItem,
} from '~/frequent_items/utils';
import { HOUR_IN_MS, FREQUENT_ITEMS } from '~/frequent_items/constants'; import { HOUR_IN_MS, FREQUENT_ITEMS } from '~/frequent_items/constants';
import { mockProject, unsortedFrequentItems, sortedFrequentItems } from './mock_data'; import { mockProject, unsortedFrequentItems, sortedFrequentItems } from './mock_data';
...@@ -86,4 +91,16 @@ describe('Frequent Items utils spec', () => { ...@@ -86,4 +91,16 @@ describe('Frequent Items utils spec', () => {
expect(result.frequency).toBe(mockedProject.frequency); expect(result.frequency).toBe(mockedProject.frequency);
}); });
}); });
describe('sanitizeItem', () => {
it('strips HTML tags for name and namespace', () => {
const input = {
name: '<br><b>test</b>',
namespace: '<br>test',
id: 1,
};
expect(sanitizeItem(input)).toEqual({ name: 'test', namespace: 'test', id: 1 });
});
});
}); });
...@@ -22,7 +22,7 @@ describe Banzai::ReferenceParser::MentionedUserParser do ...@@ -22,7 +22,7 @@ describe Banzai::ReferenceParser::MentionedUserParser do
end end
it 'returns empty list of users' do it 'returns empty list of users' do
expect(subject.gather_references([link])).to eq([]) expect_gathered_references(subject.gather_references([link]), [], 0)
end end
end end
end end
...@@ -35,7 +35,7 @@ describe Banzai::ReferenceParser::MentionedUserParser do ...@@ -35,7 +35,7 @@ describe Banzai::ReferenceParser::MentionedUserParser do
end end
it 'returns empty list of users' do it 'returns empty list of users' do
expect(subject.gather_references([link])).to eq([]) expect_gathered_references(subject.gather_references([link]), [], 0)
end end
end end
end end
...@@ -44,7 +44,7 @@ describe Banzai::ReferenceParser::MentionedUserParser do ...@@ -44,7 +44,7 @@ describe Banzai::ReferenceParser::MentionedUserParser do
it 'returns an Array of users' do it 'returns an Array of users' do
link['data-user'] = user.id.to_s link['data-user'] = user.id.to_s
expect(subject.referenced_by([link])).to eq([user]) expect_gathered_references(subject.gather_references([link]), [user], 0)
end end
end end
end end
... ...
......
...@@ -19,7 +19,7 @@ describe Banzai::ReferenceParser::MentionedUsersByGroupParser do ...@@ -19,7 +19,7 @@ describe Banzai::ReferenceParser::MentionedUsersByGroupParser do
it 'returns empty array' do it 'returns empty array' do
link['data-group'] = project.group.id.to_s link['data-group'] = project.group.id.to_s
expect(subject.gather_references([link])).to eq([]) expect_gathered_references(subject.gather_references([link]), [], 1)
end end
end end
...@@ -30,7 +30,7 @@ describe Banzai::ReferenceParser::MentionedUsersByGroupParser do ...@@ -30,7 +30,7 @@ describe Banzai::ReferenceParser::MentionedUsersByGroupParser do
end end
it 'returns groups' do it 'returns groups' do
expect(subject.gather_references([link])).to eq([group]) expect_gathered_references(subject.gather_references([link]), [group], 0)
end end
end end
...@@ -38,7 +38,7 @@ describe Banzai::ReferenceParser::MentionedUsersByGroupParser do ...@@ -38,7 +38,7 @@ describe Banzai::ReferenceParser::MentionedUsersByGroupParser do
it 'returns an empty Array' do it 'returns an empty Array' do
link['data-group'] = 'test-non-existing' link['data-group'] = 'test-non-existing'
expect(subject.gather_references([link])).to eq([]) expect_gathered_references(subject.gather_references([link]), [], 1)
end end
end end
end end
... ...
......
...@@ -19,7 +19,7 @@ describe Banzai::ReferenceParser::MentionedUsersByProjectParser do ...@@ -19,7 +19,7 @@ describe Banzai::ReferenceParser::MentionedUsersByProjectParser do
it 'returns empty Array' do it 'returns empty Array' do
link['data-project'] = project.id.to_s link['data-project'] = project.id.to_s
expect(subject.gather_references([link])).to eq([]) expect_gathered_references(subject.gather_references([link]), [], 1)
end end
end end
...@@ -30,7 +30,7 @@ describe Banzai::ReferenceParser::MentionedUsersByProjectParser do ...@@ -30,7 +30,7 @@ describe Banzai::ReferenceParser::MentionedUsersByProjectParser do
end end
it 'returns an Array of referenced projects' do it 'returns an Array of referenced projects' do
expect(subject.gather_references([link])).to eq([project]) expect_gathered_references(subject.gather_references([link]), [project], 0)
end end
end end
...@@ -38,7 +38,7 @@ describe Banzai::ReferenceParser::MentionedUsersByProjectParser do ...@@ -38,7 +38,7 @@ describe Banzai::ReferenceParser::MentionedUsersByProjectParser do
it 'returns an empty Array' do it 'returns an empty Array' do
link['data-project'] = 'inexisting-project-id' link['data-project'] = 'inexisting-project-id'
expect(subject.gather_references([link])).to eq([]) expect_gathered_references(subject.gather_references([link]), [], 1)
end end
end end
end end
... ...
......
...@@ -17,7 +17,7 @@ describe Banzai::ReferenceParser::ProjectParser do ...@@ -17,7 +17,7 @@ describe Banzai::ReferenceParser::ProjectParser do
it 'returns an Array of projects' do it 'returns an Array of projects' do
link['data-project'] = project.id.to_s link['data-project'] = project.id.to_s
expect(subject.gather_references([link])).to eq([project]) expect_gathered_references(subject.gather_references([link]), [project], 0)
end end
end end
...@@ -25,7 +25,7 @@ describe Banzai::ReferenceParser::ProjectParser do ...@@ -25,7 +25,7 @@ describe Banzai::ReferenceParser::ProjectParser do
it 'returns an empty Array' do it 'returns an empty Array' do
link['data-project'] = '' link['data-project'] = ''
expect(subject.gather_references([link])).to eq([]) expect_gathered_references(subject.gather_references([link]), [], 1)
end end
end end
...@@ -35,7 +35,7 @@ describe Banzai::ReferenceParser::ProjectParser do ...@@ -35,7 +35,7 @@ describe Banzai::ReferenceParser::ProjectParser do
link['data-project'] = private_project.id.to_s link['data-project'] = private_project.id.to_s
expect(subject.gather_references([link])).to eq([]) expect_gathered_references(subject.gather_references([link]), [], 1)
end end
it 'returns an Array when authorized' do it 'returns an Array when authorized' do
...@@ -43,7 +43,7 @@ describe Banzai::ReferenceParser::ProjectParser do ...@@ -43,7 +43,7 @@ describe Banzai::ReferenceParser::ProjectParser do
link['data-project'] = private_project.id.to_s link['data-project'] = private_project.id.to_s
expect(subject.gather_references([link])).to eq([private_project]) expect_gathered_references(subject.gather_references([link]), [private_project], 0)
end end
end end
end end
... ...
......
# frozen_string_literal: true
require 'spec_helper'
require 'nokogiri'
describe Gitlab::Asciidoc::IncludeProcessor do
let_it_be(:project) { create(:project, :repository) }
let(:processor_context) do
{
project: project,
max_includes: max_includes,
ref: ref
}
end
let(:ref) { project.repository.root_ref }
let(:max_includes) { 10 }
let(:reader) { Asciidoctor::PreprocessorReader.new(document, lines, 'file.adoc') }
let(:document) { Asciidoctor::Document.new(lines) }
subject(:processor) { described_class.new(processor_context) }
let(:a_blob) { double(:Blob, readable_text?: true, data: a_data) }
let(:a_data) { StringIO.new('include::b.adoc[]') }
let(:lines) { [':max-include-depth: 1000'] + Array.new(10, 'include::a.adoc[]') }
before do
allow(project.repository).to receive(:blob_at).with(ref, 'a.adoc').and_return(a_blob)
end
describe '#include_allowed?' do
it 'allows the first include' do
expect(processor.send(:include_allowed?, 'foo.adoc', reader)).to be_truthy
end
it 'allows the Nth + 1 include' do
(max_includes - 1).times { processor.send(:read_blob, ref, 'a.adoc') }
expect(processor.send(:include_allowed?, 'foo.adoc', reader)).to be_truthy
end
it 'disallows the Nth + 1 include' do
max_includes.times { processor.send(:read_blob, ref, 'a.adoc') }
expect(processor.send(:include_allowed?, 'foo.adoc', reader)).to be_falsey
end
end
end
...@@ -425,6 +425,24 @@ module Gitlab ...@@ -425,6 +425,24 @@ module Gitlab
create_file(current_file, "= AsciiDoc\n") create_file(current_file, "= AsciiDoc\n")
end end
def many_includes(target)
Array.new(10, "include::#{target}[]").join("\n")
end
context 'cyclic imports' do
before do
create_file('doc/api/a.adoc', many_includes('b.adoc'))
create_file('doc/api/b.adoc', many_includes('a.adoc'))
end
let(:include_path) { 'a.adoc' }
let(:requested_path) { 'doc/api/README.md' }
it 'completes successfully' do
is_expected.to include('<p>Include this:</p>')
end
end
context 'with path to non-existing file' do context 'with path to non-existing file' do
let(:include_path) { 'not-exists.adoc' } let(:include_path) { 'not-exists.adoc' }
... ...
......
# frozen_string_literal: true
require 'spec_helper'
describe Gitlab::Git::CrossRepoComparer do
let(:source_project) { create(:project, :repository) }
let(:target_project) { create(:project, :repository) }
let(:source_repo) { source_project.repository.raw_repository }
let(:target_repo) { target_project.repository.raw_repository }
let(:source_branch) { 'feature' }
let(:target_branch) { 'master' }
let(:straight) { false }
let(:source_commit) { source_repo.commit(source_branch) }
let(:target_commit) { source_repo.commit(target_branch) }
subject(:result) { described_class.new(source_repo, target_repo).compare(source_branch, target_branch, straight: straight) }
describe '#compare' do
context 'within a single repository' do
let(:target_project) { source_project }
context 'a non-straight comparison' do
it 'compares without fetching from another repo' do
expect(source_repo).not_to receive(:fetch_source_branch!)
expect_compare(result, from: source_commit, to: target_commit)
expect(result.straight).to eq(false)
end
end
context 'a straight comparison' do
let(:straight) { true }
it 'compares without fetching from another repo' do
expect(source_repo).not_to receive(:fetch_source_branch!)
expect_compare(result, from: source_commit, to: target_commit)
expect(result.straight).to eq(true)
end
end
end
context 'across two repositories' do
context 'target ref exists in source repo' do
it 'compares without fetching from another repo' do
expect(source_repo).not_to receive(:fetch_source_branch!)
expect(source_repo).not_to receive(:delete_refs)
expect_compare(result, from: source_commit, to: target_commit)
end
end
context 'target ref does not exist in source repo' do
it 'compares in the source repo by fetching from the target to a temporary ref' do
new_commit_id = create_commit(target_project.owner, target_repo, target_branch)
new_commit = target_repo.commit(new_commit_id)
# This is how the temporary ref is generated
expect(SecureRandom).to receive(:hex).at_least(:once).and_return('foo')
expect(source_repo)
.to receive(:fetch_source_branch!)
.with(target_repo, new_commit_id, 'refs/tmp/foo')
.and_call_original
expect(source_repo).to receive(:delete_refs).with('refs/tmp/foo').and_call_original
expect_compare(result, from: source_commit, to: new_commit)
end
end
context 'source ref does not exist in source repo' do
let(:source_branch) { 'does-not-exist' }
it 'returns an empty comparison' do
expect(source_repo).not_to receive(:fetch_source_branch!)
expect(source_repo).not_to receive(:delete_refs)
expect(result).to be_a(::Gitlab::Git::Compare)
expect(result.commits.size).to eq(0)
end
end
context 'target ref does not exist in target repo' do
let(:target_branch) { 'does-not-exist' }
it 'returns nil' do
expect(source_repo).not_to receive(:fetch_source_branch!)
expect(source_repo).not_to receive(:delete_refs)
is_expected.to be_nil
end
end
end
end
def expect_compare(of, from:, to:)
expect(of).to be_a(::Gitlab::Git::Compare)
expect(from).to be_a(::Gitlab::Git::Commit)
expect(to).to be_a(::Gitlab::Git::Commit)
expect(of.commits).not_to be_empty
expect(of.head).to eq(from)
expect(of.base).to eq(to)
end
def create_commit(user, repo, branch)
action = { action: :create, file_path: '/FILE', content: 'content' }
result = repo.multi_action(user, branch_name: branch, message: 'Commit', actions: [action])
result.newrev
end
end
...@@ -1959,66 +1959,15 @@ describe Gitlab::Git::Repository, :seed_helper do ...@@ -1959,66 +1959,15 @@ describe Gitlab::Git::Repository, :seed_helper do
end end
describe '#compare_source_branch' do describe '#compare_source_branch' do
let(:repository) { Gitlab::Git::Repository.new('default', TEST_GITATTRIBUTES_REPO_PATH, '', 'group/project') } it 'delegates to Gitlab::Git::CrossRepoComparer' do
expect_next_instance_of(::Gitlab::Git::CrossRepoComparer) do |instance|
context 'within same repository' do expect(instance.source_repo).to eq(:source_repository)
it 'does not create a temp ref' do expect(instance.target_repo).to eq(repository)
expect(repository).not_to receive(:fetch_source_branch!)
expect(repository).not_to receive(:delete_refs)
compare = repository.compare_source_branch('master', repository, 'feature', straight: false)
expect(compare).to be_a(Gitlab::Git::Compare)
expect(compare.commits.count).to be > 0
end
it 'returns empty commits when source ref does not exist' do
compare = repository.compare_source_branch('master', repository, 'non-existent-branch', straight: false)
expect(compare.commits).to be_empty
end
end
context 'with different repositories' do expect(instance).to receive(:compare).with('feature', 'master', straight: :straight)
context 'when ref is known by source repo, but not by target' do
before do
mutable_repository.write_ref('another-branch', 'feature')
end end
it 'creates temp ref' do repository.compare_source_branch('master', :source_repository, 'feature', straight: :straight)
expect(repository).not_to receive(:fetch_source_branch!)
expect(repository).not_to receive(:delete_refs)
compare = repository.compare_source_branch('master', mutable_repository, 'another-branch', straight: false)
expect(compare).to be_a(Gitlab::Git::Compare)
expect(compare.commits.count).to be > 0
end
end
context 'when ref is known by source and target repos' do
before do
mutable_repository.write_ref('another-branch', 'feature')
repository.write_ref('another-branch', 'feature')
end
it 'does not create a temp ref' do
expect(repository).not_to receive(:fetch_source_branch!)
expect(repository).not_to receive(:delete_refs)
compare = repository.compare_source_branch('master', mutable_repository, 'another-branch', straight: false)
expect(compare).to be_a(Gitlab::Git::Compare)
expect(compare.commits.count).to be > 0
end
end
context 'when ref is unknown by source repo' do
it 'returns nil when source ref does not exist' do
expect(repository).to receive(:fetch_source_branch!).and_call_original
expect(repository).to receive(:delete_refs).and_call_original
compare = repository.compare_source_branch('master', mutable_repository, 'non-existent-branch', straight: false)
expect(compare).to be_nil
end
end
end end
end end
... ...
......
# frozen_string_literal: true
require 'spec_helper'
describe Gitlab::NoCacheHeaders do
class NoCacheTester
include Gitlab::NoCacheHeaders
end
describe "#no_cache_headers" do
subject { NoCacheTester.new }
it "raises a RuntimeError" do
expect { subject.no_cache_headers }.to raise_error(RuntimeError)
end
end
end
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
require 'spec_helper' require 'spec_helper'
describe Gitlab::ReferenceExtractor do describe Gitlab::ReferenceExtractor do
let(:project) { create(:project) } let_it_be(:project) { create(:project) }
before do before do
project.add_developer(project.creator) project.add_developer(project.creator)
...@@ -293,4 +293,34 @@ describe Gitlab::ReferenceExtractor do ...@@ -293,4 +293,34 @@ describe Gitlab::ReferenceExtractor do
end end
end end
end end
describe '#references' do
let_it_be(:user) { create(:user) }
let_it_be(:issue) { create(:issue, project: project) }
let(:text) { "Ref. #{issue.to_reference}" }
subject { described_class.new(project, user) }
before do
subject.analyze(text)
end
context 'when references are visible' do
before do
project.add_developer(user)
end
it 'returns visible references of given type' do
expect(subject.references(:issue)).to eq([issue])
end
it 'does not increase stateful_not_visible_counter' do
expect { subject.references(:issue) }.not_to change { subject.stateful_not_visible_counter }
end
end
it 'increases stateful_not_visible_counter' do
expect { subject.references(:issue) }.to change { subject.stateful_not_visible_counter }.by(1)
end
end
end end
...@@ -19,6 +19,74 @@ describe GenericCommitStatus do ...@@ -19,6 +19,74 @@ describe GenericCommitStatus do
it { is_expected.not_to allow_value('javascript:alert(1)').for(:target_url) } it { is_expected.not_to allow_value('javascript:alert(1)').for(:target_url) }
end end
describe '#name_uniqueness_across_types' do
let(:attributes) { {} }
let(:commit_status) { described_class.new(attributes) }
let(:status_name) { 'test-job' }
subject(:errors) { commit_status.errors[:name] }
shared_examples 'it does not have uniqueness errors' do
it 'does not return errors' do
commit_status.valid?
is_expected.to be_empty
end
end
context 'without attributes' do
it_behaves_like 'it does not have uniqueness errors'
end
context 'with only a pipeline' do
let(:attributes) { { pipeline: pipeline } }
context 'without name' do
it_behaves_like 'it does not have uniqueness errors'
end
end
context 'with only a name' do
let(:attributes) { { name: status_name } }
context 'without pipeline' do
it_behaves_like 'it does not have uniqueness errors'
end
end
context 'with pipeline and name' do
let(:attributes) do
{
pipeline: pipeline,
name: status_name
}
end
context 'without other statuses' do
it_behaves_like 'it does not have uniqueness errors'
end
context 'with generic statuses' do
before do
create(:generic_commit_status, pipeline: pipeline, name: status_name)
end
it_behaves_like 'it does not have uniqueness errors'
end
context 'with ci_build statuses' do
before do
create(:ci_build, pipeline: pipeline, name: status_name)
end
it 'returns name error' do
expect(commit_status).to be_invalid
is_expected.to include('has already been taken')
end
end
end
end
describe '#context' do describe '#context' do
subject { generic_commit_status.context } subject { generic_commit_status.context }
...@@ -79,6 +147,12 @@ describe GenericCommitStatus do ...@@ -79,6 +147,12 @@ describe GenericCommitStatus do
it { is_expected.not_to be_nil } it { is_expected.not_to be_nil }
end end
describe '#stage_idx' do
subject { generic_commit_status.stage_idx }
it { is_expected.not_to be_nil }
end
end end
describe '#present' do describe '#present' do
... ...
......
...@@ -9,7 +9,7 @@ describe GrafanaIntegration do ...@@ -9,7 +9,7 @@ describe GrafanaIntegration do
describe 'validations' do describe 'validations' do
it { is_expected.to validate_presence_of(:project) } it { is_expected.to validate_presence_of(:project) }
it { is_expected.to validate_presence_of(:token) } it { is_expected.to validate_presence_of(:encrypted_token) }
it 'disallows invalid urls for grafana_url' do it 'disallows invalid urls for grafana_url' do
unsafe_url = %{https://replaceme.com/'><script>alert(document.cookie)</script>} unsafe_url = %{https://replaceme.com/'><script>alert(document.cookie)</script>}
...@@ -66,4 +66,24 @@ describe GrafanaIntegration do ...@@ -66,4 +66,24 @@ describe GrafanaIntegration do
end end
end end
end end
describe 'attribute encryption' do
subject(:grafana_integration) { create(:grafana_integration, token: 'super-secret') }
context 'token' do
it 'encrypts original value into encrypted_token attribute' do
expect(grafana_integration.encrypted_token).not_to be_nil
end
it 'locks access to raw value in private method', :aggregate_failures do
expect { grafana_integration.token }.to raise_error(NoMethodError, /private method .token. called/)
expect(grafana_integration.send(:token)).to eql('super-secret')
end
it 'prevents overriding token value with its encrypted or masked version', :aggregate_failures do
expect { grafana_integration.update(token: grafana_integration.encrypted_token) }.not_to change { grafana_integration.reload.send(:token) }
expect { grafana_integration.update(token: grafana_integration.masked_token) }.not_to change { grafana_integration.reload.send(:token) }
end
end
end
end end
...@@ -350,12 +350,12 @@ describe Note do ...@@ -350,12 +350,12 @@ describe Note do
end end
describe "cross_reference_not_visible_for?" do describe "cross_reference_not_visible_for?" do
let(:private_user) { create(:user) } let_it_be(:private_user) { create(:user) }
let(:private_project) { create(:project, namespace: private_user.namespace) { |p| p.add_maintainer(private_user) } } let_it_be(:private_project) { create(:project, namespace: private_user.namespace) { |p| p.add_maintainer(private_user) } }
let(:private_issue) { create(:issue, project: private_project) } let_it_be(:private_issue) { create(:issue, project: private_project) }
let(:ext_proj) { create(:project, :public) } let_it_be(:ext_proj) { create(:project, :public) }
let(:ext_issue) { create(:issue, project: ext_proj) } let_it_be(:ext_issue) { create(:issue, project: ext_proj) }
shared_examples "checks references" do shared_examples "checks references" do
it "returns true" do it "returns true" do
...@@ -393,10 +393,24 @@ describe Note do ...@@ -393,10 +393,24 @@ describe Note do
it_behaves_like "checks references" it_behaves_like "checks references"
end end
context "when there are two references in note" do context "when there is a reference to a label" do
let_it_be(:private_label) { create(:label, project: private_project) }
let(:note) do let(:note) do
create :note, create :note,
noteable: ext_issue, project: ext_proj, noteable: ext_issue, project: ext_proj,
note: "added label #{private_label.to_reference(ext_proj)}",
system: true
end
let!(:system_note_metadata) { create(:system_note_metadata, note: note, action: :label) }
it_behaves_like "checks references"
end
context "when there are two references in note" do
let_it_be(:ext_issue2) { create(:issue, project: ext_proj) }
let(:note) do
create :note,
noteable: ext_issue2, project: ext_proj,
note: "mentioned in issue #{private_issue.to_reference(ext_proj)} and " \ note: "mentioned in issue #{private_issue.to_reference(ext_proj)} and " \
"public issue #{ext_issue.to_reference(ext_proj)}", "public issue #{ext_issue.to_reference(ext_proj)}",
system: true system: true
... ...
......
...@@ -141,6 +141,34 @@ describe GlobalPolicy do ...@@ -141,6 +141,34 @@ describe GlobalPolicy do
it { is_expected.to be_allowed(:access_api) } it { is_expected.to be_allowed(:access_api) }
end end
end end
context 'inactive user' do
before do
current_user.update!(confirmed_at: nil, confirmation_sent_at: 5.days.ago)
end
context 'when within the confirmation grace period' do
before do
allow(User).to receive(:allow_unconfirmed_access_for).and_return(10.days)
end
it { is_expected.to be_allowed(:access_api) }
end
context 'when confirmation grace period is expired' do
before do
allow(User).to receive(:allow_unconfirmed_access_for).and_return(2.days)
end
it { is_expected.not_to be_allowed(:access_api) }
end
it 'when `inactive_policy_condition` feature flag is turned off' do
stub_feature_flags(inactive_policy_condition: false)
is_expected.to be_allowed(:access_api)
end
end
end end
describe 'receive notifications' do describe 'receive notifications' do
...@@ -202,6 +230,20 @@ describe GlobalPolicy do ...@@ -202,6 +230,20 @@ describe GlobalPolicy do
it { is_expected.not_to be_allowed(:access_git) } it { is_expected.not_to be_allowed(:access_git) }
end end
describe 'inactive user' do
before do
current_user.update!(confirmed_at: nil)
end
it { is_expected.not_to be_allowed(:access_git) }
it 'when `inactive_policy_condition` feature flag is turned off' do
stub_feature_flags(inactive_policy_condition: false)
is_expected.to be_allowed(:access_git)
end
end
context 'when terms are enforced' do context 'when terms are enforced' do
before do before do
enforce_terms enforce_terms
...@@ -298,6 +340,20 @@ describe GlobalPolicy do ...@@ -298,6 +340,20 @@ describe GlobalPolicy do
it { is_expected.not_to be_allowed(:use_slash_commands) } it { is_expected.not_to be_allowed(:use_slash_commands) }
end end
describe 'inactive user' do
before do
current_user.update!(confirmed_at: nil)
end
it { is_expected.not_to be_allowed(:use_slash_commands) }
it 'when `inactive_policy_condition` feature flag is turned off' do
stub_feature_flags(inactive_policy_condition: false)
is_expected.to be_allowed(:use_slash_commands)
end
end
context 'when access locked' do context 'when access locked' do
before do before do
current_user.lock_access! current_user.lock_access!
... ...
......
...@@ -164,6 +164,7 @@ describe API::CommitStatuses do ...@@ -164,6 +164,7 @@ describe API::CommitStatuses do
expect(response).to have_gitlab_http_status(201) expect(response).to have_gitlab_http_status(201)
expect(job.status).to eq('pending') expect(job.status).to eq('pending')
expect(job.stage_idx).to eq(GenericCommitStatus::EXTERNAL_STAGE_IDX)
end end
end end
...@@ -331,6 +332,29 @@ describe API::CommitStatuses do ...@@ -331,6 +332,29 @@ describe API::CommitStatuses do
end end
end end
context 'when updating a protected ref' do
before do
create(:protected_branch, project: project, name: 'master')
post api(post_url, user), params: { state: 'running', ref: 'master' }
end
context 'with user as developer' do
let(:user) { developer }
it 'does not create commit status' do
expect(response).to have_gitlab_http_status(403)
end
end
context 'with user as maintainer' do
let(:user) { create_user(:maintainer) }
it 'creates commit status' do
expect(response).to have_gitlab_http_status(201)
end
end
end
context 'when commit SHA is invalid' do context 'when commit SHA is invalid' do
let(:sha) { 'invalid_sha' } let(:sha) { 'invalid_sha' }
...@@ -372,6 +396,22 @@ describe API::CommitStatuses do ...@@ -372,6 +396,22 @@ describe API::CommitStatuses do
.to include 'is blocked: Only allowed schemes are http, https' .to include 'is blocked: Only allowed schemes are http, https'
end end
end end
context 'when trying to update a status of a different type' do
let!(:pipeline) { create(:ci_pipeline, project: project, sha: sha, ref: 'ref') }
let!(:ci_build) { create(:ci_build, pipeline: pipeline, name: 'test-job') }
let(:params) { { state: 'pending', name: 'test-job' } }
before do
post api(post_url, developer), params: params
end
it 'responds with bad request status and validation errors' do
expect(response).to have_gitlab_http_status(400)
expect(json_response['message']['name'])
.to include 'has already been taken'
end
end
end end
context 'reporter user' do context 'reporter user' do
... ...
......
...@@ -8,6 +8,7 @@ describe API::Commits do ...@@ -8,6 +8,7 @@ describe API::Commits do
let(:user) { create(:user) } let(:user) { create(:user) }
let(:guest) { create(:user).tap { |u| project.add_guest(u) } } let(:guest) { create(:user).tap { |u| project.add_guest(u) } }
let(:developer) { create(:user).tap { |u| project.add_developer(u) } }
let(:project) { create(:project, :repository, creator: user, path: 'my.project') } let(:project) { create(:project, :repository, creator: user, path: 'my.project') }
let(:branch_with_dot) { project.repository.find_branch('ends-with.json') } let(:branch_with_dot) { project.repository.find_branch('ends-with.json') }
let(:branch_with_slash) { project.repository.find_branch('improve/awesome') } let(:branch_with_slash) { project.repository.find_branch('improve/awesome') }
...@@ -964,6 +965,56 @@ describe API::Commits do ...@@ -964,6 +965,56 @@ describe API::Commits do
end end
end end
shared_examples_for 'ref with pipeline' do
let!(:pipeline) do
project
.ci_pipelines
.create!(source: :push, ref: 'master', sha: commit.sha, protected: false)
end
it 'includes status as "created" and a last_pipeline object' do
get api(route, current_user)
expect(response).to have_gitlab_http_status(200)
expect(response).to match_response_schema('public_api/v4/commit/detail')
expect(json_response['status']).to eq('created')
expect(json_response['last_pipeline']['id']).to eq(pipeline.id)
expect(json_response['last_pipeline']['ref']).to eq(pipeline.ref)
expect(json_response['last_pipeline']['sha']).to eq(pipeline.sha)
expect(json_response['last_pipeline']['status']).to eq(pipeline.status)
end
context 'when pipeline succeeds' do
before do
pipeline.update!(status: 'success')
end
it 'includes a "success" status' do
get api(route, current_user)
expect(response).to have_gitlab_http_status(200)
expect(response).to match_response_schema('public_api/v4/commit/detail')
expect(json_response['status']).to eq('success')
end
end
end
shared_examples_for 'ref with unaccessible pipeline' do
let!(:pipeline) do
project
.ci_pipelines
.create!(source: :push, ref: 'master', sha: commit.sha, protected: false)
end
it 'does not include last_pipeline' do
get api(route, current_user)
expect(response).to match_response_schema('public_api/v4/commit/detail')
expect(response).to have_gitlab_http_status(200)
expect(json_response['last_pipeline']).to be_nil
end
end
context 'when stat param' do context 'when stat param' do
let(:route) { "/projects/#{project_id}/repository/commits/#{commit_id}" } let(:route) { "/projects/#{project_id}/repository/commits/#{commit_id}" }
...@@ -993,6 +1044,15 @@ describe API::Commits do ...@@ -993,6 +1044,15 @@ describe API::Commits do
let(:project) { create(:project, :public, :repository) } let(:project) { create(:project, :public, :repository) }
it_behaves_like 'ref commit' it_behaves_like 'ref commit'
it_behaves_like 'ref with pipeline'
context 'with private builds' do
before do
project.project_feature.update!(builds_access_level: ProjectFeature::PRIVATE)
end
it_behaves_like 'ref with unaccessible pipeline'
end
end end
context 'when unauthenticated', 'and project is private' do context 'when unauthenticated', 'and project is private' do
...@@ -1006,6 +1066,17 @@ describe API::Commits do ...@@ -1006,6 +1066,17 @@ describe API::Commits do
let(:current_user) { user } let(:current_user) { user }
it_behaves_like 'ref commit' it_behaves_like 'ref commit'
it_behaves_like 'ref with pipeline'
context 'when builds are disabled' do
before do
project
.project_feature
.update!(builds_access_level: ProjectFeature::DISABLED)
end
it_behaves_like 'ref with unaccessible pipeline'
end
context 'when branch contains a dot' do context 'when branch contains a dot' do
let(:commit) { project.repository.commit(branch_with_dot.name) } let(:commit) { project.repository.commit(branch_with_dot.name) }
...@@ -1041,35 +1112,53 @@ describe API::Commits do ...@@ -1041,35 +1112,53 @@ describe API::Commits do
it_behaves_like 'ref commit' it_behaves_like 'ref commit'
end end
end end
end
context 'when the ref has a pipeline' do context 'when authenticated', 'as a developer' do
let!(:pipeline) { project.ci_pipelines.create(source: :push, ref: 'master', sha: commit.sha, protected: false) } let(:current_user) { developer }
it 'includes a "created" status' do it_behaves_like 'ref commit'
get api(route, current_user) it_behaves_like 'ref with pipeline'
expect(response).to have_gitlab_http_status(200) context 'with private builds' do
expect(response).to match_response_schema('public_api/v4/commit/detail') before do
expect(json_response['status']).to eq('created') project.project_feature.update!(builds_access_level: ProjectFeature::PRIVATE)
expect(json_response['last_pipeline']['id']).to eq(pipeline.id)
expect(json_response['last_pipeline']['ref']).to eq(pipeline.ref)
expect(json_response['last_pipeline']['sha']).to eq(pipeline.sha)
expect(json_response['last_pipeline']['status']).to eq(pipeline.status)
end end
context 'when pipeline succeeds' do it_behaves_like 'ref with pipeline'
before do end
pipeline.update(status: 'success')
end end
it 'includes a "success" status' do context 'when authenticated', 'as a guest' do
get api(route, current_user) let(:current_user) { guest }
expect(response).to have_gitlab_http_status(200) it_behaves_like '403 response' do
expect(response).to match_response_schema('public_api/v4/commit/detail') let(:request) { get api(route, guest) }
expect(json_response['status']).to eq('success') let(:message) { '403 Forbidden' }
end
end
context 'when authenticated', 'as a non member' do
let(:current_user) { create(:user) }
it_behaves_like '403 response' do
let(:request) { get api(route, guest) }
let(:message) { '403 Forbidden' }
end end
end end
context 'when authenticated', 'as non_member and project is public' do
let(:current_user) { create(:user) }
let(:project) { create(:project, :public, :repository) }
it_behaves_like 'ref with pipeline'
context 'with private builds' do
before do
project.project_feature.update!(builds_access_level: ProjectFeature::PRIVATE)
end
it_behaves_like 'ref with unaccessible pipeline'
end end
end end
end end
... ...
......
...@@ -447,6 +447,18 @@ describe API::Files do ...@@ -447,6 +447,18 @@ describe API::Files do
expect(response).to have_gitlab_http_status(200) expect(response).to have_gitlab_http_status(200)
end end
it 'sets no-cache headers' do
url = route('.gitignore') + "/raw"
expect(Gitlab::Workhorse).to receive(:send_git_blob)
get api(url, current_user), params: params
expect(response.headers["Cache-Control"]).to include("no-store")
expect(response.headers["Cache-Control"]).to include("no-cache")
expect(response.headers["Pragma"]).to eq("no-cache")
expect(response.headers["Expires"]).to eq("Fri, 01 Jan 1990 00:00:00 GMT")
end
context 'when mandatory params are not given' do context 'when mandatory params are not given' do
it_behaves_like '400 response' do it_behaves_like '400 response' do
let(:request) { get api(route("any%2Ffile"), current_user) } let(:request) { get api(route("any%2Ffile"), current_user) }
... ...
......