Commit ce8a0b90 authored by GitLab Bot's avatar GitLab Bot

Add latest changes from gitlab-org/gitlab@master

parent dc889678
query mergeRequest($projectPath: ID!, $mergeRequestIID: String!) {
query mergeRequest($projectPath: ID!, $mergeRequestIID: ID!) {
project(fullPath: $projectPath) {
mergeRequest(iid: $mergeRequestIID) {
createdAt
......
query ($fullPath: ID!, $iid: String!) {
query ($fullPath: ID!, $iid: ID!) {
project (fullPath: $fullPath) {
issue (iid: $iid) {
iid
......
query ($fullPath: ID!, $iid: String!) {
query ($fullPath: ID!, $iid: ID!) {
project (fullPath: $fullPath) {
issue (iid: $iid) {
iid
......
......@@ -9,7 +9,7 @@ module Mutations
required: true,
description: "The project the issue to mutate is in"
argument :iid, GraphQL::STRING_TYPE,
argument :iid, GraphQL::ID_TYPE,
required: true,
description: "The iid of the issue to mutate"
......
......@@ -9,7 +9,7 @@ module Mutations
required: true,
description: "The project the merge request to mutate is in"
argument :iid, GraphQL::STRING_TYPE,
argument :iid, GraphQL::ID_TYPE,
required: true,
description: "The iid of the merge request to mutate"
......
......@@ -2,11 +2,11 @@
module Resolvers
class IssuesResolver < BaseResolver
argument :iid, GraphQL::STRING_TYPE,
argument :iid, GraphQL::ID_TYPE,
required: false,
description: 'IID of the issue. For example, "1"'
argument :iids, [GraphQL::STRING_TYPE],
argument :iids, [GraphQL::ID_TYPE],
required: false,
description: 'List of IIDs of issues. For example, [1, 2]'
argument :state, Types::IssuableStateEnum,
......
......@@ -2,11 +2,11 @@
module Resolvers
class MergeRequestsResolver < BaseResolver
argument :iid, GraphQL::STRING_TYPE,
argument :iid, GraphQL::ID_TYPE,
required: false,
description: 'The IID of the merge request, e.g., "1"'
argument :iids, [GraphQL::STRING_TYPE],
argument :iids, [GraphQL::ID_TYPE],
required: false,
description: 'The list of IIDs of issues, e.g., [1, 2]'
......
......@@ -11,7 +11,7 @@ module Types
field :id, GraphQL::ID_TYPE, null: false,
description: 'ID of the pipeline'
field :iid, GraphQL::STRING_TYPE, null: false,
field :iid, GraphQL::ID_TYPE, null: false,
description: 'Internal ID of the pipeline'
field :sha, GraphQL::STRING_TYPE, null: false,
......
......@@ -14,7 +14,7 @@ module Types
field :id, GraphQL::ID_TYPE, null: false,
description: 'ID of the merge request'
field :iid, GraphQL::STRING_TYPE, null: false,
field :iid, GraphQL::ID_TYPE, null: false,
description: 'Internal ID of the merge request'
field :title, GraphQL::STRING_TYPE, null: false,
description: 'Title of the merge request'
......
......@@ -31,7 +31,6 @@ module AnalyticsNavbarHelper
end
def cycle_analytics_navbar_link(project, current_user)
return unless Feature.enabled?(:analytics_pages_under_project_analytics_sidebar, project, default_enabled: true)
return unless project_nav_tab?(:cycle_analytics)
navbar_sub_item(
......@@ -43,7 +42,6 @@ module AnalyticsNavbarHelper
end
def repository_analytics_navbar_link(project, current_user)
return if Feature.disabled?(:analytics_pages_under_project_analytics_sidebar, project, default_enabled: true)
return if project.empty_repo?
navbar_sub_item(
......@@ -55,7 +53,6 @@ module AnalyticsNavbarHelper
end
def ci_cd_analytics_navbar_link(project, current_user)
return unless Feature.enabled?(:analytics_pages_under_project_analytics_sidebar, project, default_enabled: true)
return unless project_nav_tab?(:pipelines)
return unless project.feature_available?(:builds, current_user) || !project.empty_repo?
......
......@@ -130,7 +130,7 @@ class Namespace < ApplicationRecord
return unless host.ends_with?(gitlab_host)
name = host.delete_suffix(gitlab_host)
Namespace.find_by_path(name)
Namespace.where(parent_id: nil).find_by_path(name)
end
# overridden in ee
......
......@@ -164,6 +164,7 @@ class User < ApplicationRecord
has_one :status, class_name: 'UserStatus'
has_one :user_preference
has_one :user_detail
has_one :user_highest_role
#
# Validations
......
# frozen_string_literal: true
class UserHighestRole < ApplicationRecord
belongs_to :user, optional: false
validates :highest_access_level, allow_nil: true, inclusion: { in: Gitlab::Access.all_values }
end
......@@ -5,9 +5,8 @@ class AvatarUploader < GitlabUploader
include RecordsUploads::Concern
include ObjectStorage::Concern
prepend ObjectStorage::Extension::RecordsUploads
include UploadTypeCheck::Concern
check_upload_type extensions: AvatarUploader::SAFE_IMAGE_EXT
MIME_WHITELIST = %w[image/png image/jpeg image/gif image/bmp image/tiff image/vnd.microsoft.icon].freeze
def exists?
model.avatar.file && model.avatar.file.present?
......@@ -29,6 +28,10 @@ class AvatarUploader < GitlabUploader
super || 'avatar'
end
def content_type_whitelist
MIME_WHITELIST
end
private
def dynamic_segment
......
# frozen_string_literal: true
# Currently we run CarrierWave 1.3.1 which means we can not whitelist files
# by their content type through magic header parsing.
#
# This is a patch to hold us over until we get to CarrierWave 2 :) It's a mashup of
# CarrierWave's lib/carrierwave/uploader/content_type_whitelist.rb and
# lib/carrierwave/sanitized_file.rb
#
# Include this concern and add a content_type_whitelist method to get the same
# behavior as you would with CarrierWave 2.
#
# This is not an exact replacement as we don't override
# SanitizedFile#content_type but we do set the content_type attribute when we
# check the whitelist.
#
# Remove this after moving to CarrierWave 2, though on practical terms it shouldn't
# break anything if left for a while.
module ContentTypeWhitelist
module Concern
extend ActiveSupport::Concern
private
# CarrierWave calls this method as part of it's before :cache callbacks.
# Here we override and extend CarrierWave's method that does not parse the
# magic headers.
def check_content_type_whitelist!(new_file)
new_file.content_type = mime_magic_content_type(new_file.path)
if content_type_whitelist && !whitelisted_content_type?(new_file.content_type)
message = I18n.translate(:"errors.messages.content_type_whitelist_error", allowed_types: Array(content_type_whitelist).join(", "))
raise CarrierWave::IntegrityError, message
end
super(new_file)
end
def whitelisted_content_type?(content_type)
Array(content_type_whitelist).any? { |item| content_type =~ /#{item}/ }
end
def mime_magic_content_type(path)
if path
File.open(path) do |file|
MimeMagic.by_magic(file).try(:type) || 'invalid/invalid'
end
end
rescue Errno::ENOENT
nil
end
end
end
# frozen_string_literal: true
class FaviconUploader < AttachmentUploader
include UploadTypeCheck::Concern
EXTENSION_WHITELIST = %w[png ico].freeze
check_upload_type extensions: EXTENSION_WHITELIST
MIME_WHITELIST = %w[image/png image/vnd.microsoft.icon].freeze
def extension_whitelist
EXTENSION_WHITELIST
end
def content_type_whitelist
MIME_WHITELIST
end
private
def filename_for_different_format(filename, format)
......
# frozen_string_literal: true
class GitlabUploader < CarrierWave::Uploader::Base
include ContentTypeWhitelist::Concern
class_attribute :options
class << self
......
- should_display_analytics_pages_in_sidebar = Feature.enabled?(:analytics_pages_under_group_analytics_sidebar, @group, default_enabled: true)
- issues_count = group_issues_count(state: 'opened')
- merge_requests_count = group_merge_requests_count(state: 'opened')
......@@ -13,8 +12,7 @@
%ul.sidebar-top-level-items.qa-group-sidebar
- if group_sidebar_link?(:overview)
- paths = group_overview_nav_link_paths
- paths << 'contribution_analytics#show' unless should_display_analytics_pages_in_sidebar
= nav_link(path: paths, unless: -> { should_display_analytics_pages_in_sidebar && current_path?('groups/contribution_analytics#show') }, html_options: { class: 'home' }) do
= nav_link(path: paths, unless: -> { current_path?('groups/contribution_analytics#show') }, html_options: { class: 'home' }) do
= link_to group_path(@group) do
.nav-icon-container
= sprite_icon('home')
......@@ -45,19 +43,10 @@
%span
= _('Activity')
- unless should_display_analytics_pages_in_sidebar
- if group_sidebar_link?(:contribution_analytics)
= nav_link(path: 'contribution_analytics#show') do
= link_to group_contribution_analytics_path(@group), title: _('Contribution'), data: { placement: 'right', qa_selector: 'contribution_analytics_link' } do
%span
= _('Contribution')
= render_if_exists 'layouts/nav/group_insights_link'
= render_if_exists "layouts/nav/ee/epic_link", group: @group
- if group_sidebar_link?(:issues)
= nav_link(path: group_issues_sub_menu_items, unless: -> { should_display_analytics_pages_in_sidebar && current_path?('issues_analytics#show') }) do
= nav_link(path: group_issues_sub_menu_items, unless: -> { current_path?('issues_analytics#show') }) do
= link_to issues_group_path(@group), data: { qa_selector: 'group_issues_item' } do
.nav-icon-container
= sprite_icon('issues')
......@@ -84,9 +73,6 @@
%span
= boards_link_text
- unless should_display_analytics_pages_in_sidebar
= render_if_exists 'layouts/nav/issues_analytics_link'
- if group_sidebar_link?(:labels)
= nav_link(path: 'labels#index') do
= link_to group_labels_path(@group), title: _('Labels') do
......
- should_display_analytics_pages_in_sidebar = Feature.enabled?(:analytics_pages_under_project_analytics_sidebar, @project, default_enabled: true)
.nav-sidebar{ class: ("sidebar-collapsed-desktop" if collapsed_sidebar?) }
.nav-sidebar-inner-scroll
- can_edit = can?(current_user, :admin_project, @project)
......@@ -10,9 +8,7 @@
.sidebar-context-title
= @project.name
%ul.sidebar-top-level-items.qa-project-sidebar
- paths = sidebar_projects_paths
- paths << 'cycle_analytics#show' unless should_display_analytics_pages_in_sidebar
= nav_link(path: paths, html_options: { class: 'home' }) do
= nav_link(path: sidebar_projects_paths, html_options: { class: 'home' }) do
= link_to project_path(@project), class: 'shortcuts-project rspec-project-link', data: { qa_selector: 'project_link' } do
.nav-icon-container
= sprite_icon('home')
......@@ -39,17 +35,8 @@
%span= _('Releases')
- unless should_display_analytics_pages_in_sidebar
- if can?(current_user, :read_cycle_analytics, @project)
= nav_link(path: 'cycle_analytics#show') do
= link_to project_cycle_analytics_path(@project), title: _('Value Stream'), class: 'shortcuts-project-cycle-analytics' do
%span= _('Value Stream')
= render_if_exists 'layouts/nav/project_insights_link'
- if project_nav_tab? :files
= nav_link(controller: sidebar_repository_paths, unless: -> { should_display_analytics_pages_in_sidebar && current_path?('projects/graphs#charts') }) do
= nav_link(controller: sidebar_repository_paths, unless: -> { current_path?('projects/graphs#charts') }) do
= link_to project_tree_path(@project), class: 'shortcuts-tree qa-project-menu-repo' do
.nav-icon-container
= sprite_icon('doc-text')
......@@ -90,11 +77,6 @@
= link_to project_compare_index_path(@project, from: @repository.root_ref, to: current_ref) do
= _('Compare')
- unless should_display_analytics_pages_in_sidebar
= nav_link(path: 'graphs#charts') do
= link_to charts_project_graph_path(@project, current_ref) do
= _('Charts')
= render_if_exists 'projects/sidebar/repository_locked_files'
- if project_nav_tab? :issues
......@@ -178,7 +160,7 @@
= number_with_delimiter(@project.open_merge_requests_count)
- if project_nav_tab? :pipelines
= nav_link(controller: [:pipelines, :builds, :jobs, :pipeline_schedules, :artifacts], unless: -> { should_display_analytics_pages_in_sidebar && current_path?('projects/pipelines#charts') }) do
= nav_link(controller: [:pipelines, :builds, :jobs, :pipeline_schedules, :artifacts], unless: -> { current_path?('projects/pipelines#charts') }) do
= link_to project_pipelines_path(@project), class: 'shortcuts-pipelines qa-link-pipelines rspec-link-pipelines', data: { qa_selector: 'ci_cd_link' } do
.nav-icon-container
= sprite_icon('rocket')
......@@ -215,12 +197,6 @@
%span
= _('Schedules')
- if !should_display_analytics_pages_in_sidebar && @project.feature_available?(:builds, current_user) && !@project.empty_repo?
= nav_link(path: 'pipelines#charts') do
= link_to charts_project_pipelines_path(@project), title: _('Charts'), class: 'shortcuts-pipelines-charts' do
%span
= _('Charts')
= render_if_exists 'layouts/nav/sidebar/project_security_link' # EE-specific
- if project_nav_tab? :operations
......@@ -426,13 +402,6 @@
= link_to project_network_path(@project, current_ref), title: _('Network'), class: 'shortcuts-network' do
= _('Graph')
- unless should_display_analytics_pages_in_sidebar
-# Shortcut to Repository > Charts (formerly, top-nav item "Graphs")
- unless @project.empty_repo?
%li.hidden
= link_to charts_project_graph_path(@project, current_ref), title: _('Charts'), class: 'shortcuts-repository-charts' do
= _('Charts')
-# Shortcut to Issues > New Issue
- if project_nav_tab?(:issues)
%li.hidden
......
This diff is collapsed.
......@@ -8,7 +8,7 @@ module WorkerAttributes
VALID_RESOURCE_BOUNDARIES = [:memory, :cpu, :unknown].freeze
# Urgencies that workers can declare through the `urgencies` attribute
VALID_URGENCIES = [:high, :default, :none].freeze
VALID_URGENCIES = [:high, :low, :throttled].freeze
NAMESPACE_WEIGHTS = {
auto_devops: 2,
......@@ -65,7 +65,7 @@ module WorkerAttributes
end
def get_urgency
worker_attributes[:urgency] || :default
worker_attributes[:urgency] || :low
end
# Set this attribute on a job when it will call to services outside of the
......
......@@ -26,8 +26,8 @@ class ErrorTrackingIssueLinkWorker # rubocop:disable Scalability/IdempotentWorke
logger.info("Linking Sentry issue #{sentry_issue_id} to GitLab issue #{issue.id}")
sentry_client.create_issue_link(integration_id, sentry_issue_id, issue)
rescue Sentry::Client::Error
logger.info("Failed to link Sentry issue #{sentry_issue_id} to GitLab issue #{issue.id}")
rescue Sentry::Client::Error => e
logger.info("Failed to link Sentry issue #{sentry_issue_id} to GitLab issue #{issue.id} with error: #{e.message}")
end
end
......
......@@ -18,21 +18,7 @@ class UpdateMergeRequestsWorker # rubocop:disable Scalability/IdempotentWorker
user = User.find_by(id: user_id)
return unless user
# TODO: remove this benchmarking when we have rich logging
time = Benchmark.measure do
MergeRequests::RefreshService.new(project, user).execute(oldrev, newrev, ref)
end
args_log = [
"elapsed=#{time.real}",
"project_id=#{project_id}",
"user_id=#{user_id}",
"oldrev=#{oldrev}",
"newrev=#{newrev}",
"ref=#{ref}"
].join(',')
Rails.logger.info("UpdateMergeRequestsWorker#perform #{args_log}") if time.real > LOG_TIME_THRESHOLD # rubocop:disable Gitlab/RailsLogger
MergeRequests::RefreshService.new(project, user).execute(oldrev, newrev, ref)
end
# rubocop: enable CodeReuse/ActiveRecord
end
---
title: Replace avatar and favicon upload type consistency validation with content whitelist validation
merge_request: 25401
author:
type: changed
---
title: Introduce database table for user highest roles
merge_request: 26987
author:
type: added
......@@ -6,8 +6,8 @@ en:
carrierwave_download_error: could not be downloaded
extension_whitelist_error: "You are not allowed to upload %{extension} files, allowed types: %{allowed_types}"
extension_blacklist_error: "You are not allowed to upload %{extension} files, prohibited types: %{prohibited_types}"
content_type_whitelist_error: "You are not allowed to upload %{content_type} files"
content_type_blacklist_error: "You are not allowed to upload %{content_type} files"
content_type_whitelist_error: "file format is not supported. Please try one of the following supported formats: %{allowed_types}"
content_type_blacklist_error: "You are not allowed to upload %{content_type} files, prohibited types: %{allowed_types}"
rmagick_processing_error: "Failed to manipulate with rmagick, maybe it is not an image?"
mini_magick_processing_error: "Failed to manipulate with MiniMagick, maybe it is not an image? Original Error: %{e}"
min_size_error: "File size should be greater than %{min_size}"
......
# frozen_string_literal: true
class CreateUserHighestRoles < ActiveRecord::Migration[6.0]
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
def up
with_lock_retries do
create_table :user_highest_roles, id: false do |t|
t.datetime_with_timezone :updated_at, null: false
t.references :user, primary_key: true, default: nil, index: false, foreign_key: { on_delete: :cascade }
t.integer :highest_access_level
t.index [:user_id, :highest_access_level]
end
end
end
def down
with_lock_retries do
drop_table :user_highest_roles
end
end
end
......@@ -4251,6 +4251,12 @@ ActiveRecord::Schema.define(version: 2020_03_11_165635) do
t.index ["user_id"], name: "index_user_details_on_user_id", unique: true
end
create_table "user_highest_roles", primary_key: "user_id", id: :bigint, default: nil, force: :cascade do |t|
t.datetime_with_timezone "updated_at", null: false
t.integer "highest_access_level"
t.index ["user_id", "highest_access_level"], name: "index_user_highest_roles_on_user_id_and_highest_access_level"
end
create_table "user_interacted_projects", id: false, force: :cascade do |t|
t.integer "user_id", null: false
t.integer "project_id", null: false
......@@ -5135,6 +5141,7 @@ ActiveRecord::Schema.define(version: 2020_03_11_165635) do
add_foreign_key "user_callouts", "users", on_delete: :cascade
add_foreign_key "user_custom_attributes", "users", on_delete: :cascade
add_foreign_key "user_details", "users", on_delete: :cascade
add_foreign_key "user_highest_roles", "users", on_delete: :cascade
add_foreign_key "user_interacted_projects", "projects", name: "fk_722ceba4f7", on_delete: :cascade
add_foreign_key "user_interacted_projects", "users", name: "fk_0894651f08", on_delete: :cascade
add_foreign_key "user_preferences", "users", on_delete: :cascade
......
......@@ -30,7 +30,7 @@ This configuration is supported in [GitLab Starter, Premium and Ultimate](https:
References:
- [Installation Docs](../../install/README.html)
- [Installation Docs](../../install/README.md)
- [Backup/Restore Docs](https://docs.gitlab.com/omnibus/settings/backups.html#backup-and-restore-omnibus-gitlab-configuration)
### Level 2: Multiple application servers
......@@ -68,7 +68,7 @@ This configuration is supported in [GitLab Premium and Ultimate](https://about.g
References:
- [Geo Documentation](../../gitlab-geo/README.html)
- [GitLab Geo with a highly available configuration](../geo/replication/high_availability.html)
- [GitLab Geo with a highly available configuration](../geo/replication/high_availability.md)
## Recommended setups based on number of users
......
......@@ -130,7 +130,7 @@ following attributes:
- `has_external_dependencies` - whether or not the queue connects to external
services. For example, all importers have this set to `true`.
- `urgency` - how important it is that this queue's jobs run
quickly. Can be `high`, `default`, or `none`. For example, the
quickly. Can be `high`, `low`, or `throttled`. For example, the
`authorized_projects` queue is used to refresh user permissions, and
is high urgency.
- `name` - the queue name. The other attributes are typically more useful as
......
......@@ -3757,7 +3757,7 @@ input IssueSetConfidentialInput {
"""
The iid of the issue to mutate
"""
iid: String!
iid: ID!
"""
The project the issue to mutate is in
......@@ -3802,7 +3802,7 @@ input IssueSetDueDateInput {
"""
The iid of the issue to mutate
"""
iid: String!
iid: ID!
"""
The project the issue to mutate is in
......@@ -3842,7 +3842,7 @@ input IssueSetWeightInput {
"""
The iid of the issue to mutate
"""
iid: String!
iid: ID!
"""
The project the issue to mutate is in
......@@ -4150,7 +4150,7 @@ type MergeRequest implements Noteable {
"""
Internal ID of the merge request
"""
iid: String!
iid: ID!
"""
Commit SHA of the merge request if merge is in progress
......@@ -4550,7 +4550,7 @@ input MergeRequestSetAssigneesInput {
"""
The iid of the merge request to mutate
"""
iid: String!
iid: ID!
"""
The operation to perform. Defaults to REPLACE.
......@@ -4595,7 +4595,7 @@ input MergeRequestSetLabelsInput {
"""
The iid of the merge request to mutate
"""
iid: String!
iid: ID!
"""
The Label IDs to set. Replaces existing labels by default.
......@@ -4645,7 +4645,7 @@ input MergeRequestSetLockedInput {
"""
The iid of the merge request to mutate
"""
iid: String!
iid: ID!
"""
Whether or not to lock the merge request.
......@@ -4690,7 +4690,7 @@ input MergeRequestSetMilestoneInput {
"""
The iid of the merge request to mutate
"""
iid: String!
iid: ID!
"""
The milestone to assign to the merge request.
......@@ -4735,7 +4735,7 @@ input MergeRequestSetSubscriptionInput {
"""
The iid of the merge request to mutate
"""
iid: String!
iid: ID!
"""
The project the merge request to mutate is in
......@@ -4780,7 +4780,7 @@ input MergeRequestSetWipInput {
"""
The iid of the merge request to mutate
"""
iid: String!
iid: ID!
"""
The project the merge request to mutate is in
......@@ -5351,7 +5351,7 @@ type Pipeline {
"""
Internal ID of the pipeline
"""
iid: String!
iid: ID!
"""
SHA of the pipeline's commit
......@@ -5629,12 +5629,12 @@ type Project {
"""
IID of the issue. For example, "1"
"""
iid: String
iid: ID
"""
List of IIDs of issues. For example, [1, 2]
"""
iids: [String!]
iids: [ID!]
"""
Labels applied to this issue
......@@ -5724,12 +5724,12 @@ type Project {
"""
IID of the issue. For example, "1"
"""
iid: String
iid: ID
"""
List of IIDs of issues. For example, [1, 2]
"""
iids: [String!]
iids: [ID!]
"""
Labels applied to this issue
......@@ -5799,12 +5799,12 @@ type Project {
"""
The IID of the merge request, e.g., "1"
"""
iid: String
iid: ID
"""
The list of IIDs of issues, e.g., [1, 2]
"""
iids: [String!]
iids: [ID!]
): MergeRequest
"""
......@@ -5829,12 +5829,12 @@ type Project {
"""
The IID of the merge request, e.g., "1"
"""
iid: String
iid: ID
"""
The list of IIDs of issues, e.g., [1, 2]
"""
iids: [String!]
iids: [ID!]
"""
Returns the last _n_ elements from the list.
......@@ -8112,7 +8112,7 @@ input UpdateIssueInput {
"""
The iid of the issue to mutate
"""
iid: String!
iid: ID!
"""
The project the issue to mutate is in
......
......@@ -702,7 +702,7 @@
"description": "IID of the issue. For example, \"1\"",
"type": {
"kind": "SCALAR",
"name": "String",
"name": "ID",
"ofType": null
},
"defaultValue": null
......@@ -718,7 +718,7 @@
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "String",
"name": "ID",
"ofType": null
}
}
......@@ -881,7 +881,7 @@
"description": "IID of the issue. For example, \"1\"",
"type": {
"kind": "SCALAR",
"name": "String",
"name": "ID",
"ofType": null
},
"defaultValue": null
......@@ -897,7 +897,7 @@
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "String",
"name": "ID",
"ofType": null
}
}
......@@ -1156,7 +1156,7 @@
"description": "The IID of the merge request, e.g., \"1\"",
"type": {
"kind": "SCALAR",
"name": "String",
"name": "ID",
"ofType": null
},
"defaultValue": null
......@@ -1172,7 +1172,7 @@
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "String",
"name": "ID",
"ofType": null
}
}
......@@ -1197,7 +1197,7 @@
"description": "The IID of the merge request, e.g., \"1\"",
"type": {
"kind": "SCALAR",
"name": "String",
"name": "ID",
"ofType": null
},
"defaultValue": null
......@@ -1213,7 +1213,7 @@
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "String",
"name": "ID",
"ofType": null
}
}
......@@ -14425,7 +14425,7 @@
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "String",
"name": "ID",
"ofType": null
}
},
......@@ -16204,7 +16204,7 @@
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "String",
"name": "ID",
"ofType": null
}
},
......@@ -20999,7 +20999,7 @@
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "String",
"name": "ID",
"ofType": null
}
},
......@@ -21129,7 +21129,7 @@
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "String",
"name": "ID",
"ofType": null
}
},
......@@ -21259,7 +21259,7 @@
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "String",
"name": "ID",
"ofType": null
}
},
......@@ -21425,7 +21425,7 @@
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "String",
"name": "ID",
"ofType": null
}
},
......@@ -21602,7 +21602,7 @@
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "String",
"name": "ID",
"ofType": null
}
},
......@@ -21732,7 +21732,7 @@
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "String",
"name": "ID",
"ofType": null
}
},
......@@ -21858,7 +21858,7 @@
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "String",
"name": "ID",
"ofType": null
}
},
......@@ -21988,7 +21988,7 @@
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "String",
"name": "ID",
"ofType": null
}
},
......@@ -22118,7 +22118,7 @@
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "String",
"name": "ID",
"ofType": null
}
},
......@@ -24554,7 +24554,7 @@
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "String",
"name": "ID",
"ofType": null
}
},
......
......@@ -630,7 +630,7 @@ Autogenerated return type of MarkAsSpamSnippet
| `forceRemoveSourceBranch` | Boolean | Indicates if the project settings will lead to source branch deletion after merge |
| `headPipeline` | Pipeline | The pipeline running on the branch HEAD of the merge request |
| `id` | ID! | ID of the merge request |
| `iid` | String! | Internal ID of the merge request |
| `iid` | ID! | Internal ID of the merge request |
| `inProgressMergeCommitSha` | String | Commit SHA of the merge request if merge is in progress |
| `mergeCommitMessage` **{warning-solid}** | String | **Deprecated:** Renamed to defaultMergeCommitMessage |
| `mergeCommitSha` | String | SHA of the merge request commit (set once merged) |
......@@ -834,7 +834,7 @@ Information about pagination in a connection.
| `duration` | Int | Duration of the pipeline in seconds |
| `finishedAt` | Time | Timestamp of the pipeline's completion |
| `id` | ID! | ID of the pipeline |
| `iid` | String! | Internal ID of the pipeline |
| `iid` | ID! | Internal ID of the pipeline |
| `sha` | String! | SHA of the pipeline's commit |
| `startedAt` | Time | Timestamp when the pipeline was started |
| `status` | PipelineStatusEnum! | Status of the pipeline (CREATED, WAITING_FOR_RESOURCE, PREPARING, PENDING, RUNNING, FAILED, SUCCESS, CANCELED, SKIPPED, MANUAL, SCHEDULED) |
......
......@@ -541,7 +541,7 @@ argument :project_path, GraphQL::ID_TYPE,
required: true,
description: "The project the merge request to mutate is in"
argument :iid, GraphQL::STRING_TYPE,
argument :iid, GraphQL::ID_TYPE,
required: true,
description: "The iid of the merge request to mutate"
......
......@@ -124,13 +124,13 @@ Consider skipping the cop if you're not confident your job can safely run multip
## Job urgency
Jobs can have an `urgency` attribute set, which can be `:high`,
`:default`, or `:none`. These have the below targets:
`:low`, or `:throttled`. These have the below targets:
| **Urgency** | **Queue Scheduling Target** | **Execution Latency Requirement** |
|-------------|-----------------------------|------------------------------------|
| `:high` | 100 milliseconds | p50 of 1 second, p99 of 10 seconds |
| `:default` | 1 minute | Maximum run time of 1 hour |
| `:none` | None | Maximum run time of 1 hour |
| **Urgency** | **Queue Scheduling Target** | **Execution Latency Requirement** |
|--------------|-----------------------------|------------------------------------|
| `:high` | 100 milliseconds | p50 of 1 second, p99 of 10 seconds |
| `:low` | 1 minute | Maximum run time of 1 hour |
| `:throttled` | None | Maximum run time of 1 hour |
To set a job's urgency, use the `urgency` class method:
......@@ -175,7 +175,7 @@ these jobs also have very strict execution duration requirements:
If a worker cannot meet these expectations, then it cannot be treated as a
`urgency :high` worker: consider redesigning the worker, or splitting the
work between two different workers, one with `urgency :high` code that
executes quickly, and the other with `urgency :default`, which has no
executes quickly, and the other with `urgency :low`, which has no
execution latency requirements (but also has lower scheduling targets).
## Jobs with External Dependencies
......
......@@ -6,6 +6,7 @@ module Gitlab
module Test
class Junit
JunitParserError = Class.new(Gitlab::Ci::Parsers::ParserError)
ATTACHMENT_TAG_REGEX = /\[\[ATTACHMENT\|(?<path>.+?)\]\]/.freeze
def parse!(xml_data, test_suite)
root = Hash.from_xml(xml_data)
......@@ -49,6 +50,7 @@ module Gitlab
if data['failure']
status = ::Gitlab::Ci::Reports::TestCase::STATUS_FAILED
system_output = data['failure']
attachment = attachment_path(data['system_out'])
elsif data['error']
status = ::Gitlab::Ci::Reports::TestCase::STATUS_ERROR
system_output = data['error']
......@@ -63,9 +65,17 @@ module Gitlab
file: data['file'],
execution_time: data['time'],
status: status,
system_output: system_output
system_output: system_output,
attachment: attachment
)
end
def attachment_path(data)
return unless data
matches = data.match(ATTACHMENT_TAG_REGEX)
matches[:path] if matches
end
end
end
end
......
......@@ -10,9 +10,9 @@ module Gitlab
STATUS_ERROR = 'error'
STATUS_TYPES = [STATUS_SUCCESS, STATUS_FAILED, STATUS_SKIPPED, STATUS_ERROR].freeze
attr_reader :name, :classname, :execution_time, :status, :file, :system_output, :stack_trace, :key
attr_reader :name, :classname, :execution_time, :status, :file, :system_output, :stack_trace, :key, :attachment
def initialize(name:, classname:, execution_time:, status:, file: nil, system_output: nil, stack_trace: nil)
def initialize(name:, classname:, execution_time:, status:, file: nil, system_output: nil, stack_trace: nil, attachment: nil)
@name = name
@classname = classname
@file = file
......@@ -21,6 +21,11 @@ module Gitlab
@system_output = system_output
@stack_trace = stack_trace
@key = sanitize_key_name("#{classname}_#{name}")
@attachment = attachment
end
def has_attachment?
attachment.present?
end
private
......
......@@ -3406,9 +3406,6 @@ msgstr ""
msgid "Changing group path can have unintended side effects."
msgstr ""
msgid "Charts"
msgstr ""
msgid "Charts can't be displayed as the request for data has timed out. %{documentationLink}"
msgstr ""
......@@ -19917,10 +19914,10 @@ msgstr ""
msgid "There was an error fetching median data for stages"
msgstr ""
msgid "There was an error fetching the Designs"
msgid "There was an error fetching the Node's Groups"
msgstr ""
msgid "There was an error fetching the Node's Groups"
msgid "There was an error fetching the designs"
msgstr ""
msgid "There was an error fetching the environments information."
......@@ -19965,7 +19962,7 @@ msgstr ""
msgid "There was an error subscribing to this label."
msgstr ""
msgid "There was an error syncing the Design Repositories."
msgid "There was an error syncing the designs."
msgstr ""
msgid "There was an error trying to validate your query"
......
......@@ -10,7 +10,6 @@ module QA
element :group_settings_item
element :group_members_item
element :general_settings_link
element :contribution_analytics_link
end
view 'app/views/layouts/nav/sidebar/_analytics_links.html.haml' do
......
# frozen_string_literal: true
FactoryBot.define do
factory :test_case, class: 'Gitlab::Ci::Reports::TestCase' do
name { "test-1" }
classname { "trace" }
file { "spec/trace_spec.rb" }
execution_time { 1.23 }
status { "success" }
system_output { nil }
attachment { nil }
trait :with_attachment do
attachment { "some/path.png" }
end
skip_create
initialize_with do
new(
name: name,
classname: classname,
file: file,
execution_time: execution_time,
status: status,
system_output: system_output,
attachment: attachment
)
end
end
end
# frozen_string_literal: true
FactoryBot.define do
factory :user_highest_role do
user
end
end
......@@ -230,7 +230,7 @@ describe 'Merge request > User creates image diff notes', :js do
it_behaves_like 'onion skin'
end
describe 'swipe view' do
describe 'swipe view', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/209999' do
before do
switch_to_swipe_view
end
......
......@@ -7,8 +7,6 @@ describe 'Project active tab' do
let(:project) { create(:project, :repository) }
before do
stub_feature_flags(analytics_pages_under_project_analytics_sidebar: { enabled: false, thing: project })
project.add_maintainer(user)
sign_in(user)
end
......@@ -45,7 +43,7 @@ describe 'Project active tab' do
it_behaves_like 'page has active tab', 'Repository'
%w(Files Commits Graph Compare Charts Branches Tags).each do |sub_menu|
%w(Files Commits Graph Compare Branches Tags).each do |sub_menu|
context "on project Repository/#{sub_menu}" do
before do
click_tab(sub_menu)
......@@ -124,29 +122,23 @@ describe 'Project active tab' do
end
end
context 'when `analytics_pages_under_project_analytics_sidebar` feature flag is enabled' do
context 'on project Analytics' do
before do
stub_feature_flags(analytics_pages_under_project_analytics_sidebar: { enabled: true, thing: project })
visit charts_project_graph_path(project, 'master')
end
context 'on project Analytics' do
before do
visit charts_project_graph_path(project, 'master')
end
context 'on project Analytics/Repository Analytics' do
it_behaves_like 'page has active tab', _('Analytics')
it_behaves_like 'page has active sub tab', _('Repository')
end
context 'on project Analytics/Repository Analytics' do
it_behaves_like 'page has active tab', _('Analytics')
it_behaves_like 'page has active sub tab', _('Repository')
context 'on project Analytics/Cycle Analytics' do
before do
click_tab(_('CI / CD'))
end
context 'on project Analytics/Cycle Analytics' do
before do
click_tab(_('CI / CD'))
end
it_behaves_like 'page has active tab', _('Analytics')
it_behaves_like 'page has active sub tab', _('CI / CD')
end
it_behaves_like 'page has active tab', _('Analytics')
it_behaves_like 'page has active sub tab', _('CI / CD')
end
end
end
......@@ -7,8 +7,6 @@ describe 'User uses shortcuts', :js do
let(:user) { create(:user) }
before do
stub_feature_flags(analytics_pages_under_project_analytics_sidebar: { enabled: false, thing: project })
project.add_maintainer(user)
sign_in(user)
......@@ -119,8 +117,8 @@ describe 'User uses shortcuts', :js do
find('body').native.send_key('g')
find('body').native.send_key('d')
expect(page).to have_active_navigation('Repository')
expect(page).to have_active_sub_navigation('Charts')
expect(page).to have_active_navigation(_('Analytics'))
expect(page).to have_active_sub_navigation(_('Repository'))
end
end
......@@ -211,18 +209,4 @@ describe 'User uses shortcuts', :js do
expect(page).to have_active_navigation('Wiki')
end
end
context 'when `analytics_pages_under_project_analytics_sidebar` feature flag is enabled' do
before do
stub_feature_flags(analytics_pages_under_project_analytics_sidebar: { enabled: true, thing: project })
end
it 'redirects to the repository charts page' do
find('body').native.send_key('g')
find('body').native.send_key('d')
expect(page).to have_active_navigation(_('Analytics'))
expect(page).to have_active_sub_navigation(_('Repository'))
end
end
end
......@@ -205,6 +205,75 @@ describe Gitlab::Ci::Parsers::Test::Junit do
end
end
context 'when data contains an attachment tag' do
let(:junit) do
<<~EOF
<testsuites>
<testsuite>
<testcase classname='Calculator' name='sumTest1' time='0.01'>
<failure>Some failure</failure>
<system-out>[[ATTACHMENT|some/path.png]]</system-out>
</testcase>
</testsuite>
</testsuites>
EOF
end
it 'add attachment to a test case' do
expect { subject }.not_to raise_error
expect(test_cases[0].has_attachment?).to be_truthy
expect(test_cases[0].attachment).to eq("some/path.png")
end
end
context 'when data contains multiple attachments tag' do
let(:junit) do
<<~EOF
<testsuites>
<testsuite>
<testcase classname='Calculator' name='sumTest1' time='0.01'>
<failure>Some failure</failure>
<system-out>
[[ATTACHMENT|some/path.png]]
[[ATTACHMENT|some/path.html]]
</system-out>
</testcase>
</testsuite>
</testsuites>
EOF
end
it 'adds the first match attachment to a test case' do
expect { subject }.not_to raise_error
expect(test_cases[0].has_attachment?).to be_truthy
expect(test_cases[0].attachment).to eq("some/path.png")
end
end
context 'when data does not match attachment tag regex' do
let(:junit) do
<<~EOF
<testsuites>
<testsuite>
<testcase classname='Calculator' name='sumTest1' time='0.01'>
<failure>Some failure</failure>
<system-out>[[attachment]some/path.png]]</system-out>
</testcase>
</testsuite>
</testsuites>
EOF
end
it 'does not add attachment to a test case' do
expect { subject }.not_to raise_error
expect(test_cases[0].has_attachment?).to be_falsy
expect(test_cases[0].attachment).to be_nil
end
end
private
def flattened_test_cases(test_suite)
......
......@@ -88,5 +88,17 @@ describe Gitlab::Ci::Reports::TestCase do
expect { test_case }.to raise_error(ArgumentError)
end
end
context 'when attachment is present' do
let(:attachment_test_case) { build(:test_case, :with_attachment) }
it "initializes the attachment if present" do
expect(attachment_test_case.attachment).to eq("some/path.png")
end
it '#has_attachment?' do
expect(attachment_test_case.has_attachment?).to be_truthy
end
end
end
end
......@@ -124,7 +124,7 @@ describe Gitlab::SidekiqConfig::CliMethods do
name: 'a',
feature_category: :category_a,
has_external_dependencies: false,
urgency: :default,
urgency: :low,
resource_boundary: :cpu
},
{
......@@ -145,7 +145,7 @@ describe Gitlab::SidekiqConfig::CliMethods do
name: 'c',
feature_category: :category_c,
has_external_dependencies: false,
urgency: :none,
urgency: :throttled,
resource_boundary: :memory
}
]
......@@ -168,9 +168,9 @@ describe Gitlab::SidekiqConfig::CliMethods do
# urgency
'urgency=high' | %w(a:2 b)
'urgency=default' | %w(a)
'urgency=high,default,none' | %w(a a:2 b c)
'urgency=default|urgency=none' | %w(a c)
'urgency=low' | %w(a)
'urgency=high,low,throttled' | %w(a a:2 b c)
'urgency=low|urgency=throttled' | %w(a c)
'urgency!=high' | %w(a c)
# name
......
......@@ -88,7 +88,7 @@ describe Gitlab::SidekiqConfig::Worker do
attributes_a = {
feature_category: :source_code_management,
has_external_dependencies: false,
urgency: :default,
urgency: :low,
resource_boundary: :memory,
weight: 2,
idempotent: true
......
......@@ -9,7 +9,7 @@ describe Gitlab::SidekiqMiddleware::ClientMetrics do
let(:queue) { :test }
let(:worker_class) { worker.class }
let(:job) { {} }
let(:default_labels) { { queue: queue.to_s, boundary: "", external_dependencies: "no", feature_category: "", urgency: "default" } }
let(:default_labels) { { queue: queue.to_s, boundary: "", external_dependencies: "no", feature_category: "", urgency: "low" } }
shared_examples "a metrics client middleware" do
context "with mocked prometheus" do
......@@ -80,8 +80,8 @@ describe Gitlab::SidekiqMiddleware::ClientMetrics do
context "no urgency" do
it_behaves_like "a metrics client middleware" do
let(:urgency) { :none }
let(:labels) { default_labels.merge(urgency: "none") }
let(:urgency) { :throttled }
let(:labels) { default_labels.merge(urgency: "throttled") }
end
end
......
......@@ -11,7 +11,7 @@ describe Gitlab::SidekiqMiddleware::ServerMetrics do
let(:job) { {} }
let(:job_status) { :done }
let(:labels_with_job_status) { labels.merge(job_status: job_status.to_s) }
let(:default_labels) { { queue: queue.to_s, boundary: "", external_dependencies: "no", feature_category: "", urgency: "default" } }
let(:default_labels) { { queue: queue.to_s, boundary: "", external_dependencies: "no", feature_category: "", urgency: "low" } }
shared_examples "a metrics middleware" do
context "with mocked prometheus" do
......@@ -202,11 +202,11 @@ describe Gitlab::SidekiqMiddleware::ServerMetrics do
end
context "combined" do
let(:urgency) { :none }
let(:urgency) { :throttled }
let(:external_dependencies) { true }
let(:resource_boundary) { :cpu }
let(:feature_category) { :authentication }
let(:labels) { default_labels.merge(urgency: "none", external_dependencies: "yes", boundary: "cpu", feature_category: "authentication") }
let(:labels) { default_labels.merge(urgency: "throttled", external_dependencies: "yes", boundary: "cpu", feature_category: "authentication") }
it_behaves_like "a metrics middleware"
end
......
......@@ -201,6 +201,26 @@ describe Namespace do
expect(described_class.find_by_pages_host(host)).to eq(namespace)
end
context 'when there is non-top-level group with searched name' do
before do
create(:group, :nested, path: 'pages')
end
it 'ignores this group' do
host = "pages.#{Settings.pages.host.upcase}"
expect(described_class.find_by_pages_host(host)).to be_nil
end
it 'finds right top level group' do
group = create(:group, path: 'pages')
host = "pages.#{Settings.pages.host.upcase}"
expect(described_class.find_by_pages_host(host)).to eq(group)
end
end
it "returns no result if the provided host is not subdomain of the Pages host" do
create(:namespace, name: 'namespace.io')
host = "namespace.io"
......
# frozen_string_literal: true
require 'spec_helper'
describe UserHighestRole do
describe 'associations' do
it { is_expected.to belong_to(:user).required }
end
describe 'validations' do
it { is_expected.to validate_inclusion_of(:highest_access_level).in_array([nil, *Gitlab::Access.all_values]) }
end
end
......@@ -30,6 +30,7 @@ describe User, :do_not_mock_admin_mode do
it { is_expected.to have_one(:status) }
it { is_expected.to have_one(:max_access_level_membership) }
it { is_expected.to have_one(:user_detail) }
it { is_expected.to have_one(:user_highest_role) }
it { is_expected.to have_many(:snippets).dependent(:destroy) }
it { is_expected.to have_many(:members) }
it { is_expected.to have_many(:project_members) }
......
......@@ -48,7 +48,7 @@ describe API::Groups do
context 'when file format is not supported' do
let(:file_path) { 'spec/fixtures/doc_sample.txt' }
let(:message) { 'file format is not supported. Please try one of the following supported formats: png, jpg, jpeg, gif, bmp, tiff, ico' }
let(:message) { 'file format is not supported. Please try one of the following supported formats: image/png, image/jpeg, image/gif, image/bmp, image/tiff, image/vnd.microsoft.icon' }
it_behaves_like 'invalid file upload request'
end
......
......@@ -20,6 +20,7 @@ RSpec.shared_context 'uploader with type check' do
end
end
# This works with the UploadTypeCheck::Concern
RSpec.shared_context 'stubbed MimeMagic mime type detection' do
let(:mime_type) { '' }
let(:magic_mime) { mime_type }
......@@ -31,3 +32,19 @@ RSpec.shared_context 'stubbed MimeMagic mime type detection' do
allow(MimeMagic).to receive(:by_path).with(anything).and_return(ext_mime_obj)
end
end
# @param uploader [CarrierWave::Uploader::Base] uploader with extension_whitelist method.
RSpec.shared_context 'ignore extension whitelist check' do
before do
allow(uploader).to receive(:extension_whitelist).and_return(nil)
end
end
# This works with a content_type_whitelist and content_type_blacklist type check.
# @param mime_type [String] mime type to forcibly detect.
RSpec.shared_context 'force content type detection to mime_type' do
before do
magic_mime_obj = MimeMagic.new(mime_type)
allow(MimeMagic).to receive(:by_magic).with(anything).and_return(magic_mime_obj)
end
end
# frozen_string_literal: true
# @param path [String] the path to file to upload. E.g. File.join('spec', 'fixtures', 'sanitized.svg')
# @param uploader [CarrierWave::Uploader::Base] uploader to handle the upload.
shared_examples 'denied carrierwave upload' do
it 'will deny upload' do
fixture_file = fixture_file_upload(path)
expect { uploader.cache!(fixture_file) }.to raise_exception(CarrierWave::IntegrityError)
end
end
# @param path [String] the path to file to upload. E.g. File.join('spec', 'fixtures', 'sanitized.svg')
# @param uploader [CarrierWave::Uploader::Base] uploader to handle the upload.
shared_examples 'accepted carrierwave upload' do
let(:fixture_file) { fixture_file_upload(path) }
before do
uploader.remove!
end
it 'will accept upload' do
expect { uploader.cache!(fixture_file) }.not_to raise_exception
end
it 'will cache uploaded file' do
expect { uploader.cache!(fixture_file) }.to change { uploader.file }.from(nil).to(kind_of(CarrierWave::SanitizedFile))
end
end
def check_content_matches_extension!(file = double(read: nil, path: ''))
magic_file = UploadTypeCheck::MagicFile.new(file)
uploader.check_content_matches_extension!(magic_file)
......
......@@ -47,15 +47,29 @@ describe AvatarUploader do
end
end
context 'upload type check' do
AvatarUploader::SAFE_IMAGE_EXT.each do |ext|
context "#{ext} extension" do
it_behaves_like 'type checked uploads', filenames: "image.#{ext}"
end
end
context 'accept whitelist file content type' do
# We need to feed through a valid path, but we force the parsed mime type
# in a stub below so we can set any path.
let_it_be(:path) { File.join('spec', 'fixtures', 'video_sample.mp4') }
where(:mime_type) { described_class::MIME_WHITELIST }
with_them do
include_context 'force content type detection to mime_type'
context 'skip image/svg+xml integrity check' do
it_behaves_like 'skipped type checked uploads', filenames: 'image.svg'
it_behaves_like 'accepted carrierwave upload'
end
end
context 'upload non-whitelisted file content type' do
let_it_be(:path) { File.join('spec', 'fixtures', 'sanitized.svg') }
it_behaves_like 'denied carrierwave upload'
end
context 'upload misnamed non-whitelisted file content type' do
let_it_be(:path) { File.join('spec', 'fixtures', 'not_a_png.png') }
it_behaves_like 'denied carrierwave upload'
end
end
# frozen_string_literal: true
require 'spec_helper'
describe ContentTypeWhitelist do
class DummyUploader < CarrierWave::Uploader::Base
include ContentTypeWhitelist::Concern
def content_type_whitelist
%w[image/png image/jpeg]
end
end
let_it_be(:model) { build_stubbed(:user) }
let_it_be(:uploader) { DummyUploader.new(model, :dummy) }
context 'upload whitelisted file content type' do
let(:path) { File.join('spec', 'fixtures', 'rails_sample.jpg') }
it_behaves_like 'accepted carrierwave upload'
end
context 'upload non-whitelisted file content type' do
let(:path) { File.join('spec', 'fixtures', 'sanitized.svg') }
it_behaves_like 'denied carrierwave upload'
end
context 'upload misnamed non-whitelisted file content type' do
let(:path) { File.join('spec', 'fixtures', 'not_a_png.png') }
it_behaves_like 'denied carrierwave upload'
end
end
......@@ -6,19 +6,35 @@ describe FaviconUploader do
let_it_be(:model) { build_stubbed(:user) }
let_it_be(:uploader) { described_class.new(model, :favicon) }
context 'upload type check' do
FaviconUploader::EXTENSION_WHITELIST.each do |ext|
context "#{ext} extension" do
it_behaves_like 'type checked uploads', filenames: "image.#{ext}"
end
context 'accept whitelist file content type' do
include_context 'ignore extension whitelist check'
# We need to feed through a valid path, but we force the parsed mime type
# in a stub below so we can set any path.
let_it_be(:path) { File.join('spec', 'fixtures', 'video_sample.mp4') }
where(:mime_type) { described_class::MIME_WHITELIST }
with_them do
include_context 'force content type detection to mime_type'
it_behaves_like 'accepted carrierwave upload'
end
end
context 'upload non-whitelisted file extensions' do
it 'will deny upload' do
path = File.join('spec', 'fixtures', 'banana_sample.gif')
fixture_file = fixture_file_upload(path)
expect { uploader.cache!(fixture_file) }.to raise_exception(CarrierWave::IntegrityError)
end
context 'upload non-whitelisted file content type' do
include_context 'ignore extension whitelist check'
let_it_be(:path) { File.join('spec', 'fixtures', 'sanitized.svg') }
it_behaves_like 'denied carrierwave upload'
end
context 'upload misnamed non-whitelisted file content type' do
include_context 'ignore extension whitelist check'
let_it_be(:path) { File.join('spec', 'fixtures', 'not_a_png.png') }
it_behaves_like 'denied carrierwave upload'
end
end
......@@ -166,7 +166,6 @@ describe 'layouts/nav/sidebar/_project' do
before do
allow(view).to receive(:can?).with(nil, :read_cycle_analytics, project).and_return(read_cycle_analytics)
stub_feature_flags(analytics_pages_under_project_analytics_sidebar: { enabled: false, thing: project })
end
describe 'when value stream analytics is enabled' do
......
......@@ -26,17 +26,5 @@ describe UpdateMergeRequestsWorker do
perform
end
context 'when slow' do
before do
stub_const("UpdateMergeRequestsWorker::LOG_TIME_THRESHOLD", -1)
end
it 'logs debug info' do
expect(Rails.logger).to receive(:info).with(a_string_matching(/\AUpdateMergeRequestsWorker#perform.*project_id=#{project.id},user_id=#{user.id},oldrev=#{oldrev},newrev=#{newrev},ref=#{ref}/))
perform
end
end
end
end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment