diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2020-07-06 18:09:13 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2020-07-06 18:09:13 +0000 |
commit | 691ed55a053853e58f36635524d2615ac60e445e (patch) | |
tree | 923c7097cfe2c4beaee82d0b5227f443b760bbed /spec | |
parent | ce06ce825b9ef5204a84aaa37d0dfc7742da5037 (diff) | |
download | gitlab-ce-691ed55a053853e58f36635524d2615ac60e445e.tar.gz |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec')
21 files changed, 627 insertions, 347 deletions
diff --git a/spec/controllers/concerns/controller_with_feature_category/config_spec.rb b/spec/controllers/concerns/controller_with_feature_category/config_spec.rb deleted file mode 100644 index 9b8ffd2baab..00000000000 --- a/spec/controllers/concerns/controller_with_feature_category/config_spec.rb +++ /dev/null @@ -1,53 +0,0 @@ -# frozen_string_literal: true - -require "fast_spec_helper" -require "rspec-parameterized" -require_relative "../../../../app/controllers/concerns/controller_with_feature_category/config" - -RSpec.describe ControllerWithFeatureCategory::Config do - describe "#matches?" do - using RSpec::Parameterized::TableSyntax - - where(:only_actions, :except_actions, :if_proc, :unless_proc, :test_action, :expected) do - nil | nil | nil | nil | "action" | true - [:included] | nil | nil | nil | "action" | false - [:included] | nil | nil | nil | "included" | true - nil | [:excluded] | nil | nil | "excluded" | false - nil | nil | true | nil | "action" | true - [:included] | nil | true | nil | "action" | false - [:included] | nil | true | nil | "included" | true - nil | [:excluded] | true | nil | "excluded" | false - nil | nil | false | nil | "action" | false - [:included] | nil | false | nil | "action" | false - [:included] | nil | false | nil | "included" | false - nil | [:excluded] | false | nil | "excluded" | false - nil | nil | nil | true | "action" | false - [:included] | nil | nil | true | "action" | false - [:included] | nil | nil | true | "included" | false - nil | [:excluded] | nil | true | "excluded" | false - nil | nil | nil | false | "action" | true - [:included] | nil | nil | false | "action" | false - [:included] | nil | nil | false | "included" | true - nil | [:excluded] | nil | false | "excluded" | false - nil | nil | true | false | "action" | true - [:included] | nil | true | false | "action" | false - [:included] | nil | true | false | "included" | true - nil | [:excluded] | true | false | "excluded" | false - nil | nil | false | true | "action" | false - [:included] | nil | false | true | "action" | false - [:included] | nil | false | true | "included" | false - nil | [:excluded] | false | true | "excluded" | false - end - - with_them do - let(:config) do - if_to_proc = if_proc.nil? ? nil : -> (_) { if_proc } - unless_to_proc = unless_proc.nil? ? nil : -> (_) { unless_proc } - - described_class.new(:category, only_actions, except_actions, if_to_proc, unless_to_proc) - end - - specify { expect(config.matches?(test_action)).to be(expected) } - end - end -end diff --git a/spec/controllers/concerns/controller_with_feature_category_spec.rb b/spec/controllers/concerns/controller_with_feature_category_spec.rb deleted file mode 100644 index e603a7d14c4..00000000000 --- a/spec/controllers/concerns/controller_with_feature_category_spec.rb +++ /dev/null @@ -1,66 +0,0 @@ -# frozen_string_literal: true - -require 'fast_spec_helper' -require_relative "../../../app/controllers/concerns/controller_with_feature_category" -require_relative "../../../app/controllers/concerns/controller_with_feature_category/config" - -RSpec.describe ControllerWithFeatureCategory do - describe ".feature_category_for_action" do - let(:base_controller) do - Class.new do - include ControllerWithFeatureCategory - end - end - - let(:controller) do - Class.new(base_controller) do - feature_category :baz - feature_category :foo, except: %w(update edit) - feature_category :bar, only: %w(index show) - feature_category :quux, only: %w(destroy) - feature_category :quuz, only: %w(destroy) - end - end - - let(:subclass) do - Class.new(controller) do - feature_category :qux, only: %w(index) - end - end - - it "is nil when nothing was defined" do - expect(base_controller.feature_category_for_action("hello")).to be_nil - end - - it "returns the expected category", :aggregate_failures do - expect(controller.feature_category_for_action("update")).to eq(:baz) - expect(controller.feature_category_for_action("hello")).to eq(:foo) - expect(controller.feature_category_for_action("index")).to eq(:bar) - end - - it "returns the closest match for categories defined in subclasses" do - expect(subclass.feature_category_for_action("index")).to eq(:qux) - expect(subclass.feature_category_for_action("show")).to eq(:bar) - end - - it "returns the last defined feature category when multiple match" do - expect(controller.feature_category_for_action("destroy")).to eq(:quuz) - end - - it "raises an error when using including and excluding the same action" do - expect do - Class.new(base_controller) do - feature_category :hello, only: [:world], except: [:world] - end - end.to raise_error(%r(cannot configure both `only` and `except`)) - end - - it "raises an error when using unknown arguments" do - expect do - Class.new(base_controller) do - feature_category :hello, hello: :world - end - end.to raise_error(%r(unknown arguments)) - end - end -end diff --git a/spec/controllers/every_controller_spec.rb b/spec/controllers/every_controller_spec.rb deleted file mode 100644 index 4785ee9ed8f..00000000000 --- a/spec/controllers/every_controller_spec.rb +++ /dev/null @@ -1,82 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.describe "Every controller" do - context "feature categories" do - let_it_be(:feature_categories) do - YAML.load_file(Rails.root.join('config', 'feature_categories.yml')).map(&:to_sym).to_set - end - - let_it_be(:controller_actions) do - # This will return tuples of all controller actions defined in the routes - # Only for controllers inheriting ApplicationController - # Excluding controllers from gems (OAuth, Sidekiq) - Rails.application.routes.routes - .map { |route| route.required_defaults.presence } - .compact - .select { |route| route[:controller].present? && route[:action].present? } - .map { |route| [constantize_controller(route[:controller]), route[:action]] } - .reject { |route| route.first.nil? || !route.first.include?(ControllerWithFeatureCategory) } - end - - let_it_be(:routes_without_category) do - controller_actions.map do |controller, action| - "#{controller}##{action}" unless controller.feature_category_for_action(action) - end.compact - end - - it "has feature categories" do - pending("We'll work on defining categories for all controllers: "\ - "https://gitlab.com/gitlab-com/gl-infra/scalability/-/issues/463") - - expect(routes_without_category).to be_empty, "#{routes_without_category.first(10)} did not have a category" - end - - it "completed controllers don't get new routes without categories" do - completed_controllers = [Projects::MergeRequestsController].map(&:to_s) - - newly_introduced_missing_category = routes_without_category.select do |route| - completed_controllers.any? { |controller| route.start_with?(controller) } - end - - expect(newly_introduced_missing_category).to be_empty - end - - it "recognizes the feature categories" do - routes_unknown_category = controller_actions.map do |controller, action| - used_category = controller.feature_category_for_action(action) - next unless used_category - next if used_category == :not_owned - - ["#{controller}##{action}", used_category] unless feature_categories.include?(used_category) - end.compact - - expect(routes_unknown_category).to be_empty, "#{routes_unknown_category.first(10)} had an unknown category" - end - - it "doesn't define or exclude categories on removed actions", :aggregate_failures do - controller_actions.group_by(&:first).each do |controller, controller_action| - existing_actions = controller_action.map(&:last) - used_actions = actions_defined_in_feature_category_config(controller) - non_existing_used_actions = used_actions - existing_actions - - expect(non_existing_used_actions).to be_empty, - "#{controller} used #{non_existing_used_actions} to define feature category, but the route does not exist" - end - end - end - - def constantize_controller(name) - "#{name.camelize}Controller".constantize - rescue NameError - nil # some controllers, like the omniauth ones are dynamic - end - - def actions_defined_in_feature_category_config(controller) - feature_category_configs = controller.send(:class_attributes)[:feature_category_config] - feature_category_configs.map do |config| - Array(config.send(:only)) + Array(config.send(:except)) - end.flatten.uniq.map(&:to_s) - end -end diff --git a/spec/controllers/import/bitbucket_server_controller_spec.rb b/spec/controllers/import/bitbucket_server_controller_spec.rb index af471b478fa..3292a1ab39e 100644 --- a/spec/controllers/import/bitbucket_server_controller_spec.rb +++ b/spec/controllers/import/bitbucket_server_controller_spec.rb @@ -46,7 +46,7 @@ RSpec.describe Import::BitbucketServerController do .to receive(:new).with(project_key, repo_slug, anything, project_name, user.namespace, user, anything) .and_return(double(execute: project)) - post :create, params: { project: project_key, repository: repo_slug }, format: :json + post :create, params: { bitbucketServerProject: project_key, bitbucketServerRepo: repo_slug }, format: :json expect(response).to have_gitlab_http_status(:ok) end @@ -59,20 +59,20 @@ RSpec.describe Import::BitbucketServerController do .to receive(:new).with(project_key, repo_slug, anything, project_name, user.namespace, user, anything) .and_return(double(execute: project)) - post :create, params: { project: project_key, repository: repo_slug, format: :json } + post :create, params: { bitbucketServerProject: project_key, bitbucketServerRepo: repo_slug, format: :json } expect(response).to have_gitlab_http_status(:ok) end end it 'returns an error when an invalid project key is used' do - post :create, params: { project: 'some&project' } + post :create, params: { bitbucket_server_project: 'some&project' } expect(response).to have_gitlab_http_status(:unprocessable_entity) end it 'returns an error when an invalid repository slug is used' do - post :create, params: { project: 'some-project', repository: 'try*this' } + post :create, params: { bitbucket_server_project: 'some-project', bitbucket_server_repo: 'try*this' } expect(response).to have_gitlab_http_status(:unprocessable_entity) end @@ -80,7 +80,7 @@ RSpec.describe Import::BitbucketServerController do it 'returns an error when the project cannot be found' do allow(client).to receive(:repo).with(project_key, repo_slug).and_return(nil) - post :create, params: { project: project_key, repository: repo_slug }, format: :json + post :create, params: { bitbucket_server_project: project_key, bitbucket_server_repo: repo_slug }, format: :json expect(response).to have_gitlab_http_status(:unprocessable_entity) end @@ -90,15 +90,15 @@ RSpec.describe Import::BitbucketServerController do .to receive(:new).with(project_key, repo_slug, anything, project_name, user.namespace, user, anything) .and_return(double(execute: build(:project))) - post :create, params: { project: project_key, repository: repo_slug }, format: :json + post :create, params: { bitbucket_server_project: project_key, bitbucket_server_repo: repo_slug }, format: :json expect(response).to have_gitlab_http_status(:unprocessable_entity) end it "returns an error when the server can't be contacted" do - expect(client).to receive(:repo).with(project_key, repo_slug).and_raise(::BitbucketServer::Connection::ConnectionError) + allow(client).to receive(:repo).with(project_key, repo_slug).and_return([nil, nil]) - post :create, params: { project: project_key, repository: repo_slug }, format: :json + post :create, params: { bitbucket_server_project: project_key, bitbucket_server_repo: repo_slug }, format: :json expect(response).to have_gitlab_http_status(:unprocessable_entity) end @@ -123,7 +123,9 @@ RSpec.describe Import::BitbucketServerController do end it 'sets the session variables' do - post :configure, params: { personal_access_token: token, bitbucket_username: username, bitbucket_server_url: url } + allow(controller).to receive(:allow_local_requests?).and_return(true) + + post :configure, params: { personal_access_token: token, bitbucket_server_username: username, bitbucket_server_url: url } expect(session[:bitbucket_server_url]).to eq(url) expect(session[:bitbucket_server_username]).to eq(username) diff --git a/spec/features/projects/pipelines/pipeline_spec.rb b/spec/features/projects/pipelines/pipeline_spec.rb index c6a002ad18b..78024b2e93c 100644 --- a/spec/features/projects/pipelines/pipeline_spec.rb +++ b/spec/features/projects/pipelines/pipeline_spec.rb @@ -383,8 +383,8 @@ RSpec.describe 'Pipeline', :js do context 'without test reports' do let(:pipeline) { create(:ci_pipeline, project: project) } - it 'shows nothing' do - expect(page.find('.js-test-report-badge-counter', visible: :all).text).to eq("") + it 'shows zero' do + expect(page.find('.js-test-report-badge-counter', visible: :all).text).to eq("0") end end end diff --git a/spec/frontend/fixtures/branches.rb b/spec/frontend/fixtures/branches.rb index 4667dfb69f8..df2d1af7ecf 100644 --- a/spec/frontend/fixtures/branches.rb +++ b/spec/frontend/fixtures/branches.rb @@ -2,33 +2,51 @@ require 'spec_helper' -RSpec.describe Projects::BranchesController, '(JavaScript fixtures)', type: :controller do +RSpec.describe 'Branches (JavaScript fixtures)' do include JavaScriptFixturesHelpers - let(:admin) { create(:admin) } - let(:namespace) { create(:namespace, name: 'frontend-fixtures' )} - let(:project) { create(:project, :repository, namespace: namespace, path: 'branches-project') } - - render_views + let_it_be(:admin) { create(:admin) } + let_it_be(:namespace) { create(:namespace, name: 'frontend-fixtures' )} + let_it_be(:project) { create(:project, :repository, namespace: namespace, path: 'branches-project') } before(:all) do clean_frontend_fixtures('branches/') + clean_frontend_fixtures('api/branches/') end - before do - sign_in(admin) + after(:all) do + remove_repository(project) end - after do - remove_repository(project) + describe Projects::BranchesController, '(JavaScript fixtures)', type: :controller do + render_views + + before do + sign_in(admin) + end + + it 'branches/new_branch.html' do + get :new, params: { + namespace_id: project.namespace.to_param, + project_id: project + } + + expect(response).to be_successful + end end - it 'branches/new_branch.html' do - get :new, params: { - namespace_id: project.namespace.to_param, - project_id: project - } + describe API::Branches, '(JavaScript fixtures)', type: :request do + include ApiHelpers + + it 'api/branches/branches.json' do + # The search query "ma" matches a few branch names in the test + # repository with a variety of different properties, including: + # - "master": default, protected + # - "markdown": non-default, protected + # - "many_files": non-default, not protected + get api("/projects/#{project.id}/repository/branches?search=ma", admin) - expect(response).to be_successful + expect(response).to be_successful + end end end diff --git a/spec/frontend/fixtures/commit.rb b/spec/frontend/fixtures/commit.rb index c5c00afd4ca..9175a757b73 100644 --- a/spec/frontend/fixtures/commit.rb +++ b/spec/frontend/fixtures/commit.rb @@ -2,34 +2,55 @@ require 'spec_helper' -RSpec.describe Projects::CommitController, '(JavaScript fixtures)', type: :controller do +RSpec.describe 'Commit (JavaScript fixtures)' do include JavaScriptFixturesHelpers let_it_be(:project) { create(:project, :repository) } let_it_be(:user) { create(:user) } - let(:commit) { project.commit("master") } - - render_views + let_it_be(:commit) { project.commit("master") } before(:all) do clean_frontend_fixtures('commit/') + clean_frontend_fixtures('api/commits/') + + project.add_maintainer(user) end before do - project.add_maintainer(user) - sign_in(user) allow(SecureRandom).to receive(:hex).and_return('securerandomhex:thereisnospoon') end - it 'commit/show.html' do - params = { - namespace_id: project.namespace, - project_id: project, - id: commit.id - } + after(:all) do + remove_repository(project) + end + + describe Projects::CommitController, '(JavaScript fixtures)', type: :controller do + render_views + + before do + sign_in(user) + end + + it 'commit/show.html' do + params = { + namespace_id: project.namespace, + project_id: project, + id: commit.id + } + + get :show, params: params + + expect(response).to be_successful + end + end + + describe API::Commits, '(JavaScript fixtures)', type: :request do + include ApiHelpers - get :show, params: params + it 'api/commits/commit.json' do + get api("/projects/#{project.id}/repository/commits/#{commit.id}", user) - expect(response).to be_successful + expect(response).to be_successful + end end end diff --git a/spec/frontend/fixtures/tags.rb b/spec/frontend/fixtures/tags.rb new file mode 100644 index 00000000000..b2a5429fac8 --- /dev/null +++ b/spec/frontend/fixtures/tags.rb @@ -0,0 +1,28 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe 'Tags (JavaScript fixtures)' do + include JavaScriptFixturesHelpers + + let_it_be(:admin) { create(:admin) } + let_it_be(:project) { create(:project, :repository, path: 'tags-project') } + + before(:all) do + clean_frontend_fixtures('api/tags/') + end + + after(:all) do + remove_repository(project) + end + + describe API::Tags, '(JavaScript fixtures)', type: :request do + include ApiHelpers + + it 'api/tags/tags.json' do + get api("/projects/#{project.id}/repository/tags", admin) + + expect(response).to be_successful + end + end +end diff --git a/spec/frontend/pipelines/test_reports/stores/actions_spec.js b/spec/frontend/pipelines/test_reports/stores/actions_spec.js index 56148361e0a..bd42db36645 100644 --- a/spec/frontend/pipelines/test_reports/stores/actions_spec.js +++ b/spec/frontend/pipelines/test_reports/stores/actions_spec.js @@ -14,12 +14,16 @@ describe('Actions TestReports Store', () => { let state; const testReports = getJSONFixture('pipelines/test_report.json'); + const summary = { total_count: 1 }; - const endpoint = `${TEST_HOST}/test_reports.json`; + const fullReportEndpoint = `${TEST_HOST}/test_reports.json`; + const summaryEndpoint = `${TEST_HOST}/test_reports/summary.json`; const defaultState = { - endpoint, + fullReportEndpoint, + summaryEndpoint, testReports: {}, selectedSuite: {}, + summary: {}, }; beforeEach(() => { @@ -31,14 +35,47 @@ describe('Actions TestReports Store', () => { mock.restore(); }); - describe('fetch reports', () => { + describe('fetch report summary', () => { beforeEach(() => { - mock.onGet(`${TEST_HOST}/test_reports.json`).replyOnce(200, testReports, {}); + mock.onGet(summaryEndpoint).replyOnce(200, summary, {}); }); it('sets testReports and shows tests', done => { testAction( - actions.fetchReports, + actions.fetchSummary, + null, + state, + [{ type: types.SET_SUMMARY, payload: summary }], + [], + done, + ); + }); + + it('should create flash on API error', done => { + testAction( + actions.fetchSummary, + null, + { + summaryEndpoint: null, + }, + [], + [], + () => { + expect(createFlash).toHaveBeenCalled(); + done(); + }, + ); + }); + }); + + describe('fetch full report', () => { + beforeEach(() => { + mock.onGet(fullReportEndpoint).replyOnce(200, testReports, {}); + }); + + it('sets testReports and shows tests', done => { + testAction( + actions.fetchFullReport, null, state, [{ type: types.SET_REPORTS, payload: testReports }], @@ -49,10 +86,10 @@ describe('Actions TestReports Store', () => { it('should create flash on API error', done => { testAction( - actions.fetchReports, + actions.fetchFullReport, null, { - endpoint: null, + fullReportEndpoint: null, }, [], [{ type: 'toggleLoading' }, { type: 'toggleLoading' }], diff --git a/spec/frontend/pipelines/test_reports/stores/mutations_spec.js b/spec/frontend/pipelines/test_reports/stores/mutations_spec.js index a0eb93c4e6b..cf54cacab60 100644 --- a/spec/frontend/pipelines/test_reports/stores/mutations_spec.js +++ b/spec/frontend/pipelines/test_reports/stores/mutations_spec.js @@ -18,15 +18,6 @@ describe('Mutations TestReports Store', () => { mockState = defaultState; }); - describe('set endpoint', () => { - it('should set endpoint', () => { - const expectedState = { ...mockState, endpoint: 'foo' }; - mutations[types.SET_ENDPOINT](mockState, 'foo'); - - expect(mockState.endpoint).toEqual(expectedState.endpoint); - }); - }); - describe('set reports', () => { it('should set testReports', () => { const expectedState = { ...mockState, testReports }; @@ -45,6 +36,15 @@ describe('Mutations TestReports Store', () => { }); }); + describe('set summary', () => { + it('should set summary', () => { + const summary = { total_count: 1 }; + mutations[types.SET_SUMMARY](mockState, summary); + + expect(mockState.summary).toEqual(summary); + }); + }); + describe('toggle loading', () => { it('should set to true', () => { const expectedState = { ...mockState, isLoading: true }; diff --git a/spec/frontend/pipelines/test_reports/test_reports_spec.js b/spec/frontend/pipelines/test_reports/test_reports_spec.js index cc86ba6d46d..dd2b7220d7c 100644 --- a/spec/frontend/pipelines/test_reports/test_reports_spec.js +++ b/spec/frontend/pipelines/test_reports/test_reports_spec.js @@ -1,9 +1,12 @@ import Vuex from 'vuex'; -import { shallowMount } from '@vue/test-utils'; +import { shallowMount, createLocalVue } from '@vue/test-utils'; import { getJSONFixture } from 'helpers/fixtures'; import TestReports from '~/pipelines/components/test_reports/test_reports.vue'; import * as actions from '~/pipelines/stores/test_reports/actions'; +const localVue = createLocalVue(); +localVue.use(Vuex); + describe('Test reports app', () => { let wrapper; let store; @@ -22,11 +25,15 @@ describe('Test reports app', () => { testReports, ...state, }, - actions, + actions: { + ...actions, + fetchSummary: () => {}, + }, }); wrapper = shallowMount(TestReports, { store, + localVue, }); }; diff --git a/spec/frontend/pipelines/test_reports/test_suite_table_spec.js b/spec/frontend/pipelines/test_reports/test_suite_table_spec.js index a5b093cf769..5d448bcb439 100644 --- a/spec/frontend/pipelines/test_reports/test_suite_table_spec.js +++ b/spec/frontend/pipelines/test_reports/test_suite_table_spec.js @@ -1,11 +1,14 @@ import Vuex from 'vuex'; -import { shallowMount } from '@vue/test-utils'; +import { shallowMount, createLocalVue } from '@vue/test-utils'; import { getJSONFixture } from 'helpers/fixtures'; import SuiteTable from '~/pipelines/components/test_reports/test_suite_table.vue'; import * as getters from '~/pipelines/stores/test_reports/getters'; import { TestStatus } from '~/pipelines/constants'; import skippedTestCases from './mock_data'; +const localVue = createLocalVue(); +localVue.use(Vuex); + describe('Test reports suite table', () => { let wrapper; let store; @@ -32,6 +35,7 @@ describe('Test reports suite table', () => { wrapper = shallowMount(SuiteTable, { store, + localVue, }); }; diff --git a/spec/helpers/gitlab_routing_helper_spec.rb b/spec/helpers/gitlab_routing_helper_spec.rb index 99c3fad660b..bd48fc7cee2 100644 --- a/spec/helpers/gitlab_routing_helper_spec.rb +++ b/spec/helpers/gitlab_routing_helper_spec.rb @@ -181,6 +181,23 @@ RSpec.describe GitlabRoutingHelper do end end + describe '#gitlab_raw_snippet_blob_path' do + let(:ref) { 'test-ref' } + + it_behaves_like 'snippet blob raw path' do + subject { gitlab_raw_snippet_blob_path(blob, ref) } + end + + context 'without a ref' do + let(:blob) { personal_snippet.blobs.first } + let(:ref) { blob.repository.root_ref } + + it 'uses the root ref' do + expect(gitlab_raw_snippet_blob_path(blob)).to eq("/-/snippets/#{personal_snippet.id}/raw/#{ref}/#{blob.path}") + end + end + end + describe '#gitlab_raw_snippet_url' do it 'returns the raw personal snippet url' do expect(gitlab_raw_snippet_url(personal_snippet)).to eq("http://test.host/snippets/#{personal_snippet.id}/raw") diff --git a/spec/lib/gitlab/class_attributes_spec.rb b/spec/lib/gitlab/class_attributes_spec.rb deleted file mode 100644 index f8766f20495..00000000000 --- a/spec/lib/gitlab/class_attributes_spec.rb +++ /dev/null @@ -1,41 +0,0 @@ -# frozen_string_literal: true -require 'fast_spec_helper' - -RSpec.describe Gitlab::ClassAttributes do - let(:klass) do - Class.new do - include Gitlab::ClassAttributes - - def self.get_attribute(name) - get_class_attribute(name) - end - - def self.set_attribute(name, value) - class_attributes[name] = value - end - end - end - - let(:subclass) { Class.new(klass) } - - describe ".get_class_attribute" do - it "returns values set on the class" do - klass.set_attribute(:foo, :bar) - - expect(klass.get_attribute(:foo)).to eq(:bar) - end - - it "returns values set on a superclass" do - klass.set_attribute(:foo, :bar) - - expect(subclass.get_attribute(:foo)).to eq(:bar) - end - - it "returns values from the subclass over attributes from a superclass" do - klass.set_attribute(:foo, :baz) - subclass.set_attribute(:foo, :bar) - - expect(subclass.get_attribute(:foo)).to eq(:bar) - end - end -end diff --git a/spec/lib/gitlab/danger/roulette_spec.rb b/spec/lib/gitlab/danger/roulette_spec.rb index 272666fceac..24f547a2e92 100644 --- a/spec/lib/gitlab/danger/roulette_spec.rb +++ b/spec/lib/gitlab/danger/roulette_spec.rb @@ -155,8 +155,8 @@ RSpec.describe Gitlab::Danger::Roulette do context 'when change contains QA category' do let(:categories) { [:qa] } - it 'assigns QA reviewer and sets optional QA maintainer' do - expect(spins).to contain_exactly(matching_spin(:qa, reviewer: software_engineer_in_test, optional: :maintainer)) + it 'assigns QA reviewer' do + expect(spins).to contain_exactly(matching_spin(:qa, reviewer: software_engineer_in_test)) end end @@ -171,8 +171,8 @@ RSpec.describe Gitlab::Danger::Roulette do context 'when change contains test category' do let(:categories) { [:test] } - it 'assigns corresponding SET and sets optional test maintainer' do - expect(spins).to contain_exactly(matching_spin(:test, reviewer: software_engineer_in_test, optional: :maintainer)) + it 'assigns corresponding SET' do + expect(spins).to contain_exactly(matching_spin(:test, reviewer: software_engineer_in_test)) end end end diff --git a/spec/lib/gitlab/metrics/web_transaction_spec.rb b/spec/lib/gitlab/metrics/web_transaction_spec.rb index 389a0a04904..6ceba186660 100644 --- a/spec/lib/gitlab/metrics/web_transaction_spec.rb +++ b/spec/lib/gitlab/metrics/web_transaction_spec.rb @@ -70,9 +70,6 @@ RSpec.describe Gitlab::Metrics::WebTransaction do end describe '#labels' do - let(:request) { double(:request, format: double(:format, ref: :html)) } - let(:controller_class) { double(:controller_class, name: 'TestController') } - context 'when request goes to Grape endpoint' do before do route = double(:route, request_method: 'GET', path: '/:version/projects/:id/archive(.:format)') @@ -80,9 +77,8 @@ RSpec.describe Gitlab::Metrics::WebTransaction do env['api.endpoint'] = endpoint end - it 'provides labels with the method and path of the route in the grape endpoint' do - expect(transaction.labels).to eq({ controller: 'Grape', action: 'GET /projects/:id/archive', feature_category: '' }) + expect(transaction.labels).to eq({ controller: 'Grape', action: 'GET /projects/:id/archive' }) end it 'does not provide labels if route infos are missing' do @@ -96,21 +92,24 @@ RSpec.describe Gitlab::Metrics::WebTransaction do end context 'when request goes to ActionController' do + let(:request) { double(:request, format: double(:format, ref: :html)) } + before do - controller = double(:controller, class: controller_class, action_name: 'show', request: request) + klass = double(:klass, name: 'TestController') + controller = double(:controller, class: klass, action_name: 'show', request: request) env['action_controller.instance'] = controller end it 'tags a transaction with the name and action of a controller' do - expect(transaction.labels).to eq({ controller: 'TestController', action: 'show', feature_category: '' }) + expect(transaction.labels).to eq({ controller: 'TestController', action: 'show' }) end context 'when the request content type is not :html' do let(:request) { double(:request, format: double(:format, ref: :json)) } it 'appends the mime type to the transaction action' do - expect(transaction.labels).to eq({ controller: 'TestController', action: 'show.json', feature_category: '' }) + expect(transaction.labels).to eq({ controller: 'TestController', action: 'show.json' }) end end @@ -118,31 +117,11 @@ RSpec.describe Gitlab::Metrics::WebTransaction do let(:request) { double(:request, format: double(:format, ref: 'http://example.com')) } it 'does not append the MIME type to the transaction action' do - expect(transaction.labels).to eq({ controller: 'TestController', action: 'show', feature_category: '' }) - end - end - - context 'when the feature category is known' do - it 'includes it in the feature category label' do - expect(controller_class).to receive(:feature_category_for_action).with('show').and_return(:source_code_management) - expect(transaction.labels).to eq({ controller: 'TestController', action: 'show', feature_category: "source_code_management" }) + expect(transaction.labels).to eq({ controller: 'TestController', action: 'show' }) end end end - it 'returns the same labels for API and controller requests' do - route = double(:route, request_method: 'GET', path: '/:version/projects/:id/archive(.:format)') - endpoint = double(:endpoint, route: route) - api_env = { 'api.endpoint' => endpoint } - api_labels = described_class.new(api_env).labels - - controller = double(:controller, class: controller_class, action_name: 'show', request: request) - controller_env = { 'action_controller.instance' => controller } - controller_labels = described_class.new(controller_env).labels - - expect(api_labels.keys).to contain_exactly(*controller_labels.keys) - end - it 'returns no labels when no route information is present in env' do expect(transaction.labels).to eq({}) end diff --git a/spec/models/namespace/root_storage_statistics_spec.rb b/spec/models/namespace/root_storage_statistics_spec.rb index 9d516dc5ec2..ce6f875ee09 100644 --- a/spec/models/namespace/root_storage_statistics_spec.rb +++ b/spec/models/namespace/root_storage_statistics_spec.rb @@ -70,7 +70,16 @@ RSpec.describe Namespace::RootStorageStatistics, type: :model do end end + shared_examples 'does not include personal snippets' do + specify do + expect(root_storage_statistics).not_to receive(:from_personal_snippets) + + root_storage_statistics.recalculate! + end + end + it_behaves_like 'data refresh' + it_behaves_like 'does not include personal snippets' context 'with subgroups' do let(:subgroup1) { create(:group, parent: namespace)} @@ -80,12 +89,45 @@ RSpec.describe Namespace::RootStorageStatistics, type: :model do let(:project2) { create(:project, namespace: subgroup2) } it_behaves_like 'data refresh' + it_behaves_like 'does not include personal snippets' end context 'with a personal namespace' do - let(:namespace) { create(:user).namespace } + let_it_be(:user) { create(:user) } + let(:namespace) { user.namespace } it_behaves_like 'data refresh' + + context 'when user has personal snippets' do + let(:total_project_snippets_size) { stat1.snippets_size + stat2.snippets_size } + + it 'aggregates personal and project snippets size' do + # This is just a a snippet authored by other user + # to ensure we only pick snippets from the namespace + # user + create(:personal_snippet, :repository).statistics.refresh! + + snippets = create_list(:personal_snippet, 3, :repository, author: user) + snippets.each { |s| s.statistics.refresh! } + + total_personal_snippets_size = snippets.map { |s| s.statistics.repository_size }.sum + + root_storage_statistics.recalculate! + + expect(root_storage_statistics.snippets_size).to eq(total_personal_snippets_size + total_project_snippets_size) + end + + context 'when personal snippets do not have statistics' do + it 'does not raise any error' do + snippets = create_list(:personal_snippet, 2, :repository, author: user) + snippets.last.statistics.refresh! + + root_storage_statistics.recalculate! + + expect(root_storage_statistics.snippets_size).to eq(total_project_snippets_size + snippets.last.statistics.repository_size) + end + end + end end end end diff --git a/spec/presenters/snippet_blob_presenter_spec.rb b/spec/presenters/snippet_blob_presenter_spec.rb index 28c86468c78..7464c0ac15b 100644 --- a/spec/presenters/snippet_blob_presenter_spec.rb +++ b/spec/presenters/snippet_blob_presenter_spec.rb @@ -109,22 +109,38 @@ RSpec.describe SnippetBlobPresenter do end describe '#raw_path' do - subject { described_class.new(snippet.blob).raw_path } + let_it_be(:project) { create(:project) } + let_it_be(:user) { create(:user) } + let_it_be(:personal_snippet) { create(:personal_snippet, :repository, author: user) } + let_it_be(:project_snippet) { create(:project_snippet, :repository, project: project, author: user) } - context 'with ProjectSnippet' do - let!(:project) { create(:project) } - let(:snippet) { create(:project_snippet, project: project) } + before do + project.add_developer(user) + end + + subject { described_class.new(snippet.blobs.first, current_user: user).raw_path } - it 'returns the raw path' do - expect(subject).to eq "/#{snippet.project.full_path}/snippets/#{snippet.id}/raw" + it_behaves_like 'snippet blob raw path' + + context 'with snippet_multiple_files feature disabled' do + before do + stub_feature_flags(snippet_multiple_files: false) end - end - context 'with PersonalSnippet' do - let(:snippet) { create(:personal_snippet) } + context 'with ProjectSnippet' do + let(:snippet) { project_snippet } - it 'returns the raw path' do - expect(subject).to eq "/snippets/#{snippet.id}/raw" + it 'returns the raw path' do + expect(subject).to eq "/#{snippet.project.full_path}/snippets/#{snippet.id}/raw" + end + end + + context 'with PersonalSnippet' do + let(:snippet) { personal_snippet } + + it 'returns the raw path' do + expect(subject).to eq "/snippets/#{snippet.id}/raw" + end end end end diff --git a/spec/requests/api/import_bitbucket_server_spec.rb b/spec/requests/api/import_bitbucket_server_spec.rb new file mode 100644 index 00000000000..be5afdfab61 --- /dev/null +++ b/spec/requests/api/import_bitbucket_server_spec.rb @@ -0,0 +1,214 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe API::ImportBitbucketServer do + let(:base_uri) { "https://test:7990" } + let(:user) { create(:user) } + let(:token) { "asdasd12345" } + let(:secret) { "sekrettt" } + let(:project_key) { 'TES' } + let(:repo_slug) { 'vim' } + let(:repo) { { name: 'vim' } } + + describe "POST /import/bitbucket_server" do + context 'with no optional parameters' do + let_it_be(:project) { create(:project) } + let(:client) { double(BitbucketServer::Client) } + + before do + Grape::Endpoint.before_each do |endpoint| + allow(endpoint).to receive(:client).and_return(client.as_null_object) + allow(client).to receive(:repo).with(project_key, repo_slug).and_return(double(name: repo_slug)) + end + end + + after do + Grape::Endpoint.before_each nil + end + + it 'returns 201 response when the project is imported successfully' do + allow(Gitlab::BitbucketServerImport::ProjectCreator) + .to receive(:new).with(project_key, repo_slug, anything, repo_slug, user.namespace, user, anything) + .and_return(double(execute: project)) + + post api("/import/bitbucket_server", user), params: { + bitbucket_server_url: base_uri, + bitbucket_server_username: user, + personal_access_token: token, + bitbucket_server_project: project_key, + bitbucket_server_repo: repo_slug + } + + expect(response).to have_gitlab_http_status(:created) + expect(json_response).to be_a Hash + expect(json_response['name']).to eq(project.name) + end + end + + context 'with a new project name' do + let_it_be(:project) { create(:project, name: 'new-name') } + let(:client) { instance_double(BitbucketServer::Client) } + + before do + Grape::Endpoint.before_each do |endpoint| + allow(endpoint).to receive(:client).and_return(client) + allow(client).to receive(:repo).with(project_key, repo_slug).and_return(double(name: repo_slug)) + end + end + + after do + Grape::Endpoint.before_each nil + end + + it 'returns 201 response when the project is imported successfully with a new project name' do + allow(Gitlab::BitbucketServerImport::ProjectCreator) + .to receive(:new).with(project_key, repo_slug, anything, project.name, user.namespace, user, anything) + .and_return(double(execute: project)) + + post api("/import/bitbucket_server", user), params: { + bitbucket_server_url: base_uri, + bitbucket_server_username: user, + personal_access_token: token, + bitbucket_server_project: project_key, + bitbucket_server_repo: repo_slug, + new_name: 'new-name' + } + + expect(response).to have_gitlab_http_status(:created) + expect(json_response).to be_a Hash + expect(json_response['name']).to eq('new-name') + end + end + + context 'with an invalid URL' do + let_it_be(:project) { create(:project, name: 'new-name') } + let(:client) { instance_double(BitbucketServer::Client) } + + before do + Grape::Endpoint.before_each do |endpoint| + allow(endpoint).to receive(:client).and_return(client) + allow(client).to receive(:repo).with(project_key, repo_slug).and_return(double(name: repo_slug)) + end + end + + after do + Grape::Endpoint.before_each nil + end + + it 'returns 400 response due to a blcoked URL' do + allow(Gitlab::BitbucketServerImport::ProjectCreator) + .to receive(:new).with(project_key, repo_slug, anything, project.name, user.namespace, user, anything) + .and_return(double(execute: project)) + + allow(Gitlab::UrlBlocker) + .to receive(:blocked_url?) + .and_return(true) + post api("/import/bitbucket_server", user), params: { + bitbucket_server_url: base_uri, + bitbucket_server_username: user, + personal_access_token: token, + bitbucket_server_project: project_key, + bitbucket_server_repo: repo_slug, + new_name: 'new-name' + } + + expect(response).to have_gitlab_http_status(:bad_request) + end + end + + context 'with a new namespace' do + let(:bitbucket_client) { instance_double(BitbucketServer::Client) } + + before do + Grape::Endpoint.before_each do |endpoint| + allow(endpoint).to receive(:client).and_return(bitbucket_client) + repo = double(name: repo_slug, full_path: "/other-namespace/#{repo_slug}") + allow(bitbucket_client).to receive(:repo).with(project_key, repo_slug).and_return(repo) + end + end + + after do + Grape::Endpoint.before_each nil + end + + it 'returns 201 response when the project is imported successfully to a new namespace' do + allow(Gitlab::BitbucketServerImport::ProjectCreator) + .to receive(:new).with(project_key, repo_slug, anything, repo_slug, an_instance_of(Group), user, anything) + .and_return(double(execute: create(:project, name: repo_slug))) + + post api("/import/bitbucket_server", user), params: { + bitbucket_server_url: base_uri, + bitbucket_server_username: user, + personal_access_token: token, + bitbucket_server_project: project_key, + bitbucket_server_repo: repo_slug, + new_namespace: 'new-namespace' + } + + expect(response).to have_gitlab_http_status(:created) + expect(json_response).to be_a Hash + expect(json_response['full_path']).not_to eq("/#{user.namespace}/#{repo_slug}") + end + end + + context 'with a private inaccessible namespace' do + let(:bitbucket_client) { instance_double(BitbucketServer::Client) } + let(:project) { create(:project, import_type: 'bitbucket', creator_id: user.id, import_source: 'asd/vim', namespace: 'private-group/vim') } + + before do + Grape::Endpoint.before_each do |endpoint| + allow(endpoint).to receive(:client).and_return(bitbucket_client) + repo = double(name: repo_slug, full_path: "/private-group/#{repo_slug}") + allow(bitbucket_client).to receive(:repo).with(project_key, repo_slug).and_return(repo) + end + end + + after do + Grape::Endpoint.before_each nil + end + + it 'returns 401 response when user can not create projects in the chosen namespace' do + allow(Gitlab::BitbucketServerImport::ProjectCreator) + .to receive(:new).with(project_key, repo_slug, anything, repo_slug, an_instance_of(Group), user, anything) + .and_return(double(execute: build(:project))) + + other_namespace = create(:group, :private, name: 'private-group') + + post api("/import/bitbucket_server", user), params: { + bitbucket_server_url: base_uri, + bitbucket_server_username: user, + personal_access_token: token, + bitbucket_server_project: project_key, + bitbucket_server_repo: repo_slug, + new_namespace: other_namespace.name + } + expect(response).to have_gitlab_http_status(:unauthorized) + end + end + + context 'with an inaccessible bitbucket server instance' do + let(:bitbucket_client) { instance_double(BitbucketServer::Client) } + let(:project) { create(:project, import_type: 'bitbucket', creator_id: user.id, import_source: 'asd/vim', namespace: 'private-group/vim') } + + before do + Grape::Endpoint.before_each do |endpoint| + allow(endpoint).to receive(:client).and_return(bitbucket_client) + allow(bitbucket_client).to receive(:repo).with(project_key, repo_slug).and_raise(::BitbucketServer::Connection::ConnectionError) + end + end + + it 'raises a connection error' do + post api("/import/bitbucket_server", user), params: { + bitbucket_server_url: base_uri, + bitbucket_server_username: user, + personal_access_token: token, + bitbucket_server_project: project_key, + bitbucket_server_repo: repo_slug, + new_namespace: 'new-namespace' + } + expect(response).to have_gitlab_http_status(:bad_request) + end + end + end +end diff --git a/spec/services/import/bitbucket_server_service_spec.rb b/spec/services/import/bitbucket_server_service_spec.rb new file mode 100644 index 00000000000..c548e87b040 --- /dev/null +++ b/spec/services/import/bitbucket_server_service_spec.rb @@ -0,0 +1,113 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Import::BitbucketServerService do + let_it_be(:user) { create(:user) } + let(:base_uri) { "https://test:7990" } + let(:token) { "asdasd12345" } + let(:secret) { "sekrettt" } + let(:project_key) { 'TES' } + let(:repo_slug) { 'vim' } + let(:repo) do + { + name: 'vim', + description: 'test', + visibility_level: Gitlab::VisibilityLevel::PUBLIC, + browse_url: 'http://repo.com/repo/repo', + clone_url: 'http://repo.com/repo/repo.git' + } + end + + let(:client) { double(BitbucketServer::Client) } + + let(:credentials) { { base_uri: base_uri, user: user, password: token } } + let(:params) { { bitbucket_server_url: base_uri, bitbucket_server_username: user, personal_access_token: token, bitbucket_server_project: project_key, bitbucket_server_repo: repo_slug } } + + subject { described_class.new(client, user, params) } + + before do + allow(subject).to receive(:authorized?).and_return(true) + end + + context 'when no repo is found' do + before do + allow(subject).to receive(:authorized?).and_return(true) + allow(client).to receive(:repo).and_return(nil) + end + + it 'returns an error' do + result = subject.execute(credentials) + + expect(result).to include( + message: "Project #{project_key}/#{repo_slug} could not be found", + status: :error, + http_status: :unprocessable_entity + ) + end + end + + context 'when user is unauthorized' do + before do + allow(subject).to receive(:authorized?).and_return(false) + end + + it 'returns an error' do + result = subject.execute(credentials) + + expect(result).to include( + message: "You don't have permissions to create this project", + status: :error, + http_status: :unauthorized + ) + end + end + + context 'verify url' do + shared_examples 'denies local request' do + before do + allow(client).to receive(:repo).with(project_key, repo_slug).and_return(double(repo)) + end + + it 'does not allow requests' do + result = subject.execute(credentials) + expect(result[:status]).to eq(:error) + expect(result[:message]).to include("Invalid URL:") + end + end + + context 'when host is localhost' do + before do + allow(subject).to receive(:url).and_return('https://localhost:3000') + end + + include_examples 'denies local request' + end + + context 'when host is on local network' do + before do + allow(subject).to receive(:url).and_return('https://192.168.0.191') + end + + include_examples 'denies local request' + end + + context 'when host is ftp protocol' do + before do + allow(subject).to receive(:url).and_return('ftp://testing') + end + + include_examples 'denies local request' + end + end + + it 'raises an exception for unknown error causes' do + exception = StandardError.new('Not Implemented') + + allow(client).to receive(:repo).and_raise(exception) + + expect(Gitlab::Import::Logger).not_to receive(:error) + + expect { subject.execute(credentials) }.to raise_error(exception) + end +end diff --git a/spec/support/shared_examples/snippet_blob_shared_examples.rb b/spec/support/shared_examples/snippet_blob_shared_examples.rb new file mode 100644 index 00000000000..ba97688d017 --- /dev/null +++ b/spec/support/shared_examples/snippet_blob_shared_examples.rb @@ -0,0 +1,24 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.shared_examples 'snippet blob raw path' do + let(:blob) { snippet.blobs.first } + let(:ref) { blob.repository.root_ref } + + context 'for PersonalSnippets' do + let(:snippet) { personal_snippet } + + it 'returns the raw personal snippet blob path' do + expect(subject).to eq("/-/snippets/#{snippet.id}/raw/#{ref}/#{blob.path}") + end + end + + context 'for ProjectSnippets' do + let(:snippet) { project_snippet } + + it 'returns the raw project snippet blob path' do + expect(subject).to eq("/#{snippet.project.full_path}/-/snippets/#{snippet.id}/raw/#{ref}/#{blob.path}") + end + end +end |