summaryrefslogtreecommitdiff
path: root/spec
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2020-04-30 21:09:47 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2020-04-30 21:09:47 +0000
commit3aeda4e6146bea1920c3283e98b01ca4fcf796a8 (patch)
treeb44e6298a749bd8a02283bc5867ab4a3269b62c3 /spec
parentadafb996ef88da50b30c737cdb8caee8307ec6d6 (diff)
downloadgitlab-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.rb25
-rw-r--r--spec/controllers/groups/clusters_controller_spec.rb25
-rw-r--r--spec/controllers/oauth/authorized_applications_controller_spec.rb21
-rw-r--r--spec/controllers/projects/clusters_controller_spec.rb27
-rw-r--r--spec/fixtures/api/schemas/cluster_list.json14
-rw-r--r--spec/frontend/alert_management/components/alert_management_detail_spec.js34
-rw-r--r--spec/frontend/monitoring/components/dashboard_panel_spec.js17
-rw-r--r--spec/frontend/pipelines/time_ago_spec.js67
-rw-r--r--spec/helpers/application_helper_spec.rb23
-rw-r--r--spec/javascripts/pipelines/time_ago_spec.js64
-rw-r--r--spec/lib/gitlab/middleware/multipart_spec.rb109
-rw-r--r--spec/lib/uploaded_file_spec.rb167
-rw-r--r--spec/serializers/cluster_entity_spec.rb6
-rw-r--r--spec/serializers/remote_mirror_entity_spec.rb7
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