...@@ -99,6 +99,7 @@ ...@@ -99,6 +99,7 @@
"jszip": "^3.1.3", "jszip": "^3.1.3",
"jszip-utils": "^0.0.2", "jszip-utils": "^0.0.2",
"katex": "^0.10.0", "katex": "^0.10.0",
"lodash": "^4.17.15",
"marked": "^0.3.12", "marked": "^0.3.12",
"mermaid": "^8.4.2", "mermaid": "^8.4.2",
"monaco-editor": "^0.15.6", "monaco-editor": "^0.15.6",
... ...
......
...@@ -59,5 +59,48 @@ module QA ...@@ -59,5 +59,48 @@ module QA
a_hash_including(message: '202 Accepted') a_hash_including(message: '202 Accepted')
) )
end end
describe 'raw file access' do
let(:svg_file) do
<<-SVG
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" baseProfile="full" xmlns="http://www.w3.org/2000/svg">
<polygon id="triangle" points="0,0 0,50 50,0" fill="#009900" stroke="#004400"/>
<script type="text/javascript">
alert("surprise");
</script>
</svg>
SVG
end
it 'sets no-cache headers as expected' do
create_project_request = Runtime::API::Request.new(@api_client, '/projects')
post create_project_request.url, path: project_name, name: project_name
create_file_request = Runtime::API::Request.new(@api_client, "/projects/#{sanitized_project_path}/repository/files/test.svg")
post create_file_request.url, branch: 'master', content: svg_file, commit_message: 'Add test.svg'
get_file_request = Runtime::API::Request.new(@api_client, "/projects/#{sanitized_project_path}/repository/files/test.svg/raw", ref: 'master')
3.times do
response = get get_file_request.url
# Subsequent responses aren't cached, so headers should match from
# request to request, especially a 200 response rather than a 304
# (indicating a cached response.) Further, :content_disposition
# should include `attachment` for all responses.
#
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")
expect(response.headers[:content_disposition]).to include("attachment")
expect(response.headers[:content_disposition]).not_to include("inline")
expect(response.headers[:content_type]).to include("image/svg+xml")
end
end
end
end end
end end
...@@ -23,6 +23,47 @@ describe DashboardController do ...@@ -23,6 +23,47 @@ describe DashboardController do
end end
end end
describe "GET activity as JSON" do
render_views
let(:user) { create(:user) }
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: { 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: { format: :json }
expect(json_response['html']).to include(_('No activities found'))
end
it 'filters out invisible event when calculating the count' do
get :activity, params: { format: :json }
expect(json_response['count']).to eq(0)
end
end
end
it_behaves_like 'authenticates sessionless user', :issues, :atom, author_id: User.first it_behaves_like 'authenticates sessionless user', :issues, :atom, author_id: User.first
it_behaves_like 'authenticates sessionless user', :issues_calendar, :ics it_behaves_like 'authenticates sessionless user', :issues_calendar, :ics
... ...
......
...@@ -47,7 +47,7 @@ describe GroupsController do ...@@ -47,7 +47,7 @@ describe GroupsController do
it 'assigns events for all the projects in the group', :sidekiq_might_not_need_inline do it 'assigns events for all the projects in the group', :sidekiq_might_not_need_inline do
subject subject
expect(assigns(:events)).to contain_exactly(event) expect(assigns(:events).map(&:id)).to contain_exactly(event.id)
end end
end end
end end
...@@ -119,12 +119,12 @@ describe GroupsController do ...@@ -119,12 +119,12 @@ describe GroupsController do
describe 'GET #activity' do describe 'GET #activity' do
render_views render_views
context 'as json' do
before do before do
sign_in(user) sign_in(user)
project project
end end
context 'as json' do
it 'includes events from all projects in group and subgroups', :sidekiq_might_not_need_inline do it 'includes events from all projects in group and subgroups', :sidekiq_might_not_need_inline do
2.times do 2.times do
project = create(:project, group: group) project = create(:project, group: group)
...@@ -141,6 +141,31 @@ describe GroupsController do ...@@ -141,6 +141,31 @@ describe GroupsController do
expect(assigns(:projects).limit_value).to be_nil expect(assigns(:projects).limit_value).to be_nil
end end
end end
context 'when user has no permission to see the event' do
let(:user) { create(:user) }
let(:group) { create(:group) }
let(:project) { create(:project, group: group) }
let(:project_with_restricted_access) do
create(:project, :public, issues_access_level: ProjectFeature::PRIVATE, group: group)
end
before do
create(:event, project: project)
create(:event, :created, project: project_with_restricted_access, target: create(:issue))
group.add_guest(user)
sign_in(user)
end
it 'filters out invisible event' do
get :activity, params: { id: group.to_param }, format: :json
expect(json_response['count']).to eq(1)
end
end
end end
describe 'POST #create' do describe 'POST #create' do
... ...
......
...@@ -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
... ...
......