Commit 9b98dbb4 authored by GitLab Bot's avatar GitLab Bot

Automatic merge of gitlab-org/gitlab master

parents f996e767 5912f3de
...@@ -7,4 +7,5 @@ fragment TimelogFragment on Timelog { ...@@ -7,4 +7,5 @@ fragment TimelogFragment on Timelog {
note { note {
body body
} }
summary
} }
...@@ -62,8 +62,8 @@ export default { ...@@ -62,8 +62,8 @@ export default {
formatDate(date) { formatDate(date) {
return formatDate(date, TIME_DATE_FORMAT); return formatDate(date, TIME_DATE_FORMAT);
}, },
getNote(note) { getSummary(summary, note) {
return note?.body; return summary ?? note?.body;
}, },
getTotalTimeSpent() { getTotalTimeSpent() {
const seconds = this.report.reduce((acc, item) => acc + item.timeSpent, 0); const seconds = this.report.reduce((acc, item) => acc + item.timeSpent, 0);
...@@ -81,7 +81,7 @@ export default { ...@@ -81,7 +81,7 @@ export default {
{ key: 'spentAt', label: __('Spent At'), sortable: true }, { key: 'spentAt', label: __('Spent At'), sortable: true },
{ key: 'user', label: __('User'), sortable: true }, { key: 'user', label: __('User'), sortable: true },
{ key: 'timeSpent', label: __('Time Spent'), sortable: true }, { key: 'timeSpent', label: __('Time Spent'), sortable: true },
{ key: 'note', label: __('Note'), sortable: true }, { key: 'summary', label: __('Summary / Note'), sortable: true },
], ],
}; };
</script> </script>
...@@ -107,8 +107,8 @@ export default { ...@@ -107,8 +107,8 @@ export default {
<div>{{ getTotalTimeSpent() }}</div> <div>{{ getTotalTimeSpent() }}</div>
</template> </template>
<template #cell(note)="{ item: { note } }"> <template #cell(summary)="{ item: { summary, note } }">
<div>{{ getNote(note) }}</div> <div>{{ getSummary(summary, note) }}</div>
</template> </template>
<template #foot(note)>&nbsp;</template> <template #foot(note)>&nbsp;</template>
</gl-table> </gl-table>
......
...@@ -36,6 +36,10 @@ module Types ...@@ -36,6 +36,10 @@ module Types
null: true, null: true,
description: 'The note where the quick action to add the logged time was executed.' description: 'The note where the quick action to add the logged time was executed.'
field :summary, GraphQL::Types::String,
null: true,
description: 'The summary of how the time was spent.'
def user def user
Gitlab::Graphql::Loaders::BatchModelLoader.new(User, object.user_id).find Gitlab::Graphql::Loaders::BatchModelLoader.new(User, object.user_id).find
end end
......
...@@ -175,6 +175,8 @@ class Member < ApplicationRecord ...@@ -175,6 +175,8 @@ class Member < ApplicationRecord
after_update :post_update_hook, unless: [:pending?, :importing?], if: :hook_prerequisites_met? after_update :post_update_hook, unless: [:pending?, :importing?], if: :hook_prerequisites_met?
after_destroy :destroy_notification_setting after_destroy :destroy_notification_setting
after_destroy :post_destroy_hook, unless: :pending?, if: :hook_prerequisites_met? after_destroy :post_destroy_hook, unless: :pending?, if: :hook_prerequisites_met?
after_save :log_invitation_token_cleanup
after_commit :refresh_member_authorized_projects after_commit :refresh_member_authorized_projects
default_value_for :notification_level, NotificationSetting.levels[:global] default_value_for :notification_level, NotificationSetting.levels[:global]
...@@ -449,6 +451,13 @@ class Member < ApplicationRecord ...@@ -449,6 +451,13 @@ class Member < ApplicationRecord
def project_bot? def project_bot?
user&.project_bot? user&.project_bot?
end end
def log_invitation_token_cleanup
return true unless Gitlab.com? && invite? && invite_accepted_at?
error = StandardError.new("Invitation token is present but invite was already accepted!")
Gitlab::ErrorTracking.track_exception(error, attributes.slice(%w["invite_accepted_at created_at source_type source_id user_id id"]))
end
end end
Member.prepend_mod_with('Member') Member.prepend_mod_with('Member')
...@@ -587,7 +587,7 @@ class Note < ApplicationRecord ...@@ -587,7 +587,7 @@ class Note < ApplicationRecord
end end
def post_processed_cache_key def post_processed_cache_key
cache_key_items = [cache_key] cache_key_items = [cache_key, author.cache_key]
cache_key_items << Digest::SHA1.hexdigest(redacted_note_html) if redacted_note_html.present? cache_key_items << Digest::SHA1.hexdigest(redacted_note_html) if redacted_note_html.present?
cache_key_items.join(':') cache_key_items.join(':')
......
...@@ -11,11 +11,12 @@ module Ci ...@@ -11,11 +11,12 @@ module Ci
Gitlab::Ci::Pipeline::Chain::Validate::Abilities, Gitlab::Ci::Pipeline::Chain::Validate::Abilities,
Gitlab::Ci::Pipeline::Chain::Validate::Repository, Gitlab::Ci::Pipeline::Chain::Validate::Repository,
Gitlab::Ci::Pipeline::Chain::Validate::SecurityOrchestrationPolicy, Gitlab::Ci::Pipeline::Chain::Validate::SecurityOrchestrationPolicy,
Gitlab::Ci::Pipeline::Chain::Skip,
Gitlab::Ci::Pipeline::Chain::Config::Content, Gitlab::Ci::Pipeline::Chain::Config::Content,
Gitlab::Ci::Pipeline::Chain::Config::Process, Gitlab::Ci::Pipeline::Chain::Config::Process,
Gitlab::Ci::Pipeline::Chain::Validate::AfterConfig, Gitlab::Ci::Pipeline::Chain::Validate::AfterConfig,
Gitlab::Ci::Pipeline::Chain::RemoveUnwantedChatJobs, Gitlab::Ci::Pipeline::Chain::RemoveUnwantedChatJobs,
Gitlab::Ci::Pipeline::Chain::Skip, Gitlab::Ci::Pipeline::Chain::LegacySkip,
Gitlab::Ci::Pipeline::Chain::SeedBlock, Gitlab::Ci::Pipeline::Chain::SeedBlock,
Gitlab::Ci::Pipeline::Chain::EvaluateWorkflowRules, Gitlab::Ci::Pipeline::Chain::EvaluateWorkflowRules,
Gitlab::Ci::Pipeline::Chain::Seed, Gitlab::Ci::Pipeline::Chain::Seed,
......
...@@ -28,11 +28,12 @@ module Users ...@@ -28,11 +28,12 @@ module Users
params[:emoji] = UserStatus::DEFAULT_EMOJI if params[:emoji].blank? params[:emoji] = UserStatus::DEFAULT_EMOJI if params[:emoji].blank?
params[:availability] = UserStatus.availabilities[:not_set] unless new_user_availability params[:availability] = UserStatus.availabilities[:not_set] unless new_user_availability
user_status.update(params) bump_user if user_status.update(params)
end end
def remove_status def remove_status
UserStatus.delete(target_user.id) bump_user if UserStatus.delete(target_user.id).nonzero?
true
end end
def user_status def user_status
...@@ -48,5 +49,12 @@ module Users ...@@ -48,5 +49,12 @@ module Users
def new_user_availability def new_user_availability
UserStatus.availabilities[params[:availability]] UserStatus.availabilities[params[:availability]]
end end
def bump_user
# Intentionally not calling `touch` as that will trigger other callbacks
# on target_user (e.g. after_touch, after_commit, after_rollback) and we
# don't need them to happen here.
target_user.update_column(:updated_at, Time.current)
end
end end
end end
= render_if_exists "layouts/nav/ee/security_link" # EE-specific
= render_if_exists "layouts/nav/ee/push_rules_link" # EE-specific = render_if_exists "layouts/nav/ee/push_rules_link" # EE-specific
- if group_sidebar_link?(:runners) - if group_sidebar_link?(:runners)
......
---
name: ci_skip_before_parsing_yaml
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/66147
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/337167
milestone: '14.2'
type: development
group: group::pipeline execution
default_enabled: false
...@@ -13482,6 +13482,7 @@ Represents a historically accurate report about the timebox. ...@@ -13482,6 +13482,7 @@ Represents a historically accurate report about the timebox.
| <a id="timelogmergerequest"></a>`mergeRequest` | [`MergeRequest`](#mergerequest) | The merge request that logged time was added to. | | <a id="timelogmergerequest"></a>`mergeRequest` | [`MergeRequest`](#mergerequest) | The merge request that logged time was added to. |
| <a id="timelognote"></a>`note` | [`Note`](#note) | The note where the quick action to add the logged time was executed. | | <a id="timelognote"></a>`note` | [`Note`](#note) | The note where the quick action to add the logged time was executed. |
| <a id="timelogspentat"></a>`spentAt` | [`Time`](#time) | Timestamp of when the time tracked was spent at. | | <a id="timelogspentat"></a>`spentAt` | [`Time`](#time) | Timestamp of when the time tracked was spent at. |
| <a id="timelogsummary"></a>`summary` | [`String`](#string) | The summary of how the time was spent. |
| <a id="timelogtimespent"></a>`timeSpent` | [`Int!`](#int) | The time spent displayed in seconds. | | <a id="timelogtimespent"></a>`timeSpent` | [`Int!`](#int) | The time spent displayed in seconds. |
| <a id="timeloguser"></a>`user` | [`UserCore!`](#usercore) | The user that logged the time. | | <a id="timeloguser"></a>`user` | [`UserCore!`](#usercore) | The user that logged the time. |
......
doc/user/workspace/img/1.3-Admin.png

31.8 KB | W: | H:

doc/user/workspace/img/1.3-Admin.png

15.7 KB | W: | H:

doc/user/workspace/img/1.3-Admin.png
doc/user/workspace/img/1.3-Admin.png
doc/user/workspace/img/1.3-Admin.png
doc/user/workspace/img/1.3-Admin.png
  • 2-up
  • Swipe
  • Onion skin
...@@ -11,5 +11,12 @@ module EE ...@@ -11,5 +11,12 @@ module EE
show_discover_project_security: show_discover_project_security?(project) show_discover_project_security: show_discover_project_security?(project)
}) })
end end
override :group_sidebar_context_data
def group_sidebar_context_data(group, user)
super.merge(
show_discover_group_security: show_discover_group_security?(group)
)
end
end end
end end
# frozen_string_literal: true # frozen_string_literal: true
module Groups::SecurityFeaturesHelper module Groups::SecurityFeaturesHelper
def group_level_security_dashboard_available?(group)
group.licensed_feature_available?(:security_dashboard)
end
def group_level_compliance_dashboard_available?(group) def group_level_compliance_dashboard_available?(group)
group.licensed_feature_available?(:group_level_compliance_dashboard) && group.licensed_feature_available?(:group_level_compliance_dashboard) &&
can?(current_user, :read_group_compliance_dashboard, group) can?(current_user, :read_group_compliance_dashboard, group)
...@@ -20,23 +16,6 @@ module Groups::SecurityFeaturesHelper ...@@ -20,23 +16,6 @@ module Groups::SecurityFeaturesHelper
group.enforced_group_managed_accounts? group.enforced_group_managed_accounts?
end end
def primary_group_level_security_feature_path(group)
if group_level_security_dashboard_available?(group)
group_security_dashboard_path(group)
elsif group_level_compliance_dashboard_available?(group)
group_security_compliance_dashboard_path(group)
elsif group_level_credentials_inventory_available?(group)
group_security_credentials_path(group)
elsif group_level_audit_events_available?(group)
group_audit_events_path(group)
end
end
def group_level_audit_events_available?(group)
group.licensed_feature_available?(:audit_events) &&
can?(current_user, :read_group_audit_events, group)
end
def group_level_security_dashboard_data(group) def group_level_security_dashboard_data(group)
{ {
projects_endpoint: expose_url(api_v4_groups_projects_path(id: group.id)), projects_endpoint: expose_url(api_v4_groups_projects_path(id: group.id)),
......
- main_path = primary_group_level_security_feature_path(@group)
- if main_path.present?
= nav_link(path: %w[dashboard#show vulnerabilities#index compliance_dashboards#show credentials#index audit_events#index]) do
= link_to main_path, data: { qa_selector: 'security_compliance_link' }, class: 'has-sub-items' do
.nav-icon-container
= sprite_icon('shield')
%span.nav-item-name
= _('Security & Compliance')
%ul.sidebar-sub-level-items{ data: { qa_selector: 'group_secure_submenu' } }
= nav_link(path: %w[dashboard#show vulnerabilities#index compliance_dashboards#show credentials#index audit_events#index], html_options: { class: "fly-out-top-item" } ) do
%span.fly-out-top-item-container
%strong.fly-out-top-item-name
= _('Security & Compliance')
%li.divider.fly-out-top-item
- if group_level_security_dashboard_available?(@group)
= nav_link(path: 'dashboard#show') do
= link_to group_security_dashboard_path(@group), title: _('Security Dashboard'), data: { qa_selector: 'security_dashboard_link' } do
%span= _('Security Dashboard')
- if group_level_security_dashboard_available?(@group)
= nav_link(path: 'vulnerabilities#index') do
= link_to group_security_vulnerabilities_path(@group), title: _('Vulnerability Report'), data: { qa_selector: 'vulnerability_report_link' } do
%span= _('Vulnerability Report')
- if group_level_compliance_dashboard_available?(@group)
= nav_link(path: 'compliance_dashboards#show') do
= link_to group_security_compliance_dashboard_path(@group), title: _('Compliance') do
%span= _('Compliance')
- if group_level_credentials_inventory_available?(@group)
= nav_link(path: 'credentials#index') do
= link_to group_security_credentials_path(@group), title: _('Credentials') do
%span= _('Credentials')
- if group_level_audit_events_available?(@group)
= nav_link(path: 'audit_events#index') do
= link_to group_audit_events_path(@group), title: _('Audit Events'), data: { qa_selector: 'audit_events_settings_link' } do
%span= _('Audit Events')
- elsif show_discover_group_security?(@group)
= nav_link(path: group_security_discover_path(@group)) do
= link_to group_security_discover_path(@group) do
.nav-icon-container
= sprite_icon('shield')
%span.nav-item-name
= _('Security')
...@@ -12,6 +12,7 @@ module EE ...@@ -12,6 +12,7 @@ module EE
insert_menu_before(::Sidebars::Groups::Menus::GroupInformationMenu, ::Sidebars::Groups::Menus::TrialExperimentMenu.new(context)) insert_menu_before(::Sidebars::Groups::Menus::GroupInformationMenu, ::Sidebars::Groups::Menus::TrialExperimentMenu.new(context))
insert_menu_after(::Sidebars::Groups::Menus::GroupInformationMenu, ::Sidebars::Groups::Menus::EpicsMenu.new(context)) insert_menu_after(::Sidebars::Groups::Menus::GroupInformationMenu, ::Sidebars::Groups::Menus::EpicsMenu.new(context))
insert_menu_after(::Sidebars::Groups::Menus::MergeRequestsMenu, ::Sidebars::Groups::Menus::SecurityComplianceMenu.new(context))
end end
end end
end end
......
# frozen_string_literal: true
module Sidebars
module Groups
module Menus
class SecurityComplianceMenu < ::Sidebars::Menu
override :configure_menu_items
def configure_menu_items
add_item(security_dashboard_menu_item)
add_item(vulnerability_report_menu_item)
add_item(compliance_menu_item)
add_item(credentials_menu_item)
add_item(audit_events_menu_item)
true
end
override :link
def link
return renderable_items.first.link if renderable_items.any?
group_security_discover_path(context.group)
end
override :title
def title
renderable_items.any? ? _('Security & Compliance') : _('Security')
end
override :sprite_icon
def sprite_icon
'shield'
end
override :render?
def render?
super || context.show_discover_group_security
end
override :active_routes
def active_routes
return {} if renderable_items.empty?
{ page: link }
end
private
def security_dashboard_menu_item
unless context.group.licensed_feature_available?(:security_dashboard)
return ::Sidebars::NilMenuItem.new(item_id: :security_dashboard)
end
::Sidebars::MenuItem.new(
title: _('Security Dashboard'),
link: group_security_dashboard_path(context.group),
active_routes: { path: 'dashboard#show' },
item_id: :security_dashboard
)
end
def vulnerability_report_menu_item
unless context.group.licensed_feature_available?(:security_dashboard)
return ::Sidebars::NilMenuItem.new(item_id: :vulnerability_report)
end
::Sidebars::MenuItem.new(
title: _('Vulnerability Report'),
link: group_security_vulnerabilities_path(context.group),
active_routes: { path: 'vulnerabilities#index' },
item_id: :vulnerability_report
)
end
def compliance_menu_item
unless group_level_compliance_dashboard_available?
return ::Sidebars::NilMenuItem.new(item_id: :compliance)
end
::Sidebars::MenuItem.new(
title: _('Compliance'),
link: group_security_compliance_dashboard_path(context.group),
active_routes: { path: 'compliance_dashboards#show' },
item_id: :compliance
)
end
def group_level_compliance_dashboard_available?
context.group.licensed_feature_available?(:group_level_compliance_dashboard) &&
can?(context.current_user, :read_group_compliance_dashboard, context.group)
end
def credentials_menu_item
unless group_level_credentials_inventory_available?
return ::Sidebars::NilMenuItem.new(item_id: :credentials)
end
::Sidebars::MenuItem.new(
title: _('Credentials'),
link: group_security_credentials_path(context.group),
active_routes: { path: 'credentials#index' },
item_id: :credentials
)
end
def group_level_credentials_inventory_available?
context.group.licensed_feature_available?(:credentials_inventory) &&
can?(context.current_user, :read_group_credentials_inventory, context.group) &&
context.group.enforced_group_managed_accounts?
end
def audit_events_menu_item
unless group_level_audit_events_available?
return ::Sidebars::NilMenuItem.new(item_id: :audit_events)
end
::Sidebars::MenuItem.new(
title: _('Audit Events'),
link: group_audit_events_path(context.group),
active_routes: { path: 'audit_events#index' },
item_id: :audit_events
)
end
def group_level_audit_events_available?
context.group.licensed_feature_available?(:audit_events) &&
can?(context.current_user, :read_group_audit_events, context.group)
end
end
end
end
end
...@@ -36,7 +36,7 @@ RSpec.describe 'Groups > Audit Events', :js do ...@@ -36,7 +36,7 @@ RSpec.describe 'Groups > Audit Events', :js do
end end
it 'has Audit Events button in head nav bar' do it 'has Audit Events button in head nav bar' do
visit group_security_dashboard_path(group) visit group_audit_events_path(group)
expect(page).to have_link('Audit Events') expect(page).to have_link('Audit Events')
end end
......
...@@ -13,23 +13,6 @@ RSpec.describe Groups::SecurityFeaturesHelper do ...@@ -13,23 +13,6 @@ RSpec.describe Groups::SecurityFeaturesHelper do
allow(helper).to receive(:can?).and_return(false) allow(helper).to receive(:can?).and_return(false)
end end
describe '#group_level_security_dashboard_available?' do
where(:security_dashboard_feature_enabled, :result) do
true | true
false | false
end
with_them do
before do
stub_licensed_features(security_dashboard: security_dashboard_feature_enabled)
end
it 'returns the expected result' do
expect(helper.group_level_security_dashboard_available?(group)).to eq(result)
end
end
end
describe '#group_level_security_dashboard_available?' do describe '#group_level_security_dashboard_available?' do
where(:group_level_compliance_dashboard_enabled, :read_group_compliance_dashboard_permission, :result) do where(:group_level_compliance_dashboard_enabled, :read_group_compliance_dashboard_permission, :result) do
false | false | false false | false | false
...@@ -75,84 +58,6 @@ RSpec.describe Groups::SecurityFeaturesHelper do ...@@ -75,84 +58,6 @@ RSpec.describe Groups::SecurityFeaturesHelper do
end end
end end
describe '#group_level_audit_events_available?' do
where(:audit_events_feature_enabled, :read_group_audit_events_permission, :result) do
true | false | false
true | true | true
false | false | false
false | true | false
end
with_them do
before do
stub_licensed_features(audit_events: audit_events_feature_enabled)
allow(helper).to receive(:can?).with(user, :read_group_audit_events, group)
.and_return(read_group_audit_events_permission)
end
it 'returns the expected result' do
expect(helper.group_level_audit_events_available?(group)).to eq(result)
end
end
end
describe '#primary_group_level_security_feature_path' do
subject { helper.primary_group_level_security_feature_path(group) }
context 'group_level_security_dashboard is available' do
before do
allow(helper).to receive(:group_level_security_dashboard_available?).with(group).and_return(true)
end
it 'returns path to security dashboard' do
expect(subject).to eq(group_security_dashboard_path(group))
end
end
context 'group_level_compliance_dashboard is available' do
before do
allow(helper).to receive(:group_level_compliance_dashboard_available?).with(group).and_return(true)
end
it 'returns path to compliance dashboard' do
expect(subject).to eq(group_security_compliance_dashboard_path(group))
end
end
context 'group_level_credentials_inventory is available' do
before do
allow(helper).to receive(:group_level_credentials_inventory_available?).with(group).and_return(true)
end
it 'returns path to credentials inventory dashboard' do
expect(subject).to eq(group_security_credentials_path(group))
end
end
context 'group_level_audit_events is available' do
before do
allow(helper).to receive(:group_level_audit_events_available?).with(group).and_return(true)
end
it 'returns path to audit events' do
expect(subject).to eq(group_audit_events_path(group))
end
end
context 'when no security features are available' do
before do
allow(helper).to receive(:group_level_security_dashboard_available?).with(group).and_return(false)
allow(helper).to receive(:group_level_compliance_dashboard_available?).with(group).and_return(false)
allow(helper).to receive(:group_level_credentials_inventory_available?).with(group).and_return(false)
allow(helper).to receive(:group_level_audit_events_available?).with(group).and_return(false)
end
it 'returns nil' do
expect(subject).to be_nil
end
end
end
describe '#group_level_security_dashboard_data' do describe '#group_level_security_dashboard_data' do
subject { helper.group_level_security_dashboard_data(group) } subject { helper.group_level_security_dashboard_data(group) }
......
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Sidebars::Groups::Menus::SecurityComplianceMenu do
let_it_be(:owner) { create(:user) }
let_it_be_with_refind(:group) do
create(:group, :private).tap do |g|
g.add_owner(owner)
end
end
let(:user) { owner }
let(:show_group_discover_security) { false }
let(:context) { Sidebars::Groups::Context.new(current_user: user, container: group, show_discover_group_security: show_group_discover_security) }
let(:menu) { described_class.new(context) }
describe '#link' do
subject { menu.link }
context 'when menu has menu items' do
it 'returns first visible menu item link' do
expect(subject).to eq menu.renderable_items.first.link
end
end
context 'when menu does no have any menu item' do
let(:user) { nil }
it 'returns show group security page' do
expect(subject).to eq "/groups/#{group.full_path}/-/security/discover"
end
end
end
describe '#title' do
subject { menu.title }
specify do
is_expected.to eq 'Security & Compliance'
end
context 'when menu does not have any menu items' do
let(:user) { nil }
specify do
is_expected.to eq 'Security'
end
end
end
describe '#render?' do
subject { menu.render? }
it 'returns true if there are menu items' do
is_expected.to be true
end
context 'when there are no menu items' do
let(:user) { nil }
it 'returns false if there are no menu items' do
is_expected.to be false
end
context 'when show group discover security option is enabled' do
let(:show_group_discover_security) { true }
specify { is_expected.to be true }
end
end
end
describe 'Menu Items' do
subject { described_class.new(context).renderable_items.index { |e| e.item_id == item_id } }
shared_examples 'menu access rights' do
specify { is_expected.not_to be_nil }
describe 'when the user does not have access' do
let(:user) { nil }
specify { is_expected.to be_nil }
end
end
describe 'Security Dashboard' do
let(:item_id) { :security_dashboard }
context 'when security_dashboard feature is enabled' do
before do
stub_licensed_features(security_dashboard: true)
end
specify { is_expected.not_to be_nil }
end
context 'when security_dashboard feature is not enabled' do
specify { is_expected.to be_nil }
end
end
describe 'Vulnerability Report' do
let(:item_id) { :vulnerability_report }
context 'when security_dashboard feature is enabled' do
before do
stub_licensed_features(security_dashboard: true)
end
specify { is_expected.not_to be_nil }
end
context 'when security_dashboard feature is not enabled' do
specify { is_expected.to be_nil }
end
end
describe 'Compliance' do
let(:item_id) { :compliance }
context 'when group_level_compliance_dashboard feature is enabled' do
before do
stub_licensed_features(group_level_compliance_dashboard: true)
end
it_behaves_like 'menu access rights'
end
context 'when group_level_compliance_dashboard feature is not enabled' do
specify { is_expected.to be_nil }
end
end
describe 'Credentials' do
let(:item_id) { :credentials }
context 'when credentials_inventory feature is enabled' do
before do
stub_licensed_features(credentials_inventory: true)
end
context 'when group magement is not enforced' do
specify { is_expected.to be_nil }
end
context 'when group magement is enforced' do
before do
allow(group).to receive(:enforced_group_managed_accounts?).and_return(true)
end
it_behaves_like 'menu access rights'
end
end
context 'when credentials_inventory feature is not enabled' do
specify { is_expected.to be_nil }
end
end
describe 'Audit Events' do
let(:item_id) { :audit_events }
context 'when audit_events feature is enabled' do
before do
stub_licensed_features(audit_events: true)
end
it_behaves_like 'menu access rights'
end
context 'when audit_events feature is not enabled' do
before do
stub_licensed_features(audit_events: false)
end
specify { is_expected.to be_nil }
end
end
end
end
...@@ -175,128 +175,7 @@ RSpec.describe 'layouts/nav/sidebar/_group' do ...@@ -175,128 +175,7 @@ RSpec.describe 'layouts/nav/sidebar/_group' do
end end
end end
describe 'DevOps adoption link' do describe 'Security & Compliance menu' do
let!(:current_user) { create(:user) }
before do
group.add_maintainer(current_user)
allow(view).to receive(:current_user).and_return(current_user)
end
context 'DevOps adoption feature is available' do
before do
stub_licensed_features(group_level_devops_adoption: true)
end
it 'is visible' do
render
expect(rendered).to have_text 'DevOps adoption'
end
end
context 'DevOps adoption feature is not available' do
before do
stub_licensed_features(group_level_devops_adoption: false)
end
it 'is not visible' do
render
expect(rendered).not_to have_text 'DevOps adoption'
end
end
end
describe 'contribution analytics tab' do
let!(:current_user) { create(:user) }
before do
group.add_guest(current_user)
allow(view).to receive(:current_user).and_return(current_user)
end
context 'contribution analytics feature is available' do
before do
stub_licensed_features(contribution_analytics: true)
end
it 'is visible' do
render
expect(rendered).to have_text 'Contribution'
end
end
context 'contribution analytics feature is not available' do
before do
stub_licensed_features(contribution_analytics: false)
end
context 'we do not show promotions' do
before do
allow(LicenseHelper).to receive(:show_promotions?).and_return(false)
end
it 'is not visible' do
render
expect(rendered).not_to have_text 'Contribution'
end
end
end
context 'no license installed' do
before do
allow(License).to receive(:current).and_return(nil)
stub_application_setting(check_namespace_plan: false)
allow(view).to receive(:can?) { |*args| Ability.allowed?(*args) }
end
it 'is visible when there is no valid license but we show promotions' do
stub_licensed_features(contribution_analytics: false)
render
expect(rendered).to have_text 'Contribution'
end
end
it 'is visible' do
stub_licensed_features(contribution_analytics: true)
render
expect(rendered).to have_text 'Contribution'
end
describe 'group issue boards link' do
context 'when multiple issue board is disabled' do
it 'shows link text in singular' do
render
expect(rendered).to have_text 'Board'
end
end
context 'when multiple issue board is enabled' do
before do
stub_licensed_features(multiple_group_issue_boards: true)
end
it 'shows link text in plural' do
render
expect(rendered).to have_text 'Boards'
end
end
end
end
describe 'security dashboard tab' do
let(:group) { create(:group_with_plan, plan: :ultimate_plan) } let(:group) { create(:group_with_plan, plan: :ultimate_plan) }
before do before do
...@@ -433,6 +312,127 @@ RSpec.describe 'layouts/nav/sidebar/_group' do ...@@ -433,6 +312,127 @@ RSpec.describe 'layouts/nav/sidebar/_group' do
end end
end end
describe 'DevOps adoption link' do
let!(:current_user) { create(:user) }
before do
group.add_maintainer(current_user)
allow(view).to receive(:current_user).and_return(current_user)
end
context 'DevOps adoption feature is available' do
before do
stub_licensed_features(group_level_devops_adoption: true)
end
it 'is visible' do
render
expect(rendered).to have_text 'DevOps adoption'
end
end
context 'DevOps adoption feature is not available' do
before do
stub_licensed_features(group_level_devops_adoption: false)
end
it 'is not visible' do
render
expect(rendered).not_to have_text 'DevOps adoption'
end
end
end
describe 'contribution analytics tab' do
let!(:current_user) { create(:user) }
before do
group.add_guest(current_user)
allow(view).to receive(:current_user).and_return(current_user)
end
context 'contribution analytics feature is available' do
before do
stub_licensed_features(contribution_analytics: true)
end
it 'is visible' do
render
expect(rendered).to have_text 'Contribution'
end
end
context 'contribution analytics feature is not available' do
before do
stub_licensed_features(contribution_analytics: false)
end
context 'we do not show promotions' do
before do
allow(LicenseHelper).to receive(:show_promotions?).and_return(false)
end
it 'is not visible' do
render
expect(rendered).not_to have_text 'Contribution'
end
end
end
context 'no license installed' do
before do
allow(License).to receive(:current).and_return(nil)
stub_application_setting(check_namespace_plan: false)
allow(view).to receive(:can?) { |*args| Ability.allowed?(*args) }
end
it 'is visible when there is no valid license but we show promotions' do
stub_licensed_features(contribution_analytics: false)
render
expect(rendered).to have_text 'Contribution'
end
end
it 'is visible' do
stub_licensed_features(contribution_analytics: true)
render
expect(rendered).to have_text 'Contribution'
end
describe 'group issue boards link' do
context 'when multiple issue board is disabled' do
it 'shows link text in singular' do
render
expect(rendered).to have_text 'Board'
end
end
context 'when multiple issue board is enabled' do
before do
stub_licensed_features(multiple_group_issue_boards: true)
end
it 'shows link text in plural' do
render
expect(rendered).to have_text 'Boards'
end
end
end
end
describe 'wiki tab' do describe 'wiki tab' do
let(:can_read_wiki) { true } let(:can_read_wiki) { true }
......
...@@ -136,7 +136,6 @@ module Gitlab ...@@ -136,7 +136,6 @@ module Gitlab
def self.process_name def self.process_name
return 'sidekiq' if Gitlab::Runtime.sidekiq? return 'sidekiq' if Gitlab::Runtime.sidekiq?
return 'action_cable' if Gitlab::Runtime.action_cable?
return 'console' if Gitlab::Runtime.console? return 'console' if Gitlab::Runtime.console?
return 'test' if Rails.env.test? return 'test' if Rails.env.test?
......
# frozen_string_literal: true
module Gitlab
module Ci
module Pipeline
module Chain
# This will be removed with https://gitlab.com/gitlab-org/gitlab/-/issues/337167
class LegacySkip < Chain::Skip
include ::Gitlab::Utils::StrongMemoize
SKIP_PATTERN = /\[(ci[ _-]skip|skip[ _-]ci)\]/i.freeze
def perform!
return if ::Feature.enabled?(:ci_skip_before_parsing_yaml, project, default_enabled: :yaml)
if skipped?
if @command.save_incompleted
# Project iid must be called outside a transaction, so we ensure it is set here
# otherwise it may be set within the state transition transaction of the skip call
# which it will lock the InternalId row for the whole transaction
@pipeline.ensure_project_iid!
@pipeline.skip
end
end
end
def break?
return if ::Feature.enabled?(:ci_skip_before_parsing_yaml, project, default_enabled: :yaml)
skipped?
end
private
def skipped?
!@command.ignore_skip_ci && (commit_message_skips_ci? || push_option_skips_ci?)
end
def commit_message_skips_ci?
return false unless @pipeline.git_commit_message
strong_memoize(:commit_message_skips_ci) do
!!(@pipeline.git_commit_message =~ SKIP_PATTERN)
end
end
def push_option_skips_ci?
@command.push_options.present? &&
@command.push_options.deep_symbolize_keys.dig(:ci, :skip).present?
end
end
end
end
end
end
...@@ -10,6 +10,8 @@ module Gitlab ...@@ -10,6 +10,8 @@ module Gitlab
SKIP_PATTERN = /\[(ci[ _-]skip|skip[ _-]ci)\]/i.freeze SKIP_PATTERN = /\[(ci[ _-]skip|skip[ _-]ci)\]/i.freeze
def perform! def perform!
return unless ::Feature.enabled?(:ci_skip_before_parsing_yaml, project, default_enabled: :yaml)
if skipped? if skipped?
if @command.save_incompleted if @command.save_incompleted
# Project iid must be called outside a transaction, so we ensure it is set here # Project iid must be called outside a transaction, so we ensure it is set here
...@@ -22,16 +24,18 @@ module Gitlab ...@@ -22,16 +24,18 @@ module Gitlab
end end
end end
def skipped?
!@command.ignore_skip_ci && (commit_message_skips_ci? || push_option_skips_ci?)
end
def break? def break?
return unless ::Feature.enabled?(:ci_skip_before_parsing_yaml, project, default_enabled: :yaml)
skipped? skipped?
end end
private private
def skipped?
!@command.ignore_skip_ci && (commit_message_skips_ci? || push_option_skips_ci?)
end
def commit_message_skips_ci? def commit_message_skips_ci?
return false unless @pipeline.git_commit_message return false unless @pipeline.git_commit_message
......
...@@ -31828,6 +31828,9 @@ msgstr "" ...@@ -31828,6 +31828,9 @@ msgstr ""
msgid "Summary" msgid "Summary"
msgstr "" msgstr ""
msgid "Summary / Note"
msgstr ""
msgid "Sunday" msgid "Sunday"
msgstr "" msgstr ""
......
...@@ -33,14 +33,6 @@ module QA ...@@ -33,14 +33,6 @@ module QA
element :billing_link element :billing_link
end end
view 'ee/app/views/layouts/nav/ee/_security_link.html.haml' do
element :security_compliance_link
element :group_secure_submenu
element :security_dashboard_link
element :vulnerability_report_link
element :audit_events_settings_link
end
view 'ee/app/views/layouts/nav/_group_insights_link.html.haml' do view 'ee/app/views/layouts/nav/_group_insights_link.html.haml' do
element :group_insights_link element :group_insights_link
end end
...@@ -53,14 +45,6 @@ module QA ...@@ -53,14 +45,6 @@ module QA
end end
end end
def go_to_audit_events_settings
hover_element(:security_compliance_link) do
within_submenu(:group_secure_submenu) do
click_element(:audit_events_settings_link)
end
end
end
def go_to_issue_boards def go_to_issue_boards
hover_issues do hover_issues do
within_submenu do within_submenu do
...@@ -116,17 +100,25 @@ module QA ...@@ -116,17 +100,25 @@ module QA
end end
def click_group_security_link def click_group_security_link
hover_element(:security_compliance_link) do hover_security_and_compliance do
within_submenu(:group_secure_submenu) do within_submenu do
click_element(:security_dashboard_link) click_element(:sidebar_menu_item_link, menu_item: 'Security Dashboard')
end end
end end
end end
def click_group_vulnerability_link def click_group_vulnerability_link
hover_element(:security_compliance_link) do hover_security_and_compliance do
within_submenu(:group_secure_submenu) do within_submenu do
click_element(:vulnerability_report_link) click_element(:sidebar_menu_item_link, menu_item: 'Vulnerability Report')
end
end
end
def go_to_audit_events
hover_security_and_compliance do
within_submenu do
click_element(:sidebar_menu_item_link, menu_item: 'Audit Events')
end end
end end
end end
...@@ -152,6 +144,17 @@ module QA ...@@ -152,6 +144,17 @@ module QA
end end
end end
end end
private
def hover_security_and_compliance
within_sidebar do
scroll_to_element(:sidebar_menu_link, menu_item: 'Security & Compliance')
find_element(:sidebar_menu_link, menu_item: 'Security & Compliance').hover
yield
end
end
end end
end end
end end
......
...@@ -11,7 +11,7 @@ module QA ...@@ -11,7 +11,7 @@ module QA
it 'logs audit events for UI operations' do it 'logs audit events for UI operations' do
wait_for_audit_events(expected_events, group) wait_for_audit_events(expected_events, group)
Page::Group::Menu.perform(&:go_to_audit_events_settings) Page::Group::Menu.perform(&:go_to_audit_events)
expected_events.each do |expected_event| expected_events.each do |expected_event|
# Sometimes the audit logs are not displayed in the UI # Sometimes the audit logs are not displayed in the UI
# right away so a refresh may be needed. # right away so a refresh may be needed.
......
...@@ -5,7 +5,7 @@ module QA ...@@ -5,7 +5,7 @@ module QA
RSpec.describe 'Manage' do RSpec.describe 'Manage' do
shared_examples 'audit event' do |expected_events| shared_examples 'audit event' do |expected_events|
it 'logs audit events for UI operations' do it 'logs audit events for UI operations' do
Page::Group::Menu.perform(&:go_to_audit_events_settings) Page::Group::Menu.perform(&:go_to_audit_events)
expected_events.each do |expected_event| expected_events.each do |expected_event|
expect(page).to have_text(expected_event) expect(page).to have_text(expected_event)
end end
......
...@@ -16,9 +16,10 @@ export const getIssueTimelogsQueryResponse = { ...@@ -16,9 +16,10 @@ export const getIssueTimelogsQueryResponse = {
}, },
spentAt: '2020-05-01T00:00:00Z', spentAt: '2020-05-01T00:00:00Z',
note: { note: {
body: 'I paired with @root on this last week.', body: 'A note',
__typename: 'Note', __typename: 'Note',
}, },
summary: 'A summary',
}, },
{ {
__typename: 'Timelog', __typename: 'Timelog',
...@@ -29,6 +30,7 @@ export const getIssueTimelogsQueryResponse = { ...@@ -29,6 +30,7 @@ export const getIssueTimelogsQueryResponse = {
}, },
spentAt: '2021-05-07T13:19:01Z', spentAt: '2021-05-07T13:19:01Z',
note: null, note: null,
summary: 'A summary',
}, },
{ {
__typename: 'Timelog', __typename: 'Timelog',
...@@ -39,9 +41,10 @@ export const getIssueTimelogsQueryResponse = { ...@@ -39,9 +41,10 @@ export const getIssueTimelogsQueryResponse = {
}, },
spentAt: '2021-05-01T00:00:00Z', spentAt: '2021-05-01T00:00:00Z',
note: { note: {
body: 'I did some work on this last week.', body: 'A note',
__typename: 'Note', __typename: 'Note',
}, },
summary: null,
}, },
], ],
__typename: 'TimelogConnection', __typename: 'TimelogConnection',
...@@ -70,6 +73,7 @@ export const getMrTimelogsQueryResponse = { ...@@ -70,6 +73,7 @@ export const getMrTimelogsQueryResponse = {
body: 'Thirty minutes!', body: 'Thirty minutes!',
__typename: 'Note', __typename: 'Note',
}, },
summary: null,
}, },
{ {
__typename: 'Timelog', __typename: 'Timelog',
...@@ -80,6 +84,7 @@ export const getMrTimelogsQueryResponse = { ...@@ -80,6 +84,7 @@ export const getMrTimelogsQueryResponse = {
}, },
spentAt: '2021-05-07T14:44:39Z', spentAt: '2021-05-07T14:44:39Z',
note: null, note: null,
summary: null,
}, },
{ {
__typename: 'Timelog', __typename: 'Timelog',
...@@ -93,6 +98,7 @@ export const getMrTimelogsQueryResponse = { ...@@ -93,6 +98,7 @@ export const getMrTimelogsQueryResponse = {
body: 'A note with some time', body: 'A note with some time',
__typename: 'Note', __typename: 'Note',
}, },
summary: null,
}, },
], ],
__typename: 'TimelogConnection', __typename: 'TimelogConnection',
......
...@@ -74,6 +74,8 @@ describe('Issuable Time Tracking Report', () => { ...@@ -74,6 +74,8 @@ describe('Issuable Time Tracking Report', () => {
expect(getAllByRole(wrapper.element, 'row', { name: /John Doe18/i })).toHaveLength(1); expect(getAllByRole(wrapper.element, 'row', { name: /John Doe18/i })).toHaveLength(1);
expect(getAllByRole(wrapper.element, 'row', { name: /Administrator/i })).toHaveLength(2); expect(getAllByRole(wrapper.element, 'row', { name: /Administrator/i })).toHaveLength(2);
expect(getAllByRole(wrapper.element, 'row', { name: /A note/i })).toHaveLength(1);
expect(getAllByRole(wrapper.element, 'row', { name: /A summary/i })).toHaveLength(2);
}); });
}); });
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
require 'spec_helper' require 'spec_helper'
RSpec.describe GitlabSchema.types['Timelog'] do RSpec.describe GitlabSchema.types['Timelog'] do
let(:fields) { %i[spent_at time_spent user issue merge_request note] } let(:fields) { %i[spent_at time_spent user issue merge_request note summary] }
it { expect(described_class.graphql_name).to eq('Timelog') } it { expect(described_class.graphql_name).to eq('Timelog') }
it { expect(described_class).to have_graphql_fields(fields) } it { expect(described_class).to have_graphql_fields(fields) }
......
...@@ -749,4 +749,44 @@ RSpec.describe Member do ...@@ -749,4 +749,44 @@ RSpec.describe Member do
end end
end end
end end
describe 'log_invitation_token_cleanup' do
let_it_be(:project) { create :project }
context 'when on gitlab.com' do
before do
allow(Gitlab).to receive(:com?).and_return true
end
it "doesn't log info for members without invitation or accepted invitation" do
expect(Gitlab::ErrorTracking).not_to receive(:track_exception)
create :project_member
create :project_member, :invited, invite_accepted_at: nil
create :project_member, invite_token: nil, invite_accepted_at: Time.zone.now
end
it 'logs error for accepted members with token and creates membership' do
expect(Gitlab::ErrorTracking).to receive(:track_exception).with(kind_of(StandardError), kind_of(Hash))
expect do
create :project_member, :invited, source: project, invite_accepted_at: Time.zone.now
end.to change { Member.count }.by(1)
end
end
context 'when not on gitlab.com' do
before do
allow(Gitlab).to receive(:com?).and_return false
end
it 'does not log error for accepted members with token and creates membership' do
expect(Gitlab::ErrorTracking).not_to receive(:track_exception)
expect do
create :project_member, :invited, source: project, invite_accepted_at: Time.zone.now
end.to change { Member.count }.by(1)
end
end
end
end end
...@@ -1542,8 +1542,8 @@ RSpec.describe Note do ...@@ -1542,8 +1542,8 @@ RSpec.describe Note do
describe '#post_processed_cache_key' do describe '#post_processed_cache_key' do
let(:note) { build(:note) } let(:note) { build(:note) }
it 'returns cache key by default' do it 'returns cache key and author cache key by default' do
expect(note.post_processed_cache_key).to eq(note.cache_key) expect(note.post_processed_cache_key).to eq("#{note.cache_key}:#{note.author.cache_key}")
end end
context 'when note has redacted_note_html' do context 'when note has redacted_note_html' do
...@@ -1554,7 +1554,7 @@ RSpec.describe Note do ...@@ -1554,7 +1554,7 @@ RSpec.describe Note do
end end
it 'returns cache key with redacted_note_html sha' do it 'returns cache key with redacted_note_html sha' do
expect(note.post_processed_cache_key).to eq("#{note.cache_key}:#{Digest::SHA1.hexdigest(redacted_note_html)}") expect(note.post_processed_cache_key).to eq("#{note.cache_key}:#{note.author.cache_key}:#{Digest::SHA1.hexdigest(redacted_note_html)}")
end end
end end
end end
......
...@@ -55,7 +55,8 @@ RSpec.describe 'merge requests discussions' do ...@@ -55,7 +55,8 @@ RSpec.describe 'merge requests discussions' do
context 'caching', :use_clean_rails_memory_store_caching do context 'caching', :use_clean_rails_memory_store_caching do
let(:reference) { create(:issue, project: project) } let(:reference) { create(:issue, project: project) }
let!(:first_note) { create(:diff_note_on_merge_request, noteable: merge_request, project: project, note: "reference: #{reference.to_reference}") } let(:author) { create(:user) }
let!(:first_note) { create(:diff_note_on_merge_request, author: author, noteable: merge_request, project: project, note: "reference: #{reference.to_reference}") }
let!(:second_note) { create(:diff_note_on_merge_request, in_reply_to: first_note, noteable: merge_request, project: project) } let!(:second_note) { create(:diff_note_on_merge_request, in_reply_to: first_note, noteable: merge_request, project: project) }
let!(:award_emoji) { create(:award_emoji, awardable: first_note) } let!(:award_emoji) { create(:award_emoji, awardable: first_note) }
...@@ -181,6 +182,26 @@ RSpec.describe 'merge requests discussions' do ...@@ -181,6 +182,26 @@ RSpec.describe 'merge requests discussions' do
end end
end end
context 'when author detail changes' do
before do
author.update!(name: "#{author.name} (Updated)")
end
it_behaves_like 'cache miss' do
let(:changed_notes) { [first_note, second_note] }
end
end
context 'when author status changes' do
before do
Users::SetStatusService.new(author, message: "updated status").execute
end
it_behaves_like 'cache miss' do
let(:changed_notes) { [first_note, second_note] }
end
end
context 'when merge_request_discussion_cache is disabled' do context 'when merge_request_discussion_cache is disabled' do
before do before do
stub_feature_flags(merge_request_discussion_cache: false) stub_feature_flags(merge_request_discussion_cache: false)
......
...@@ -19,7 +19,6 @@ RSpec.describe Ci::CreatePipelineService do ...@@ -19,7 +19,6 @@ RSpec.describe Ci::CreatePipelineService do
def execute_service( def execute_service(
source: :push, source: :push,
after: project.commit.id, after: project.commit.id,
message: 'Message',
ref: ref_name, ref: ref_name,
trigger_request: nil, trigger_request: nil,
variables_attributes: nil, variables_attributes: nil,
...@@ -32,7 +31,6 @@ RSpec.describe Ci::CreatePipelineService do ...@@ -32,7 +31,6 @@ RSpec.describe Ci::CreatePipelineService do
params = { ref: ref, params = { ref: ref,
before: '00000000', before: '00000000',
after: after, after: after,
commits: [{ message: message }],
variables_attributes: variables_attributes, variables_attributes: variables_attributes,
push_options: push_options, push_options: push_options,
source_sha: source_sha, source_sha: source_sha,
...@@ -508,7 +506,7 @@ RSpec.describe Ci::CreatePipelineService do ...@@ -508,7 +506,7 @@ RSpec.describe Ci::CreatePipelineService do
it 'creates failed pipeline' do it 'creates failed pipeline' do
stub_ci_pipeline_yaml_file(ci_yaml) stub_ci_pipeline_yaml_file(ci_yaml)
pipeline = execute_service(message: message).payload pipeline = execute_service.payload
expect(pipeline).to be_persisted expect(pipeline).to be_persisted
expect(pipeline.builds.any?).to be false expect(pipeline.builds.any?).to be false
...@@ -671,9 +669,30 @@ RSpec.describe Ci::CreatePipelineService do ...@@ -671,9 +669,30 @@ RSpec.describe Ci::CreatePipelineService do
end end
context 'when commit contains a [ci skip] directive' do context 'when commit contains a [ci skip] directive' do
let(:message) { "some message[ci skip]" } shared_examples 'creating a pipeline' do
it 'does not skip pipeline creation' do
pipeline = execute_service.payload
expect(pipeline).to be_persisted
expect(pipeline.builds.first.name).to eq("rspec")
end
end
shared_examples 'skipping a pipeline' do
it 'skips pipeline creation' do
pipeline = execute_service.payload
ci_messages = [ expect(pipeline).to be_persisted
expect(pipeline.builds.any?).to be false
expect(pipeline.status).to eq("skipped")
end
end
before do
allow_any_instance_of(Ci::Pipeline).to receive(:git_commit_message) { commit_message }
end
skip_commit_messages = [
"some message[ci skip]", "some message[ci skip]",
"some message[skip ci]", "some message[skip ci]",
"some message[CI SKIP]", "some message[CI SKIP]",
...@@ -684,28 +703,19 @@ RSpec.describe Ci::CreatePipelineService do ...@@ -684,28 +703,19 @@ RSpec.describe Ci::CreatePipelineService do
"some message[skip-ci]" "some message[skip-ci]"
] ]
before do skip_commit_messages.each do |skip_commit_message|
allow_any_instance_of(Ci::Pipeline).to receive(:git_commit_message) { message } context "when the commit message is #{skip_commit_message}" do
end let(:commit_message) { skip_commit_message }
ci_messages.each do |ci_message| it_behaves_like 'skipping a pipeline'
it "skips builds creation if the commit message is #{ci_message}" do
pipeline = execute_service(message: ci_message).payload
expect(pipeline).to be_persisted context 'when the FF ci_skip_before_parsing_yaml is disabled' do
expect(pipeline.builds.any?).to be false before do
expect(pipeline.status).to eq("skipped") stub_feature_flags(ci_skip_before_parsing_yaml: false)
end end
end
shared_examples 'creating a pipeline' do
it 'does not skip pipeline creation' do
allow_any_instance_of(Ci::Pipeline).to receive(:git_commit_message) { commit_message }
pipeline = execute_service(message: commit_message).payload
expect(pipeline).to be_persisted it_behaves_like 'skipping a pipeline'
expect(pipeline.builds.first.name).to eq("rspec") end
end end
end end
...@@ -713,6 +723,14 @@ RSpec.describe Ci::CreatePipelineService do ...@@ -713,6 +723,14 @@ RSpec.describe Ci::CreatePipelineService do
let(:commit_message) { 'some message' } let(:commit_message) { 'some message' }
it_behaves_like 'creating a pipeline' it_behaves_like 'creating a pipeline'
context 'when the FF ci_skip_before_parsing_yaml is disabled' do
before do
stub_feature_flags(ci_skip_before_parsing_yaml: false)
end
it_behaves_like 'creating a pipeline'
end
end end
context 'when commit message is nil' do context 'when commit message is nil' do
...@@ -722,9 +740,27 @@ RSpec.describe Ci::CreatePipelineService do ...@@ -722,9 +740,27 @@ RSpec.describe Ci::CreatePipelineService do
end end
context 'when there is [ci skip] tag in commit message and yaml is invalid' do context 'when there is [ci skip] tag in commit message and yaml is invalid' do
let(:commit_message) { 'some message [ci skip]' }
let(:ci_yaml) { 'invalid: file: fiile' } let(:ci_yaml) { 'invalid: file: fiile' }
it_behaves_like 'a failed pipeline' before do
stub_ci_pipeline_yaml_file(ci_yaml)
end
it_behaves_like 'skipping a pipeline'
context 'when the FF ci_skip_before_parsing_yaml is disabled' do
before do
stub_feature_flags(ci_skip_before_parsing_yaml: false)
end
it 'creates the pipeline with error' do
pipeline = execute_service.payload
expect(pipeline).to be_persisted
expect(pipeline.status).to eq("failed")
end
end
end end
end end
......
...@@ -8,6 +8,18 @@ RSpec.describe Users::SetStatusService do ...@@ -8,6 +8,18 @@ RSpec.describe Users::SetStatusService do
subject(:service) { described_class.new(current_user, params) } subject(:service) { described_class.new(current_user, params) }
describe '#execute' do describe '#execute' do
shared_examples_for 'bumps user' do
it 'bumps User#updated_at' do
expect { service.execute }.to change { current_user.updated_at }
end
end
shared_examples_for 'does not bump user' do
it 'does not bump User#updated_at' do
expect { service.execute }.not_to change { current_user.updated_at }
end
end
context 'when params are set' do context 'when params are set' do
let(:params) { { emoji: 'taurus', message: 'a random status', availability: 'busy' } } let(:params) { { emoji: 'taurus', message: 'a random status', availability: 'busy' } }
...@@ -31,6 +43,8 @@ RSpec.describe Users::SetStatusService do ...@@ -31,6 +43,8 @@ RSpec.describe Users::SetStatusService do
expect(service.execute).to be(true) expect(service.execute).to be(true)
end end
it_behaves_like 'bumps user'
context 'when setting availability to not_set' do context 'when setting availability to not_set' do
before do before do
params[:availability] = 'not_set' params[:availability] = 'not_set'
...@@ -72,6 +86,8 @@ RSpec.describe Users::SetStatusService do ...@@ -72,6 +86,8 @@ RSpec.describe Users::SetStatusService do
it 'does not update the status if the current user is not allowed' do it 'does not update the status if the current user is not allowed' do
expect { service.execute }.not_to change { target_user.status } expect { service.execute }.not_to change { target_user.status }
end end
it_behaves_like 'does not bump user'
end end
end end
...@@ -79,20 +95,28 @@ RSpec.describe Users::SetStatusService do ...@@ -79,20 +95,28 @@ RSpec.describe Users::SetStatusService do
let(:params) { {} } let(:params) { {} }
shared_examples 'removes user status record' do shared_examples 'removes user status record' do
it 'deletes the status' do it 'deletes the user status record' do
status = create(:user_status, user: current_user)
expect { service.execute } expect { service.execute }
.to change { current_user.reload.status }.from(status).to(nil) .to change { current_user.reload.status }.from(user_status).to(nil)
end end
end
it_behaves_like 'removes user status record' it_behaves_like 'bumps user'
end
context 'when not_set is given for availability' do context 'when user has existing user status record' do
let(:params) { { availability: 'not_set' } } let!(:user_status) { create(:user_status, user: current_user) }
it_behaves_like 'removes user status record' it_behaves_like 'removes user status record'
context 'when not_set is given for availability' do
let(:params) { { availability: 'not_set' } }
it_behaves_like 'removes user status record'
end
end
context 'when user has no existing user status record' do
it_behaves_like 'does not bump user'
end end
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