Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
d84f18d66c
commit
680d188025
|
@ -19,7 +19,7 @@ See [the general developer security release guidelines](https://gitlab.com/gitla
|
||||||
- [ ] A [CHANGELOG entry](https://docs.gitlab.com/ee/development/changelog.html) is added without a `merge_request` value, with `type` set to `security`
|
- [ ] A [CHANGELOG entry](https://docs.gitlab.com/ee/development/changelog.html) is added without a `merge_request` value, with `type` set to `security`
|
||||||
- [ ] Assign to a reviewer and maintainer, per our [Code Review process].
|
- [ ] Assign to a reviewer and maintainer, per our [Code Review process].
|
||||||
- [ ] For the MR targeting `master`:
|
- [ ] For the MR targeting `master`:
|
||||||
- [ ] Ping appsec team member who created the issue and ask for a non-blocking review with `Please review this MR`.
|
- [ ] Ask for a non-blocking review from the AppSec team member associated to the issue in the [Canonical repository](https://gitlab.com/gitlab-org/gitlab). If you're unsure who to ping, ask on `#sec-appsec` Slack channel.
|
||||||
- [ ] Ensure it's approved according to our [Approval Guidelines].
|
- [ ] Ensure it's approved according to our [Approval Guidelines].
|
||||||
- [ ] Merge request _must not_ close the corresponding security issue, _unless_ it targets `master`.
|
- [ ] Merge request _must not_ close the corresponding security issue, _unless_ it targets `master`.
|
||||||
|
|
||||||
|
|
|
@ -359,9 +359,6 @@ RSpec/LeakyConstantDeclaration:
|
||||||
- 'spec/lib/gitlab/database/obsolete_ignored_columns_spec.rb'
|
- 'spec/lib/gitlab/database/obsolete_ignored_columns_spec.rb'
|
||||||
- 'spec/lib/gitlab/database/with_lock_retries_spec.rb'
|
- 'spec/lib/gitlab/database/with_lock_retries_spec.rb'
|
||||||
- 'spec/lib/gitlab/git/diff_collection_spec.rb'
|
- 'spec/lib/gitlab/git/diff_collection_spec.rb'
|
||||||
- 'spec/lib/gitlab/graphql/pagination/keyset/connection_spec.rb'
|
|
||||||
- 'spec/lib/gitlab/health_checks/master_check_spec.rb'
|
|
||||||
- 'spec/lib/gitlab/import_export/attribute_configuration_spec.rb'
|
|
||||||
- 'spec/lib/gitlab/import_export/import_test_coverage_spec.rb'
|
- 'spec/lib/gitlab/import_export/import_test_coverage_spec.rb'
|
||||||
- 'spec/lib/gitlab/import_export/project/relation_factory_spec.rb'
|
- 'spec/lib/gitlab/import_export/project/relation_factory_spec.rb'
|
||||||
- 'spec/lib/gitlab/jira_import/issues_importer_spec.rb'
|
- 'spec/lib/gitlab/jira_import/issues_importer_spec.rb'
|
||||||
|
@ -372,17 +369,14 @@ RSpec/LeakyConstantDeclaration:
|
||||||
- 'spec/lib/gitlab/sidekiq_middleware/server_metrics_spec.rb'
|
- 'spec/lib/gitlab/sidekiq_middleware/server_metrics_spec.rb'
|
||||||
- 'spec/lib/gitlab/view/presenter/factory_spec.rb'
|
- 'spec/lib/gitlab/view/presenter/factory_spec.rb'
|
||||||
- 'spec/lib/marginalia_spec.rb'
|
- 'spec/lib/marginalia_spec.rb'
|
||||||
- 'spec/lib/system_check/simple_executor_spec.rb'
|
|
||||||
- 'spec/mailers/notify_spec.rb'
|
- 'spec/mailers/notify_spec.rb'
|
||||||
- 'spec/migrations/20191125114345_add_admin_mode_protected_path_spec.rb'
|
- 'spec/migrations/20191125114345_add_admin_mode_protected_path_spec.rb'
|
||||||
- 'spec/migrations/encrypt_plaintext_attributes_on_application_settings_spec.rb'
|
|
||||||
- 'spec/models/concerns/batch_destroy_dependent_associations_spec.rb'
|
- 'spec/models/concerns/batch_destroy_dependent_associations_spec.rb'
|
||||||
- 'spec/models/concerns/bulk_insert_safe_spec.rb'
|
- 'spec/models/concerns/bulk_insert_safe_spec.rb'
|
||||||
- 'spec/models/concerns/bulk_insertable_associations_spec.rb'
|
- 'spec/models/concerns/bulk_insertable_associations_spec.rb'
|
||||||
- 'spec/models/concerns/triggerable_hooks_spec.rb'
|
- 'spec/models/concerns/triggerable_hooks_spec.rb'
|
||||||
- 'spec/models/repository_spec.rb'
|
- 'spec/models/repository_spec.rb'
|
||||||
- 'spec/requests/api/graphql/tasks/task_completion_status_spec.rb'
|
- 'spec/requests/api/graphql/tasks/task_completion_status_spec.rb'
|
||||||
- 'spec/rubocop/cop/rspec/env_assignment_spec.rb'
|
|
||||||
- 'spec/serializers/commit_entity_spec.rb'
|
- 'spec/serializers/commit_entity_spec.rb'
|
||||||
- 'spec/services/clusters/applications/check_installation_progress_service_spec.rb'
|
- 'spec/services/clusters/applications/check_installation_progress_service_spec.rb'
|
||||||
- 'spec/services/clusters/applications/check_uninstall_progress_service_spec.rb'
|
- 'spec/services/clusters/applications/check_uninstall_progress_service_spec.rb'
|
||||||
|
|
|
@ -1,22 +1,32 @@
|
||||||
<script>
|
<script>
|
||||||
import { mapState, mapActions } from 'vuex';
|
import { mapState, mapActions } from 'vuex';
|
||||||
import { GlTable, GlLink, GlLoadingIcon, GlBadge } from '@gitlab/ui';
|
import { GlBadge, GlLink, GlLoadingIcon, GlPagination, GlTable } from '@gitlab/ui';
|
||||||
import tooltip from '~/vue_shared/directives/tooltip';
|
import tooltip from '~/vue_shared/directives/tooltip';
|
||||||
import { CLUSTER_TYPES, STATUSES } from '../constants';
|
import { CLUSTER_TYPES, STATUSES } from '../constants';
|
||||||
import { __, sprintf } from '~/locale';
|
import { __, sprintf } from '~/locale';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
GlTable,
|
GlBadge,
|
||||||
GlLink,
|
GlLink,
|
||||||
GlLoadingIcon,
|
GlLoadingIcon,
|
||||||
GlBadge,
|
GlPagination,
|
||||||
|
GlTable,
|
||||||
},
|
},
|
||||||
directives: {
|
directives: {
|
||||||
tooltip,
|
tooltip,
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapState(['clusters', 'loading']),
|
...mapState(['clusters', 'clustersPerPage', 'loading', 'page', 'totalCulsters']),
|
||||||
|
currentPage: {
|
||||||
|
get() {
|
||||||
|
return this.page;
|
||||||
|
},
|
||||||
|
set(newVal) {
|
||||||
|
this.setPage(newVal);
|
||||||
|
this.fetchClusters();
|
||||||
|
},
|
||||||
|
},
|
||||||
fields() {
|
fields() {
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
|
@ -47,12 +57,15 @@ export default {
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
},
|
},
|
||||||
|
hasClusters() {
|
||||||
|
return this.clustersPerPage > 0;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
this.fetchClusters();
|
this.fetchClusters();
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
...mapActions(['fetchClusters']),
|
...mapActions(['fetchClusters', 'setPage']),
|
||||||
statusClass(status) {
|
statusClass(status) {
|
||||||
const iconClass = STATUSES[status] || STATUSES.default;
|
const iconClass = STATUSES[status] || STATUSES.default;
|
||||||
return iconClass.className;
|
return iconClass.className;
|
||||||
|
@ -67,33 +80,46 @@ export default {
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<gl-loading-icon v-if="loading" size="md" class="mt-3" />
|
<gl-loading-icon v-if="loading" size="md" class="mt-3" />
|
||||||
<gl-table v-else :items="clusters" :fields="fields" stacked="md" class="qa-clusters-table">
|
|
||||||
<template #cell(name)="{ item }">
|
|
||||||
<div class="d-flex flex-row-reverse flex-md-row js-status">
|
|
||||||
<gl-link data-qa-selector="cluster" :data-qa-cluster-name="item.name" :href="item.path">
|
|
||||||
{{ item.name }}
|
|
||||||
</gl-link>
|
|
||||||
|
|
||||||
<gl-loading-icon
|
<section v-else>
|
||||||
v-if="item.status === 'deleting'"
|
<gl-table :items="clusters" :fields="fields" stacked="md" class="qa-clusters-table">
|
||||||
v-tooltip
|
<template #cell(name)="{ item }">
|
||||||
:title="statusTitle(item.status)"
|
<div class="d-flex flex-row-reverse flex-md-row js-status">
|
||||||
size="sm"
|
<gl-link data-qa-selector="cluster" :data-qa-cluster-name="item.name" :href="item.path">
|
||||||
class="mr-2 ml-md-2"
|
{{ item.name }}
|
||||||
/>
|
</gl-link>
|
||||||
<div
|
|
||||||
v-else
|
<gl-loading-icon
|
||||||
v-tooltip
|
v-if="item.status === 'deleting'"
|
||||||
class="cluster-status-indicator rounded-circle align-self-center gl-w-4 gl-h-4 mr-2 ml-md-2"
|
v-tooltip
|
||||||
:class="statusClass(item.status)"
|
:title="statusTitle(item.status)"
|
||||||
:title="statusTitle(item.status)"
|
size="sm"
|
||||||
></div>
|
class="mr-2 ml-md-2"
|
||||||
</div>
|
/>
|
||||||
</template>
|
<div
|
||||||
<template #cell(cluster_type)="{value}">
|
v-else
|
||||||
<gl-badge variant="light">
|
v-tooltip
|
||||||
{{ value }}
|
class="cluster-status-indicator rounded-circle align-self-center gl-w-4 gl-h-4 mr-2 ml-md-2"
|
||||||
</gl-badge>
|
:class="statusClass(item.status)"
|
||||||
</template>
|
:title="statusTitle(item.status)"
|
||||||
</gl-table>
|
></div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template #cell(cluster_type)="{value}">
|
||||||
|
<gl-badge variant="light">
|
||||||
|
{{ value }}
|
||||||
|
</gl-badge>
|
||||||
|
</template>
|
||||||
|
</gl-table>
|
||||||
|
|
||||||
|
<gl-pagination
|
||||||
|
v-if="hasClusters"
|
||||||
|
v-model="currentPage"
|
||||||
|
:per-page="clustersPerPage"
|
||||||
|
:total-items="totalCulsters"
|
||||||
|
:prev-text="__('Prev')"
|
||||||
|
:next-text="__('Next')"
|
||||||
|
align="center"
|
||||||
|
/>
|
||||||
|
</section>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -2,18 +2,22 @@ import Poll from '~/lib/utils/poll';
|
||||||
import axios from '~/lib/utils/axios_utils';
|
import axios from '~/lib/utils/axios_utils';
|
||||||
import flash from '~/flash';
|
import flash from '~/flash';
|
||||||
import { __ } from '~/locale';
|
import { __ } from '~/locale';
|
||||||
|
import { parseIntPagination, normalizeHeaders } from '~/lib/utils/common_utils';
|
||||||
import * as types from './mutation_types';
|
import * as types from './mutation_types';
|
||||||
|
|
||||||
export const fetchClusters = ({ state, commit }) => {
|
export const fetchClusters = ({ state, commit }) => {
|
||||||
const poll = new Poll({
|
const poll = new Poll({
|
||||||
resource: {
|
resource: {
|
||||||
fetchClusters: endpoint => axios.get(endpoint),
|
fetchClusters: paginatedEndPoint => axios.get(paginatedEndPoint),
|
||||||
},
|
},
|
||||||
data: state.endpoint,
|
data: `${state.endpoint}?page=${state.page}`,
|
||||||
method: 'fetchClusters',
|
method: 'fetchClusters',
|
||||||
successCallback: ({ data }) => {
|
successCallback: ({ data, headers }) => {
|
||||||
if (data.clusters) {
|
if (data.clusters) {
|
||||||
commit(types.SET_CLUSTERS_DATA, data);
|
const normalizedHeaders = normalizeHeaders(headers);
|
||||||
|
const paginationInformation = parseIntPagination(normalizedHeaders);
|
||||||
|
|
||||||
|
commit(types.SET_CLUSTERS_DATA, { data, paginationInformation });
|
||||||
commit(types.SET_LOADING_STATE, false);
|
commit(types.SET_LOADING_STATE, false);
|
||||||
poll.stop();
|
poll.stop();
|
||||||
}
|
}
|
||||||
|
@ -24,5 +28,9 @@ export const fetchClusters = ({ state, commit }) => {
|
||||||
poll.makeRequest();
|
poll.makeRequest();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const setPage = ({ commit }, page) => {
|
||||||
|
commit(types.SET_PAGE, page);
|
||||||
|
};
|
||||||
|
|
||||||
// prevent babel-plugin-rewire from generating an invalid default during karma tests
|
// prevent babel-plugin-rewire from generating an invalid default during karma tests
|
||||||
export default () => {};
|
export default () => {};
|
||||||
|
|
|
@ -1,2 +1,3 @@
|
||||||
export const SET_CLUSTERS_DATA = 'SET_CLUSTERS_DATA';
|
export const SET_CLUSTERS_DATA = 'SET_CLUSTERS_DATA';
|
||||||
export const SET_LOADING_STATE = 'SET_LOADING_STATE';
|
export const SET_LOADING_STATE = 'SET_LOADING_STATE';
|
||||||
|
export const SET_PAGE = 'SET_PAGE';
|
||||||
|
|
|
@ -4,10 +4,15 @@ export default {
|
||||||
[types.SET_LOADING_STATE](state, value) {
|
[types.SET_LOADING_STATE](state, value) {
|
||||||
state.loading = value;
|
state.loading = value;
|
||||||
},
|
},
|
||||||
[types.SET_CLUSTERS_DATA](state, data) {
|
[types.SET_CLUSTERS_DATA](state, { data, paginationInformation }) {
|
||||||
Object.assign(state, {
|
Object.assign(state, {
|
||||||
clusters: data.clusters,
|
clusters: data.clusters,
|
||||||
|
clustersPerPage: paginationInformation.perPage,
|
||||||
hasAncestorClusters: data.has_ancestor_clusters,
|
hasAncestorClusters: data.has_ancestor_clusters,
|
||||||
|
totalCulsters: paginationInformation.total,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
[types.SET_PAGE](state, value) {
|
||||||
|
state.page = Number(value) || 1;
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -3,4 +3,7 @@ export default (initialState = {}) => ({
|
||||||
hasAncestorClusters: false,
|
hasAncestorClusters: false,
|
||||||
loading: true,
|
loading: true,
|
||||||
clusters: [],
|
clusters: [],
|
||||||
|
clustersPerPage: 0,
|
||||||
|
page: 1,
|
||||||
|
totalCulsters: 0,
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
<script>
|
<script>
|
||||||
import { mapActions } from 'vuex';
|
import { mapActions } from 'vuex';
|
||||||
|
import { GlIcon, GlTooltipDirective } from '@gitlab/ui';
|
||||||
import timeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
|
import timeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
@ -7,6 +8,10 @@ export default {
|
||||||
timeAgoTooltip,
|
timeAgoTooltip,
|
||||||
GitlabTeamMemberBadge: () =>
|
GitlabTeamMemberBadge: () =>
|
||||||
import('ee_component/vue_shared/components/user_avatar/badges/gitlab_team_member_badge.vue'),
|
import('ee_component/vue_shared/components/user_avatar/badges/gitlab_team_member_badge.vue'),
|
||||||
|
GlIcon,
|
||||||
|
},
|
||||||
|
directives: {
|
||||||
|
GlTooltip: GlTooltipDirective,
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
author: {
|
author: {
|
||||||
|
@ -44,6 +49,11 @@ export default {
|
||||||
required: false,
|
required: false,
|
||||||
default: true,
|
default: true,
|
||||||
},
|
},
|
||||||
|
isConfidential: {
|
||||||
|
type: Boolean,
|
||||||
|
required: false,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
@ -174,6 +184,15 @@ export default {
|
||||||
</a>
|
</a>
|
||||||
<time-ago-tooltip v-else ref="noteTimestamp" :time="createdAt" tooltip-placement="bottom" />
|
<time-ago-tooltip v-else ref="noteTimestamp" :time="createdAt" tooltip-placement="bottom" />
|
||||||
</template>
|
</template>
|
||||||
|
<gl-icon
|
||||||
|
v-if="isConfidential"
|
||||||
|
v-gl-tooltip:tooltipcontainer.bottom
|
||||||
|
data-testid="confidentialIndicator"
|
||||||
|
name="eye-slash"
|
||||||
|
:size="14"
|
||||||
|
:title="s__('Notes|Private comments are accessible by internal staff only')"
|
||||||
|
class="gl-ml-1 gl-text-gray-800 align-middle"
|
||||||
|
/>
|
||||||
<slot name="extra-controls"></slot>
|
<slot name="extra-controls"></slot>
|
||||||
<i
|
<i
|
||||||
v-if="showSpinner"
|
v-if="showSpinner"
|
||||||
|
|
|
@ -255,7 +255,13 @@ export default {
|
||||||
</div>
|
</div>
|
||||||
<div class="timeline-content">
|
<div class="timeline-content">
|
||||||
<div class="note-header">
|
<div class="note-header">
|
||||||
<note-header v-once :author="author" :created-at="note.created_at" :note-id="note.id">
|
<note-header
|
||||||
|
v-once
|
||||||
|
:author="author"
|
||||||
|
:created-at="note.created_at"
|
||||||
|
:note-id="note.id"
|
||||||
|
:is-confidential="note.confidential"
|
||||||
|
>
|
||||||
<slot slot="note-header-info" name="note-header-info"></slot>
|
<slot slot="note-header-info" name="note-header-info"></slot>
|
||||||
<span v-if="commit" v-html="actionText"></span>
|
<span v-if="commit" v-html="actionText"></span>
|
||||||
<span v-else-if="note.created_at" class="d-none d-sm-inline">·</span>
|
<span v-else-if="note.created_at" class="d-none d-sm-inline">·</span>
|
||||||
|
|
|
@ -68,7 +68,7 @@ export default {
|
||||||
footer-primary-button-variant="warning"
|
footer-primary-button-variant="warning"
|
||||||
@submit="onSubmit"
|
@submit="onSubmit"
|
||||||
>
|
>
|
||||||
<template slot="title">
|
<template #title>
|
||||||
{{ title }}
|
{{ title }}
|
||||||
</template>
|
</template>
|
||||||
<div>
|
<div>
|
||||||
|
|
|
@ -316,6 +316,7 @@ class ProjectPolicy < BasePolicy
|
||||||
enable :update_deployment
|
enable :update_deployment
|
||||||
enable :create_release
|
enable :create_release
|
||||||
enable :update_release
|
enable :update_release
|
||||||
|
enable :daily_statistics
|
||||||
enable :create_metrics_dashboard_annotation
|
enable :create_metrics_dashboard_annotation
|
||||||
enable :delete_metrics_dashboard_annotation
|
enable :delete_metrics_dashboard_annotation
|
||||||
enable :update_metrics_dashboard_annotation
|
enable :update_metrics_dashboard_annotation
|
||||||
|
@ -358,7 +359,6 @@ class ProjectPolicy < BasePolicy
|
||||||
enable :create_environment_terminal
|
enable :create_environment_terminal
|
||||||
enable :destroy_release
|
enable :destroy_release
|
||||||
enable :destroy_artifacts
|
enable :destroy_artifacts
|
||||||
enable :daily_statistics
|
|
||||||
enable :admin_operations
|
enable :admin_operations
|
||||||
enable :read_deploy_token
|
enable :read_deploy_token
|
||||||
enable :create_deploy_token
|
enable :create_deploy_token
|
||||||
|
|
|
@ -2,8 +2,32 @@
|
||||||
|
|
||||||
module Snippets
|
module Snippets
|
||||||
class BaseService < ::BaseService
|
class BaseService < ::BaseService
|
||||||
|
include SpamCheckMethods
|
||||||
|
|
||||||
|
CreateRepositoryError = Class.new(StandardError)
|
||||||
|
|
||||||
|
attr_reader :uploaded_files
|
||||||
|
|
||||||
|
def initialize(project, user = nil, params = {})
|
||||||
|
super
|
||||||
|
|
||||||
|
@uploaded_files = Array(@params.delete(:files).presence)
|
||||||
|
|
||||||
|
filter_spam_check_params
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
|
def visibility_allowed?(snippet, visibility_level)
|
||||||
|
Gitlab::VisibilityLevel.allowed_for?(current_user, visibility_level)
|
||||||
|
end
|
||||||
|
|
||||||
|
def error_forbidden_visibility(snippet)
|
||||||
|
deny_visibility_level(snippet)
|
||||||
|
|
||||||
|
snippet_error_response(snippet, 403)
|
||||||
|
end
|
||||||
|
|
||||||
def snippet_error_response(snippet, http_status)
|
def snippet_error_response(snippet, http_status)
|
||||||
ServiceResponse.error(
|
ServiceResponse.error(
|
||||||
message: snippet.errors.full_messages.to_sentence,
|
message: snippet.errors.full_messages.to_sentence,
|
||||||
|
|
|
@ -2,25 +2,11 @@
|
||||||
|
|
||||||
module Snippets
|
module Snippets
|
||||||
class CreateService < Snippets::BaseService
|
class CreateService < Snippets::BaseService
|
||||||
include SpamCheckMethods
|
|
||||||
|
|
||||||
CreateRepositoryError = Class.new(StandardError)
|
|
||||||
|
|
||||||
def execute
|
def execute
|
||||||
filter_spam_check_params
|
@snippet = build_from_params
|
||||||
|
|
||||||
@files = Array(params.delete(:files).presence)
|
unless visibility_allowed?(@snippet, @snippet.visibility_level)
|
||||||
|
return error_forbidden_visibility(@snippet)
|
||||||
@snippet = if project
|
|
||||||
project.snippets.build(params)
|
|
||||||
else
|
|
||||||
PersonalSnippet.new(params)
|
|
||||||
end
|
|
||||||
|
|
||||||
unless Gitlab::VisibilityLevel.allowed_for?(current_user, @snippet.visibility_level)
|
|
||||||
deny_visibility_level(@snippet)
|
|
||||||
|
|
||||||
return snippet_error_response(@snippet, 403)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
@snippet.author = current_user
|
@snippet.author = current_user
|
||||||
|
@ -41,6 +27,14 @@ module Snippets
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
|
def build_from_params
|
||||||
|
if project
|
||||||
|
project.snippets.build(params)
|
||||||
|
else
|
||||||
|
PersonalSnippet.new(params)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def save_and_commit
|
def save_and_commit
|
||||||
snippet_saved = @snippet.save
|
snippet_saved = @snippet.save
|
||||||
|
|
||||||
|
@ -91,7 +85,7 @@ module Snippets
|
||||||
def move_temporary_files
|
def move_temporary_files
|
||||||
return unless @snippet.is_a?(PersonalSnippet)
|
return unless @snippet.is_a?(PersonalSnippet)
|
||||||
|
|
||||||
@files.each do |file|
|
uploaded_files.each do |file|
|
||||||
FileMover.new(file, from_model: current_user, to_model: @snippet).execute
|
FileMover.new(file, from_model: current_user, to_model: @snippet).execute
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -2,26 +2,15 @@
|
||||||
|
|
||||||
module Snippets
|
module Snippets
|
||||||
class UpdateService < Snippets::BaseService
|
class UpdateService < Snippets::BaseService
|
||||||
include SpamCheckMethods
|
|
||||||
|
|
||||||
COMMITTABLE_ATTRIBUTES = %w(file_name content).freeze
|
COMMITTABLE_ATTRIBUTES = %w(file_name content).freeze
|
||||||
|
|
||||||
UpdateError = Class.new(StandardError)
|
UpdateError = Class.new(StandardError)
|
||||||
CreateRepositoryError = Class.new(StandardError)
|
|
||||||
|
|
||||||
def execute(snippet)
|
def execute(snippet)
|
||||||
# check that user is allowed to set specified visibility_level
|
if visibility_changed?(snippet) && !visibility_allowed?(snippet, visibility_level)
|
||||||
new_visibility = visibility_level
|
return error_forbidden_visibility(snippet)
|
||||||
|
|
||||||
if new_visibility && new_visibility.to_i != snippet.visibility_level
|
|
||||||
unless Gitlab::VisibilityLevel.allowed_for?(current_user, new_visibility)
|
|
||||||
deny_visibility_level(snippet, new_visibility)
|
|
||||||
|
|
||||||
return snippet_error_response(snippet, 403)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
filter_spam_check_params
|
|
||||||
snippet.assign_attributes(params)
|
snippet.assign_attributes(params)
|
||||||
spam_check(snippet, current_user)
|
spam_check(snippet, current_user)
|
||||||
|
|
||||||
|
@ -36,6 +25,10 @@ module Snippets
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
|
def visibility_changed?(snippet)
|
||||||
|
visibility_level && visibility_level.to_i != snippet.visibility_level
|
||||||
|
end
|
||||||
|
|
||||||
def save_and_commit(snippet)
|
def save_and_commit(snippet)
|
||||||
return false unless snippet.save
|
return false unless snippet.save
|
||||||
|
|
||||||
|
|
|
@ -27,7 +27,7 @@
|
||||||
%td
|
%td
|
||||||
- if token.expires?
|
- if token.expires?
|
||||||
%span{ class: ('text-warning' if token.expires_soon?) }
|
%span{ class: ('text-warning' if token.expires_soon?) }
|
||||||
In #{distance_of_time_in_words_to_now(token.expires_at)}
|
= _('In %{time_to_now}') % { time_to_now: distance_of_time_in_words_to_now(token.expires_at) }
|
||||||
- else
|
- else
|
||||||
%span.token-never-expires-label= _('Never')
|
%span.token-never-expires-label= _('Never')
|
||||||
%td= token.scopes.present? ? token.scopes.join(', ') : _('<no scopes selected>')
|
%td= token.scopes.present? ? token.scopes.join(', ') : _('<no scopes selected>')
|
||||||
|
|
|
@ -22,14 +22,14 @@
|
||||||
- elsif event.target
|
- elsif event.target
|
||||||
= link_to event.target.to_reference, [event.project.namespace.becomes(Namespace), event.project, event.target], class: 'has-tooltip', title: event.target_title
|
= link_to event.target.to_reference, [event.project.namespace.becomes(Namespace), event.project, event.target], class: 'has-tooltip', title: event.target_title
|
||||||
|
|
||||||
at
|
= s_('UserProfile|at')
|
||||||
%strong
|
%strong
|
||||||
- if event.project
|
- if event.project
|
||||||
= link_to_project(event.project)
|
= link_to_project(event.project)
|
||||||
- else
|
- else
|
||||||
= event.resource_parent_name
|
= event.resource_parent_name
|
||||||
- else
|
- else
|
||||||
made a private contribution
|
= s_('UserProfile|made a private contribution')
|
||||||
- else
|
- else
|
||||||
%p
|
%p
|
||||||
= _('No contributions were found')
|
= _('No contributions were found')
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
title: Add confidential status support for comment and replies
|
||||||
|
merge_request: 31622
|
||||||
|
author:
|
||||||
|
type: added
|
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
title: Externalize i18n strings from ./app/views/shared/_personal_access_tokens_table.html.haml
|
||||||
|
merge_request: 32116
|
||||||
|
author: Gilang Gumilar
|
||||||
|
type: changed
|
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
title: Externalize i18n strings from ./app/views/users/calendar_activities.html.haml
|
||||||
|
merge_request: 32094
|
||||||
|
author: Gilang Gumilar
|
||||||
|
type: changed
|
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
title: 'Cluster index refactor: Add missing pagination'
|
||||||
|
merge_request: 32338
|
||||||
|
author:
|
||||||
|
type: changed
|
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
title: Fix leaky constant issue in env assignment spec
|
||||||
|
merge_request: 32040
|
||||||
|
author: Rajendra Kadam
|
||||||
|
type: fixed
|
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
title: Fix leaky constant issue in application settings encrypt spec
|
||||||
|
merge_request: 32066
|
||||||
|
author: Rajendra Kadam
|
||||||
|
type: fixed
|
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
title: Fix leaky constant issue in simple executor spec
|
||||||
|
merge_request: 32082
|
||||||
|
author: Rajendra Kadam
|
||||||
|
type: fixed
|
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
title: Fix leaky constant issue connection, master check and attr config spec
|
||||||
|
merge_request: 32144
|
||||||
|
author: Rajendra Kadam
|
||||||
|
type: fixed
|
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
title: Correct the permission according to docs
|
||||||
|
merge_request: 28657
|
||||||
|
author:
|
||||||
|
type: fixed
|
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
title: Validate package types in package metadatum models
|
||||||
|
merge_request: 32091
|
||||||
|
author: Sashi Kumar
|
||||||
|
type: other
|
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
title: Update deprecated slot syntax in ./app/assets/javascripts/pages/milestones/shared/components/promote_milestone_modal.vue
|
||||||
|
merge_request: 31980
|
||||||
|
author: Gilang Gumilar
|
||||||
|
type: other
|
|
@ -62,7 +62,7 @@ The following settings are:
|
||||||
|
|
||||||
| Setting | Description | Default |
|
| Setting | Description | Default |
|
||||||
|---------|-------------|---------|
|
|---------|-------------|---------|
|
||||||
| `enabled` | Enable/disable object storage | `true` |
|
| `enabled` | Enable/disable object storage | `false` |
|
||||||
| `remote_directory` | The bucket name where Terraform state files will be stored | |
|
| `remote_directory` | The bucket name where Terraform state files will be stored | |
|
||||||
| `connection` | Various connection options described below | |
|
| `connection` | Various connection options described below | |
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
type: howto
|
type: howto
|
||||||
---
|
---
|
||||||
|
|
||||||
# Installing GitLab HA on Amazon Web Services (AWS)
|
# Installing GitLab on Amazon Web Services (AWS)
|
||||||
|
|
||||||
This page offers a walkthrough of a common configuration
|
This page offers a walkthrough of a common configuration
|
||||||
for GitLab on AWS. You should customize it to accommodate your needs.
|
for GitLab on AWS. You should customize it to accommodate your needs.
|
||||||
|
@ -16,7 +16,7 @@ GitLab on AWS can leverage many of the services that are already
|
||||||
configurable. These services offer a great deal of
|
configurable. These services offer a great deal of
|
||||||
flexibility and can be adapted to the needs of most companies.
|
flexibility and can be adapted to the needs of most companies.
|
||||||
|
|
||||||
In this guide, we'll go through a basic HA setup where we'll start by
|
In this guide, we'll go through a multi-node setup where we'll start by
|
||||||
configuring our Virtual Private Cloud and subnets to later integrate
|
configuring our Virtual Private Cloud and subnets to later integrate
|
||||||
services such as RDS for our database server and ElastiCache as a Redis
|
services such as RDS for our database server and ElastiCache as a Redis
|
||||||
cluster to finally manage them within an auto scaling group with custom
|
cluster to finally manage them within an auto scaling group with custom
|
||||||
|
@ -306,7 +306,7 @@ Now, it's time to create the database:
|
||||||
1. Under **Settings**, set a DB instance identifier, a master username, and a master password. We'll use `gitlab-db-ha`, `gitlab`, and a very secure password respectively. Make a note of these as we'll need them later.
|
1. Under **Settings**, set a DB instance identifier, a master username, and a master password. We'll use `gitlab-db-ha`, `gitlab`, and a very secure password respectively. Make a note of these as we'll need them later.
|
||||||
1. For the DB instance size, select **Standard classes** and select an instance size that meets your requirements from the dropdown menu. We'll use a `db.m4.large` instance.
|
1. For the DB instance size, select **Standard classes** and select an instance size that meets your requirements from the dropdown menu. We'll use a `db.m4.large` instance.
|
||||||
1. Under **Storage**, configure the following:
|
1. Under **Storage**, configure the following:
|
||||||
1. Select **Provisioned IOPS (SSD)** from the storage type dropdown menu. Provisioned IOPS (SSD) storage is best suited for HA (though you can choose General Purpose (SSD) to reduce the costs). Read more about it at [Storage for Amazon RDS](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/CHAP_Storage.html).
|
1. Select **Provisioned IOPS (SSD)** from the storage type dropdown menu. Provisioned IOPS (SSD) storage is best suited for this use (though you can choose General Purpose (SSD) to reduce the costs). Read more about it at [Storage for Amazon RDS](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/CHAP_Storage.html).
|
||||||
1. Allocate storage and set provisioned IOPS. We'll use the minimum values, `100` and `1000`, respectively.
|
1. Allocate storage and set provisioned IOPS. We'll use the minimum values, `100` and `1000`, respectively.
|
||||||
1. Enable storage autoscaling (optional) and set a maximum storage threshold.
|
1. Enable storage autoscaling (optional) and set a maximum storage threshold.
|
||||||
1. Under **Availability & durability**, select **Create a standby instance** to have a standby RDS instance provisioned in a different [Availability Zone](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/Concepts.MultiAZ.html).
|
1. Under **Availability & durability**, select **Create a standby instance** to have a standby RDS instance provisioned in a different [Availability Zone](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/Concepts.MultiAZ.html).
|
||||||
|
|
|
@ -83,7 +83,7 @@ The following table depicts the various user permission levels in a project.
|
||||||
| See a container registry | | ✓ | ✓ | ✓ | ✓ |
|
| See a container registry | | ✓ | ✓ | ✓ | ✓ |
|
||||||
| See environments | | ✓ | ✓ | ✓ | ✓ |
|
| See environments | | ✓ | ✓ | ✓ | ✓ |
|
||||||
| See a list of merge requests | | ✓ | ✓ | ✓ | ✓ |
|
| See a list of merge requests | | ✓ | ✓ | ✓ | ✓ |
|
||||||
| View project statistics | | ✓ | ✓ | ✓ | ✓ |
|
| View project statistics | | | ✓ | ✓ | ✓ |
|
||||||
| View Error Tracking list | | ✓ | ✓ | ✓ | ✓ |
|
| View Error Tracking list | | ✓ | ✓ | ✓ | ✓ |
|
||||||
| Create new merge request | | ✓ | ✓ | ✓ | ✓ |
|
| Create new merge request | | ✓ | ✓ | ✓ | ✓ |
|
||||||
| View metrics dashboard annotations | | ✓ | ✓ | ✓ | ✓ |
|
| View metrics dashboard annotations | | ✓ | ✓ | ✓ | ✓ |
|
||||||
|
|
|
@ -11589,6 +11589,9 @@ msgstr ""
|
||||||
msgid "Improve search with Advanced Global Search and GitLab Enterprise Edition."
|
msgid "Improve search with Advanced Global Search and GitLab Enterprise Edition."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "In %{time_to_now}"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "In order to enable instance-level analytics, please ask an admin to enable %{usage_ping_link_start}usage ping%{usage_ping_link_end}."
|
msgid "In order to enable instance-level analytics, please ask an admin to enable %{usage_ping_link_start}usage ping%{usage_ping_link_end}."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -14519,6 +14522,9 @@ msgstr ""
|
||||||
msgid "Notes|Collapse replies"
|
msgid "Notes|Collapse replies"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Notes|Private comments are accessible by internal staff only"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "Notes|Show all activity"
|
msgid "Notes|Show all activity"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -14965,6 +14971,9 @@ msgstr ""
|
||||||
msgid "Package type must be NuGet"
|
msgid "Package type must be NuGet"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Package type must be PyPi"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "Package was removed"
|
msgid "Package was removed"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -23775,6 +23784,12 @@ msgstr ""
|
||||||
msgid "UserProfile|Your projects can be available publicly, internally, or privately, at your choice."
|
msgid "UserProfile|Your projects can be available publicly, internally, or privately, at your choice."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "UserProfile|at"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "UserProfile|made a private contribution"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "Username (optional)"
|
msgid "Username (optional)"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
|
|
@ -57,6 +57,10 @@ module QA
|
||||||
click_element :download_export_link
|
click_element :download_export_link
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def has_download_export_link?
|
||||||
|
has_element? :download_export_link
|
||||||
|
end
|
||||||
|
|
||||||
def archive_project
|
def archive_project
|
||||||
page.accept_alert("Are you sure that you want to archive this project?") do
|
page.accept_alert("Are you sure that you want to archive this project?") do
|
||||||
click_element :archive_project_link
|
click_element :archive_project_link
|
||||||
|
|
|
@ -4,7 +4,7 @@ import ClusterStore from '~/clusters_list/store';
|
||||||
import MockAdapter from 'axios-mock-adapter';
|
import MockAdapter from 'axios-mock-adapter';
|
||||||
import { apiData } from '../mock_data';
|
import { apiData } from '../mock_data';
|
||||||
import { mount } from '@vue/test-utils';
|
import { mount } from '@vue/test-utils';
|
||||||
import { GlTable, GlLoadingIcon } from '@gitlab/ui';
|
import { GlLoadingIcon, GlTable, GlPagination } from '@gitlab/ui';
|
||||||
|
|
||||||
describe('Clusters', () => {
|
describe('Clusters', () => {
|
||||||
let mock;
|
let mock;
|
||||||
|
@ -14,11 +14,12 @@ describe('Clusters', () => {
|
||||||
const endpoint = 'some/endpoint';
|
const endpoint = 'some/endpoint';
|
||||||
|
|
||||||
const findLoader = () => wrapper.find(GlLoadingIcon);
|
const findLoader = () => wrapper.find(GlLoadingIcon);
|
||||||
|
const findPaginatedButtons = () => wrapper.find(GlPagination);
|
||||||
const findTable = () => wrapper.find(GlTable);
|
const findTable = () => wrapper.find(GlTable);
|
||||||
const findStatuses = () => findTable().findAll('.js-status');
|
const findStatuses = () => findTable().findAll('.js-status');
|
||||||
|
|
||||||
const mockPollingApi = (response, body, header) => {
|
const mockPollingApi = (response, body, header) => {
|
||||||
mock.onGet(endpoint).reply(response, body, header);
|
mock.onGet(`${endpoint}?page=${header['x-page']}`).reply(response, body, header);
|
||||||
};
|
};
|
||||||
|
|
||||||
const mountWrapper = () => {
|
const mountWrapper = () => {
|
||||||
|
@ -29,7 +30,11 @@ describe('Clusters', () => {
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
mock = new MockAdapter(axios);
|
mock = new MockAdapter(axios);
|
||||||
mockPollingApi(200, apiData, {});
|
mockPollingApi(200, apiData, {
|
||||||
|
'x-total': apiData.clusters.length,
|
||||||
|
'x-per-page': 20,
|
||||||
|
'x-page': 1,
|
||||||
|
});
|
||||||
|
|
||||||
return mountWrapper();
|
return mountWrapper();
|
||||||
});
|
});
|
||||||
|
@ -93,4 +98,47 @@ describe('Clusters', () => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('pagination', () => {
|
||||||
|
const perPage = apiData.clusters.length;
|
||||||
|
const totalFirstPage = 100;
|
||||||
|
const totalSecondPage = 500;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
mockPollingApi(200, apiData, {
|
||||||
|
'x-total': totalFirstPage,
|
||||||
|
'x-per-page': perPage,
|
||||||
|
'x-page': 1,
|
||||||
|
});
|
||||||
|
return mountWrapper();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should load to page 1 with header values', () => {
|
||||||
|
const buttons = findPaginatedButtons();
|
||||||
|
|
||||||
|
expect(buttons.props('perPage')).toBe(perPage);
|
||||||
|
expect(buttons.props('totalItems')).toBe(totalFirstPage);
|
||||||
|
expect(buttons.props('value')).toBe(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('when updating currentPage', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
mockPollingApi(200, apiData, {
|
||||||
|
'x-total': totalSecondPage,
|
||||||
|
'x-per-page': perPage,
|
||||||
|
'x-page': 2,
|
||||||
|
});
|
||||||
|
wrapper.setData({ currentPage: 2 });
|
||||||
|
return axios.waitForAll();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should change pagination when currentPage changes', () => {
|
||||||
|
const buttons = findPaginatedButtons();
|
||||||
|
|
||||||
|
expect(buttons.props('perPage')).toBe(perPage);
|
||||||
|
expect(buttons.props('totalItems')).toBe(totalSecondPage);
|
||||||
|
expect(buttons.props('value')).toBe(2);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -19,14 +19,29 @@ describe('Clusters store actions', () => {
|
||||||
afterEach(() => mock.restore());
|
afterEach(() => mock.restore());
|
||||||
|
|
||||||
it('should commit SET_CLUSTERS_DATA with received response', done => {
|
it('should commit SET_CLUSTERS_DATA with received response', done => {
|
||||||
mock.onGet().reply(200, apiData);
|
const headers = {
|
||||||
|
'x-total': apiData.clusters.length,
|
||||||
|
'x-per-page': 20,
|
||||||
|
'x-page': 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
const paginationInformation = {
|
||||||
|
nextPage: NaN,
|
||||||
|
page: 1,
|
||||||
|
perPage: 20,
|
||||||
|
previousPage: NaN,
|
||||||
|
total: apiData.clusters.length,
|
||||||
|
totalPages: NaN,
|
||||||
|
};
|
||||||
|
|
||||||
|
mock.onGet().reply(200, apiData, headers);
|
||||||
|
|
||||||
testAction(
|
testAction(
|
||||||
actions.fetchClusters,
|
actions.fetchClusters,
|
||||||
{ endpoint: apiData.endpoint },
|
{ endpoint: apiData.endpoint },
|
||||||
{},
|
{},
|
||||||
[
|
[
|
||||||
{ type: types.SET_CLUSTERS_DATA, payload: apiData },
|
{ type: types.SET_CLUSTERS_DATA, payload: { data: apiData, paginationInformation } },
|
||||||
{ type: types.SET_LOADING_STATE, payload: false },
|
{ type: types.SET_LOADING_STATE, payload: false },
|
||||||
],
|
],
|
||||||
[],
|
[],
|
||||||
|
|
|
@ -39,7 +39,7 @@ describe('Dashboard', () => {
|
||||||
const findEnvironmentsDropdown = () => wrapper.find({ ref: 'monitorEnvironmentsDropdown' });
|
const findEnvironmentsDropdown = () => wrapper.find({ ref: 'monitorEnvironmentsDropdown' });
|
||||||
const findAllEnvironmentsDropdownItems = () => findEnvironmentsDropdown().findAll(GlDropdownItem);
|
const findAllEnvironmentsDropdownItems = () => findEnvironmentsDropdown().findAll(GlDropdownItem);
|
||||||
const setSearchTerm = searchTerm => {
|
const setSearchTerm = searchTerm => {
|
||||||
wrapper.vm.$store.commit(`monitoringDashboard/${types.SET_ENVIRONMENTS_FILTER}`, searchTerm);
|
store.commit(`monitoringDashboard/${types.SET_ENVIRONMENTS_FILTER}`, searchTerm);
|
||||||
};
|
};
|
||||||
|
|
||||||
const createShallowWrapper = (props = {}, options = {}) => {
|
const createShallowWrapper = (props = {}, options = {}) => {
|
||||||
|
@ -135,7 +135,7 @@ describe('Dashboard', () => {
|
||||||
it('hides the group panels when showPanels is false', () => {
|
it('hides the group panels when showPanels is false', () => {
|
||||||
createMountedWrapper({ hasMetrics: true, showPanels: false });
|
createMountedWrapper({ hasMetrics: true, showPanels: false });
|
||||||
|
|
||||||
setupStoreWithData(wrapper.vm.$store);
|
setupStoreWithData(store);
|
||||||
|
|
||||||
return wrapper.vm.$nextTick().then(() => {
|
return wrapper.vm.$nextTick().then(() => {
|
||||||
expect(wrapper.vm.showEmptyState).toEqual(false);
|
expect(wrapper.vm.showEmptyState).toEqual(false);
|
||||||
|
@ -148,7 +148,7 @@ describe('Dashboard', () => {
|
||||||
|
|
||||||
createMountedWrapper({ hasMetrics: true });
|
createMountedWrapper({ hasMetrics: true });
|
||||||
|
|
||||||
wrapper.vm.$store.commit(
|
store.commit(
|
||||||
`monitoringDashboard/${types.RECEIVE_ENVIRONMENTS_DATA_SUCCESS}`,
|
`monitoringDashboard/${types.RECEIVE_ENVIRONMENTS_DATA_SUCCESS}`,
|
||||||
environmentData,
|
environmentData,
|
||||||
);
|
);
|
||||||
|
@ -188,7 +188,7 @@ describe('Dashboard', () => {
|
||||||
);
|
);
|
||||||
|
|
||||||
createMountedWrapper({ hasMetrics: true });
|
createMountedWrapper({ hasMetrics: true });
|
||||||
setupStoreWithData(wrapper.vm.$store);
|
setupStoreWithData(store);
|
||||||
|
|
||||||
return wrapper.vm.$nextTick().then(() => {
|
return wrapper.vm.$nextTick().then(() => {
|
||||||
expect(store.dispatch).toHaveBeenCalledWith('monitoringDashboard/setExpandedPanel', {
|
expect(store.dispatch).toHaveBeenCalledWith('monitoringDashboard/setExpandedPanel', {
|
||||||
|
@ -205,7 +205,7 @@ describe('Dashboard', () => {
|
||||||
setSearch('');
|
setSearch('');
|
||||||
|
|
||||||
createMountedWrapper({ hasMetrics: true });
|
createMountedWrapper({ hasMetrics: true });
|
||||||
setupStoreWithData(wrapper.vm.$store);
|
setupStoreWithData(store);
|
||||||
|
|
||||||
return wrapper.vm.$nextTick().then(() => {
|
return wrapper.vm.$nextTick().then(() => {
|
||||||
expect(store.dispatch).not.toHaveBeenCalledWith(
|
expect(store.dispatch).not.toHaveBeenCalledWith(
|
||||||
|
@ -228,7 +228,7 @@ describe('Dashboard', () => {
|
||||||
);
|
);
|
||||||
|
|
||||||
createMountedWrapper({ hasMetrics: true });
|
createMountedWrapper({ hasMetrics: true });
|
||||||
setupStoreWithData(wrapper.vm.$store);
|
setupStoreWithData(store);
|
||||||
|
|
||||||
return wrapper.vm.$nextTick().then(() => {
|
return wrapper.vm.$nextTick().then(() => {
|
||||||
expect(createFlash).toHaveBeenCalled();
|
expect(createFlash).toHaveBeenCalled();
|
||||||
|
@ -328,7 +328,7 @@ describe('Dashboard', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
createMountedWrapper({ hasMetrics: true });
|
createMountedWrapper({ hasMetrics: true });
|
||||||
|
|
||||||
setupStoreWithData(wrapper.vm.$store);
|
setupStoreWithData(store);
|
||||||
|
|
||||||
return wrapper.vm.$nextTick();
|
return wrapper.vm.$nextTick();
|
||||||
});
|
});
|
||||||
|
@ -436,7 +436,7 @@ describe('Dashboard', () => {
|
||||||
it('hides the environments dropdown list when there is no environments', () => {
|
it('hides the environments dropdown list when there is no environments', () => {
|
||||||
createMountedWrapper({ hasMetrics: true });
|
createMountedWrapper({ hasMetrics: true });
|
||||||
|
|
||||||
setupStoreWithDashboard(wrapper.vm.$store);
|
setupStoreWithDashboard(store);
|
||||||
|
|
||||||
return wrapper.vm.$nextTick().then(() => {
|
return wrapper.vm.$nextTick().then(() => {
|
||||||
expect(findAllEnvironmentsDropdownItems()).toHaveLength(0);
|
expect(findAllEnvironmentsDropdownItems()).toHaveLength(0);
|
||||||
|
@ -446,7 +446,7 @@ describe('Dashboard', () => {
|
||||||
it('renders the datetimepicker dropdown', () => {
|
it('renders the datetimepicker dropdown', () => {
|
||||||
createMountedWrapper({ hasMetrics: true });
|
createMountedWrapper({ hasMetrics: true });
|
||||||
|
|
||||||
setupStoreWithData(wrapper.vm.$store);
|
setupStoreWithData(store);
|
||||||
|
|
||||||
return wrapper.vm.$nextTick().then(() => {
|
return wrapper.vm.$nextTick().then(() => {
|
||||||
expect(wrapper.find(DateTimePicker).exists()).toBe(true);
|
expect(wrapper.find(DateTimePicker).exists()).toBe(true);
|
||||||
|
@ -456,7 +456,7 @@ describe('Dashboard', () => {
|
||||||
it('renders the refresh dashboard button', () => {
|
it('renders the refresh dashboard button', () => {
|
||||||
createMountedWrapper({ hasMetrics: true });
|
createMountedWrapper({ hasMetrics: true });
|
||||||
|
|
||||||
setupStoreWithData(wrapper.vm.$store);
|
setupStoreWithData(store);
|
||||||
|
|
||||||
return wrapper.vm.$nextTick().then(() => {
|
return wrapper.vm.$nextTick().then(() => {
|
||||||
const refreshBtn = wrapper.findAll({ ref: 'refreshDashboardBtn' });
|
const refreshBtn = wrapper.findAll({ ref: 'refreshDashboardBtn' });
|
||||||
|
@ -469,8 +469,8 @@ describe('Dashboard', () => {
|
||||||
describe('variables section', () => {
|
describe('variables section', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
createShallowWrapper({ hasMetrics: true });
|
createShallowWrapper({ hasMetrics: true });
|
||||||
setupStoreWithData(wrapper.vm.$store);
|
setupStoreWithData(store);
|
||||||
setupStoreWithVariable(wrapper.vm.$store);
|
setupStoreWithVariable(store);
|
||||||
|
|
||||||
return wrapper.vm.$nextTick();
|
return wrapper.vm.$nextTick();
|
||||||
});
|
});
|
||||||
|
@ -486,7 +486,7 @@ describe('Dashboard', () => {
|
||||||
describe('when the panel is not expanded', () => {
|
describe('when the panel is not expanded', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
createShallowWrapper({ hasMetrics: true });
|
createShallowWrapper({ hasMetrics: true });
|
||||||
setupStoreWithData(wrapper.vm.$store);
|
setupStoreWithData(store);
|
||||||
return wrapper.vm.$nextTick();
|
return wrapper.vm.$nextTick();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -524,14 +524,14 @@ describe('Dashboard', () => {
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
createShallowWrapper({ hasMetrics: true }, { stubs: { DashboardPanel: MockPanel } });
|
createShallowWrapper({ hasMetrics: true }, { stubs: { DashboardPanel: MockPanel } });
|
||||||
setupStoreWithData(wrapper.vm.$store);
|
setupStoreWithData(store);
|
||||||
|
|
||||||
const { panelGroups } = wrapper.vm.$store.state.monitoringDashboard.dashboard;
|
const { panelGroups } = store.state.monitoringDashboard.dashboard;
|
||||||
|
|
||||||
group = panelGroups[0].group;
|
group = panelGroups[0].group;
|
||||||
[panel] = panelGroups[0].panels;
|
[panel] = panelGroups[0].panels;
|
||||||
|
|
||||||
wrapper.vm.$store.commit(`monitoringDashboard/${types.SET_EXPANDED_PANEL}`, {
|
store.commit(`monitoringDashboard/${types.SET_EXPANDED_PANEL}`, {
|
||||||
group,
|
group,
|
||||||
panel,
|
panel,
|
||||||
});
|
});
|
||||||
|
@ -593,10 +593,8 @@ describe('Dashboard', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
createShallowWrapper({ hasMetrics: true });
|
createShallowWrapper({ hasMetrics: true });
|
||||||
|
|
||||||
const { $store } = wrapper.vm;
|
setupStoreWithDashboard(store);
|
||||||
|
setMetricResult({ store, result: [], panel: 2 });
|
||||||
setupStoreWithDashboard($store);
|
|
||||||
setMetricResult({ $store, result: [], panel: 2 });
|
|
||||||
|
|
||||||
return wrapper.vm.$nextTick();
|
return wrapper.vm.$nextTick();
|
||||||
});
|
});
|
||||||
|
@ -622,7 +620,7 @@ describe('Dashboard', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
createMountedWrapper({ hasMetrics: true }, { attachToDocument: true });
|
createMountedWrapper({ hasMetrics: true }, { attachToDocument: true });
|
||||||
|
|
||||||
setupStoreWithData(wrapper.vm.$store);
|
setupStoreWithData(store);
|
||||||
|
|
||||||
return wrapper.vm.$nextTick();
|
return wrapper.vm.$nextTick();
|
||||||
});
|
});
|
||||||
|
@ -673,7 +671,7 @@ describe('Dashboard', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('shows loading element when environments fetch is still loading', () => {
|
it('shows loading element when environments fetch is still loading', () => {
|
||||||
wrapper.vm.$store.commit(`monitoringDashboard/${types.REQUEST_ENVIRONMENTS_DATA}`);
|
store.commit(`monitoringDashboard/${types.REQUEST_ENVIRONMENTS_DATA}`);
|
||||||
|
|
||||||
return wrapper.vm
|
return wrapper.vm
|
||||||
.$nextTick()
|
.$nextTick()
|
||||||
|
@ -681,7 +679,7 @@ describe('Dashboard', () => {
|
||||||
expect(wrapper.find({ ref: 'monitorEnvironmentsDropdownLoading' }).exists()).toBe(true);
|
expect(wrapper.find({ ref: 'monitorEnvironmentsDropdownLoading' }).exists()).toBe(true);
|
||||||
})
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
wrapper.vm.$store.commit(
|
store.commit(
|
||||||
`monitoringDashboard/${types.RECEIVE_ENVIRONMENTS_DATA_SUCCESS}`,
|
`monitoringDashboard/${types.RECEIVE_ENVIRONMENTS_DATA_SUCCESS}`,
|
||||||
environmentData,
|
environmentData,
|
||||||
);
|
);
|
||||||
|
@ -703,7 +701,7 @@ describe('Dashboard', () => {
|
||||||
store.dispatch.mockRestore();
|
store.dispatch.mockRestore();
|
||||||
|
|
||||||
createShallowWrapper({ hasMetrics: true });
|
createShallowWrapper({ hasMetrics: true });
|
||||||
setupStoreWithData(wrapper.vm.$store);
|
setupStoreWithData(store);
|
||||||
|
|
||||||
return wrapper.vm.$nextTick();
|
return wrapper.vm.$nextTick();
|
||||||
});
|
});
|
||||||
|
@ -791,7 +789,7 @@ describe('Dashboard', () => {
|
||||||
createShallowWrapper({ hasMetrics: true, showHeader: false });
|
createShallowWrapper({ hasMetrics: true, showHeader: false });
|
||||||
|
|
||||||
// all_dashboards is not defined in health dashboards
|
// all_dashboards is not defined in health dashboards
|
||||||
wrapper.vm.$store.commit(`monitoringDashboard/${types.SET_ALL_DASHBOARDS}`, undefined);
|
store.commit(`monitoringDashboard/${types.SET_ALL_DASHBOARDS}`, undefined);
|
||||||
return wrapper.vm.$nextTick();
|
return wrapper.vm.$nextTick();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -917,7 +915,7 @@ describe('Dashboard', () => {
|
||||||
customMetricsPath: '/endpoint',
|
customMetricsPath: '/endpoint',
|
||||||
customMetricsAvailable: true,
|
customMetricsAvailable: true,
|
||||||
});
|
});
|
||||||
setupStoreWithData(wrapper.vm.$store);
|
setupStoreWithData(store);
|
||||||
|
|
||||||
origPage = document.body.dataset.page;
|
origPage = document.body.dataset.page;
|
||||||
document.body.dataset.page = 'projects:environments:metrics';
|
document.body.dataset.page = 'projects:environments:metrics';
|
||||||
|
|
|
@ -47,7 +47,7 @@ describe('Metrics dashboard/variables section component', () => {
|
||||||
|
|
||||||
it('shows the variables section', () => {
|
it('shows the variables section', () => {
|
||||||
createShallowWrapper();
|
createShallowWrapper();
|
||||||
wrapper.vm.$store.commit(`monitoringDashboard/${types.SET_VARIABLES}`, sampleVariables);
|
store.commit(`monitoringDashboard/${types.SET_VARIABLES}`, sampleVariables);
|
||||||
|
|
||||||
return wrapper.vm.$nextTick(() => {
|
return wrapper.vm.$nextTick(() => {
|
||||||
const allInputs = findTextInput().length + findCustomInput().length;
|
const allInputs = findTextInput().length + findCustomInput().length;
|
||||||
|
|
|
@ -2,48 +2,48 @@ import * as types from '~/monitoring/stores/mutation_types';
|
||||||
import { metricsResult, environmentData, dashboardGitResponse } from './mock_data';
|
import { metricsResult, environmentData, dashboardGitResponse } from './mock_data';
|
||||||
import { metricsDashboardPayload } from './fixture_data';
|
import { metricsDashboardPayload } from './fixture_data';
|
||||||
|
|
||||||
export const setMetricResult = ({ $store, result, group = 0, panel = 0, metric = 0 }) => {
|
export const setMetricResult = ({ store, result, group = 0, panel = 0, metric = 0 }) => {
|
||||||
const { dashboard } = $store.state.monitoringDashboard;
|
const { dashboard } = store.state.monitoringDashboard;
|
||||||
const { metricId } = dashboard.panelGroups[group].panels[panel].metrics[metric];
|
const { metricId } = dashboard.panelGroups[group].panels[panel].metrics[metric];
|
||||||
|
|
||||||
$store.commit(`monitoringDashboard/${types.RECEIVE_METRIC_RESULT_SUCCESS}`, {
|
store.commit(`monitoringDashboard/${types.RECEIVE_METRIC_RESULT_SUCCESS}`, {
|
||||||
metricId,
|
metricId,
|
||||||
result,
|
result,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const setEnvironmentData = $store => {
|
const setEnvironmentData = store => {
|
||||||
$store.commit(`monitoringDashboard/${types.RECEIVE_ENVIRONMENTS_DATA_SUCCESS}`, environmentData);
|
store.commit(`monitoringDashboard/${types.RECEIVE_ENVIRONMENTS_DATA_SUCCESS}`, environmentData);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const setupAllDashboards = $store => {
|
export const setupAllDashboards = store => {
|
||||||
$store.commit(`monitoringDashboard/${types.SET_ALL_DASHBOARDS}`, dashboardGitResponse);
|
store.commit(`monitoringDashboard/${types.SET_ALL_DASHBOARDS}`, dashboardGitResponse);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const setupStoreWithDashboard = $store => {
|
export const setupStoreWithDashboard = store => {
|
||||||
$store.commit(
|
store.commit(
|
||||||
`monitoringDashboard/${types.RECEIVE_METRICS_DASHBOARD_SUCCESS}`,
|
`monitoringDashboard/${types.RECEIVE_METRICS_DASHBOARD_SUCCESS}`,
|
||||||
metricsDashboardPayload,
|
metricsDashboardPayload,
|
||||||
);
|
);
|
||||||
$store.commit(
|
store.commit(
|
||||||
`monitoringDashboard/${types.RECEIVE_METRICS_DASHBOARD_SUCCESS}`,
|
`monitoringDashboard/${types.RECEIVE_METRICS_DASHBOARD_SUCCESS}`,
|
||||||
metricsDashboardPayload,
|
metricsDashboardPayload,
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const setupStoreWithVariable = $store => {
|
export const setupStoreWithVariable = store => {
|
||||||
$store.commit(`monitoringDashboard/${types.SET_VARIABLES}`, {
|
store.commit(`monitoringDashboard/${types.SET_VARIABLES}`, {
|
||||||
label1: 'pod',
|
label1: 'pod',
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export const setupStoreWithData = $store => {
|
export const setupStoreWithData = store => {
|
||||||
setupAllDashboards($store);
|
setupAllDashboards(store);
|
||||||
setupStoreWithDashboard($store);
|
setupStoreWithDashboard(store);
|
||||||
|
|
||||||
setMetricResult({ $store, result: [], panel: 0 });
|
setMetricResult({ store, result: [], panel: 0 });
|
||||||
setMetricResult({ $store, result: metricsResult, panel: 1 });
|
setMetricResult({ store, result: metricsResult, panel: 1 });
|
||||||
setMetricResult({ $store, result: metricsResult, panel: 2 });
|
setMetricResult({ store, result: metricsResult, panel: 2 });
|
||||||
|
|
||||||
setEnvironmentData($store);
|
setEnvironmentData(store);
|
||||||
};
|
};
|
||||||
|
|
|
@ -18,6 +18,7 @@ describe('NoteHeader component', () => {
|
||||||
const findActionText = () => wrapper.find({ ref: 'actionText' });
|
const findActionText = () => wrapper.find({ ref: 'actionText' });
|
||||||
const findTimestampLink = () => wrapper.find({ ref: 'noteTimestampLink' });
|
const findTimestampLink = () => wrapper.find({ ref: 'noteTimestampLink' });
|
||||||
const findTimestamp = () => wrapper.find({ ref: 'noteTimestamp' });
|
const findTimestamp = () => wrapper.find({ ref: 'noteTimestamp' });
|
||||||
|
const findConfidentialIndicator = () => wrapper.find('[data-testid="confidentialIndicator"]');
|
||||||
const findSpinner = () => wrapper.find({ ref: 'spinner' });
|
const findSpinner = () => wrapper.find({ ref: 'spinner' });
|
||||||
|
|
||||||
const author = {
|
const author = {
|
||||||
|
@ -231,4 +232,15 @@ describe('NoteHeader component', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('with confidentiality indicator', () => {
|
||||||
|
it.each`
|
||||||
|
status | condition
|
||||||
|
${true} | ${'shows'}
|
||||||
|
${false} | ${'hides'}
|
||||||
|
`('$condition icon indicator when isConfidential is $status', ({ status }) => {
|
||||||
|
createComponent({ isConfidential: status });
|
||||||
|
expect(findConfidentialIndicator().exists()).toBe(status);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -9,6 +9,14 @@ describe Gitlab::Graphql::Pagination::Keyset::Connection do
|
||||||
let(:schema) { GraphQL::Schema.define(query: query_type, mutation: nil)}
|
let(:schema) { GraphQL::Schema.define(query: query_type, mutation: nil)}
|
||||||
let(:context) { GraphQL::Query::Context.new(query: OpenStruct.new(schema: schema), values: nil, object: nil) }
|
let(:context) { GraphQL::Query::Context.new(query: OpenStruct.new(schema: schema), values: nil, object: nil) }
|
||||||
|
|
||||||
|
before do
|
||||||
|
stub_const('NoPrimaryKey', Class.new(ActiveRecord::Base))
|
||||||
|
NoPrimaryKey.class_eval do
|
||||||
|
self.table_name = 'no_primary_key'
|
||||||
|
self.primary_key = nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
subject(:connection) do
|
subject(:connection) do
|
||||||
described_class.new(nodes, { context: context, max_page_size: 3 }.merge(arguments))
|
described_class.new(nodes, { context: context, max_page_size: 3 }.merge(arguments))
|
||||||
end
|
end
|
||||||
|
@ -303,9 +311,4 @@ describe Gitlab::Graphql::Pagination::Keyset::Connection do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
class NoPrimaryKey < ActiveRecord::Base
|
|
||||||
self.table_name = 'no_primary_key'
|
|
||||||
self.primary_key = nil
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -6,10 +6,9 @@ require_relative './simple_check_shared'
|
||||||
describe Gitlab::HealthChecks::MasterCheck do
|
describe Gitlab::HealthChecks::MasterCheck do
|
||||||
let(:result_class) { Gitlab::HealthChecks::Result }
|
let(:result_class) { Gitlab::HealthChecks::Result }
|
||||||
|
|
||||||
SUCCESS_CODE = 100
|
|
||||||
FAILURE_CODE = 101
|
|
||||||
|
|
||||||
before do
|
before do
|
||||||
|
stub_const('SUCCESS_CODE', 100)
|
||||||
|
stub_const('FAILURE_CODE', 101)
|
||||||
described_class.register_master
|
described_class.register_master
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -43,7 +43,4 @@ describe 'Import/Export attribute configuration' do
|
||||||
IMPORT_EXPORT_CONFIG: #{Gitlab::ImportExport.config_file}
|
IMPORT_EXPORT_CONFIG: #{Gitlab::ImportExport.config_file}
|
||||||
MSG
|
MSG
|
||||||
end
|
end
|
||||||
|
|
||||||
class Author < User
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -4,99 +4,109 @@ require 'spec_helper'
|
||||||
require 'rake_helper'
|
require 'rake_helper'
|
||||||
|
|
||||||
describe SystemCheck::SimpleExecutor do
|
describe SystemCheck::SimpleExecutor do
|
||||||
class SimpleCheck < SystemCheck::BaseCheck
|
before do
|
||||||
set_name 'my simple check'
|
stub_const('SimpleCheck', Class.new(SystemCheck::BaseCheck))
|
||||||
|
stub_const('OtherCheck', Class.new(SystemCheck::BaseCheck))
|
||||||
|
stub_const('SkipCheck', Class.new(SystemCheck::BaseCheck))
|
||||||
|
stub_const('DynamicSkipCheck', Class.new(SystemCheck::BaseCheck))
|
||||||
|
stub_const('MultiCheck', Class.new(SystemCheck::BaseCheck))
|
||||||
|
stub_const('SkipMultiCheck', Class.new(SystemCheck::BaseCheck))
|
||||||
|
stub_const('RepairCheck', Class.new(SystemCheck::BaseCheck))
|
||||||
|
stub_const('BugousCheck', Class.new(SystemCheck::BaseCheck))
|
||||||
|
|
||||||
def check?
|
SimpleCheck.class_eval do
|
||||||
true
|
set_name 'my simple check'
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
class OtherCheck < SystemCheck::BaseCheck
|
def check?
|
||||||
set_name 'other check'
|
true
|
||||||
|
end
|
||||||
def check?
|
|
||||||
false
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def show_error
|
OtherCheck.class_eval do
|
||||||
$stdout.puts 'this is an error text'
|
set_name 'other check'
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
class SkipCheck < SystemCheck::BaseCheck
|
def check?
|
||||||
set_name 'skip check'
|
false
|
||||||
set_skip_reason 'this is a skip reason'
|
end
|
||||||
|
|
||||||
def skip?
|
def show_error
|
||||||
true
|
$stdout.puts 'this is an error text'
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def check?
|
SkipCheck.class_eval do
|
||||||
raise 'should not execute this'
|
set_name 'skip check'
|
||||||
end
|
set_skip_reason 'this is a skip reason'
|
||||||
end
|
|
||||||
|
|
||||||
class DynamicSkipCheck < SystemCheck::BaseCheck
|
def skip?
|
||||||
set_name 'dynamic skip check'
|
true
|
||||||
set_skip_reason 'this is a skip reason'
|
end
|
||||||
|
|
||||||
def skip?
|
def check?
|
||||||
self.skip_reason = 'this is a dynamic skip reason'
|
raise 'should not execute this'
|
||||||
true
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def check?
|
DynamicSkipCheck.class_eval do
|
||||||
raise 'should not execute this'
|
set_name 'dynamic skip check'
|
||||||
end
|
set_skip_reason 'this is a skip reason'
|
||||||
end
|
|
||||||
|
|
||||||
class MultiCheck < SystemCheck::BaseCheck
|
def skip?
|
||||||
set_name 'multi check'
|
self.skip_reason = 'this is a dynamic skip reason'
|
||||||
|
true
|
||||||
|
end
|
||||||
|
|
||||||
def multi_check
|
def check?
|
||||||
$stdout.puts 'this is a multi output check'
|
raise 'should not execute this'
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def check?
|
MultiCheck.class_eval do
|
||||||
raise 'should not execute this'
|
set_name 'multi check'
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
class SkipMultiCheck < SystemCheck::BaseCheck
|
def multi_check
|
||||||
set_name 'skip multi check'
|
$stdout.puts 'this is a multi output check'
|
||||||
|
end
|
||||||
|
|
||||||
def skip?
|
def check?
|
||||||
true
|
raise 'should not execute this'
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def multi_check
|
SkipMultiCheck.class_eval do
|
||||||
raise 'should not execute this'
|
set_name 'skip multi check'
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
class RepairCheck < SystemCheck::BaseCheck
|
def skip?
|
||||||
set_name 'repair check'
|
true
|
||||||
|
end
|
||||||
|
|
||||||
def check?
|
def multi_check
|
||||||
false
|
raise 'should not execute this'
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def repair!
|
RepairCheck.class_eval do
|
||||||
true
|
set_name 'repair check'
|
||||||
|
|
||||||
|
def check?
|
||||||
|
false
|
||||||
|
end
|
||||||
|
|
||||||
|
def repair!
|
||||||
|
true
|
||||||
|
end
|
||||||
|
|
||||||
|
def show_error
|
||||||
|
$stdout.puts 'this is an error message'
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def show_error
|
BugousCheck.class_eval do
|
||||||
$stdout.puts 'this is an error message'
|
set_name 'my bugous check'
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
class BugousCheck < SystemCheck::BaseCheck
|
def check?
|
||||||
CustomError = Class.new(StandardError)
|
raise StandardError, 'omg'
|
||||||
set_name 'my bugous check'
|
end
|
||||||
|
|
||||||
def check?
|
|
||||||
raise CustomError, 'omg'
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@ describe EncryptPlaintextAttributesOnApplicationSettings do
|
||||||
let(:application_settings) { table(:application_settings) }
|
let(:application_settings) { table(:application_settings) }
|
||||||
let(:plaintext) { 'secret-token' }
|
let(:plaintext) { 'secret-token' }
|
||||||
|
|
||||||
PLAINTEXT_ATTRIBUTES = %w[
|
plaintext_attributes = %w[
|
||||||
akismet_api_key
|
akismet_api_key
|
||||||
elasticsearch_aws_secret_access_key
|
elasticsearch_aws_secret_access_key
|
||||||
recaptcha_private_key
|
recaptcha_private_key
|
||||||
|
@ -21,7 +21,7 @@ describe EncryptPlaintextAttributesOnApplicationSettings do
|
||||||
it 'encrypts token and saves it' do
|
it 'encrypts token and saves it' do
|
||||||
application_setting = application_settings.create
|
application_setting = application_settings.create
|
||||||
application_setting.update_columns(
|
application_setting.update_columns(
|
||||||
PLAINTEXT_ATTRIBUTES.each_with_object({}) do |plaintext_attribute, attributes|
|
plaintext_attributes.each_with_object({}) do |plaintext_attribute, attributes|
|
||||||
attributes[plaintext_attribute] = plaintext
|
attributes[plaintext_attribute] = plaintext
|
||||||
end
|
end
|
||||||
)
|
)
|
||||||
|
@ -29,7 +29,7 @@ describe EncryptPlaintextAttributesOnApplicationSettings do
|
||||||
migration.up
|
migration.up
|
||||||
|
|
||||||
application_setting.reload
|
application_setting.reload
|
||||||
PLAINTEXT_ATTRIBUTES.each do |plaintext_attribute|
|
plaintext_attributes.each do |plaintext_attribute|
|
||||||
expect(application_setting[plaintext_attribute]).not_to be_nil
|
expect(application_setting[plaintext_attribute]).not_to be_nil
|
||||||
expect(application_setting["encrypted_#{plaintext_attribute}"]).not_to be_nil
|
expect(application_setting["encrypted_#{plaintext_attribute}"]).not_to be_nil
|
||||||
expect(application_setting["encrypted_#{plaintext_attribute}_iv"]).not_to be_nil
|
expect(application_setting["encrypted_#{plaintext_attribute}_iv"]).not_to be_nil
|
||||||
|
@ -40,7 +40,7 @@ describe EncryptPlaintextAttributesOnApplicationSettings do
|
||||||
describe '#down' do
|
describe '#down' do
|
||||||
it 'decrypts encrypted token and saves it' do
|
it 'decrypts encrypted token and saves it' do
|
||||||
application_setting = application_settings.create(
|
application_setting = application_settings.create(
|
||||||
PLAINTEXT_ATTRIBUTES.each_with_object({}) do |plaintext_attribute, attributes|
|
plaintext_attributes.each_with_object({}) do |plaintext_attribute, attributes|
|
||||||
attributes[plaintext_attribute] = plaintext
|
attributes[plaintext_attribute] = plaintext
|
||||||
end
|
end
|
||||||
)
|
)
|
||||||
|
@ -48,7 +48,7 @@ describe EncryptPlaintextAttributesOnApplicationSettings do
|
||||||
migration.down
|
migration.down
|
||||||
|
|
||||||
application_setting.reload
|
application_setting.reload
|
||||||
PLAINTEXT_ATTRIBUTES.each do |plaintext_attribute|
|
plaintext_attributes.each do |plaintext_attribute|
|
||||||
expect(application_setting[plaintext_attribute]).to eq(plaintext)
|
expect(application_setting[plaintext_attribute]).to eq(plaintext)
|
||||||
expect(application_setting["encrypted_#{plaintext_attribute}"]).to be_nil
|
expect(application_setting["encrypted_#{plaintext_attribute}"]).to be_nil
|
||||||
expect(application_setting["encrypted_#{plaintext_attribute}_iv"]).to be_nil
|
expect(application_setting["encrypted_#{plaintext_attribute}_iv"]).to be_nil
|
||||||
|
|
|
@ -42,7 +42,7 @@ describe ProjectPolicy do
|
||||||
admin_tag admin_milestone admin_merge_request update_merge_request create_commit_status
|
admin_tag admin_milestone admin_merge_request update_merge_request create_commit_status
|
||||||
update_commit_status create_build update_build create_pipeline
|
update_commit_status create_build update_build create_pipeline
|
||||||
update_pipeline create_merge_request_from create_wiki push_code
|
update_pipeline create_merge_request_from create_wiki push_code
|
||||||
resolve_note create_container_image update_container_image destroy_container_image
|
resolve_note create_container_image update_container_image destroy_container_image daily_statistics
|
||||||
create_environment update_environment create_deployment update_deployment create_release update_release
|
create_environment update_environment create_deployment update_deployment create_release update_release
|
||||||
create_metrics_dashboard_annotation delete_metrics_dashboard_annotation update_metrics_dashboard_annotation
|
create_metrics_dashboard_annotation delete_metrics_dashboard_annotation update_metrics_dashboard_annotation
|
||||||
]
|
]
|
||||||
|
@ -54,7 +54,7 @@ describe ProjectPolicy do
|
||||||
admin_snippet admin_project_member admin_note admin_wiki admin_project
|
admin_snippet admin_project_member admin_note admin_wiki admin_project
|
||||||
admin_commit_status admin_build admin_container_image
|
admin_commit_status admin_build admin_container_image
|
||||||
admin_pipeline admin_environment admin_deployment destroy_release add_cluster
|
admin_pipeline admin_environment admin_deployment destroy_release add_cluster
|
||||||
daily_statistics read_deploy_token create_deploy_token destroy_deploy_token
|
read_deploy_token create_deploy_token destroy_deploy_token
|
||||||
admin_terraform_state
|
admin_terraform_state
|
||||||
]
|
]
|
||||||
end
|
end
|
||||||
|
|
|
@ -3,23 +3,23 @@
|
||||||
require 'spec_helper'
|
require 'spec_helper'
|
||||||
|
|
||||||
describe API::ProjectStatistics do
|
describe API::ProjectStatistics do
|
||||||
let(:maintainer) { create(:user) }
|
let_it_be(:developer) { create(:user) }
|
||||||
let(:public_project) { create(:project, :public) }
|
let_it_be(:public_project) { create(:project, :public) }
|
||||||
|
|
||||||
before do
|
before do
|
||||||
public_project.add_maintainer(maintainer)
|
public_project.add_developer(developer)
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'GET /projects/:id/statistics' do
|
describe 'GET /projects/:id/statistics' do
|
||||||
let!(:fetch_statistics1) { create(:project_daily_statistic, project: public_project, fetch_count: 30, date: 29.days.ago) }
|
let_it_be(:fetch_statistics1) { create(:project_daily_statistic, project: public_project, fetch_count: 30, date: 29.days.ago) }
|
||||||
let!(:fetch_statistics2) { create(:project_daily_statistic, project: public_project, fetch_count: 4, date: 3.days.ago) }
|
let_it_be(:fetch_statistics2) { create(:project_daily_statistic, project: public_project, fetch_count: 4, date: 3.days.ago) }
|
||||||
let!(:fetch_statistics3) { create(:project_daily_statistic, project: public_project, fetch_count: 3, date: 2.days.ago) }
|
let_it_be(:fetch_statistics3) { create(:project_daily_statistic, project: public_project, fetch_count: 3, date: 2.days.ago) }
|
||||||
let!(:fetch_statistics4) { create(:project_daily_statistic, project: public_project, fetch_count: 2, date: 1.day.ago) }
|
let_it_be(:fetch_statistics4) { create(:project_daily_statistic, project: public_project, fetch_count: 2, date: 1.day.ago) }
|
||||||
let!(:fetch_statistics5) { create(:project_daily_statistic, project: public_project, fetch_count: 1, date: Date.today) }
|
let_it_be(:fetch_statistics5) { create(:project_daily_statistic, project: public_project, fetch_count: 1, date: Date.today) }
|
||||||
let!(:fetch_statistics_other_project) { create(:project_daily_statistic, project: create(:project), fetch_count: 29, date: 29.days.ago) }
|
let_it_be(:fetch_statistics_other_project) { create(:project_daily_statistic, project: create(:project), fetch_count: 29, date: 29.days.ago) }
|
||||||
|
|
||||||
it 'returns the fetch statistics of the last 30 days' do
|
it 'returns the fetch statistics of the last 30 days' do
|
||||||
get api("/projects/#{public_project.id}/statistics", maintainer)
|
get api("/projects/#{public_project.id}/statistics", developer)
|
||||||
|
|
||||||
expect(response).to have_gitlab_http_status(:ok)
|
expect(response).to have_gitlab_http_status(:ok)
|
||||||
fetches = json_response['fetches']
|
fetches = json_response['fetches']
|
||||||
|
@ -32,7 +32,7 @@ describe API::ProjectStatistics do
|
||||||
it 'excludes the fetch statistics older than 30 days' do
|
it 'excludes the fetch statistics older than 30 days' do
|
||||||
create(:project_daily_statistic, fetch_count: 31, project: public_project, date: 30.days.ago)
|
create(:project_daily_statistic, fetch_count: 31, project: public_project, date: 30.days.ago)
|
||||||
|
|
||||||
get api("/projects/#{public_project.id}/statistics", maintainer)
|
get api("/projects/#{public_project.id}/statistics", developer)
|
||||||
|
|
||||||
expect(response).to have_gitlab_http_status(:ok)
|
expect(response).to have_gitlab_http_status(:ok)
|
||||||
fetches = json_response['fetches']
|
fetches = json_response['fetches']
|
||||||
|
@ -41,11 +41,11 @@ describe API::ProjectStatistics do
|
||||||
expect(fetches['days'].last).to eq({ 'count' => fetch_statistics1.fetch_count, 'date' => fetch_statistics1.date.to_s })
|
expect(fetches['days'].last).to eq({ 'count' => fetch_statistics1.fetch_count, 'date' => fetch_statistics1.date.to_s })
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'responds with 403 when the user is not a maintainer of the repository' do
|
it 'responds with 403 when the user is not a developer of the repository' do
|
||||||
developer = create(:user)
|
guest = create(:user)
|
||||||
public_project.add_developer(developer)
|
public_project.add_guest(guest)
|
||||||
|
|
||||||
get api("/projects/#{public_project.id}/statistics", developer)
|
get api("/projects/#{public_project.id}/statistics", guest)
|
||||||
|
|
||||||
expect(response).to have_gitlab_http_status(:forbidden)
|
expect(response).to have_gitlab_http_status(:forbidden)
|
||||||
expect(json_response['message']).to eq('403 Forbidden')
|
expect(json_response['message']).to eq('403 Forbidden')
|
||||||
|
|
|
@ -10,8 +10,8 @@ require_relative '../../../../rubocop/cop/rspec/env_assignment'
|
||||||
describe RuboCop::Cop::RSpec::EnvAssignment do
|
describe RuboCop::Cop::RSpec::EnvAssignment do
|
||||||
include CopHelper
|
include CopHelper
|
||||||
|
|
||||||
OFFENSE_CALL_SINGLE_QUOTES_KEY = %(ENV['FOO'] = 'bar').freeze
|
offense_call_single_quotes_key = %(ENV['FOO'] = 'bar').freeze
|
||||||
OFFENSE_CALL_DOUBLE_QUOTES_KEY = %(ENV["FOO"] = 'bar').freeze
|
offense_call_double_quotes_key = %(ENV["FOO"] = 'bar').freeze
|
||||||
|
|
||||||
let(:source_file) { 'spec/foo_spec.rb' }
|
let(:source_file) { 'spec/foo_spec.rb' }
|
||||||
|
|
||||||
|
@ -36,12 +36,12 @@ describe RuboCop::Cop::RSpec::EnvAssignment do
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'with a key using single quotes' do
|
context 'with a key using single quotes' do
|
||||||
it_behaves_like 'an offensive ENV#[]= call', OFFENSE_CALL_SINGLE_QUOTES_KEY
|
it_behaves_like 'an offensive ENV#[]= call', offense_call_single_quotes_key
|
||||||
it_behaves_like 'an autocorrected ENV#[]= call', OFFENSE_CALL_SINGLE_QUOTES_KEY, %(stub_env('FOO', 'bar'))
|
it_behaves_like 'an autocorrected ENV#[]= call', offense_call_single_quotes_key, %(stub_env('FOO', 'bar'))
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'with a key using double quotes' do
|
context 'with a key using double quotes' do
|
||||||
it_behaves_like 'an offensive ENV#[]= call', OFFENSE_CALL_DOUBLE_QUOTES_KEY
|
it_behaves_like 'an offensive ENV#[]= call', offense_call_double_quotes_key
|
||||||
it_behaves_like 'an autocorrected ENV#[]= call', OFFENSE_CALL_DOUBLE_QUOTES_KEY, %(stub_env("FOO", 'bar'))
|
it_behaves_like 'an autocorrected ENV#[]= call', offense_call_double_quotes_key, %(stub_env("FOO", 'bar'))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -74,47 +74,6 @@ describe Snippets::CreateService do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
shared_examples 'spam check is performed' do
|
|
||||||
shared_examples 'marked as spam' do
|
|
||||||
it 'marks a snippet as spam' do
|
|
||||||
expect(snippet).to be_spam
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'invalidates the snippet' do
|
|
||||||
expect(snippet).to be_invalid
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'creates a new spam_log' do
|
|
||||||
expect { snippet }
|
|
||||||
.to have_spam_log(title: snippet.title, noteable_type: snippet.class.name)
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'assigns a spam_log to an issue' do
|
|
||||||
expect(snippet.spam_log).to eq(SpamLog.last)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
let(:extra_opts) do
|
|
||||||
{ visibility_level: Gitlab::VisibilityLevel::PUBLIC, request: double(:request, env: {}) }
|
|
||||||
end
|
|
||||||
|
|
||||||
before do
|
|
||||||
expect_next_instance_of(Spam::AkismetService) do |akismet_service|
|
|
||||||
expect(akismet_service).to receive_messages(spam?: true)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
[true, false, nil].each do |allow_possible_spam|
|
|
||||||
context "when recaptcha_disabled flag is #{allow_possible_spam.inspect}" do
|
|
||||||
before do
|
|
||||||
stub_feature_flags(allow_possible_spam: allow_possible_spam) unless allow_possible_spam.nil?
|
|
||||||
end
|
|
||||||
|
|
||||||
it_behaves_like 'marked as spam'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
shared_examples 'snippet create data is tracked' do
|
shared_examples 'snippet create data is tracked' do
|
||||||
let(:counter) { Gitlab::UsageDataCounters::SnippetCounter }
|
let(:counter) { Gitlab::UsageDataCounters::SnippetCounter }
|
||||||
|
|
||||||
|
@ -280,7 +239,7 @@ describe Snippets::CreateService do
|
||||||
|
|
||||||
it_behaves_like 'a service that creates a snippet'
|
it_behaves_like 'a service that creates a snippet'
|
||||||
it_behaves_like 'public visibility level restrictions apply'
|
it_behaves_like 'public visibility level restrictions apply'
|
||||||
it_behaves_like 'spam check is performed'
|
it_behaves_like 'snippets spam check is performed'
|
||||||
it_behaves_like 'snippet create data is tracked'
|
it_behaves_like 'snippet create data is tracked'
|
||||||
it_behaves_like 'an error service response when save fails'
|
it_behaves_like 'an error service response when save fails'
|
||||||
it_behaves_like 'creates repository and files'
|
it_behaves_like 'creates repository and files'
|
||||||
|
@ -306,7 +265,7 @@ describe Snippets::CreateService do
|
||||||
|
|
||||||
it_behaves_like 'a service that creates a snippet'
|
it_behaves_like 'a service that creates a snippet'
|
||||||
it_behaves_like 'public visibility level restrictions apply'
|
it_behaves_like 'public visibility level restrictions apply'
|
||||||
it_behaves_like 'spam check is performed'
|
it_behaves_like 'snippets spam check is performed'
|
||||||
it_behaves_like 'snippet create data is tracked'
|
it_behaves_like 'snippet create data is tracked'
|
||||||
it_behaves_like 'an error service response when save fails'
|
it_behaves_like 'an error service response when save fails'
|
||||||
it_behaves_like 'creates repository and files'
|
it_behaves_like 'creates repository and files'
|
||||||
|
|
|
@ -7,7 +7,7 @@ describe Snippets::UpdateService do
|
||||||
let_it_be(:user) { create(:user) }
|
let_it_be(:user) { create(:user) }
|
||||||
let_it_be(:admin) { create :user, admin: true }
|
let_it_be(:admin) { create :user, admin: true }
|
||||||
let(:visibility_level) { Gitlab::VisibilityLevel::PRIVATE }
|
let(:visibility_level) { Gitlab::VisibilityLevel::PRIVATE }
|
||||||
let(:options) do
|
let(:base_opts) do
|
||||||
{
|
{
|
||||||
title: 'Test snippet',
|
title: 'Test snippet',
|
||||||
file_name: 'snippet.rb',
|
file_name: 'snippet.rb',
|
||||||
|
@ -15,6 +15,8 @@ describe Snippets::UpdateService do
|
||||||
visibility_level: visibility_level
|
visibility_level: visibility_level
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
let(:extra_opts) { {} }
|
||||||
|
let(:options) { base_opts.merge(extra_opts) }
|
||||||
let(:updater) { user }
|
let(:updater) { user }
|
||||||
let(:service) { Snippets::UpdateService.new(project, updater, options) }
|
let(:service) { Snippets::UpdateService.new(project, updater, options) }
|
||||||
|
|
||||||
|
@ -85,7 +87,7 @@ describe Snippets::UpdateService do
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when update fails' do
|
context 'when update fails' do
|
||||||
let(:options) { { title: '' } }
|
let(:extra_opts) { { title: '' } }
|
||||||
|
|
||||||
it 'does not increment count' do
|
it 'does not increment count' do
|
||||||
expect { subject }.not_to change { counter.read(:update) }
|
expect { subject }.not_to change { counter.read(:update) }
|
||||||
|
@ -273,7 +275,7 @@ describe Snippets::UpdateService do
|
||||||
|
|
||||||
shared_examples 'committable attributes' do
|
shared_examples 'committable attributes' do
|
||||||
context 'when file_name is updated' do
|
context 'when file_name is updated' do
|
||||||
let(:options) { { file_name: 'snippet.rb' } }
|
let(:extra_opts) { { file_name: 'snippet.rb' } }
|
||||||
|
|
||||||
it 'commits to repository' do
|
it 'commits to repository' do
|
||||||
expect(service).to receive(:create_commit)
|
expect(service).to receive(:create_commit)
|
||||||
|
@ -282,7 +284,7 @@ describe Snippets::UpdateService do
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when content is updated' do
|
context 'when content is updated' do
|
||||||
let(:options) { { content: 'puts "hello world"' } }
|
let(:extra_opts) { { content: 'puts "hello world"' } }
|
||||||
|
|
||||||
it 'commits to repository' do
|
it 'commits to repository' do
|
||||||
expect(service).to receive(:create_commit)
|
expect(service).to receive(:create_commit)
|
||||||
|
@ -314,6 +316,11 @@ describe Snippets::UpdateService do
|
||||||
it_behaves_like 'updates repository content'
|
it_behaves_like 'updates repository content'
|
||||||
it_behaves_like 'commit operation fails'
|
it_behaves_like 'commit operation fails'
|
||||||
it_behaves_like 'committable attributes'
|
it_behaves_like 'committable attributes'
|
||||||
|
it_behaves_like 'snippets spam check is performed' do
|
||||||
|
before do
|
||||||
|
subject
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
context 'when snippet does not have a repository' do
|
context 'when snippet does not have a repository' do
|
||||||
let!(:snippet) { create(:project_snippet, author: user, project: project) }
|
let!(:snippet) { create(:project_snippet, author: user, project: project) }
|
||||||
|
@ -333,6 +340,11 @@ describe Snippets::UpdateService do
|
||||||
it_behaves_like 'updates repository content'
|
it_behaves_like 'updates repository content'
|
||||||
it_behaves_like 'commit operation fails'
|
it_behaves_like 'commit operation fails'
|
||||||
it_behaves_like 'committable attributes'
|
it_behaves_like 'committable attributes'
|
||||||
|
it_behaves_like 'snippets spam check is performed' do
|
||||||
|
before do
|
||||||
|
subject
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
context 'when snippet does not have a repository' do
|
context 'when snippet does not have a repository' do
|
||||||
let!(:snippet) { create(:personal_snippet, author: user, project: project) }
|
let!(:snippet) { create(:personal_snippet, author: user, project: project) }
|
||||||
|
|
|
@ -39,7 +39,7 @@ RSpec.shared_context 'ProjectPolicy context' do
|
||||||
update_pipeline create_merge_request_from create_wiki push_code
|
update_pipeline create_merge_request_from create_wiki push_code
|
||||||
resolve_note create_container_image update_container_image
|
resolve_note create_container_image update_container_image
|
||||||
create_environment create_deployment update_deployment create_release update_release
|
create_environment create_deployment update_deployment create_release update_release
|
||||||
update_environment
|
update_environment daily_statistics
|
||||||
]
|
]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -49,7 +49,6 @@ RSpec.shared_context 'ProjectPolicy context' do
|
||||||
admin_snippet admin_project_member admin_note admin_wiki admin_project
|
admin_snippet admin_project_member admin_note admin_wiki admin_project
|
||||||
admin_commit_status admin_build admin_container_image
|
admin_commit_status admin_build admin_container_image
|
||||||
admin_pipeline admin_environment admin_deployment destroy_release add_cluster
|
admin_pipeline admin_environment admin_deployment destroy_release add_cluster
|
||||||
daily_statistics
|
|
||||||
]
|
]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,42 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
RSpec.shared_examples 'snippets spam check is performed' do
|
||||||
|
shared_examples 'marked as spam' do
|
||||||
|
it 'marks a snippet as spam' do
|
||||||
|
expect(snippet).to be_spam
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'invalidates the snippet' do
|
||||||
|
expect(snippet).to be_invalid
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'creates a new spam_log' do
|
||||||
|
expect { snippet }
|
||||||
|
.to have_spam_log(title: snippet.title, noteable_type: snippet.class.name)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'assigns a spam_log to an issue' do
|
||||||
|
expect(snippet.spam_log).to eq(SpamLog.last)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
let(:extra_opts) do
|
||||||
|
{ visibility_level: Gitlab::VisibilityLevel::PUBLIC, request: double(:request, env: {}) }
|
||||||
|
end
|
||||||
|
|
||||||
|
before do
|
||||||
|
expect_next_instance_of(Spam::AkismetService) do |akismet_service|
|
||||||
|
expect(akismet_service).to receive_messages(spam?: true)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
[true, false, nil].each do |allow_possible_spam|
|
||||||
|
context "when allow_possible_spam flag is #{allow_possible_spam.inspect}" do
|
||||||
|
before do
|
||||||
|
stub_feature_flags(allow_possible_spam: allow_possible_spam) unless allow_possible_spam.nil?
|
||||||
|
end
|
||||||
|
|
||||||
|
it_behaves_like 'marked as spam'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
Loading…
Reference in New Issue