Skip to content
Snippets Groups Projects
Commit 0ab47b99 authored by GitLab Bot's avatar GitLab Bot
Browse files

Add latest changes from gitlab-org/gitlab@master

parent 1308dc5e
No related branches found
No related tags found
No related merge requests found
Showing
with 435 additions and 80 deletions
Loading
Loading
@@ -15,10 +15,17 @@ module Gitlab
repository_resolver: -> (project) { project.wiki.repository },
suffix: :wiki
).freeze
SNIPPET = RepoType.new(
name: :snippet,
access_checker_class: Gitlab::GitAccessSnippet,
repository_resolver: -> (snippet) { snippet.repository },
container_resolver: -> (id) { Snippet.find_by_id(id) }
).freeze
 
TYPES = {
PROJECT.name.to_s => PROJECT,
WIKI.name.to_s => WIKI
WIKI.name.to_s => WIKI,
SNIPPET.name.to_s => SNIPPET
}.freeze
 
def self.types
Loading
Loading
Loading
Loading
@@ -47,6 +47,10 @@ module Gitlab
self == PROJECT
end
 
def snippet?
self == SNIPPET
end
def path_suffix
suffix ? ".#{suffix}" : ''
end
Loading
Loading
Loading
Loading
@@ -65,7 +65,7 @@ module Gitlab
def find_all_paths_from_source(project)
Gitlab::Metrics::Dashboard::Cache.delete_all!
 
system_service.all_dashboard_paths(project)
default_dashboard_path(project)
.+ project_service.all_dashboard_paths(project)
end
 
Loading
Loading
@@ -79,6 +79,18 @@ module Gitlab
::Metrics::Dashboard::ProjectDashboardService
end
 
def self_monitoring_service
::Metrics::Dashboard::SelfMonitoringDashboardService
end
def default_dashboard_path(project)
if project.self_monitoring?
self_monitoring_service.all_dashboard_paths(project)
else
system_service.all_dashboard_paths(project)
end
end
def service_for(options)
Gitlab::Metrics::Dashboard::ServiceSelector.call(options)
end
Loading
Loading
Loading
Loading
@@ -18,6 +18,7 @@ module Gitlab
::Metrics::Dashboard::DefaultEmbedService,
::Metrics::Dashboard::SystemDashboardService,
::Metrics::Dashboard::PodDashboardService,
::Metrics::Dashboard::SelfMonitoringDashboardService,
::Metrics::Dashboard::ProjectDashboardService
].freeze
 
Loading
Loading
Loading
Loading
@@ -5184,7 +5184,7 @@ msgstr ""
msgid "ContainerRegistry|We are having trouble connecting to Docker, which could be due to an issue with your project name or path. %{docLinkStart}More Information%{docLinkEnd}"
msgstr ""
 
msgid "ContainerRegistry|Wildcards such as %{codeStart}*-stable%{codeEnd} or %{codeStart}production/*%{codeEnd} are supported. To select all tags, use %{codeStart}.*%{codeEnd}"
msgid "ContainerRegistry|Wildcards such as %{codeStart}.*-stable%{codeEnd} or %{codeStart}production/.*%{codeEnd} are supported. To select all tags, use %{codeStart}.*%{codeEnd}"
msgstr ""
 
msgid "ContainerRegistry|With the Container Registry, every project can have its own space to store its Docker images. %{docLinkStart}More Information%{docLinkEnd}"
Loading
Loading
@@ -17681,6 +17681,18 @@ msgstr ""
msgid "SnippetsEmptyState|There are no snippets to show."
msgstr ""
 
msgid "Snippets|Description (optional)"
msgstr ""
msgid "Snippets|File"
msgstr ""
msgid "Snippets|Give your file a name to add code highlighting, e.g. example.rb for Ruby"
msgstr ""
msgid "Snippets|Optionally add a description about what your snippet does or how to use it..."
msgstr ""
msgid "Snowplow"
msgstr ""
 
Loading
Loading
Loading
Loading
@@ -201,6 +201,7 @@
"yarn-deduplicate": "^1.1.1"
},
"resolutions": {
"at.js": "https://gitlab.com/gitlab-org/frontend/At.js.git#121ce9a557b51c33f5693ac8df52d2bda1e53cbe",
"vue-jest/ts-jest": "24.0.0",
"monaco-editor": "0.18.1"
},
Loading
Loading
Loading
Loading
@@ -55,7 +55,7 @@ module QA
element :diffs_tab
end
 
view 'app/assets/javascripts/diffs/components/diff_line_gutter_content.vue' do
view 'app/assets/javascripts/diffs/components/diff_table_cell.vue' do
element :diff_comment
end
 
Loading
Loading
module RuboCop
module Cop
module Gitlab
class KeysFirstAndValuesFirst < RuboCop::Cop::Cop
FIRST_PATTERN = /\Afirst\z/.freeze
def message(used_method)
<<~MSG
Don't use `.keys.first` and `.values.first`.
Instead use `.each_key.first` and `.each_value.first` (or `.first.first` and `first.second`)
This will reduce memory usage and execution time.
MSG
end
def on_send(node)
if find_on_keys_or_values?(node)
add_offense(node, location: :selector, message: message(node.method_name))
end
end
def autocorrect(node)
lambda do |corrector|
replace_with = if node.descendants.first.method_name == :values
'.each_value'
elsif node.descendants.first.method_name == :keys
'.each_key'
else
throw("Expect '.values.first' or '.keys.first', but get #{node.descendants.first.method_name}.first")
end
upto_including_keys_or_values = node.descendants.first.source_range
before_keys_or_values = node.descendants[1].source_range
range_to_replace = node.source_range
.with(begin_pos: before_keys_or_values.end_pos,
end_pos: upto_including_keys_or_values.end_pos)
corrector.replace(range_to_replace, replace_with)
end
end
def find_on_keys_or_values?(node)
chained_on_node = node.descendants.first
node.method_name.to_s =~ FIRST_PATTERN &&
chained_on_node.is_a?(RuboCop::AST::SendNode) &&
[:keys, :values].include?(chained_on_node.method_name) &&
node.descendants[1]
end
def method_name_for_node(node)
children[1].to_s
end
end
end
end
end
Loading
Loading
@@ -5,6 +5,7 @@ require_relative 'cop/gitlab/httparty'
require_relative 'cop/gitlab/finder_with_find_by'
require_relative 'cop/gitlab/union'
require_relative 'cop/gitlab/rails_logger'
require_relative 'cop/gitlab/keys-first-and-values-first'
require_relative 'cop/include_sidekiq_worker'
require_relative 'cop/safe_params'
require_relative 'cop/active_record_association_reload'
Loading
Loading
Loading
Loading
@@ -13,7 +13,7 @@ describe UserCalloutsController do
subject { post :create, params: { feature_name: feature_name }, format: :json }
 
context 'with valid feature name' do
let(:feature_name) { UserCallout.feature_names.keys.first }
let(:feature_name) { UserCallout.feature_names.each_key.first }
 
context 'when callout entry does not exist' do
it 'creates a callout entry with dismissed state' do
Loading
Loading
@@ -28,7 +28,7 @@ describe UserCalloutsController do
end
 
context 'when callout entry already exists' do
let!(:callout) { create(:user_callout, feature_name: UserCallout.feature_names.keys.first, user: user) }
let!(:callout) { create(:user_callout, feature_name: UserCallout.feature_names.each_key.first, user: user) }
 
it 'returns success' do
subject
Loading
Loading
# frozen_string_literal: true
FactoryBot.define do
factory :snippet_repository do
snippet
after(:build) do |snippet_repository, _|
snippet_repository.shard_name = snippet_repository.snippet.repository_storage
snippet_repository.disk_path = snippet_repository.snippet.disk_path
end
end
end
Loading
Loading
@@ -20,6 +20,21 @@ FactoryBot.define do
trait :private do
visibility_level { Snippet::PRIVATE }
end
# Test repository - https://gitlab.com/gitlab-org/gitlab-test
trait :repository do
after :create do |snippet|
TestEnv.copy_repo(snippet,
bare_repo: TestEnv.factory_repo_path_bare,
refs: TestEnv::BRANCH_SHA)
end
end
trait :empty_repo do
after(:create) do |snippet|
raise "Failed to create repository!" unless snippet.repository.create_if_not_exists
end
end
end
 
factory :project_snippet, parent: :snippet, class: :ProjectSnippet do
Loading
Loading
Loading
Loading
@@ -8,9 +8,17 @@ describe 'Projects > Snippets > Create Snippet', :js do
let(:user) { create(:user) }
let(:project) { create(:project, :public) }
 
def description_field
find('.js-description-input input,textarea')
end
def fill_form
fill_in 'project_snippet_title', with: 'My Snippet Title'
# Click placeholder first to expand full description field
description_field.click
fill_in 'project_snippet_description', with: 'My Snippet **Description**'
page.within('.file-editor') do
find('.ace_text-input', visible: false).send_keys('Hello World!')
end
Loading
Loading
@@ -27,6 +35,18 @@ describe 'Projects > Snippets > Create Snippet', :js do
click_on('New snippet')
end
 
it 'shows collapsible description input' do
collapsed = description_field
expect(page).not_to have_field('project_snippet_description')
expect(collapsed).to be_visible
collapsed.click
expect(page).to have_field('project_snippet_description')
expect(collapsed).not_to be_visible
end
it 'creates a new snippet' do
fill_form
click_button('Create snippet')
Loading
Loading
Loading
Loading
@@ -5,6 +5,10 @@ require 'spec_helper'
describe 'User creates snippet', :js do
let(:user) { create(:user) }
 
def description_field
find('.js-description-input input,textarea')
end
before do
stub_feature_flags(allow_possible_spam: false)
stub_feature_flags(snippets_vue: false)
Loading
Loading
@@ -22,7 +26,11 @@ describe 'User creates snippet', :js do
visit new_snippet_path
 
fill_in 'personal_snippet_title', with: 'My Snippet Title'
# Click placeholder first to expand full description field
description_field.click
fill_in 'personal_snippet_description', with: 'My Snippet **Description**'
find('#personal_snippet_visibility_level_20').set(true)
page.within('.file-editor') do
find('.ace_text-input', visible: false).send_keys 'Hello World!'
Loading
Loading
Loading
Loading
@@ -13,9 +13,17 @@ describe 'User creates snippet', :js do
visit new_snippet_path
end
 
def description_field
find('.js-description-input input,textarea')
end
def fill_form
fill_in 'personal_snippet_title', with: 'My Snippet Title'
# Click placeholder first to expand full description field
description_field.click
fill_in 'personal_snippet_description', with: 'My Snippet **Description**'
page.within('.file-editor') do
find('.ace_text-input', visible: false).send_keys 'Hello World!'
end
Loading
Loading
@@ -36,6 +44,8 @@ describe 'User creates snippet', :js do
end
 
it 'previews a snippet with file' do
# Click placeholder first to expand full description field
description_field.click
fill_in 'personal_snippet_description', with: 'My Snippet'
dropzone_file Rails.root.join('spec', 'fixtures', 'banana_sample.gif')
find('.js-md-preview-button').click
Loading
Loading
import { createLocalVue, shallowMount } from '@vue/test-utils';
import Vuex from 'vuex';
import { shallowMount, createLocalVue } from '@vue/test-utils';
import DiffLineGutterContent from '~/diffs/components/diff_line_gutter_content.vue';
import DiffTableCell from '~/diffs/components/diff_table_cell.vue';
import DiffGutterAvatars from '~/diffs/components/diff_gutter_avatars.vue';
import { LINE_POSITION_RIGHT } from '~/diffs/constants';
import { createStore } from '~/mr_notes/stores';
Loading
Loading
@@ -17,7 +17,7 @@ const TEST_LINE_NUMBER = 1;
const TEST_LINE_CODE = 'LC_42';
const TEST_FILE_HASH = diffFileMockData.file_hash;
 
describe('DiffLineGutterContent', () => {
describe('DiffTableCell', () => {
let wrapper;
let line;
let store;
Loading
Loading
@@ -49,22 +49,40 @@ describe('DiffLineGutterContent', () => {
value,
});
};
const createComponent = (props = {}) => {
wrapper = shallowMount(DiffLineGutterContent, {
wrapper = shallowMount(DiffTableCell, {
localVue,
store,
propsData: {
line,
fileHash: TEST_FILE_HASH,
contextLinesPath: '/context/lines/path',
isHighlighted: false,
...props,
},
});
};
const findNoteButton = () => wrapper.find('.js-add-diff-note-button');
const findTd = () => wrapper.find({ ref: 'td' });
const findNoteButton = () => wrapper.find({ ref: 'addDiffNoteButton' });
const findLineNumber = () => wrapper.find({ ref: 'lineNumberRef' });
const findAvatars = () => wrapper.find(DiffGutterAvatars);
 
describe('td', () => {
it('highlights when isHighlighted true', () => {
createComponent({ isHighlighted: true });
expect(findTd().classes()).toContain('hll');
});
it('does not highlight when isHighlighted false', () => {
createComponent({ isHighlighted: false });
expect(findTd().classes()).not.toContain('hll');
});
});
describe('comment button', () => {
it.each`
showCommentButton | userData | query | expectation
Loading
Loading
@@ -84,13 +102,13 @@ describe('DiffLineGutterContent', () => {
);
 
it.each`
isHover | otherProps | discussions | expectation
${true} | ${{}} | ${[]} | ${true}
${false} | ${{}} | ${[]} | ${false}
${true} | ${{ isMatchLine: true }} | ${[]} | ${false}
${true} | ${{ isContextLine: true }} | ${[]} | ${false}
${true} | ${{ isMetaLine: true }} | ${[]} | ${false}
${true} | ${{}} | ${[{}]} | ${false}
isHover | otherProps | discussions | expectation
${true} | ${{}} | ${[]} | ${true}
${false} | ${{}} | ${[]} | ${false}
${true} | ${{ line: { ...line, type: 'match' } }} | ${[]} | ${false}
${true} | ${{ line: { ...line, type: 'context' } }} | ${[]} | ${false}
${true} | ${{ line: { ...line, type: 'old-nonewline' } }} | ${[]} | ${false}
${true} | ${{}} | ${[{}]} | ${false}
`(
'visible is $expectation - with isHover ($isHover), discussions ($discussions), otherProps ($otherProps)',
({ isHover, otherProps, discussions, expectation }) => {
Loading
Loading
@@ -109,7 +127,7 @@ describe('DiffLineGutterContent', () => {
describe('line number', () => {
describe('without lineNumber prop', () => {
it('does not render', () => {
createComponent();
createComponent({ lineType: 'old' });
 
expect(findLineNumber().exists()).toBe(false);
});
Loading
Loading
Loading
Loading
@@ -74,6 +74,8 @@ describe('Time series component', () => {
describe('general functions', () => {
let timeSeriesChart;
 
const findChart = () => timeSeriesChart.find({ ref: 'chart' });
beforeEach(done => {
timeSeriesChart = makeTimeSeriesChart(mockGraphData, 'area-chart');
timeSeriesChart.vm.$nextTick(done);
Loading
Loading
@@ -109,8 +111,6 @@ describe('Time series component', () => {
let startValue;
let endValue;
 
const findChart = () => timeSeriesChart.find({ ref: 'chart' });
beforeEach(done => {
eChartMock = {
handlers: {},
Loading
Loading
@@ -285,6 +285,8 @@ describe('Time series component', () => {
});
 
describe('computed', () => {
const getChartOptions = () => findChart().props('option');
describe('chartData', () => {
let chartData;
const seriesData = () => chartData[0];
Loading
Loading
@@ -329,7 +331,7 @@ describe('Time series component', () => {
});
 
return timeSeriesChart.vm.$nextTick().then(() => {
expect(timeSeriesChart.vm.chartOptions).toEqual(expect.objectContaining(mockOption));
expect(getChartOptions()).toEqual(expect.objectContaining(mockOption));
});
});
 
Loading
Loading
@@ -345,7 +347,7 @@ describe('Time series component', () => {
});
 
return timeSeriesChart.vm.$nextTick().then(() => {
const optionSeries = timeSeriesChart.vm.chartOptions.series;
const optionSeries = getChartOptions().series;
 
expect(optionSeries.length).toEqual(2);
expect(optionSeries[0].name).toEqual(mockSeriesName);
Loading
Loading
@@ -354,33 +356,58 @@ describe('Time series component', () => {
});
 
describe('yAxis formatter', () => {
let format;
let dataFormatter;
let deploymentFormatter;
 
beforeEach(() => {
format = timeSeriesChart.vm.chartOptions.yAxis.axisLabel.formatter;
dataFormatter = getChartOptions().yAxis[0].axisLabel.formatter;
deploymentFormatter = getChartOptions().yAxis[1].axisLabel.formatter;
});
 
it('rounds to 3 decimal places', () => {
expect(format(0.88888)).toBe('0.889');
expect(dataFormatter(0.88888)).toBe('0.889');
});
it('deployment formatter is set as is required to display a tooltip', () => {
expect(deploymentFormatter).toEqual(expect.any(Function));
});
});
});
 
describe('scatterSeries', () => {
describe('deploymentSeries', () => {
it('utilizes deployment data', () => {
expect(timeSeriesChart.vm.scatterSeries.data).toEqual([
['2019-07-16T10:14:25.589Z', 0],
['2019-07-16T11:14:25.589Z', 0],
['2019-07-16T12:14:25.589Z', 0],
expect(timeSeriesChart.vm.deploymentSeries.yAxisIndex).toBe(1); // same as deployment y axis
expect(timeSeriesChart.vm.deploymentSeries.data).toEqual([
['2019-07-16T10:14:25.589Z', expect.any(Number)],
['2019-07-16T11:14:25.589Z', expect.any(Number)],
['2019-07-16T12:14:25.589Z', expect.any(Number)],
]);
 
expect(timeSeriesChart.vm.scatterSeries.symbolSize).toBe(14);
expect(timeSeriesChart.vm.deploymentSeries.symbolSize).toBe(14);
});
});
 
describe('yAxisLabel', () => {
it('y axis is configured correctly', () => {
const { yAxis } = getChartOptions();
expect(yAxis).toHaveLength(2);
const [dataAxis, deploymentAxis] = yAxis;
expect(dataAxis.boundaryGap).toHaveLength(2);
expect(dataAxis.scale).toBe(true);
expect(deploymentAxis.show).toBe(false);
expect(deploymentAxis.min).toEqual(expect.any(Number));
expect(deploymentAxis.max).toEqual(expect.any(Number));
expect(deploymentAxis.min).toBeLessThan(deploymentAxis.max);
});
it('constructs a label for the chart y-axis', () => {
expect(timeSeriesChart.vm.yAxisLabel).toBe('Memory Used per Pod');
const { yAxis } = getChartOptions();
expect(yAxis[0].name).toBe('Memory Used per Pod');
});
});
});
Loading
Loading
@@ -405,7 +432,7 @@ describe('Time series component', () => {
glChartComponents.forEach(dynamicComponent => {
describe(`GitLab UI: ${dynamicComponent.chartType}`, () => {
let timeSeriesAreaChart;
const findChart = () => timeSeriesAreaChart.find(dynamicComponent.component);
const findChartComponent = () => timeSeriesAreaChart.find(dynamicComponent.component);
 
beforeEach(done => {
timeSeriesAreaChart = makeTimeSeriesChart(mockGraphData, dynamicComponent.chartType);
Loading
Loading
@@ -417,12 +444,12 @@ describe('Time series component', () => {
});
 
it('is a Vue instance', () => {
expect(findChart().exists()).toBe(true);
expect(findChart().isVueInstance()).toBe(true);
expect(findChartComponent().exists()).toBe(true);
expect(findChartComponent().isVueInstance()).toBe(true);
});
 
it('receives data properties needed for proper chart render', () => {
const props = findChart().props();
const props = findChartComponent().props();
 
expect(props.data).toBe(timeSeriesAreaChart.vm.chartData);
expect(props.option).toBe(timeSeriesAreaChart.vm.chartOptions);
Loading
Loading
@@ -435,9 +462,9 @@ describe('Time series component', () => {
timeSeriesAreaChart.vm.tooltip.title = mockTitle;
 
timeSeriesAreaChart.vm.$nextTick(() => {
expect(shallowWrapperContainsSlotText(findChart(), 'tooltipTitle', mockTitle)).toBe(
true,
);
expect(
shallowWrapperContainsSlotText(findChartComponent(), 'tooltipTitle', mockTitle),
).toBe(true);
done();
});
});
Loading
Loading
@@ -452,9 +479,9 @@ describe('Time series component', () => {
});
 
it('uses deployment title', () => {
expect(shallowWrapperContainsSlotText(findChart(), 'tooltipTitle', 'Deployed')).toBe(
true,
);
expect(
shallowWrapperContainsSlotText(findChartComponent(), 'tooltipTitle', 'Deployed'),
).toBe(true);
});
 
it('renders clickable commit sha in tooltip content', done => {
Loading
Loading
import setupCollapsibleInputs from '~/snippet/collapsible_input';
import { setHTMLFixture } from 'helpers/fixtures';
describe('~/snippet/collapsible_input', () => {
let formEl;
let descriptionEl;
let titleEl;
let fooEl;
beforeEach(() => {
setHTMLFixture(`
<form>
<div class="js-collapsible-input js-title">
<div class="js-collapsed d-none">
<input type="text" />
</div>
<div class="js-expanded">
<textarea>Hello World!</textarea>
</div>
</div>
<div class="js-collapsible-input js-description">
<div class="js-collapsed">
<input type="text" />
</div>
<div class="js-expanded d-none">
<textarea></textarea>
</div>
</div>
<input type="text" class="js-foo" />
</form>
`);
formEl = document.querySelector('form');
titleEl = formEl.querySelector('.js-title');
descriptionEl = formEl.querySelector('.js-description');
fooEl = formEl.querySelector('.js-foo');
setupCollapsibleInputs();
});
const findInput = el => el.querySelector('textarea,input');
const findCollapsed = el => el.querySelector('.js-collapsed');
const findExpanded = el => el.querySelector('.js-expanded');
const findCollapsedInput = el => findInput(findCollapsed(el));
const findExpandedInput = el => findInput(findExpanded(el));
const focusIn = target => target.dispatchEvent(new Event('focusin', { bubbles: true }));
const expectIsCollapsed = (el, isCollapsed) => {
expect(findCollapsed(el).classList.contains('d-none')).toEqual(!isCollapsed);
expect(findExpanded(el).classList.contains('d-none')).toEqual(isCollapsed);
};
describe('when collapsed', () => {
it('is collapsed', () => {
expectIsCollapsed(descriptionEl, true);
});
describe('when focused', () => {
beforeEach(() => {
focusIn(findCollapsedInput(descriptionEl));
});
it('is expanded', () => {
expectIsCollapsed(descriptionEl, false);
});
describe.each`
desc | value | isCollapsed
${'is collapsed'} | ${''} | ${true}
${'stays open if given value'} | ${'Hello world!'} | ${false}
`('when loses focus', ({ desc, value, isCollapsed }) => {
it(desc, () => {
findExpandedInput(descriptionEl).value = value;
focusIn(fooEl);
expectIsCollapsed(descriptionEl, isCollapsed);
});
});
});
});
describe('when expanded and has value', () => {
it('does not collapse, when focusing out', () => {
expectIsCollapsed(titleEl, false);
focusIn(fooEl);
expectIsCollapsed(titleEl, false);
});
describe('and loses value', () => {
beforeEach(() => {
findExpandedInput(titleEl).value = '';
});
it('collapses, when focusing out', () => {
expectIsCollapsed(titleEl, false);
focusIn(fooEl);
expectIsCollapsed(titleEl, true);
});
});
});
});
import Vue from 'vue';
import { createComponentWithStore } from 'spec/helpers/vue_mount_component_helper';
import { createStore } from '~/mr_notes/stores';
import DiffTableCell from '~/diffs/components/diff_table_cell.vue';
import diffFileMockData from '../mock_data/diff_file';
describe('DiffTableCell', () => {
const createComponent = options =>
createComponentWithStore(Vue.extend(DiffTableCell), createStore(), {
line: diffFileMockData.highlighted_diff_lines[0],
fileHash: diffFileMockData.file_hash,
contextLinesPath: 'contextLinesPath',
...options,
}).$mount();
it('does not highlight row when isHighlighted prop is false', done => {
const vm = createComponent({ isHighlighted: false });
vm.$nextTick()
.then(() => {
expect(vm.$el.classList).not.toContain('hll');
})
.then(done)
.catch(done.fail);
});
it('highlights row when isHighlighted prop is true', done => {
const vm = createComponent({ isHighlighted: true });
vm.$nextTick()
.then(() => {
expect(vm.$el.classList).toContain('hll');
})
.then(done)
.catch(done.fail);
});
});
# frozen_string_literal: true
require 'spec_helper'
describe Gitlab::GitAccessSnippet do
include GitHelpers
let_it_be(:user) { create(:user) }
let_it_be(:personal_snippet) { create(:personal_snippet, :private, :repository) }
let(:protocol) { 'ssh' }
let(:changes) { Gitlab::GitAccess::ANY }
let(:push_access_check) { access.check('git-receive-pack', changes) }
let(:pull_access_check) { access.check('git-upload-pack', changes) }
let(:snippet) { personal_snippet }
let(:actor) { personal_snippet.author }
describe 'when feature flag :version_snippets is enabled' do
it 'allows push and pull access' do
aggregate_failures do
expect { pull_access_check }.not_to raise_error
expect { push_access_check }.not_to raise_error
end
end
end
describe 'when feature flag :version_snippets is disabled' do
before do
stub_feature_flags(version_snippets: false)
end
it 'does not allow push and pull access' do
aggregate_failures do
expect { push_access_check }.to raise_snippet_not_found
expect { pull_access_check }.to raise_snippet_not_found
end
end
end
describe '#check_snippet_accessibility!' do
context 'when the snippet exists' do
it 'allows push and pull access' do
aggregate_failures do
expect { pull_access_check }.not_to raise_error
expect { push_access_check }.not_to raise_error
end
end
end
context 'when the snippet is nil' do
let(:snippet) { nil }
it 'blocks push and pull with "not found"' do
aggregate_failures do
expect { pull_access_check }.to raise_snippet_not_found
expect { push_access_check }.to raise_snippet_not_found
end
end
end
context 'when the snippet does not have a repository' do
let(:snippet) { build_stubbed(:personal_snippet) }
it 'blocks push and pull with "not found"' do
aggregate_failures do
expect { pull_access_check }.to raise_snippet_not_found
expect { push_access_check }.to raise_snippet_not_found
end
end
end
end
private
def access
described_class.new(actor, snippet, protocol,
authentication_abilities: [],
namespace_path: nil, project_path: nil,
redirected_path: nil, auth_result_type: nil)
end
def raise_snippet_not_found
raise_error(Gitlab::GitAccess::NotFoundError, Gitlab::GitAccess::ERROR_MESSAGES[:snippet_not_found])
end
end
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment