Commit 8fe37dcb authored by GitLab Bot's avatar GitLab Bot

Automatic merge of gitlab-org/gitlab master

parents 1eb01533 2fb63ac4
......@@ -18,6 +18,10 @@ export const templateTypes = () => [
name: __('Dockerfile'),
key: 'dockerfiles',
},
{
name: '.metrics-dashboard.yml',
key: 'metrics_dashboard_ymls',
},
];
export const showFileTemplatesBar = (_, getters, rootState) => name =>
......
......@@ -26,7 +26,6 @@ ActiveSupport::Inflector.inflections do |inflect|
project_statistics
system_note_metadata
vulnerabilities_feedback
vulnerability_export_registry
vulnerability_feedback
)
inflect.acronym 'EE'
......
......@@ -55,7 +55,7 @@ making the software lifecycle faster and radically improving the speed of busine
GitLab provides solutions for [each of the stages of the DevOps lifecycle](https://about.gitlab.com/stages-devops-lifecycle/):
![DevOps Stages](img/devops-stages.png)
![DevOps Stages](img/devops-stages-13_3.png)
GitLab is like a top-of-the-line kitchen for making software. As the executive
chef, you decide what software you want to serve. Using your recipe, GitLab handles
......@@ -71,10 +71,11 @@ The following sections provide links to documentation for each DevOps stage:
| [Create](#create) | Source code, data creation, and management features. |
| [Verify](#verify) | Testing, code quality, and continuous integration features. |
| [Package](#package) | Docker container registry. |
| [Secure](#secure) | Security capability features. |
| [Release](#release) | Application release and delivery features. |
| [Configure](#configure) | Application and infrastructure configuration tools. |
| [Monitor](#monitor) | Application monitoring and metrics features. |
| [Secure](#secure) | Security capability features. |
| [Defend](#defend) | Protection against security intrusions. |
<div align="right">
<a type="button" class="btn btn-default" href="#overview">
......@@ -274,6 +275,30 @@ The following documentation relates to the DevOps **Package** stage:
</a>
</div>
### Secure
Check your application for security vulnerabilities that may lead to unauthorized access, data
leaks, or denial of service. GitLab can perform static and dynamic tests on your application's code,
looking for known flaws and reporting them in the merge request. You can then fix flaws prior to
merge. Security teams can use dashboards to get a high-level view on projects and groups, and start
remediation processes when needed.
The following documentation relates to the DevOps **Secure** stage:
| Secure topics | Description |
|:------------------------------------------------------------------------------------------------------|:-----------------------------------------------------------------------|
| [Compliance Dashboard](user/compliance/compliance_dashboard/index.md) **(ULTIMATE)** | View the most recent Merge Request activity in a group. |
| [Container Scanning](user/application_security/container_scanning/index.md) **(ULTIMATE)** | Use Clair to scan Docker images for known vulnerabilities. |
| [Dependency List](user/application_security/dependency_list/index.md) **(ULTIMATE)** | View your project's dependencies and their known vulnerabilities. |
| [Dependency Scanning](user/application_security/dependency_scanning/index.md) **(ULTIMATE)** | Analyze your dependencies for known vulnerabilities. |
| [Dynamic Application Security Testing (DAST)](user/application_security/dast/index.md) **(ULTIMATE)** | Analyze running web applications for known vulnerabilities. |
| [Group Security Dashboard](user/application_security/security_dashboard/index.md#group-security-dashboard) **(ULTIMATE)** | View vulnerabilities in all the projects in a group and its subgroups. |
| [Instance Security Dashboard](user/application_security/security_dashboard/index.md#instance-security-dashboard) **(ULTIMATE)** | View vulnerabilities in all the projects you're interested in. |
| [License Compliance](user/compliance/license_compliance/index.md) **(ULTIMATE)** | Search your project's dependencies for their licenses. |
| [Pipeline Security](user/application_security/security_dashboard/index.md#pipeline-security) **(ULTIMATE)** | View the security reports for your project's pipelines. |
| [Project Security Dashboard](user/application_security/security_dashboard/index.md#project-security-dashboard) **(ULTIMATE)** | View the latest security reports for your project. |
| [Static Application Security Testing (SAST)](user/application_security/sast/index.md) **(ULTIMATE)** | Analyze source code for known vulnerabilities. |
### Release
Spend less time configuring your tools, and more time creating. Whether you’re
......@@ -352,29 +377,21 @@ The following documentation relates to the DevOps **Monitor** stage:
</a>
</div>
### Secure
### Defend
Check your application for security vulnerabilities that may lead to unauthorized access,
data leaks, and denial of services. GitLab will perform static and dynamic tests on the
code of your application, looking for known flaws and report them in the merge request
so you can fix them before merging. Security teams can use dashboards to get a
high-level view on projects and groups, and start remediation processes when needed.
GitLab Defend enables organizations to proactively protect cloud-native environments by providing
context-aware technologies to reduce overall security risk. Defend is a natural extension of your
existing operation's practices and provides security visibility across the entire DevSecOps
lifecycle. This empowers your organization to apply DevSecOps best practices from the first line of
code through monitoring and protecting your applications deployed into production.
The following documentation relates to the DevOps **Secure** stage:
The following documentation relates to the DevOps **Defend** stage:
| Secure topics | Description |
| Defend topics | Description |
|:------------------------------------------------------------------------------------------------------|:-----------------------------------------------------------------------|
| [Compliance Dashboard](user/compliance/compliance_dashboard/index.md) **(ULTIMATE)** | View the most recent Merge Request activity in a group. |
| [Container Scanning](user/application_security/container_scanning/index.md) **(ULTIMATE)** | Use Clair to scan Docker images for known vulnerabilities. |
| [Dependency List](user/application_security/dependency_list/index.md) **(ULTIMATE)** | View your project's dependencies and their known vulnerabilities. |
| [Dependency Scanning](user/application_security/dependency_scanning/index.md) **(ULTIMATE)** | Analyze your dependencies for known vulnerabilities. |
| [Dynamic Application Security Testing (DAST)](user/application_security/dast/index.md) **(ULTIMATE)** | Analyze running web applications for known vulnerabilities. |
| [Group Security Dashboard](user/application_security/security_dashboard/index.md#group-security-dashboard) **(ULTIMATE)** | View vulnerabilities in all the projects in a group and its subgroups. |
| [Instance Security Dashboard](user/application_security/security_dashboard/index.md#instance-security-dashboard) **(ULTIMATE)** | View vulnerabilities in all the projects you're interested in. |
| [License Compliance](user/compliance/license_compliance/index.md) **(ULTIMATE)** | Search your project's dependencies for their licenses. |
| [Pipeline Security](user/application_security/security_dashboard/index.md#pipeline-security) **(ULTIMATE)** | View the security reports for your project's pipelines. |
| [Project Security Dashboard](user/application_security/security_dashboard/index.md#project-security-dashboard) **(ULTIMATE)** | View the latest security reports for your project. |
| [Static Application Security Testing (SAST)](user/application_security/sast/index.md) **(ULTIMATE)** | Analyze source code for known vulnerabilities. |
| [Web Application Firewall with ModSecurity](user/compliance/compliance_dashboard/index.md) **(ULTIMATE)** | Filter, monitor, and block HTTP traffic to and from a web application. |
| [Container Host Security](user/clusters/applications.md#install-falco-using-gitlab-cicd) | Detect and respond to security threats at the Kubernetes, network, and host level. |
| [Container Network Security](user/clusters/applications.md#install-cilium-using-gitlab-cicd) | Detect and block unauthorized network traffic between pods and to/from the internet.|
## New to Git and GitLab?
......
......@@ -827,9 +827,40 @@ Using the Markdown extension is necessary for the [`/help`](index.md#gitlab-help
### Links to external documentation
When describing interactions with external software, it's often helpful to include links to external
documentation. When possible, make sure that you are linking to an **authoritative** source.
documentation. When possible, make sure that you're linking to an [**authoritative** source](#authoritative-sources).
For example, if you're describing a feature in Microsoft's Active Directory, include a link to official Microsoft documentation.
### Authoritative sources
When citing external information, use sources that are written by the people who created
the item or product in question. These sources are the most likely to
be accurate and remain up to date.
Examples of authoritative sources include:
- Specifications, such as a [Request for Comments](https://www.ietf.org/standards/rfcs/) document
from the Internet Engineering Task Force.
- Official documentation for a product. For example, if you're setting up an interface with the
Google OAuth 2 authorization server, include a link to Google's documentation.
- Official documentation for a project. For example, if you're citing NodeJS functionality,
refer directly to [NodeJS documentation](https://nodejs.org/en/docs/).
- Books from an authoritative publisher.
Examples of sources to avoid include:
- Personal blog posts.
- Wikipedia.
- Non-trustworthy articles.
- Discussions on forums such as Stack Overflow.
- Documentation from a company that describes another company's product.
While many of these sources to avoid can help you learn skills and or features, they can become
obsolete quickly. Nobody is obliged to maintain any of these sites. Therefore, we should avoid using them as reference literature.
NOTE: **Note:**
Non-authoritative sources are acceptable only if there is no equivalent authoritative source.
Even then, focus on non-authoritative sources that are extensively cited or peer-reviewed.
### Links requiring permissions
Don't link directly to:
......
......@@ -140,6 +140,14 @@ Enables the feature for lists of users created [in the Feature Flags UI](#create
Similar to [User IDs](#user-ids), it uses the Unleash [`userWithId`](https://unleash.github.io/docs/activation_strategy#userwithid)
activation strategy.
It's not possible to *disable* a feature for members of a user list, but you can achieve the same
effect by enabling a feature for a user list that doesn't contain the excluded users.
For example:
- `Full-user-list` = `User1A, User1B, User2A, User2B, User3A, User3B, ...`
- `Full-user-list-excluding-B-users` = `User1A, User2A, User3A, ...`
#### Create a user list
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/13308) in GitLab 13.3.
......
......@@ -608,8 +608,11 @@ Alternatively, you can use the variable `SECURE_ANALYZERS_PREFIX` to override th
> - It's able to be enabled or disabled per-project.
> - To use it in GitLab self-managed instances, ask a GitLab administrator to [enable it](#enable-or-disable-on-demand-scans).
Passive DAST scans may be run on demand against a target website, outside the DevOps lifecycle. These scans will
always be associated with the default or `master` branch of your project and the results can be seen in the project dashboard.
Passive DAST scans may be run on demand against a target website, outside the DevOps lifecycle. These scans are
always associated with the default or `master` branch of your project and the results can be seen in the project dashboard.
NOTE: **Note:**
You cannot run an on-demand DAST scan against a protected branch unless you have permission to do so. The `master` branch is protected by default. For more details, see [Pipeline security on protected branches](../../../ci/pipelines/index.md#pipeline-security-on-protected-branches).
![DAST On-Demand Scan](img/dast_on_demand_v13_2.png)
......
# Design Management
> - [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/660) in [GitLab Premium](https://about.gitlab.com/pricing/) 12.2.
> - [Moved](https://gitlab.com/gitlab-org/gitlab/-/issues/212566) to GitLab Core in 13.0.
> - Support for SVGs was [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/12771) in [GitLab Premium](https://about.gitlab.com/pricing/) 12.4.
> - Design Management was [moved](https://gitlab.com/gitlab-org/gitlab/-/issues/212566) to GitLab Core in 13.0.
## Overview
......@@ -41,10 +42,9 @@ If the requirements are not met, the **Designs** tab displays a message to the u
## Supported files
Files uploaded must have a file extension of either `png`, `jpg`, `jpeg`,
`gif`, `bmp`, `tiff` or `ico`.
`gif`, `bmp`, `tiff`, `ico`, or `svg`.
Support for [SVG files](https://gitlab.com/gitlab-org/gitlab/-/issues/12771)
and [PDFs](https://gitlab.com/gitlab-org/gitlab/-/issues/32811) is planned for a future release.
Support for [PDF](https://gitlab.com/gitlab-org/gitlab/issues/32811) is planned for a future release.
## Limitations
......
......@@ -336,7 +336,7 @@ export default {
{{ createLocationString(item.location) }}
</div>
</div>
<remediated-badge v-if="item.resolved_on_default_branch" class="ml-2" />
<remediated-badge v-if="item.resolvedOnDefaultBranch" class="ml-2" />
</template>
<template #cell(identifier)="{ item }">
......
......@@ -5,6 +5,7 @@ fragment Vulnerability on Vulnerability {
severity
vulnerabilityPath
userNotesCount
resolvedOnDefaultBranch
issueLinks(linkType: CREATED) {
nodes {
issue {
......
......@@ -15,6 +15,8 @@ module Registrations
@group = Groups::CreateService.new(current_user, group_params).execute
if @group.persisted?
invite_teammates
redirect_to new_users_sign_up_project_path(namespace_id: @group.id)
else
render action: :new
......@@ -31,8 +33,21 @@ module Registrations
access_denied! unless experiment_enabled?(:onboarding_issues)
end
def invite_teammates
invite_params = {
user_ids: emails_param[:emails].reject(&:blank?).join(','),
access_level: Gitlab::Access::DEVELOPER
}
Members::CreateService.new(current_user, invite_params).execute(@group)
end
def group_params
params.require(:group).permit(:name, :path, :visibility_level)
end
def emails_param
params.require(:group).permit(emails: [])
end
end
end
......@@ -33,8 +33,14 @@ module Mutations
project = authorized_find!(full_path: full_path)
raise_resource_not_available_error! unless Feature.enabled?(:security_on_demand_scans_feature_flag, project)
response = ServiceResponse.error(message: 'Not implemented')
{ errors: response.errors }
service = ::DastScannerProfiles::CreateService.new(project, current_user)
result = service.execute(name: profile_name, spider_timeout: spider_timeout, target_timeout: target_timeout)
if result.success?
{ id: result.payload.to_global_id, errors: [] }
else
{ errors: result.errors }
end
end
private
......
......@@ -16,22 +16,20 @@ module EE
def namespace_options_for_select(selected = nil)
grouped_options = {
'New' => [[_('Create group'), 0]],
'Groups' => trial_groups,
'Users' => trial_users
'Groups' => trial_group_namespaces.map { |n| [n.name, n.id] },
'Users' => trial_user_namespaces.map { |n| [n.name, n.id] }
}
grouped_options_for_select(grouped_options, selected, prompt: _('Please select'))
end
def trial_users
user_namespace = current_user.namespace
return [] if user_namespace.gitlab_subscription&.trial?
[[user_namespace.name, user_namespace.id]]
def trial_group_namespaces
current_user.manageable_groups_eligible_for_trial
end
def trial_groups
current_user.manageable_groups_eligible_for_trial.map { |g| [g.name, g.id] }
def trial_user_namespaces
user_namespace = current_user.namespace
user_namespace.eligible_for_trial? ? [user_namespace] : []
end
def show_trial_errors?(namespace, service_result)
......
......@@ -5,14 +5,15 @@ module EE
extend ActiveSupport::Concern
extend ::Gitlab::Utils::Override
TEXT_LIMIT = {
TRUNCATED_FIELDS = {
entity_path: 5_500,
target_details: 5_500
}.freeze
prepended do
scope :by_entity, -> (entity_type, entity_id) { by_entity_type(entity_type).by_entity_id(entity_id) }
before_validation :truncate_target_details
before_validation :truncate_fields
end
def entity
......@@ -43,8 +44,13 @@ module EE
private
def truncate_target_details
self.target_details = self.details[:target_details] = target_details&.truncate(TEXT_LIMIT[:target_details])
def truncate_fields
TRUNCATED_FIELDS.each do |name, limit|
original = self[name] || self.details[name]
next unless original
self[name] = self.details[name] = String(original).truncate(limit)
end
end
end
end
......@@ -37,6 +37,14 @@ module EE
scope :include_gitlab_subscription, -> { includes(:gitlab_subscription) }
scope :join_gitlab_subscription, -> { joins("LEFT OUTER JOIN gitlab_subscriptions ON gitlab_subscriptions.namespace_id=namespaces.id") }
scope :eligible_for_trial, -> do
left_joins(gitlab_subscription: :hosted_plan)
.where(
gitlab_subscriptions: { trial: [nil, false], trial_ends_on: [nil] },
plans: { name: [nil, *::Plan::PLANS_ELIGIBLE_FOR_TRIAL] }
)
end
scope :with_feature_available_in_plan, -> (feature) do
plans = plans_with_feature(feature)
matcher = ::Plan.where(name: plans)
......@@ -276,9 +284,9 @@ module EE
def eligible_for_trial?
::Gitlab.com? &&
parent_id.nil? &&
trial_ends_on.blank? &&
[::Plan::EARLY_ADOPTER, ::Plan::FREE].include?(actual_plan_name)
!has_parent? &&
never_had_trial? &&
plan_eligible_for_trial?
end
def trial_active?
......@@ -290,9 +298,7 @@ module EE
end
def trial_expired?
trial_ends_on.present? &&
trial_ends_on < Date.today &&
actual_plan_name == ::Plan::FREE
trial_ends_on.present? && trial_ends_on < Date.today
end
# A namespace may not have a file template project
......@@ -333,6 +339,10 @@ module EE
actual_plan_name == ::Plan::GOLD
end
def plan_eligible_for_trial?
::Plan::PLANS_ELIGIBLE_FOR_TRIAL.include?(actual_plan_name)
end
def use_elasticsearch?
::Gitlab::CurrentSettings.elasticsearch_indexes_namespace?(self)
end
......
......@@ -16,6 +16,7 @@ module EE
PAID_HOSTED_PLANS = [BRONZE, SILVER, GOLD].freeze
FREE_HOSTED_PLANS = [EARLY_ADOPTER].freeze
EE_ALL_PLANS = (EE_DEFAULT_PLANS + PAID_HOSTED_PLANS + FREE_HOSTED_PLANS).freeze
PLANS_ELIGIBLE_FOR_TRIAL = [FREE, *FREE_HOSTED_PLANS].freeze
# This constant must keep ordered by tier.
ALL_HOSTED_PLANS = (PAID_HOSTED_PLANS + FREE_HOSTED_PLANS).freeze
......
......@@ -258,10 +258,7 @@ module EE
end
def manageable_groups_eligible_for_trial
manageable_groups
.left_joins(:gitlab_subscription)
.where(gitlab_subscriptions: { trial: [nil, false] })
.order(:name)
manageable_groups.eligible_for_trial.order(:name)
end
override :has_current_license?
......
# frozen_string_literal: true
class Geo::VulnerabilityExportRegistry < Geo::BaseRegistry
include ::Geo::ReplicableRegistry
MODEL_CLASS = ::Vulnerabilities::Export
MODEL_FOREIGN_KEY = :vulnerability_export_id
belongs_to :vulnerability_export, class_name: 'Vulnerabilities::Export', foreign_key: :vulnerability_export_id
end
......@@ -2,25 +2,12 @@
module Vulnerabilities
class Export < ApplicationRecord
include ::Gitlab::Geo::ReplicableModel
self.table_name = "vulnerability_exports"
with_replicator Geo::VulnerabilityExportReplicator
belongs_to :project
belongs_to :group
belongs_to :author, optional: false, class_name: 'User'
has_one :vulnerability_export_verification_status, class_name: 'Vulnerabilities::ExportVerificationStatus', inverse_of: :vulnerability_export, foreign_key: :vulnerability_export_id
delegate :verification_retry_at, :verification_retry_at=,
:verified_at, :verified_at=,
:verification_checksum, :verification_checksum=,
:verification_failure, :verification_failure=,
:verification_retry_count, :verification_retry_count=,
to: :vulnerability_export_verification_status
mount_uploader :file, AttachmentUploader
after_save :update_file_store, if: :saved_change_to_file?
......@@ -96,18 +83,6 @@ module Vulnerabilities
self.update_column(:file_store, file.object_store)
end
def vulnerability_export_verification_status
super.presence || build_vulnerability_export_verification_status
end
def local?
file_store == ObjectStorage::Store::LOCAL
end
def self.replicables_for_geo_node
self.all
end
private
def make_project_level_export(project)
......
# frozen_string_literal: true
module Vulnerabilities
class ExportVerificationStatus < ApplicationRecord
self.primary_key = :vulnerability_export_id
self.table_name = 'vulnerability_export_verification_status'
belongs_to :vulnerability_export, class_name: 'Vulnerabilities::Export', inverse_of: :vulnerability_export_verification_status
end
end
# frozen_string_literal: true
module Geo
class VulnerabilityExportReplicator < Gitlab::Geo::Replicator
include ::Geo::BlobReplicatorStrategy
def self.model
::Vulnerabilities::Export
end
def carrierwave_uploader
model_record.file
end
def self.replication_enabled_by_default?
false
end
end
end
# frozen_string_literal: true
module DastScannerProfiles
class CreateService < BaseService
def execute(name:, target_timeout:, spider_timeout:)
return ServiceResponse.error(message: 'Insufficient permissions') unless allowed?
dast_scanner_profile = DastScannerProfile.create(
project: project,
name: name,
target_timeout: target_timeout,
spider_timeout: spider_timeout
)
return ServiceResponse.success(payload: dast_scanner_profile) if dast_scanner_profile.valid?
ServiceResponse.error(message: dast_scanner_profile.errors.full_messages)
end
private
def allowed?
Ability.allowed?(current_user, :run_ondemand_dast_scan, project)
end
end
end
......@@ -6,7 +6,7 @@
= html_escape(s_("BillingPlans|Learn more about each plan by reading our %{faq_link}, or start a free 30-day trial of GitLab.com Gold.")) % { faq_link: faq_link.html_safe }
- elsif namespace.trial_active?
= html_escape(s_("BillingPlans|Your GitLab.com %{plan} trial will %{strong_open}expire after %{expiration_date}%{strong_close}. You can retain access to the %{plan} features by upgrading below.")) % { plan: namespace.gitlab_subscription&.plan_title, expiration_date: namespace.trial_ends_on, strong_open: '<strong>'.html_safe, strong_close: '</strong>'.html_safe }
- elsif namespace.trial_expired?
- elsif namespace.trial_expired? && namespace.free_plan?
= s_("BillingPlans|Your GitLab.com trial expired on %{expiration_date}. You can restore access to the features at any time by upgrading below.") % { expiration_date: namespace.trial_ends_on }
- else
= html_escape(s_("BillingPlans|Learn more about each plan by visiting our %{pricing_page_link}.")) % { pricing_page_link: pricing_page_link.html_safe }
......@@ -22,8 +22,7 @@ module Geo
Geo::LfsObjectRegistry,
Geo::PackageFileRegistry,
Geo::ProjectRegistry,
Geo::UploadRegistry,
Geo::VulnerabilityExportRegistry
Geo::UploadRegistry
].freeze
BATCH_SIZE = 10000
......
---
title: Omit namespaces w/ non-free plans from trial select
merge_request: 36394
author:
type: changed
---
title: Utilize `resolvedOnDefaultBranch` field to show the remediated badge on vulnerabilities
list
merge_request: 38478
author:
type: fixed
......@@ -162,8 +162,7 @@ module Gitlab
# solutions can be found at
# https://gitlab.com/gitlab-org/gitlab/-/issues/227693
def self.replicator_classes
classes = [::Geo::PackageFileReplicator,
::Geo::VulnerabilityExportReplicator]
classes = [::Geo::PackageFileReplicator]
classes.select(&:enabled?)
end
......
......@@ -36,12 +36,14 @@ module Security
end
def sast_stage
@params[:stage] || 'test'
@params['stage'] || 'test'
end
# We only want to write variables that are set
def parse_variables(variables)
variables.map { |var| [var, @params[var]] }.to_h.compact
variables.map { |var| [var, @params[var]] }
.to_h
.select { |k, v| v.present? }
end
def sast_block
......
......@@ -48,7 +48,7 @@ RSpec.describe Registrations::GroupsController do
describe 'POST #create' do
subject { post :create, params: { group: params } }
let(:params) { { name: 'Group name', path: 'group-path', visibility_level: Gitlab::VisibilityLevel::PRIVATE } }
let(:params) { { name: 'Group name', path: 'group-path', visibility_level: Gitlab::VisibilityLevel::PRIVATE, emails: ['', ''] } }
context 'with an unauthenticated user' do
it { is_expected.to have_gitlab_http_status(:redirect) }
......@@ -68,6 +68,46 @@ RSpec.describe Registrations::GroupsController do
it { is_expected.to have_gitlab_http_status(:redirect) }
it { is_expected.to redirect_to(new_users_sign_up_project_path(namespace_id: user.groups.last.id)) }
context 'inviting teammates' do
context 'with no valid emails in the params' do
it 'does not add teammates' do
expect { subject }.to change(Member, :count).by(1)
end
it 'does not call the Members::CreateService' do
expect(Members::CreateService).not_to receive(:new)
end
end
context 'with valid emails in the params' do
before do
params[:emails] = ['a@a.a', 'b@b.b', '', '', 'x', 'y']
end
it 'adds users with developer access and ignores blank emails' do
expect_next_instance_of(Group) do |group|
expect(group).to receive(:add_users).with(
['a@a.a', 'b@b.b', 'x', 'y'],
Gitlab::Access::DEVELOPER,
expires_at: nil,
current_user: user
).and_call_original
end
subject
end
it 'sends invitations to valid emails only' do
subject
emails = assigns(:group).members.pluck(:invite_email)
expect(emails).to include('a@a.a', 'b@b.b')
expect(emails).not_to include('x', 'y')
end
end
end
context 'when the group cannot be saved' do
let(:params) { { name: '', path: '' } }
......
# frozen_string_literal: true
FactoryBot.define do
factory :geo_vulnerability_export_registry, class: 'Geo::VulnerabilityExportRegistry' do
association :vulnerability_export, factory: :vulnerability_export
state { Geo::VulnerabilityExportRegistry.state_value(:pending) }
trait :synced do
state { Geo::VulnerabilityExportRegistry.state_value(:synced) }
last_synced_at { 5.days.ago }
end
trait :failed do
state { Geo::VulnerabilityExportRegistry.state_value(:failed) }
last_synced_at { 1.day.ago }
retry_count { 2 }
last_sync_failure { 'Random error' }
end
trait :started do
state { Geo::VulnerabilityExportRegistry.state_value(:started) }
last_synced_at { 1.day.ago }
retry_count { 0 }
end
end
end
......@@ -9,6 +9,21 @@ FactoryBot.define do
end_date { Date.today.advance(years: 1) }
trial { false }
trait :expired do
start_date { Date.today.advance(years: -1, months: -1) }
end_date { Date.today.advance(months: -1) }
end
trait :active_trial do
trial { true }
trial_ends_on { Date.today.advance(months: 1) }
end
trait :expired_trial do
trial { true }
trial_ends_on { Date.today.advance(days: -1) }
end
trait :free do
hosted_plan_id { nil }
end
......
......@@ -289,7 +289,7 @@ describe('Vulnerability list component', () => {
beforeEach(() => {
newVulnerabilities = generateVulnerabilities();
newVulnerabilities[0].resolved_on_default_branch = true;
newVulnerabilities[0].resolvedOnDefaultBranch = true;
wrapper = createWrapper({ props: { vulnerabilities: newVulnerabilities } });
});
......
......@@ -298,7 +298,7 @@ describe('Vulnerability Header', () => {
});
describe('state badge', () => {
test.each(vulnerabilityStateEntries)(
it.each(vulnerabilityStateEntries)(
'the vulnerability state badge has the correct style for the %s state',
(state, stateObject) => {
createWrapper({ state });
......
......@@ -42,15 +42,15 @@ describe('IssueLink component', () => {
wrapper = createWrapper({ propsData: { issue } });
});
test('should contain the correct issue icon', () => {
it('should contain the correct issue icon', () => {
expect(findIssueWithState(state)).toBeTruthy();
});
test('should contain a link to the issue', () => {
it('should contain a link to the issue', () => {
expect(findIssueLink(issue.iid).attributes('href')).toBe(issue.webUrl);
});
test('should contain the title', () => {
it('should contain the title', () => {
const tooltip = getBinding(findIssueLink(issue.iid).element, 'gl-tooltip');
expect(tooltip).toBeDefined();
expect(tooltip.value).toBe(issue.title);
......
......@@ -37,7 +37,7 @@ describe('Vulnerability state dropdown component', () => {
afterEach(() => wrapper.destroy());
describe('tests that need to manually create the wrapper', () => {
test.each(vulnerabilityStateEntries)(
it.each(vulnerabilityStateEntries)(
'dropdown is created with the %s state already selected',
(stateString, stateObject) => {
createWrapper(stateString);
......@@ -51,7 +51,7 @@ describe('Vulnerability state dropdown component', () => {
expect(isSelected(dropdownItems())).toBe(false);
});
test.each(vulnerabilityStateEntries)(
it.each(vulnerabilityStateEntries)(
`when the %s dropdown item is clicked, it's the only one that's selected`,
(stateString, stateObject) => {
// Start off with an unknown state so we can click through each item and see it change.
......
......@@ -8,6 +8,7 @@ RSpec.describe Mutations::DastScannerProfiles::Create do
let(:user) { create(:user) }
let(:full_path) { project.full_path }
let(:profile_name) { SecureRandom.hex }
let(:dast_scanner_profile) { DastScannerProfile.find_by(project: project, name: profile_name) }
subject(:mutation) { described_class.new(object: nil, context: { current_user: user }, field: nil) }
......@@ -38,8 +39,31 @@ RSpec.describe Mutations::DastScannerProfiles::Create do
group.add_owner(user)
end
it 'stubs out the response' do
expect(subject[:errors]).to eq(['Not implemented'])
it 'returns the dast_scanner_profile id' do
expect(subject[:id]).to eq(dast_scanner_profile.to_global_id)
end
it 'calls the dast_scanner_profile creation service' do
service = double(described_class)
result = double('result', success?: false, errors: [])
expect(DastScannerProfiles::CreateService).to receive(:new).and_return(service)
expect(service).to receive(:execute).with(name: profile_name, spider_timeout: nil, target_timeout: nil).and_return(result)
subject
end
context 'when the dast_scanner_profile already exists' do
it 'returns an error' do
subject
response = mutation.resolve(
full_path: full_path,
profile_name: profile_name
)
expect(response[:errors]).to include('Name has already been taken')
end
end
context 'when security_on_demand_scans_feature_flag is disabled' do
......
......@@ -10,83 +10,40 @@ RSpec.describe EE::TrialHelper do
let_it_be(:group1) { create :group }
let_it_be(:group2) { create :group }
let(:expected_group_options) { [] }
let(:expected_user_options) { [[user.namespace.name, user.namespace.id]] }
let(:trial_user_namespaces) { [] }
let(:trial_group_namespaces) { [] }
let(:generated_html) do
grouped_options_for_select({
'New' => [['Create group', 0]],
'Groups' => expected_group_options,
'Users' => expected_user_options
'Groups' => trial_group_namespaces.map { |g| [g.name, g.id] },
'Users' => trial_user_namespaces.map { |n| [n.name, n.id] }
}, nil, prompt: 'Please select')
end
before do
allow(helper).to receive(:trial_groups).and_return(expected_group_options)
allow(helper).to receive(:trial_users).and_return(expected_user_options)
allow(helper).to receive(:trial_group_namespaces).and_return(trial_group_namespaces)
allow(helper).to receive(:trial_user_namespaces).and_return(trial_user_namespaces)
end
subject { helper.namespace_options_for_select }
context 'when the user’s namespace can be trialed' do
context 'and the user has no groups or none of their groups can be trialed' do
it { is_expected.to eq(generated_html) }
end
context 'and the user has some groups which can be trialed' do
let(:expected_group_options) { [group1, group2].map {|g| [g.name, g.id]} }
it { is_expected.to eq(generated_html) }
end
end
context 'when the user’s namespace has already been trialed' do
let(:expected_user_options) { [] }
context 'and the user has no groups or none of their groups can be trialed' do
it { is_expected.to eq(generated_html) }
end
context 'and the user has some groups which can be trialed' do
let(:expected_group_options) { [group1, group2].map {|g| [g.name, g.id]} }
it { is_expected.to eq(generated_html) }
end
end
end
describe '#trial_users' do
let_it_be(:user) { create :user }
let(:user_eligible_for_trial_result) { [[user.namespace.name, user.namespace.id]] }
let(:user_ineligible_for_trial_result) { [] }
before do
user.reload # necessary to cache-bust the user.namespace.gitlab_subscription object
allow(helper).to receive(:current_user).and_return(user)
end
where(can_trial_user: [true, false], can_trial_groups: [true, false])
subject { helper.trial_users }
with_them do
context "when the user’s namespace #{params[:can_trial_user] ? 'can be' : 'has already been'} trialed" do
let(:trial_user_namespaces) { can_trial_user ? [user.namespace] : [] }
context 'when the user has no subscription on their namespace' do
it { is_expected.to eq(user_eligible_for_trial_result) }
end
context 'when the user has a subscription on their namespace' do
let(:trialed) { false }
let!(:subscription) { create :gitlab_subscription, namespace: user.namespace, trial: trialed }
context 'and the user has not yet trialed their namespace' do
it { is_expected.to eq(user_eligible_for_trial_result) }
end
context "and the user has #{params[:can_trial_groups] ? 'some groups which' : 'no groups or none of their groups'} can be trialed" do
let(:trial_group_namespaces) { can_trial_groups ? [group1, group2] : [] }
context 'and the user has already trialed their namespace' do
let(:trialed) { true }
it { is_expected.to eq(user_ineligible_for_trial_result) }
it { is_expected.to eq(generated_html) }
end
end
end
end
describe '#trial_groups' do
describe '#trial_group_namespaces' do
let_it_be(:user) { create :user }
let(:no_groups) { [] }
......@@ -94,7 +51,7 @@ RSpec.describe EE::TrialHelper do
allow(helper).to receive(:current_user).and_return(user)
end
subject { helper.trial_groups }
subject { helper.trial_group_namespaces.map(&:id) }
context 'when the user is not an owner/maintainer of any groups' do
it { is_expected.to eq(no_groups) }
......@@ -107,7 +64,7 @@ RSpec.describe EE::TrialHelper do
let_it_be(:subgroup2) { create :group, parent: group2, name: 'Sub-Group 2' }
let_it_be(:subsubgroup1) { create :group, parent: subgroup2, name: 'Sub-Sub-Group 1' }
let(:all_groups) { [group1, group2, subgroup1, subgroup2, subsubgroup1].map {|g| [g.name, g.id] } }
let(:all_groups) { [group1, group2, subgroup1, subgroup2, subsubgroup1].map(&:id) }
before do
group1.add_owner(user)
......@@ -119,38 +76,38 @@ RSpec.describe EE::TrialHelper do
end
context 'and the groups have subscriptions' do
let(:trialed_group1) { false }
let(:trialed_subgroup1) { false }
let(:trialed_group2) { false }
let(:trialed_subgroup2) { false }
let(:trialed_subsubgroup1) { false }
let!(:subscription_group1) { create :gitlab_subscription, namespace: group1, trial: trialed_group1 }
let!(:subscription_subgroup1) { create :gitlab_subscription, namespace: subgroup1, trial: trialed_subgroup1 }
let!(:subscription_group2) { create :gitlab_subscription, namespace: group2, trial: trialed_group2 }
let!(:subscription_subgroup2) { create :gitlab_subscription, namespace: subgroup2, trial: trialed_subgroup2 }
let!(:subscription_subsubgroup1) { create :gitlab_subscription, namespace: subsubgroup1, trial: trialed_subsubgroup1 }
let(:group1_traits) { nil }
let(:subgroup1_traits) { nil }
let(:group2_traits) { nil }
let(:subgroup2_traits) { nil }
let(:subsubgroup1_traits) { nil }
let!(:subscription_group1) { create :gitlab_subscription, :free, *group1_traits, namespace: group1 }
let!(:subscription_subgroup1) { create :gitlab_subscription, :free, *subgroup1_traits, namespace: subgroup1 }
let!(:subscription_group2) { create :gitlab_subscription, :free, *group2_traits, namespace: group2 }
let!(:subscription_subgroup2) { create :gitlab_subscription, :free, *subgroup2_traits, namespace: subgroup2 }
let!(:subscription_subsubgroup1) { create :gitlab_subscription, :free, *subsubgroup1_traits, namespace: subsubgroup1 }
context 'and none of the groups have been trialed yet' do
it { is_expected.to eq(all_groups) }
end
context 'and some of the groups have been trialed' do
let(:trialed_group1) { true }
let(:trialed_subgroup1) { true }
let(:trialed_subgroup2) { true }
context 'and some of the groups are being or have been trialed' do
let(:group1_traits) { :active_trial }
let(:subgroup1_traits) { :expired_trial }
let(:subgroup2_traits) { :active_trial }
let(:some_groups) { [group2, subsubgroup1].map {|g| [g.name, g.id]} }
let(:some_groups) { [group2, subsubgroup1].map(&:id) }
it { is_expected.to eq(some_groups) }
end
context 'and all of the groups have already been trialed' do
let(:trialed_group1) { true }
let(:trialed_subgroup1) { true }
let(:trialed_group2) { true }
let(:trialed_subgroup2) { true }
let(:trialed_subsubgroup1) { true }
context 'and all of the groups are being or have been trialed' do
let(:group1_traits) { :expired_trial }
let(:subgroup1_traits) { :active_trial }
let(:group2_traits) { :expired_trial }
let(:subgroup2_traits) { :active_trial }
let(:subsubgroup1_traits) { :expired_trial }
it { is_expected.to eq(no_groups) }
end
......@@ -158,6 +115,38 @@ RSpec.describe EE::TrialHelper do
end
end
describe '#trial_user_namespaces' do
let_it_be(:user) { create :user }
let(:user_eligible_for_trial_result) { [user.namespace] }
let(:user_ineligible_for_trial_result) { [] }
before do
allow(helper).to receive(:current_user).and_return(user)
allow(::Gitlab).to receive(:com?).and_return(true)
end
subject { helper.trial_user_namespaces }
context 'when the user has no subscription on their namespace' do
it { is_expected.to eq(user_eligible_for_trial_result) }
end
context 'when the user has a subscription on their namespace' do
let(:traits) { nil }
let!(:subscription) { create :gitlab_subscription, :free, *traits, namespace: user.namespace }
context 'and the user has not yet trialed their namespace' do
it { is_expected.to eq(user_eligible_for_trial_result) }
end
context 'and the user has already trialed their namespace' do
let(:traits) { :expired_trial }
it { is_expected.to eq(user_ineligible_for_trial_result) }
end
end
end
describe '#show_trial_errors?' do
shared_examples 'shows errors based on trial generation result' do
where(:trial_result, :expected_result) do
......
......@@ -13,8 +13,6 @@ RSpec.describe Gitlab::Geo::GeoNodeStatusCheck do
describe '#replication_verification_complete?' do
before do
allow(Gitlab.config.geo.registry_replication).to receive(:enabled).and_return(true)
stub_feature_flags(geo_vulnerability_export_replication: false)
end
it 'prints messages for all verification checks' do
......
......@@ -346,11 +346,11 @@ RSpec.describe Gitlab::Geo, :geo, :request_store do
context 'when replication is disabled' do
before do
stub_feature_flags(geo_vulnerability_export_replication: false)
stub_feature_flags(geo_package_file_replication: false)
end
it 'does not return the corresponding replicator class' do
expect(described_class.replicator_classes).not_to include(Geo::VulnerabilityExportReplicator)
it 'does not return the replicator class' do
expect(described_class.replicator_classes).not_to include(Geo::PackageFileReplicator)
end
end
end
......
......@@ -6,8 +6,8 @@ RSpec.describe Security::CiConfiguration::SastBuildActions do
context 'autodevops disabled' do
let(:auto_devops_enabled) { false }
context 'with no paramaters' do
let(:params) { {} }
context 'with one empty parameter' do
let(:params) { { 'SECURE_ANALYZERS_PREFIX' => '' } }
subject(:result) { described_class.new(auto_devops_enabled, params).generate }
......@@ -18,7 +18,7 @@ RSpec.describe Security::CiConfiguration::SastBuildActions do
context 'with all parameters' do
let(:params) do
{ stage: 'security',
{ 'stage' => 'security',
'SEARCH_MAX_DEPTH' => 1,
'SECURE_ANALYZERS_PREFIX' => 'localhost:5000/analyzers',
'SAST_ANALYZER_IMAGE_TAG' => 2,
......@@ -35,7 +35,7 @@ RSpec.describe Security::CiConfiguration::SastBuildActions do
context 'with autodevops enabled' do
let(:auto_devops_enabled) { true }
let(:params) { { stage: 'custom stage' } }
let(:params) { { 'stage' => 'custom stage' } }
subject(:result) { described_class.new(auto_devops_enabled, params).generate }
......
......@@ -18,7 +18,7 @@ RSpec.describe AuditEvent, type: :model do
let_it_be(:details) do
{ author_name: 'Kungfu Panda', entity_path: 'gitlab-org/gitlab', target_details: 'Project X' }
end
let_it_be(:event) { create(:project_audit_event, details: details, target_details: nil) }
let_it_be(:event) { create(:project_audit_event, details: details, entity_path: nil, target_details: nil) }
it 'sets author_name' do
expect(event[:author_name]).to eq('Kungfu Panda')
......@@ -33,30 +33,66 @@ RSpec.describe AuditEvent, type: :model do
end
end
describe '#truncate_target_details' do
where(:database_column, :details_value, :expected_value) do
text_limit = described_class::TEXT_LIMIT[:target_details]
long_value = 'a' * (text_limit + 1)
truncated_long_value = long_value.truncate(text_limit)
short_value = 'a' * text_limit
[
[nil, nil, nil],
[long_value, nil, truncated_long_value],
[short_value, nil, short_value],
[nil, long_value, truncated_long_value],
[nil, short_value, short_value],
[long_value, 'something', truncated_long_value]
]
end
with_them do
let(:audit_event) { create(:audit_event, target_details: database_column, details: { target_details: details_value }) }
it 'expects both values to be the same and correct' do
expect(audit_event.target_details).to eq(expected_value)
expect(audit_event.details[:target_details]).to eq(expected_value)
context 'truncate_fields' do
shared_examples 'a truncated field' do
context 'when values are provided' do
using RSpec::Parameterized::TableSyntax
where(:database_column, :details_value, :expected_value) do
:long | nil | :truncated
:short | nil | :short
nil | :long | :truncated
nil | :short | :short
:long | :short | :truncated
end
with_them do
let(:values) do
{
long: 'a' * (field_limit + 1),
short: 'a' * field_limit,
truncated: 'a' * (field_limit - 3) + '...'
}
end
let(:audit_event) do
create(:audit_event,
field_name => values[database_column],
details: { field_name => values[details_value] }
)
end
it 'sets both values to be the same', :aggregate_failures do
expect(audit_event.send(field_name)).to eq(values[expected_value])
expect(audit_event.details[field_name]).to eq(values[expected_value])
end
end
end
context 'when values are not provided' do
let(:audit_event) do
create(:audit_event, field_name => nil, details: {})
end
it 'does not set', :aggregate_failures do
expect(audit_event.send(field_name)).to be_nil
expect(audit_event.details).not_to have_key(field_name)
end
end
end
context 'entity_path' do
let(:field_name) { :entity_path }
let(:field_limit) { 5_500 }
it_behaves_like 'a truncated field'
end
context 'target_details' do
let(:field_name) { :target_details }
let(:field_limit) { 5_500 }
it_behaves_like 'a truncated field'
end
end
end
......
......@@ -170,6 +170,60 @@ RSpec.describe Namespace do
end
end
end
describe '.eligible_for_trial' do
let_it_be(:namespace) { create :namespace }
subject { described_class.eligible_for_trial.first }
context 'when there is no subscription' do
it { is_expected.to eq(namespace) }
end
context 'when there is a subscription' do
context 'with a plan that is eligible for a trial' do
where(plan: ::Plan::PLANS_ELIGIBLE_FOR_TRIAL)
with_them do
context 'and has not yet been trialed' do
before do
create :gitlab_subscription, plan, namespace: namespace
end
it { is_expected.to eq(namespace) }
end
context 'but has already had a trial' do
before do
create :gitlab_subscription, plan, :expired_trial, namespace: namespace
end
it { is_expected.to be_nil }
end
context 'but is currently being trialed' do
before do
create :gitlab_subscription, plan, :active_trial, namespace: namespace
end
it { is_expected.to be_nil }
end
end
end
context 'with a plan that is ineligible for a trial' do
where(plan: ::Plan::PAID_HOSTED_PLANS)
with_them do
before do
create :gitlab_subscription, plan, namespace: namespace
end
it { is_expected.to be_nil }
end
end
end
end
end
context 'validation' do
......@@ -1262,6 +1316,36 @@ RSpec.describe Namespace do
end
end
describe '#eligible_for_trial?' do
subject { namespace.eligible_for_trial? }
where(
on_dot_com: [true, false],
has_parent: [true, false],
never_had_trial: [true, false],
plan_eligible_for_trial: [true, false]
)
with_them do
before do
allow(Gitlab).to receive(:com?).and_return(on_dot_com)
allow(namespace).to receive(:has_parent?).and_return(has_parent)
allow(namespace).to receive(:never_had_trial?).and_return(never_had_trial)
allow(namespace).to receive(:plan_eligible_for_trial?).and_return(plan_eligible_for_trial)
end
context "when#{' not' unless params[:on_dot_com]} on .com" do
context "and the namespace #{params[:has_parent] ? 'has' : 'is'} a parent namespace" do
context "and the namespace has#{' not yet' if params[:never_had_trial]} been trialed" do
context "and the namespace is#{' not' unless params[:plan_eligible_for_trial]} eligible for a trial" do
it { is_expected.to eq(on_dot_com && !has_parent && never_had_trial && plan_eligible_for_trial) }
end
end
end
end
end
end
describe '#file_template_project_id' do
it 'is cleared before validation' do
project = create(:project, namespace: namespace)
......
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Geo::VulnerabilityExportRegistry, :geo, type: :model do
let_it_be(:registry) { create(:geo_vulnerability_export_registry) }
specify 'factory is valid' do
expect(registry).to be_valid
end
include_examples 'a Geo framework registry'
end
......@@ -1052,9 +1052,9 @@ RSpec.describe User do
describe '#manageable_groups_eligible_for_trial' do
let_it_be(:user) { create :user }
let_it_be(:non_trialed_group_z) { create :group, name: 'Zeta', gitlab_subscription: create(:gitlab_subscription) }
let_it_be(:non_trialed_group_a) { create :group, name: 'Alpha', gitlab_subscription: create(:gitlab_subscription) }
let_it_be(:trialed_group) { create :group, name: 'Omitted', gitlab_subscription: create(:gitlab_subscription, trial: true) }
let_it_be(:non_trialed_group_z) { create :group, name: 'Zeta', gitlab_subscription: create(:gitlab_subscription, :free) }
let_it_be(:non_trialed_group_a) { create :group, name: 'Alpha', gitlab_subscription: create(:gitlab_subscription, :free) }
let_it_be(:trialed_group) { create :group, name: 'Omitted', gitlab_subscription: create(:gitlab_subscription, :free, trial: true) }
subject { user.manageable_groups_eligible_for_trial }
......
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Geo::VulnerabilityExportReplicator do
let(:model_record) { build(:vulnerability_export, :with_csv_file) }
it_behaves_like 'a blob replicator'
end
......@@ -9,6 +9,7 @@ RSpec.describe 'Creating a DAST Scanner Profile' do
let(:current_user) { create(:user) }
let(:full_path) { project.full_path }
let(:profile_name) { FFaker::Company.catch_phrase }
let(:dast_scanner_profile) { DastScannerProfile.find_by(project: project, name: profile_name) }
let(:mutation) do
graphql_mutation(
......@@ -43,7 +44,23 @@ RSpec.describe 'Creating a DAST Scanner Profile' do
project.add_developer(current_user)
end
it_behaves_like 'a mutation that returns errors in the response', errors: ['Not implemented']
it 'returns the dast_scanner_profile id' do
post_graphql_mutation(mutation, current_user: current_user)
expect(mutation_response["id"]).to eq(dast_scanner_profile.to_global_id.to_s)
end
context 'when dast_scanner_profile exists' do
before do
DastScannerProfile.create!(project: project, name: profile_name)
end
it 'returns errors' do
post_graphql_mutation(mutation, current_user: current_user)
expect(mutation_response["errors"]).to include('Name has already been taken')
end
end
context 'when on demand scan feature is disabled' do
before do
......
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe DastScannerProfiles::CreateService do
let(:user) { create(:user) }
let(:project) { create(:project, :repository, creator: user) }
let(:name) { FFaker::Company.catch_phrase }
let(:target_timeout) { 60 }
let(:spider_timeout) { 600 }
describe '#execute' do
subject do
described_class.new(project, user).execute(
name: name,
target_timeout: target_timeout,
spider_timeout: spider_timeout
)
end
let(:status) { subject.status }
let(:message) { subject.message }
let(:errors) { subject.errors }
let(:payload) { subject.payload }
context 'when a user does not have access to a project' do
let(:project) { create(:project) }
it 'returns an error status' do
expect(status).to eq(:error)
end
it 'populates message' do
expect(message).to eq('Insufficient permissions')
end
end
context 'when the user does not have permission to run a dast scan' do
before do
project.add_guest(user)
end
it 'returns an error status' do
expect(status).to eq(:error)
end
it 'populates message' do
expect(message).to eq('Insufficient permissions')
end
end
context 'when the user can run a dast scan' do
before do
project.add_developer(user)
end
it 'returns a success status' do
expect(status).to eq(:success)
end
it 'creates a dast_scanner_profile' do
expect { subject }.to change(DastScannerProfile, :count).by(1)
end
it 'returns a dast_scanner_profile payload' do
expect(payload).to be_a(DastScannerProfile)
end
context 'when the dast_scanner_profile name exists' do
before do
create(:dast_scanner_profile, project: project, name: name)
end
it 'does not create a new dast_scanner_profile' do
expect { subject }.not_to change(DastScannerProfile, :count)
end
it 'returns an error status' do
expect(status).to eq(:error)
end
it 'populates message' do
expect(message).to eq(['Name has already been taken'])
end
end
end
end
end
......@@ -10,7 +10,6 @@ RSpec.describe EE::AuditEvents::CustomAuditEventService do
let(:entity) { create(:project) }
let(:entity_type) { 'Project' }
let(:custom_message) { 'Custom Event' }
let(:target_details) { nil }
let(:service) { described_class.new(user, entity, ip_address, custom_message) }
end
end
......
......@@ -7,7 +7,6 @@ RSpec.describe EE::AuditEvents::ImpersonationAuditEventService do
let(:ip_address) { '127.0.0.1' }
let(:message) { 'Impersonation Started' }
let(:logger) { instance_double(Gitlab::AuditJsonLogger) }
let(:target_details) { nil }
let(:service) { described_class.new(impersonator, ip_address, message) }
describe '#security_event' do
......@@ -30,8 +29,7 @@ RSpec.describe EE::AuditEvents::ImpersonationAuditEventService do
expect(security_event.details).to eq(custom_message: message,
ip_address: ip_address,
action: :custom,
target_details: target_details)
action: :custom)
expect(security_event.author_id).to eq(impersonator.id)
expect(security_event.entity_id).to eq(impersonator.id)
expect(security_event.entity_type).to eq('User')
......
......@@ -10,7 +10,6 @@ RSpec.describe EE::AuditEvents::RepositoryDownloadStartedAuditEventService do
let(:entity) { create(:project) }
let(:entity_type) { 'Project' }
let(:custom_message) { 'Repository Download Started' }
let(:target_details) { nil }
let(:service) { described_class.new(user, entity, ip_address) }
end
end
......
......@@ -15,7 +15,6 @@ RSpec.describe Geo::RegistryConsistencyService, :geo, :use_clean_rails_memory_st
def model_class_factory_name(registry_class)
return :project_with_design if registry_class == ::Geo::DesignRegistry
return :package_file_with_file if registry_class == ::Geo::PackageFileRegistry
return :vulnerability_export if registry_class == ::Geo::VulnerabilityExportRegistry
registry_class::MODEL_CLASS.underscore.tr('/', '_').to_sym
end
......
......@@ -62,7 +62,6 @@ RSpec.shared_examples 'logs the custom audit event' do
expect(security_event.details).to eq(custom_message: custom_message,
ip_address: ip_address,
target_details: target_details,
action: :custom)
expect(security_event.author_id).to eq(user.id)
expect(security_event.entity_id).to eq(entity.id)
......
......@@ -341,7 +341,6 @@ RSpec.describe 'geo rake tasks', :geo do
before do
stub_licensed_features(geo: true)
stub_current_geo_node(current_node)
stub_feature_flags(geo_vulnerability_export_replication: false)
allow(GeoNodeStatus).to receive(:current_node_status).and_return(geo_node_status)
allow(Gitlab.config.geo.registry_replication).to receive(:enabled).and_return(true)
......
......@@ -8,7 +8,6 @@ RSpec.describe 'gitlab:geo rake tasks', :geo do
before do
Rake.application.rake_require 'tasks/gitlab/geo'
stub_licensed_features(geo: true)
stub_feature_flags(geo_vulnerability_export_replication: false)
end
describe 'gitlab:geo:check_replication_verification_status' do
......
......@@ -84,7 +84,6 @@ RSpec.describe Geo::Secondary::RegistryConsistencyWorker, :geo do
create(:design, project: project)
upload = create(:upload)
package_file = create(:conan_package_file, :conan_package)
vulnerability_export = create(:vulnerability_export, :with_csv_file)
container_repository = create(:container_repository, project: project)
expect(Geo::LfsObjectRegistry.where(lfs_object_id: lfs_object.id).count).to eq(0)
......@@ -93,7 +92,6 @@ RSpec.describe Geo::Secondary::RegistryConsistencyWorker, :geo do
expect(Geo::DesignRegistry.where(project_id: project.id).count).to eq(0)
expect(Geo::UploadRegistry.where(file_id: upload.id).count).to eq(0)
expect(Geo::PackageFileRegistry.where(package_file_id: package_file.id).count).to eq(0)
expect(Geo::VulnerabilityExportRegistry.where(vulnerability_export_id: vulnerability_export.id).count).to eq(0)
expect(Geo::ContainerRepositoryRegistry.where(container_repository_id: container_repository.id).count).to eq(0)
subject.perform
......@@ -104,7 +102,6 @@ RSpec.describe Geo::Secondary::RegistryConsistencyWorker, :geo do
expect(Geo::DesignRegistry.where(project_id: project.id).count).to eq(1)
expect(Geo::UploadRegistry.where(file_id: upload.id).count).to eq(1)
expect(Geo::PackageFileRegistry.where(package_file_id: package_file.id).count).to eq(1)
expect(Geo::VulnerabilityExportRegistry.where(vulnerability_export_id: vulnerability_export.id).count).to eq(1)
expect(Geo::ContainerRepositoryRegistry.where(container_repository_id: container_repository.id).count).to eq(1)
end
......
......@@ -82,7 +82,7 @@ module Gitlab
diff = floored_offset_hours - author.floored_offset_hours
return "same timezone as `@#{author.username}`" if diff.zero?
ahead_or_behind = diff < 0 ? 'behind' : 'ahead'
ahead_or_behind = diff < 0 ? 'behind' : 'ahead of'
pluralized_hours = pluralize(diff.abs, 'hour', 'hours')
"#{pluralized_hours} #{ahead_or_behind} `@#{author.username}`"
......
......@@ -55,7 +55,7 @@ describe('Error message', () => {
'Upload skipped. Some of the designs you tried uploading did not change: 1.jpg, 2.jpg, 3.jpg, 4.jpg, 5.jpg, and 2 more.',
],
])('designUploadSkippedWarning', (uploadedFiles, skippedFiles, expected) => {
test('returns expected warning message', () => {
it('returns expected warning message', () => {
expect(designUploadSkippedWarning(uploadedFiles, skippedFiles)).toBe(expected);
});
});
......
......@@ -9,7 +9,7 @@ describe('tokenization for .vue files', () => {
registerLanguages(vue);
});
test.each([
it.each([
[
'<div v-if="something">content</div>',
[
......
......@@ -5,7 +5,7 @@ import * as getters from '~/ide/stores/modules/file_templates/getters';
describe('IDE file templates getters', () => {
describe('templateTypes', () => {
it('returns list of template types', () => {
expect(getters.templateTypes().length).toBe(4);
expect(getters.templateTypes().length).toBe(5);
});
});
......
......@@ -436,7 +436,7 @@ describe('Dashboard header', () => {
});
});
test.each(systemDashboards)('is rendered for system dashboards', dashboardPath => {
it.each(systemDashboards)('is rendered for system dashboards', dashboardPath => {
setupAllDashboards(store, dashboardPath);
return wrapper.vm.$nextTick(() => {
......@@ -444,7 +444,7 @@ describe('Dashboard header', () => {
});
});
test.each(nonSystemDashboards)('is not rendered for non-system dashboards', dashboardPath => {
it.each(nonSystemDashboards)('is not rendered for non-system dashboards', dashboardPath => {
setupAllDashboards(store, dashboardPath);
return wrapper.vm.$nextTick(() => {
......
......@@ -24,7 +24,7 @@ describe('GroupEmptyState', () => {
'FOO STATE', // does not fail with unknown states
];
test.each(supportedStates)('Renders an empty state for %s', selectedState => {
it.each(supportedStates)('Renders an empty state for %s', selectedState => {
const wrapper = createComponent({ selectedState });
expect(wrapper.element).toMatchSnapshot();
......
......@@ -192,12 +192,12 @@ RSpec.describe Gitlab::Danger::Teammate do
context 'when author is given' do
where(:tz_offset_hours, :author_offset, :diff_text) do
-12 | -10 | "2 hours behind `@mario`"
-10 | -12 | "2 hours ahead `@mario`"
-10 | -12 | "2 hours ahead of `@mario`"
-10 | 2 | "12 hours behind `@mario`"
2 | 4 | "2 hours behind `@mario`"
4 | 2 | "2 hours ahead `@mario`"
4 | 2 | "2 hours ahead of `@mario`"
2 | 3 | "1 hour behind `@mario`"
3 | 2 | "1 hour ahead `@mario`"
3 | 2 | "1 hour ahead of `@mario`"
2 | 2 | "same timezone as `@mario`"
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