diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2020-04-30 21:09:47 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2020-04-30 21:09:47 +0000 |
commit | 3aeda4e6146bea1920c3283e98b01ca4fcf796a8 (patch) | |
tree | b44e6298a749bd8a02283bc5867ab4a3269b62c3 /spec | |
parent | adafb996ef88da50b30c737cdb8caee8307ec6d6 (diff) | |
download | gitlab-ce-3aeda4e6146bea1920c3283e98b01ca4fcf796a8.tar.gz |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec')
-rw-r--r-- | spec/controllers/admin/clusters_controller_spec.rb | 25 | ||||
-rw-r--r-- | spec/controllers/groups/clusters_controller_spec.rb | 25 | ||||
-rw-r--r-- | spec/controllers/oauth/authorized_applications_controller_spec.rb | 21 | ||||
-rw-r--r-- | spec/controllers/projects/clusters_controller_spec.rb | 27 | ||||
-rw-r--r-- | spec/fixtures/api/schemas/cluster_list.json | 14 | ||||
-rw-r--r-- | spec/frontend/alert_management/components/alert_management_detail_spec.js | 34 | ||||
-rw-r--r-- | spec/frontend/monitoring/components/dashboard_panel_spec.js | 17 | ||||
-rw-r--r-- | spec/frontend/pipelines/time_ago_spec.js | 67 | ||||
-rw-r--r-- | spec/helpers/application_helper_spec.rb | 23 | ||||
-rw-r--r-- | spec/javascripts/pipelines/time_ago_spec.js | 64 | ||||
-rw-r--r-- | spec/lib/gitlab/middleware/multipart_spec.rb | 109 | ||||
-rw-r--r-- | spec/lib/uploaded_file_spec.rb | 167 | ||||
-rw-r--r-- | spec/serializers/cluster_entity_spec.rb | 6 | ||||
-rw-r--r-- | spec/serializers/remote_mirror_entity_spec.rb | 7 |
14 files changed, 488 insertions, 118 deletions
diff --git a/spec/controllers/admin/clusters_controller_spec.rb b/spec/controllers/admin/clusters_controller_spec.rb index bd6d5614ccd..d4a12e0dc52 100644 --- a/spec/controllers/admin/clusters_controller_spec.rb +++ b/spec/controllers/admin/clusters_controller_spec.rb @@ -27,7 +27,7 @@ describe Admin::ClustersController do create(:cluster, :disabled, :provided_by_gcp, :production_environment, :instance) end - it 'lists available clusters' do + it 'lists available clusters and displays html' do get_index expect(response).to have_gitlab_http_status(:ok) @@ -35,20 +35,39 @@ describe Admin::ClustersController do expect(assigns(:clusters)).to match_array([enabled_cluster, disabled_cluster]) end + it 'lists available clusters and renders json serializer' do + get_index(format: :json) + + expect(response).to have_gitlab_http_status(:ok) + expect(response).to match_response_schema('cluster_list') + end + context 'when page is specified' do let(:last_page) { Clusters::Cluster.instance_type.page.total_pages } + let(:total_count) { Clusters::Cluster.instance_type.page.total_count } before do - allow(Clusters::Cluster).to receive(:paginates_per).and_return(1) - create_list(:cluster, 2, :provided_by_gcp, :production_environment, :instance) + create_list(:cluster, 30, :provided_by_gcp, :production_environment, :instance) end it 'redirects to the page' do + expect(last_page).to be > 1 + get_index(page: last_page) expect(response).to have_gitlab_http_status(:ok) expect(assigns(:clusters).current_page).to eq(last_page) end + + it 'displays cluster list for associated page' do + expect(last_page).to be > 1 + + get_index(page: last_page, format: :json) + + expect(response).to have_gitlab_http_status(:ok) + expect(response.headers['X-Page'].to_i).to eq(last_page) + expect(response.headers['X-Total'].to_i).to eq(total_count) + end end end diff --git a/spec/controllers/groups/clusters_controller_spec.rb b/spec/controllers/groups/clusters_controller_spec.rb index 28a174560dd..1f2f6bd811b 100644 --- a/spec/controllers/groups/clusters_controller_spec.rb +++ b/spec/controllers/groups/clusters_controller_spec.rb @@ -32,7 +32,7 @@ describe Groups::ClustersController do create(:cluster, :disabled, :provided_by_gcp, :production_environment, cluster_type: :group_type, groups: [group]) end - it 'lists available clusters' do + it 'lists available clusters and renders html' do go expect(response).to have_gitlab_http_status(:ok) @@ -40,20 +40,39 @@ describe Groups::ClustersController do expect(assigns(:clusters)).to match_array([enabled_cluster, disabled_cluster]) end + it 'lists available clusters with json serializer' do + go(format: :json) + + expect(response).to have_gitlab_http_status(:ok) + expect(response).to match_response_schema('cluster_list') + end + context 'when page is specified' do let(:last_page) { group.clusters.page.total_pages } + let(:total_count) { group.clusters.page.total_count } before do - allow(Clusters::Cluster).to receive(:paginates_per).and_return(1) - create_list(:cluster, 2, :provided_by_gcp, :production_environment, cluster_type: :group_type, groups: [group]) + create_list(:cluster, 30, :provided_by_gcp, :production_environment, cluster_type: :group_type, groups: [group]) end it 'redirects to the page' do + expect(last_page).to be > 1 + go(page: last_page) expect(response).to have_gitlab_http_status(:ok) expect(assigns(:clusters).current_page).to eq(last_page) end + + it 'displays cluster list for associated page' do + expect(last_page).to be > 1 + + go(page: last_page, format: :json) + + expect(response).to have_gitlab_http_status(:ok) + expect(response.headers['X-Page'].to_i).to eq(last_page) + expect(response.headers['X-Total'].to_i).to eq(total_count) + end end end diff --git a/spec/controllers/oauth/authorized_applications_controller_spec.rb b/spec/controllers/oauth/authorized_applications_controller_spec.rb new file mode 100644 index 00000000000..32be6a3ddb7 --- /dev/null +++ b/spec/controllers/oauth/authorized_applications_controller_spec.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe Oauth::AuthorizedApplicationsController do + let(:user) { create(:user) } + let(:guest) { create(:user) } + let(:application) { create(:oauth_application, owner: guest) } + + before do + sign_in(user) + end + + describe 'GET #index' do + it 'responds with 404' do + get :index + + expect(response).to have_gitlab_http_status(:not_found) + end + end +end diff --git a/spec/controllers/projects/clusters_controller_spec.rb b/spec/controllers/projects/clusters_controller_spec.rb index 07733ec30d9..698a3773d59 100644 --- a/spec/controllers/projects/clusters_controller_spec.rb +++ b/spec/controllers/projects/clusters_controller_spec.rb @@ -26,7 +26,7 @@ describe Projects::ClustersController do let!(:enabled_cluster) { create(:cluster, :provided_by_gcp, projects: [project]) } let!(:disabled_cluster) { create(:cluster, :disabled, :provided_by_gcp, :production_environment, projects: [project]) } - it 'lists available clusters' do + it 'lists available clusters and renders html' do go expect(response).to have_gitlab_http_status(:ok) @@ -34,20 +34,39 @@ describe Projects::ClustersController do expect(assigns(:clusters)).to match_array([enabled_cluster, disabled_cluster]) end + it 'lists available clusters with json serializer' do + go(format: :json) + + expect(response).to have_gitlab_http_status(:ok) + expect(response).to match_response_schema('cluster_list') + end + context 'when page is specified' do let(:last_page) { project.clusters.page.total_pages } + let(:total_count) { project.clusters.page.total_count } before do - allow(Clusters::Cluster).to receive(:paginates_per).and_return(1) - create_list(:cluster, 2, :provided_by_gcp, :production_environment, projects: [project]) + create_list(:cluster, 30, :provided_by_gcp, :production_environment, projects: [project]) end it 'redirects to the page' do + expect(last_page).to be > 1 + go(page: last_page) expect(response).to have_gitlab_http_status(:ok) expect(assigns(:clusters).current_page).to eq(last_page) end + + it 'displays cluster list for associated page' do + expect(last_page).to be > 1 + + go(page: last_page, format: :json) + + expect(response).to have_gitlab_http_status(:ok) + expect(response.headers['X-Page'].to_i).to eq(last_page) + expect(response.headers['X-Total'].to_i).to eq(total_count) + end end end @@ -68,9 +87,11 @@ describe Projects::ClustersController do it 'is allowed for admin when admin mode enabled', :enable_admin_mode do expect { go }.to be_allowed_for(:admin) end + it 'is disabled for admin when admin mode disabled' do expect { go }.to be_denied_for(:admin) end + it { expect { go }.to be_allowed_for(:owner).of(project) } it { expect { go }.to be_allowed_for(:maintainer).of(project) } it { expect { go }.to be_denied_for(:developer).of(project) } diff --git a/spec/fixtures/api/schemas/cluster_list.json b/spec/fixtures/api/schemas/cluster_list.json new file mode 100644 index 00000000000..ece9542eb79 --- /dev/null +++ b/spec/fixtures/api/schemas/cluster_list.json @@ -0,0 +1,14 @@ +{ + "clusters": { + "type": "array", + "items": { + "cluster_type": "string", + "enabled": "boolean", + "environment_scope": "string", + "name": "string", + "path": "string", + "status": "string" + } + }, + "has_ancestor_clusters": { "type": ["boolean", "false"] } +} diff --git a/spec/frontend/alert_management/components/alert_management_detail_spec.js b/spec/frontend/alert_management/components/alert_management_detail_spec.js new file mode 100644 index 00000000000..48cdb7027b1 --- /dev/null +++ b/spec/frontend/alert_management/components/alert_management_detail_spec.js @@ -0,0 +1,34 @@ +import { shallowMount } from '@vue/test-utils'; +import AlertDetails from '~/alert_management/components/alert_details.vue'; + +describe('AlertDetails', () => { + let wrapper; + + function mountComponent() { + wrapper = shallowMount(AlertDetails); + } + + beforeEach(() => { + mountComponent(); + }); + + afterEach(() => { + if (wrapper) { + wrapper.destroy(); + } + }); + + describe('Alert details', () => { + it('renders a tab with overview information', () => { + expect(wrapper.find('[data-testid="overviewTab"]').exists()).toBe(true); + }); + + it('renders a tab with full alert information', () => { + expect(wrapper.find('[data-testid="fullDetailsTab"]').exists()).toBe(true); + }); + + it('renders alert details', () => { + expect(wrapper.find('[data-testid="startTimeItem"]').exists()).toBe(true); + }); + }); +}); diff --git a/spec/frontend/monitoring/components/dashboard_panel_spec.js b/spec/frontend/monitoring/components/dashboard_panel_spec.js index 1eec2980f19..ccc29623c31 100644 --- a/spec/frontend/monitoring/components/dashboard_panel_spec.js +++ b/spec/frontend/monitoring/components/dashboard_panel_spec.js @@ -18,6 +18,7 @@ import { singleStatMetricsResult, graphDataPrometheusQueryRangeMultiTrack, barMockData, + propsData, } from '../mock_data'; import { panelTypes } from '~/monitoring/constants'; @@ -60,6 +61,7 @@ describe('Dashboard Panel', () => { wrapper = shallowMount(DashboardPanel, { propsData: { graphData, + settingsPath: propsData.settingsPath, ...props, }, store, @@ -239,6 +241,7 @@ describe('Dashboard Panel', () => { describe('Edit custom metric dropdown item', () => { const findEditCustomMetricLink = () => wrapper.find({ ref: 'editMetricLink' }); + const mockEditPath = '/root/kubernetes-gke-project/prometheus/metrics/23/edit'; beforeEach(() => { createWrapper(); @@ -257,7 +260,7 @@ describe('Dashboard Panel', () => { metrics: [ { ...graphData.metrics[0], - edit_path: '/root/kubernetes-gke-project/prometheus/metrics/23/edit', + edit_path: mockEditPath, }, ], }, @@ -266,10 +269,11 @@ describe('Dashboard Panel', () => { return wrapper.vm.$nextTick(() => { expect(findEditCustomMetricLink().exists()).toBe(true); expect(findEditCustomMetricLink().text()).toBe('Edit metric'); + expect(findEditCustomMetricLink().attributes('href')).toBe(mockEditPath); }); }); - it('shows an "Edit metrics" link for a panel with multiple metrics', () => { + it('shows an "Edit metrics" link pointing to settingsPath for a panel with multiple metrics', () => { wrapper.setProps({ graphData: { ...graphData, @@ -288,6 +292,7 @@ describe('Dashboard Panel', () => { return wrapper.vm.$nextTick(() => { expect(findEditCustomMetricLink().text()).toBe('Edit metrics'); + expect(findEditCustomMetricLink().attributes('href')).toBe(propsData.settingsPath); }); }); }); @@ -396,6 +401,7 @@ describe('Dashboard Panel', () => { wrapper = shallowMount(DashboardPanel, { propsData: { clipboardText: exampleText, + settingsPath: propsData.settingsPath, graphData: { y_label: 'metric', ...graphData, @@ -445,6 +451,7 @@ describe('Dashboard Panel', () => { wrapper = shallowMount(DashboardPanel, { propsData: { graphData, + settingsPath: propsData.settingsPath, namespace: mockNamespace, }, store, @@ -529,12 +536,12 @@ describe('Dashboard Panel', () => { }); describe.each` - desc | metricsSavedToDb | propsData | isShown + desc | metricsSavedToDb | props | isShown ${'with permission and no metrics in db'} | ${[]} | ${{}} | ${false} ${'with permission and related metrics in db'} | ${[graphData.metrics[0].metricId]} | ${{}} | ${true} ${'without permission and related metrics in db'} | ${[graphData.metrics[0].metricId]} | ${{ prometheusAlertsAvailable: false }} | ${false} ${'with permission and unrelated metrics in db'} | ${['another_metric_id']} | ${{}} | ${false} - `('$desc', ({ metricsSavedToDb, isShown, propsData }) => { + `('$desc', ({ metricsSavedToDb, isShown, props }) => { const showsDesc = isShown ? 'shows' : 'does not show'; beforeEach(() => { @@ -542,7 +549,7 @@ describe('Dashboard Panel', () => { createWrapper({ alertsEndpoint: '/endpoint', prometheusAlertsAvailable: true, - ...propsData, + ...props, }); return wrapper.vm.$nextTick(); }); diff --git a/spec/frontend/pipelines/time_ago_spec.js b/spec/frontend/pipelines/time_ago_spec.js new file mode 100644 index 00000000000..1bd16182d47 --- /dev/null +++ b/spec/frontend/pipelines/time_ago_spec.js @@ -0,0 +1,67 @@ +import { shallowMount } from '@vue/test-utils'; +import TimeAgo from '~/pipelines/components/time_ago.vue'; + +describe('Timeago component', () => { + let wrapper; + + const createComponent = (props = {}) => { + wrapper = shallowMount(TimeAgo, { + propsData: { + ...props, + }, + data() { + return { + iconTimerSvg: `<svg></svg>`, + }; + }, + }); + }; + + afterEach(() => { + wrapper.destroy(); + wrapper = null; + }); + + describe('with duration', () => { + beforeEach(() => { + createComponent({ duration: 10, finishedTime: '' }); + }); + + it('should render duration and timer svg', () => { + expect(wrapper.find('.duration').exists()).toBe(true); + expect(wrapper.find('.duration svg').exists()).toBe(true); + }); + }); + + describe('without duration', () => { + beforeEach(() => { + createComponent({ duration: 0, finishedTime: '' }); + }); + + it('should not render duration and timer svg', () => { + expect(wrapper.find('.duration').exists()).toBe(false); + }); + }); + + describe('with finishedTime', () => { + beforeEach(() => { + createComponent({ duration: 0, finishedTime: '2017-04-26T12:40:23.277Z' }); + }); + + it('should render time and calendar icon', () => { + expect(wrapper.find('.finished-at').exists()).toBe(true); + expect(wrapper.find('.finished-at i.fa-calendar').exists()).toBe(true); + expect(wrapper.find('.finished-at time').exists()).toBe(true); + }); + }); + + describe('without finishedTime', () => { + beforeEach(() => { + createComponent({ duration: 0, finishedTime: '' }); + }); + + it('should not render time and calendar icon', () => { + expect(wrapper.find('.finished-at').exists()).toBe(false); + }); + }); +}); diff --git a/spec/helpers/application_helper_spec.rb b/spec/helpers/application_helper_spec.rb index e7d2a027640..d679768be6c 100644 --- a/spec/helpers/application_helper_spec.rb +++ b/spec/helpers/application_helper_spec.rb @@ -280,11 +280,16 @@ describe ApplicationHelper do end context 'when @project is set' do - it 'includes all possible body data elements and associates the project elements with project' do - project = create(:project) + let_it_be(:project) { create(:project, :repository) } + let_it_be(:user) { create(:user) } + before do assign(:project, project) + allow(helper).to receive(:current_user).and_return(nil) + end + it 'includes all possible body data elements and associates the project elements with project' do + expect(helper).to receive(:can?).with(nil, :download_code, project) expect(helper.body_data).to eq( { page: 'application', @@ -305,12 +310,11 @@ describe ApplicationHelper do context 'when params[:id] is present and the issue exsits and action_name is show' do it 'sets all project and id elements correctly related to the issue' do - issue = create(:issue) + issue = create(:issue, project: project) stub_controller_method(:action_name, 'show') stub_controller_method(:params, { id: issue.id }) - assign(:project, issue.project) - + expect(helper).to receive(:can?).with(nil, :download_code, project).and_return(false) expect(helper.body_data).to eq( { page: 'projects:issues:show', @@ -325,6 +329,15 @@ describe ApplicationHelper do end end end + + context 'when current_user has download_code permission' do + it 'returns find_file with the default branch' do + allow(helper).to receive(:current_user).and_return(user) + + expect(helper).to receive(:can?).with(user, :download_code, project).and_return(true) + expect(helper.body_data[:find_file]).to end_with(project.default_branch) + end + end end def stub_controller_method(method_name, value) diff --git a/spec/javascripts/pipelines/time_ago_spec.js b/spec/javascripts/pipelines/time_ago_spec.js deleted file mode 100644 index 42b34c82f89..00000000000 --- a/spec/javascripts/pipelines/time_ago_spec.js +++ /dev/null @@ -1,64 +0,0 @@ -import Vue from 'vue'; -import timeAgo from '~/pipelines/components/time_ago.vue'; - -describe('Timeago component', () => { - let TimeAgo; - beforeEach(() => { - TimeAgo = Vue.extend(timeAgo); - }); - - describe('with duration', () => { - it('should render duration and timer svg', () => { - const component = new TimeAgo({ - propsData: { - duration: 10, - finishedTime: '', - }, - }).$mount(); - - expect(component.$el.querySelector('.duration')).toBeDefined(); - expect(component.$el.querySelector('.duration svg')).toBeDefined(); - }); - }); - - describe('without duration', () => { - it('should not render duration and timer svg', () => { - const component = new TimeAgo({ - propsData: { - duration: 0, - finishedTime: '', - }, - }).$mount(); - - expect(component.$el.querySelector('.duration')).toBe(null); - }); - }); - - describe('with finishedTime', () => { - it('should render time and calendar icon', () => { - const component = new TimeAgo({ - propsData: { - duration: 0, - finishedTime: '2017-04-26T12:40:23.277Z', - }, - }).$mount(); - - expect(component.$el.querySelector('.finished-at')).toBeDefined(); - expect(component.$el.querySelector('.finished-at i.fa-calendar')).toBeDefined(); - expect(component.$el.querySelector('.finished-at time')).toBeDefined(); - }); - }); - - describe('without finishedTime', () => { - it('should not render time and calendar icon', () => { - const component = new TimeAgo({ - propsData: { - duration: 0, - finishedTime: '', - }, - }).$mount(); - - expect(component.$el.querySelector('.finished-at')).toBe(null); - }); - }); -}); diff --git a/spec/lib/gitlab/middleware/multipart_spec.rb b/spec/lib/gitlab/middleware/multipart_spec.rb index ec153e25d44..c99281ee12c 100644 --- a/spec/lib/gitlab/middleware/multipart_spec.rb +++ b/spec/lib/gitlab/middleware/multipart_spec.rb @@ -7,11 +7,11 @@ require 'tempfile' describe Gitlab::Middleware::Multipart do include_context 'multipart middleware context' - shared_examples_for 'multipart upload files' do + RSpec.shared_examples_for 'multipart upload files' do it 'opens top-level files' do Tempfile.open('top-level') do |tempfile| rewritten = { 'file' => tempfile.path } - in_params = { 'file.name' => original_filename, 'file.path' => tempfile.path, 'file.remote_id' => remote_id } + in_params = { 'file.name' => original_filename, 'file.path' => file_path, 'file.remote_id' => remote_id, 'file.size' => file_size } env = post_env(rewritten, in_params, Gitlab::Workhorse.secret, 'gitlab-workhorse') expect_uploaded_file(tempfile, %w(file)) @@ -22,8 +22,8 @@ describe Gitlab::Middleware::Multipart do it 'opens files one level deep' do Tempfile.open('one-level') do |tempfile| - in_params = { 'user' => { 'avatar' => { '.name' => original_filename, '.path' => tempfile.path, '.remote_id' => remote_id } } } rewritten = { 'user[avatar]' => tempfile.path } + in_params = { 'user' => { 'avatar' => { '.name' => original_filename, '.path' => file_path, '.remote_id' => remote_id, '.size' => file_size } } } env = post_env(rewritten, in_params, Gitlab::Workhorse.secret, 'gitlab-workhorse') expect_uploaded_file(tempfile, %w(user avatar)) @@ -34,7 +34,7 @@ describe Gitlab::Middleware::Multipart do it 'opens files two levels deep' do Tempfile.open('two-levels') do |tempfile| - in_params = { 'project' => { 'milestone' => { 'themesong' => { '.name' => original_filename, '.path' => tempfile.path, '.remote_id' => remote_id } } } } + in_params = { 'project' => { 'milestone' => { 'themesong' => { '.name' => original_filename, '.path' => file_path, '.remote_id' => remote_id, '.size' => file_size } } } } rewritten = { 'project[milestone][themesong]' => tempfile.path } env = post_env(rewritten, in_params, Gitlab::Workhorse.secret, 'gitlab-workhorse') @@ -44,13 +44,61 @@ describe Gitlab::Middleware::Multipart do end end - def expect_uploaded_file(tempfile, path, remote: false) + def expect_uploaded_file(tempfile, path) expect(app).to receive(:call) do |env| file = get_params(env).dig(*path) expect(file).to be_a(::UploadedFile) - expect(file.path).to eq(tempfile.path) expect(file.original_filename).to eq(original_filename) - expect(file.remote_id).to eq(remote_id) + + if remote_id + expect(file.remote_id).to eq(remote_id) + expect(file.path).to be_nil + else + expect(file.path).to eq(File.realpath(tempfile.path)) + expect(file.remote_id).to be_nil + end + end + end + end + + RSpec.shared_examples_for 'handling CI artifact upload' do + it 'uploads both file and metadata' do + Tempfile.open('file') do |file| + Tempfile.open('metadata') do |metadata| + rewritten = { 'file' => file.path, 'metadata' => metadata.path } + in_params = { 'file.name' => 'file.txt', 'file.path' => file_path, 'file.remote_id' => file_remote_id, 'file.size' => file_size, 'metadata.name' => 'metadata.gz' } + env = post_env(rewritten, in_params, Gitlab::Workhorse.secret, 'gitlab-workhorse') + + with_expected_uploaded_artifact_files(file, metadata) do |uploaded_file, uploaded_metadata| + expect(uploaded_file).to be_a(::UploadedFile) + expect(uploaded_file.original_filename).to eq('file.txt') + + if file_remote_id + expect(uploaded_file.remote_id).to eq(file_remote_id) + expect(uploaded_file.size).to eq(file_size) + expect(uploaded_file.path).to be_nil + else + expect(uploaded_file.path).to eq(File.realpath(file.path)) + expect(uploaded_file.remote_id).to be_nil + end + + expect(uploaded_metadata).to be_a(::UploadedFile) + expect(uploaded_metadata.original_filename).to eq('metadata.gz') + expect(uploaded_metadata.path).to eq(File.realpath(metadata.path)) + expect(uploaded_metadata.remote_id).to be_nil + end + + middleware.call(env) + end + end + end + + def with_expected_uploaded_artifact_files(file, metadata) + expect(app).to receive(:call) do |env| + file = get_params(env).dig('file') + metadata = get_params(env).dig('metadata') + + yield file, metadata end end end @@ -67,18 +115,65 @@ describe Gitlab::Middleware::Multipart do expect { middleware.call(env) }.to raise_error(JWT::InvalidIssuerError) end + context 'with invalid rewritten field' do + invalid_field_names = [ + '[file]', + ';file', + 'file]', + ';file]', + 'file]]', + 'file;;' + ] + + invalid_field_names.each do |invalid_field_name| + it "rejects invalid rewritten field name #{invalid_field_name}" do + env = post_env({ invalid_field_name => nil }, {}, Gitlab::Workhorse.secret, 'gitlab-workhorse') + + expect { middleware.call(env) }.to raise_error(RuntimeError, "invalid field: \"#{invalid_field_name}\"") + end + end + end + context 'with remote file' do let(:remote_id) { 'someid' } + let(:file_size) { 300 } + let(:file_path) { '' } + + it_behaves_like 'multipart upload files' + end + + context 'with remote file and a file path set' do + let(:remote_id) { 'someid' } + let(:file_size) { 300 } + let(:file_path) { 'not_a_valid_file_path' } # file path will come from the rewritten_fields it_behaves_like 'multipart upload files' end context 'with local file' do let(:remote_id) { nil } + let(:file_size) { nil } + let(:file_path) { 'not_a_valid_file_path' } # file path will come from the rewritten_fields it_behaves_like 'multipart upload files' end + context 'with remote CI artifact upload' do + let(:file_remote_id) { 'someid' } + let(:file_size) { 300 } + let(:file_path) { 'not_a_valid_file_path' } # file path will come from the rewritten_fields + + it_behaves_like 'handling CI artifact upload' + end + + context 'with local CI artifact upload' do + let(:file_remote_id) { nil } + let(:file_size) { nil } + let(:file_path) { 'not_a_valid_file_path' } # file path will come from the rewritten_fields + + it_behaves_like 'handling CI artifact upload' + end + it 'allows files in uploads/tmp directory' do with_tmp_dir('public/uploads/tmp') do |dir, env| expect(app).to receive(:call) do |env| diff --git a/spec/lib/uploaded_file_spec.rb b/spec/lib/uploaded_file_spec.rb index 25536c07dd9..39055a2479f 100644 --- a/spec/lib/uploaded_file_spec.rb +++ b/spec/lib/uploaded_file_spec.rb @@ -4,7 +4,7 @@ require 'spec_helper' describe UploadedFile do let(:temp_dir) { Dir.tmpdir } - let(:temp_file) { Tempfile.new("test", temp_dir) } + let(:temp_file) { Tempfile.new(%w[test test], temp_dir) } before do FileUtils.touch(temp_file) @@ -16,13 +16,14 @@ describe UploadedFile do describe ".from_params" do let(:upload_path) { nil } + let(:file_path_override) { nil } after do FileUtils.rm_r(upload_path) if upload_path end subject do - described_class.from_params(params, :file, upload_path) + described_class.from_params(params, :file, upload_path, file_path_override) end context 'when valid file is specified' do @@ -31,9 +32,7 @@ describe UploadedFile do { 'file.path' => temp_file.path } end - it "succeeds" do - is_expected.not_to be_nil - end + it { is_expected.not_to be_nil } it "generates filename from path" do expect(subject.original_filename).to eq(::File.basename(temp_file.path)) @@ -41,33 +40,153 @@ describe UploadedFile do end context 'all parameters are specified' do - let(:params) do - { 'file.path' => temp_file.path, - 'file.name' => 'dir/my file&.txt', - 'file.type' => 'my/type', - 'file.sha256' => 'sha256', - 'file.remote_id' => 'remote_id' } + RSpec.shared_context 'filepath override' do + let(:temp_file_override) { Tempfile.new(%w[override override], temp_dir) } + let(:file_path_override) { temp_file_override.path } + + before do + FileUtils.touch(temp_file_override) + end + + after do + FileUtils.rm_f(temp_file_override) + end end - it "succeeds" do - is_expected.not_to be_nil + RSpec.shared_examples 'using the file path' do |filename:, content_type:, sha256:, path_suffix:| + it 'sets properly the attributes' do + expect(subject.original_filename).to eq(filename) + expect(subject.content_type).to eq(content_type) + expect(subject.sha256).to eq(sha256) + expect(subject.remote_id).to be_nil + expect(subject.path).to end_with(path_suffix) + end + + it 'handles a blank path' do + params['file.path'] = '' + + # Not a real file, so can't determine size itself + params['file.size'] = 1.byte + + expect { described_class.from_params(params, :file, upload_path) } + .not_to raise_error + end end - it "generates filename from path" do - expect(subject.original_filename).to eq('my_file_.txt') - expect(subject.content_type).to eq('my/type') - expect(subject.sha256).to eq('sha256') - expect(subject.remote_id).to eq('remote_id') + RSpec.shared_examples 'using the remote id' do |filename:, content_type:, sha256:, size:, remote_id:| + it 'sets properly the attributes' do + expect(subject.original_filename).to eq(filename) + expect(subject.content_type).to eq('application/octet-stream') + expect(subject.sha256).to eq('sha256') + expect(subject.path).to be_nil + expect(subject.size).to eq(123456) + expect(subject.remote_id).to eq('1234567890') + end + end + + context 'with a filepath' do + let(:params) do + { 'file.path' => temp_file.path, + 'file.name' => 'dir/my file&.txt', + 'file.type' => 'my/type', + 'file.sha256' => 'sha256' } + end + + it { is_expected.not_to be_nil } + + it_behaves_like 'using the file path', + filename: 'my_file_.txt', + content_type: 'my/type', + sha256: 'sha256', + path_suffix: 'test' + end + + context 'with a filepath override' do + include_context 'filepath override' + + let(:params) do + { 'file.path' => temp_file.path, + 'file.name' => 'dir/my file&.txt', + 'file.type' => 'my/type', + 'file.sha256' => 'sha256' } + end + + it { is_expected.not_to be_nil } + + it_behaves_like 'using the file path', + filename: 'my_file_.txt', + content_type: 'my/type', + sha256: 'sha256', + path_suffix: 'override' end - it 'handles a blank path' do - params['file.path'] = '' + context 'with a remote id' do + let(:params) do + { + 'file.name' => 'dir/my file&.txt', + 'file.sha256' => 'sha256', + 'file.remote_url' => 'http://localhost/file', + 'file.remote_id' => '1234567890', + 'file.etag' => 'etag1234567890', + 'file.size' => '123456' + } + end + + it { is_expected.not_to be_nil } + + it_behaves_like 'using the remote id', + filename: 'my_file_.txt', + content_type: 'application/octet-stream', + sha256: 'sha256', + size: 123456, + remote_id: '1234567890' + end - # Not a real file, so can't determine size itself - params['file.size'] = 1.byte + context 'with a path and a remote id' do + let(:params) do + { + 'file.path' => temp_file.path, + 'file.name' => 'dir/my file&.txt', + 'file.sha256' => 'sha256', + 'file.remote_url' => 'http://localhost/file', + 'file.remote_id' => '1234567890', + 'file.etag' => 'etag1234567890', + 'file.size' => '123456' + } + end + + it { is_expected.not_to be_nil } + + it_behaves_like 'using the remote id', + filename: 'my_file_.txt', + content_type: 'application/octet-stream', + sha256: 'sha256', + size: 123456, + remote_id: '1234567890' + end - expect { described_class.from_params(params, :file, upload_path) } - .not_to raise_error + context 'with a path override and a remote id' do + include_context 'filepath override' + + let(:params) do + { + 'file.name' => 'dir/my file&.txt', + 'file.sha256' => 'sha256', + 'file.remote_url' => 'http://localhost/file', + 'file.remote_id' => '1234567890', + 'file.etag' => 'etag1234567890', + 'file.size' => '123456' + } + end + + it { is_expected.not_to be_nil } + + it_behaves_like 'using the remote id', + filename: 'my_file_.txt', + content_type: 'application/octet-stream', + sha256: 'sha256', + size: 123456, + remote_id: '1234567890' end end end diff --git a/spec/serializers/cluster_entity_spec.rb b/spec/serializers/cluster_entity_spec.rb index e3826a7221d..16247eef655 100644 --- a/spec/serializers/cluster_entity_spec.rb +++ b/spec/serializers/cluster_entity_spec.rb @@ -7,7 +7,7 @@ describe ClusterEntity do subject { described_class.new(cluster).as_json } context 'when provider type is gcp' do - let(:cluster) { create(:cluster, provider_type: :gcp, provider_gcp: provider) } + let(:cluster) { create(:cluster, :instance, provider_type: :gcp, provider_gcp: provider) } context 'when status is creating' do let(:provider) { create(:cluster_provider_gcp, :creating) } @@ -29,7 +29,7 @@ describe ClusterEntity do end context 'when provider type is user' do - let(:cluster) { create(:cluster, provider_type: :user) } + let(:cluster) { create(:cluster, :instance, provider_type: :user) } it 'has corresponded data' do expect(subject[:status]).to eq(:created) @@ -38,7 +38,7 @@ describe ClusterEntity do end context 'when no application has been installed' do - let(:cluster) { create(:cluster) } + let(:cluster) { create(:cluster, :instance) } subject { described_class.new(cluster).as_json[:applications]} diff --git a/spec/serializers/remote_mirror_entity_spec.rb b/spec/serializers/remote_mirror_entity_spec.rb index 5f4aac213be..27472c46436 100644 --- a/spec/serializers/remote_mirror_entity_spec.rb +++ b/spec/serializers/remote_mirror_entity_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' describe RemoteMirrorEntity do - let(:project) { create(:project, :repository, :remote_mirror) } + let(:project) { create(:project, :repository, :remote_mirror, url: "https://test:password@gitlab.com") } let(:remote_mirror) { project.remote_mirrors.first } let(:entity) { described_class.new(remote_mirror) } @@ -15,4 +15,9 @@ describe RemoteMirrorEntity do :ssh_known_hosts, :ssh_public_key, :ssh_known_hosts_fingerprints ) end + + it 'does not expose password information' do + expect(subject[:url]).not_to include('password') + expect(subject[:url]).to eq(remote_mirror.safe_url) + end end |