Commit ed3b1698 authored by GitLab Bot's avatar GitLab Bot

Add latest changes from gitlab-org/gitlab@master

parent 05f4b2fb
...@@ -30,7 +30,7 @@ export default { ...@@ -30,7 +30,7 @@ export default {
EnvironmentsBlock, EnvironmentsBlock,
ErasedBlock, ErasedBlock,
Icon, Icon,
Log: () => (isNewJobLogActive() ? import('./job_log_json.vue') : import('./job_log.vue')), Log: () => (isNewJobLogActive() ? import('./log/log.vue') : import('./job_log.vue')),
LogTopBar, LogTopBar,
StuckBlock, StuckBlock,
UnmetPrerequisitesBlock, UnmetPrerequisitesBlock,
......
<script>
export default {
name: 'JobLogJSON',
};
</script>
<template>
<pre>
{{ __('This feature is in development. Please disable the `job_log_json` feature flag') }}
</pre>
</template>
...@@ -14,6 +14,7 @@ class ApplicationController < ActionController::Base ...@@ -14,6 +14,7 @@ class ApplicationController < ActionController::Base
include SessionlessAuthentication include SessionlessAuthentication
include ConfirmEmailWarning include ConfirmEmailWarning
include Gitlab::Tracking::ControllerConcern include Gitlab::Tracking::ControllerConcern
include Gitlab::Experimentation::ControllerConcern
before_action :authenticate_user! before_action :authenticate_user!
before_action :enforce_terms!, if: :should_enforce_terms? before_action :enforce_terms!, if: :should_enforce_terms?
......
...@@ -9,9 +9,16 @@ module Projects ...@@ -9,9 +9,16 @@ module Projects
end end
def execute def execute
Projects::HousekeepingService.new(@project).execute do service = Projects::HousekeepingService.new(@project)
service.execute do
repository.delete_all_refs_except(RESERVED_REF_PREFIXES) repository.delete_all_refs_except(RESERVED_REF_PREFIXES)
end end
# Right now we don't actually have a way to know if a project
# import actually changed, so we increment the counter to avoid
# causing GC to run every time.
service.increment!
rescue Projects::HousekeepingService::LeaseTaken => e rescue Projects::HousekeepingService::LeaseTaken => e
Rails.logger.info( # rubocop:disable Gitlab/RailsLogger Rails.logger.info( # rubocop:disable Gitlab/RailsLogger
"Could not perform housekeeping for project #{@project.full_path} (#{@project.id}): #{e}") "Could not perform housekeeping for project #{@project.full_path} (#{@project.id}): #{e}")
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
%fieldset %fieldset
- if omnibus_protected_paths_throttle? - if omnibus_protected_paths_throttle?
.bs-callout.bs-callout-danger .bs-callout.bs-callout-danger
- relative_url_link = 'https://docs.gitlab.com/ee/user/admin_area/settings/protected_paths.html#migrating-from-omnibus' - relative_url_link = 'https://docs.gitlab.com/ee/user/admin_area/settings/protected_paths.html#migrate-settings-from-gitlab-123-and-earlier'
- relative_url_link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: relative_url_link } - relative_url_link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: relative_url_link }
= _("Omnibus Protected Paths throttle is active. From 12.4, Omnibus throttle is deprecated and will be removed in a future release. Please read the %{relative_url_link_start}Migrating Protected Paths documentation%{relative_url_link_end}.").html_safe % { relative_url_link_start: relative_url_link_start, relative_url_link_end: '</a>'.html_safe } = _("Omnibus Protected Paths throttle is active. From 12.4, Omnibus throttle is deprecated and will be removed in a future release. Please read the %{relative_url_link_start}Migrating Protected Paths documentation%{relative_url_link_end}.").html_safe % { relative_url_link_start: relative_url_link_start, relative_url_link_end: '</a>'.html_safe }
......
---
title: Fixes wrong link on Protected paths admin settings
merge_request: 17945
author:
type: other
...@@ -88,6 +88,7 @@ Example response: ...@@ -88,6 +88,7 @@ Example response:
"due_date_from_milestones": "2018-07-31", "due_date_from_milestones": "2018-07-31",
"created_at": "2018-07-17T13:36:22.770Z", "created_at": "2018-07-17T13:36:22.770Z",
"updated_at": "2018-07-18T12:22:05.239Z", "updated_at": "2018-07-18T12:22:05.239Z",
"closed_at": "2018-08-18T12:22:05.239Z",
"labels": [], "labels": [],
"upvotes": 4, "upvotes": 4,
"downvotes": 0 "downvotes": 0
...@@ -143,6 +144,7 @@ Example response: ...@@ -143,6 +144,7 @@ Example response:
"due_date_from_milestones": "2018-07-31", "due_date_from_milestones": "2018-07-31",
"created_at": "2018-07-17T13:36:22.770Z", "created_at": "2018-07-17T13:36:22.770Z",
"updated_at": "2018-07-18T12:22:05.239Z", "updated_at": "2018-07-18T12:22:05.239Z",
"closed_at": "2018-08-18T12:22:05.239Z",
"labels": [], "labels": [],
"upvotes": 4, "upvotes": 4,
"downvotes": 0 "downvotes": 0
...@@ -209,6 +211,7 @@ Example response: ...@@ -209,6 +211,7 @@ Example response:
"due_date_from_milestones": "2018-07-31", "due_date_from_milestones": "2018-07-31",
"created_at": "2018-07-17T13:36:22.770Z", "created_at": "2018-07-17T13:36:22.770Z",
"updated_at": "2018-07-18T12:22:05.239Z", "updated_at": "2018-07-18T12:22:05.239Z",
"closed_at": "2018-08-18T12:22:05.239Z",
"labels": [], "labels": [],
"upvotes": 4, "upvotes": 4,
"downvotes": 0 "downvotes": 0
...@@ -276,6 +279,7 @@ Example response: ...@@ -276,6 +279,7 @@ Example response:
"due_date_from_milestones": "2018-07-31", "due_date_from_milestones": "2018-07-31",
"created_at": "2018-07-17T13:36:22.770Z", "created_at": "2018-07-17T13:36:22.770Z",
"updated_at": "2018-07-18T12:22:05.239Z", "updated_at": "2018-07-18T12:22:05.239Z",
"closed_at": "2018-08-18T12:22:05.239Z",
"labels": [], "labels": [],
"upvotes": 4, "upvotes": 4,
"downvotes": 0 "downvotes": 0
...@@ -358,7 +362,8 @@ Example response: ...@@ -358,7 +362,8 @@ Example response:
"start_date": null, "start_date": null,
"end_date": null, "end_date": null,
"created_at": "2018-01-21T06:21:13.165Z", "created_at": "2018-01-21T06:21:13.165Z",
"updated_at": "2018-01-22T12:41:41.166Z" "updated_at": "2018-01-22T12:41:41.166Z",
"closed_at": "2018-08-18T12:22:05.239Z"
}, },
"target_url": "https://gitlab.example.com/groups/epics/5", "target_url": "https://gitlab.example.com/groups/epics/5",
"body": "Vel voluptas atque dicta mollitia adipisci qui at.", "body": "Vel voluptas atque dicta mollitia adipisci qui at.",
......
...@@ -80,7 +80,7 @@ With the purpose of being [respectful of others' time](https://about.gitlab.com/ ...@@ -80,7 +80,7 @@ With the purpose of being [respectful of others' time](https://about.gitlab.com/
1. Before writing code, ensure your vision of the architecture is aligned with 1. Before writing code, ensure your vision of the architecture is aligned with
GitLab's architecture. GitLab's architecture.
1. Add a diagram to the issue and ask a frontend architect in the slack channel `#fe_architectural` about it. 1. Add a diagram to the issue and ask a frontend maintainer in the slack channel `#frontend_maintainers` about it.
![Diagram of Issue Boards Architecture](img/boards_diagram.png) ![Diagram of Issue Boards Architecture](img/boards_diagram.png)
......
# GraphQL # GraphQL
Our GraphQL API can be explored via GraphiQL at your instance's
`/-/graphql-explorer` or at [GitLab.com](https://gitlab.com/-/graphql-explorer).
You can check all existing queries and mutations on the right side
of GraphiQL in its **Documentation explorer**. It's also possible to
write queries and mutations directly on the left tab and check
their execution by clicking **Execute query** button on the top left:
![GraphiQL interface](img/graphiql_explorer_v12_4.png)
We use [Apollo] and [Vue Apollo][vue-apollo] for working with GraphQL We use [Apollo] and [Vue Apollo][vue-apollo] for working with GraphQL
on the frontend. on the frontend.
In order to use GraphQL, you need to enable the `graphql` feature flag,
read more about [Feature Flags][feature-flags].
## Apollo Client ## Apollo Client
To save duplicated clients getting created in different apps, we have a To save duplicated clients getting created in different apps, we have a
......
# frozen_string_literal: true
# == Experimentation
#
# Utility module used for A/B testing experimental features. Define your experiments in the `EXPERIMENTS` constant.
# The feature_toggle and environment keys are optional. If the feature_toggle is not set, a feature with the name of
# the experiment will be checked, with a default value of true. The enabled_ratio is required and should be
# the ratio for the number of users for which this experiment is enabled. For example: a ratio of 0.1 will
# enable the experiment for 10% of the users (determined by the `experimentation_subject_index`).
#
module Gitlab
module Experimentation
EXPERIMENTS = {
signup_flow: {
feature_toggle: :experimental_separate_sign_up_flow,
environment: ::Gitlab.dev_env_or_com?,
enabled_ratio: 0.1
}
}.freeze
# Controller concern that checks if an experimentation_subject_id cookie is present and sets it if absent.
# Used for A/B testing of experimental features. Exposes the `experiment_enabled?(experiment_name)` method
# to controllers and views.
#
module ControllerConcern
extend ActiveSupport::Concern
included do
before_action :set_experimentation_subject_id_cookie
helper_method :experiment_enabled?
end
def set_experimentation_subject_id_cookie
return if cookies[:experimentation_subject_id].present?
cookies.permanent.signed[:experimentation_subject_id] = {
value: SecureRandom.uuid,
domain: :all,
secure: ::Gitlab.config.gitlab.https
}
end
def experiment_enabled?(experiment)
Experimentation.enabled?(experiment, experimentation_subject_index)
end
private
def experimentation_subject_index
experimentation_subject_id = cookies.signed[:experimentation_subject_id]
return if experimentation_subject_id.blank?
experimentation_subject_id.delete('-').hex % 100
end
end
class << self
def enabled?(experiment_key, experimentation_subject_index)
return false unless EXPERIMENTS.key?(experiment_key)
experiment = Experiment.new(EXPERIMENTS[experiment_key].merge(key: experiment_key))
experiment.feature_toggle_enabled? &&
experiment.enabled_for_environment? &&
experiment.enabled_for_experimentation_subject?(experimentation_subject_index)
end
end
Experiment = Struct.new(:key, :feature_toggle, :environment, :enabled_ratio, keyword_init: true) do
def feature_toggle_enabled?
return Feature.enabled?(key, default_enabled: true) if feature_toggle.nil?
Feature.enabled?(feature_toggle)
end
def enabled_for_environment?
return true if environment.nil?
environment
end
def enabled_for_experimentation_subject?(experimentation_subject_index)
return false if enabled_ratio.nil? || experimentation_subject_index.blank?
experimentation_subject_index <= enabled_ratio * 100
end
end
end
end
...@@ -16074,9 +16074,6 @@ msgstr "" ...@@ -16074,9 +16074,6 @@ msgstr ""
msgid "This environment has no deployments yet." msgid "This environment has no deployments yet."
msgstr "" msgstr ""
msgid "This feature is in development. Please disable the `job_log_json` feature flag"
msgstr ""
msgid "This feature requires local storage to be enabled" msgid "This feature requires local storage to be enabled"
msgstr "" msgstr ""
......
...@@ -189,7 +189,7 @@ describe Projects::DiscussionsController do ...@@ -189,7 +189,7 @@ describe Projects::DiscussionsController do
context "when vue_mr_discussions cookie is present" do context "when vue_mr_discussions cookie is present" do
before do before do
allow(controller).to receive(:cookies).and_return({ vue_mr_discussions: 'true' }) cookies[:vue_mr_discussions] = 'true'
end end
it "renders discussion with serializer" do it "renders discussion with serializer" do
......
import { shallowMount, createLocalVue } from '@vue/test-utils';
import Vuex from 'vuex';
import { __ } from '~/locale';
import List from '~/ide/components/branches/search_list.vue';
import Item from '~/ide/components/branches/item.vue';
import { GlLoadingIcon } from '@gitlab/ui';
import { branches } from '../../mock_data';
const localVue = createLocalVue();
localVue.use(Vuex);
describe('IDE branches search list', () => {
let wrapper;
const fetchBranchesMock = jest.fn();
const createComponent = (state, currentBranchId = 'branch') => {
const fakeStore = new Vuex.Store({
state: {
currentBranchId,
currentProjectId: 'project',
},
modules: {
branches: {
namespaced: true,
state: { isLoading: false, branches: [], ...state },
actions: {
fetchBranches: fetchBranchesMock,
},
},
},
});
wrapper = shallowMount(List, {
localVue,
store: fakeStore,
sync: false,
});
};
afterEach(() => {
wrapper.destroy();
wrapper = null;
});
it('calls fetch on mounted', () => {
createComponent();
expect(fetchBranchesMock).toHaveBeenCalled();
});
it('renders loading icon when `isLoading` is true', () => {
createComponent({ isLoading: true });
expect(wrapper.find(GlLoadingIcon).exists()).toBe(true);
});
it('renders branches not found when search is not empty and branches list is empty', () => {
createComponent({ branches: [] });
wrapper.find('input[type="search"]').setValue('something');
return wrapper.vm.$nextTick().then(() => {
expect(wrapper.text()).toContain(__('No branches found'));
});
});
describe('with branches', () => {
it('renders list', () => {
createComponent({ branches });
const items = wrapper.findAll(Item);
expect(items.length).toBe(branches.length);
});
it('renders check next to active branch', () => {
const activeBranch = 'regular';
createComponent({ branches }, activeBranch);
const items = wrapper.findAll(Item).filter(w => w.props('isActive'));
expect(items.length).toBe(1);
expect(items.at(0).props('item').name).toBe(activeBranch);
});
});
});
import { TEST_HOST } from 'spec/test_constants';
export const projectData = {
id: 1,
name: 'abcproject',
web_url: '',
avatar_url: '',
path: '',
name_with_namespace: 'namespace/abcproject',
branches: {
master: {
treeId: 'abcproject/master',
can_push: true,
commit: {
id: '123',
},
},
},
mergeRequests: {},
merge_requests_enabled: true,
default_branch: 'master',
};
export const pipelines = [
{
id: 1,
ref: 'master',
sha: '123',
details: {
status: {
icon: 'status_failed',
group: 'failed',
text: 'Failed',
},
},
commit: { id: '123' },
},
{
id: 2,
ref: 'master',
sha: '213',
details: {
status: {
icon: 'status_failed',
group: 'failed',
text: 'Failed',
},
},
commit: { id: '213' },
},
];
export const stages = [
{
dropdown_path: `${TEST_HOST}/testing`,
name: 'build',
status: {
icon: 'status_failed',
group: 'failed',
text: 'failed',
},
},
{
dropdown_path: 'testing',
name: 'test',
status: {
icon: 'status_failed',
group: 'failed',
text: 'failed',
},
},
];
export const jobs = [
{
id: 1,
name: 'test',
path: 'testing',
status: {
icon: 'status_success',
text: 'passed',
},
stage: 'test',
duration: 1,
started: new Date(),
},
{
id: 2,
name: 'test 2',
path: 'testing2',
status: {
icon: 'status_success',
text: 'passed',
},
stage: 'test',
duration: 1,
started: new Date(),
},
{
id: 3,
name: 'test 3',
path: 'testing3',
status: {
icon: 'status_success',
text: 'passed',
},
stage: 'test',
duration: 1,
started: new Date(),
},
{
id: 4,
name: 'test 4',
path: 'testing4',
status: {
icon: 'status_failed',
text: 'failed',
},
stage: 'build',
duration: 1,
started: new Date(),
},
];
export const fullPipelinesResponse = {
data: {
count: {
all: 2,
},
pipelines: [
{
id: '51',
path: 'test',
commit: {
id: '123',
},
details: {
status: {
icon: 'status_failed',
text: 'failed',
},
stages: [...stages],
},
},
{
id: '50',
commit: {
id: 'abc123def456ghi789jkl',
},
details: {
status: {
icon: 'status_success',
text: 'passed',
},
stages: [...stages],
},
},
],
},
};
export const mergeRequests = [
{
id: 1,
iid: 1,
title: 'Test merge request',
project_id: 1,
web_url: `${TEST_HOST}/namespace/project-path/merge_requests/1`,
},
];
export const branches = [
{
id: 1,
name: 'master',
commit: {
message: 'Update master branch',
committed_date: '2018-08-01T00:20:05Z',
},
can_push: true,
protected: true,
default: true,
},
{
id: 2,
name: 'protected/no-access',
commit: {
message: 'Update some stuff',
committed_date: '2018-08-02T00:00:05Z',
},
can_push: false,
protected: true,
default: false,
},
{
id: 3,
name: 'protected/access',
commit: {
message: 'Update some stuff',
committed_date: '2018-08-02T00:00:05Z',
},
can_push: true,
protected: true,
default: false,
},
{
id: 4,
name: 'regular',
commit: {
message: 'Update some more stuff',
committed_date: '2018-06-30T00:20:05Z',
},
can_push: true,
protected: false,
default: false,
},
{
id: 5,
name: 'regular/no-access',
commit: {
message: 'Update some more stuff',
committed_date: '2018-06-30T00:20:05Z',
},
can_push: false,
protected: false,
default: false,
},
];
import Vue from 'vue';
import store from '~/ide/stores';
import * as types from '~/ide/stores/modules/branches/mutation_types';
import List from '~/ide/components/branches/search_list.vue';
import { createComponentWithStore } from '../../../helpers/vue_mount_component_helper';
import { branches as testBranches } from '../../mock_data';
import { resetStore } from '../../helpers';
describe('IDE branches search list', () => {
const Component = Vue.extend(List);
let vm;
beforeEach(() => {
vm = createComponentWithStore(Component, store, {});
spyOn(vm, 'fetchBranches');
vm.$mount();
});
afterEach(() => {
vm.$destroy();
resetStore(store);
});
it('calls fetch on mounted', () => {
expect(vm.fetchBranches).toHaveBeenCalledWith({
search: '',
});
});
it('renders loading icon', done => {
vm.$store.state.branches.isLoading = true;
vm.$nextTick()
.then(() => {
expect(vm.$el).toContainElement('.loading-container');
})
.then(done)
.catch(done.fail);
});
it('renders branches not found when search is not empty', done => {
vm.search = 'testing';
vm.$nextTick(() => {
expect(vm.$el).toContainText('No branches found');
done();
});
});
describe('with branches', () => {
const currentBranch = testBranches[1];
beforeEach(done => {
vm.$store.state.currentBranchId = currentBranch.name;
vm.$store.commit(`branches/${types.RECEIVE_BRANCHES_SUCCESS}`, testBranches);
vm.$nextTick(done);
});
it('renders list', () => {
const elementText = Array.from(vm.$el.querySelectorAll('li strong')).map(x =>
x.textContent.trim(),
);
expect(elementText).toEqual(testBranches.map(x => x.name));
});
it('renders check next to active branch', () => {
const checkedText = Array.from(vm.$el.querySelectorAll('li'))
.filter(x => x.querySelector('.ide-search-list-current-icon svg'))
.map(x => x.querySelector('strong').textContent.trim());
expect(checkedText).toEqual([currentBranch.name]);
});
});
});
import { TEST_HOST } from '../test_constants'; export * from '../../frontend/ide/mock_data';
export const projectData = {
id: 1,
name: 'abcproject',
web_url: '',
avatar_url: '',
path: '',
name_with_namespace: 'namespace/abcproject',
branches: {
master: {
treeId: 'abcproject/master',
can_push: true,
commit: {
id: '123',
},
},
},
mergeRequests: {},
merge_requests_enabled: true,
default_branch: 'master',
};
export const pipelines = [
{
id: 1,
ref: 'master',
sha: '123',
details: {
status: {
icon: 'status_failed',
group: 'failed',
text: 'Failed',
},
},
commit: { id: '123' },
},
{
id: 2,
ref: 'master',
sha: '213',
details: {
status: {
icon: 'status_failed',
group: 'failed',
text: 'Failed',
},
},
commit: { id: '213' },
},
];
export const stages = [
{
dropdown_path: `${TEST_HOST}/testing`,
name: 'build',
status: {
icon: 'status_failed',
group: 'failed',
text: 'failed',
},
},
{
dropdown_path: 'testing',
name: 'test',
status: {
icon: 'status_failed',
group: 'failed',
text: 'failed',
},
},
];
export const jobs = [
{
id: 1,
name: 'test',
path: 'testing',
status: {
icon: 'status_success',
text: 'passed',
},
stage: 'test',
duration: 1,
started: new Date(),
},
{
id: 2,
name: 'test 2',
path: 'testing2',
status: {
icon: 'status_success',
text: 'passed',
},
stage: 'test',
duration: 1,
started: new Date(),
},
{
id: 3,
name: 'test 3',
path: 'testing3',
status: {
icon: 'status_success',
text: 'passed',
},
stage: 'test',
duration: 1,
started: new Date(),
},
{
id: 4,
name: 'test 4',
path: 'testing4',
status: {
icon: 'status_failed',
text: 'failed',
},
stage: 'build',
duration: 1,
started: new Date(),
},
];
export const fullPipelinesResponse = {
data: {
count: {
all: 2,
},
pipelines: [
{
id: '51',
path: 'test',
commit: {
id: '123',
},
details: {
status: {
icon: 'status_failed',
text: 'failed',
},
stages: [...stages],
},
},
{
id: '50',
commit: {
id: 'abc123def456ghi789jkl',
},
details: {
status: {
icon: 'status_success',
text: 'passed',
},
stages: [...stages],
},
},
],
},
};
export const mergeRequests = [
{
id: 1,
iid: 1,
title: 'Test merge request',
project_id: 1,
web_url: `${TEST_HOST}/namespace/project-path/merge_requests/1`,
},
];
export const branches = [
{
id: 1,
name: 'master',
commit: {
message: 'Update master branch',
committed_date: '2018-08-01T00:20:05Z',
},
can_push: true,
protected: true,
default: true,
},
{
id: 2,
name: 'protected/no-access',
commit: {
message: 'Update some stuff',
committed_date: '2018-08-02T00:00:05Z',
},
can_push: false,
protected: true,
default: false,
},
{
id: 3,
name: 'protected/access',
commit: {
message: 'Update some stuff',
committed_date: '2018-08-02T00:00:05Z',
},
can_push: true,
protected: true,
default: false,
},
{
id: 4,
name: 'regular',
commit: {
message: 'Update some more stuff',
committed_date: '2018-06-30T00:20:05Z',
},
can_push: true,
protected: false,
default: false,
},
{
id: 5,
name: 'regular/no-access',
commit: {
message: 'Update some more stuff',
committed_date: '2018-06-30T00:20:05Z',
},
can_push: false,
protected: false,
default: false,
},
];
# frozen_string_literal: true
require 'spec_helper'
describe Gitlab::Experimentation::ControllerConcern, type: :controller do
controller(ApplicationController) do
include Gitlab::Experimentation::ControllerConcern
def index
head :ok
end
end
describe '#set_experimentation_subject_id_cookie' do
before do
get :index
end
context 'cookie is present' do
before do
cookies[:experimentation_subject_id] = 'test'
end
it 'does not change the cookie' do
expect(cookies[:experimentation_subject_id]).to eq 'test'
end
end
context 'cookie is not present' do
it 'sets a permanent signed cookie' do
expect(cookies.permanent.signed[:experimentation_subject_id]).to be_present
end
end
end
describe '#experiment_enabled?' do
context 'cookie is not present' do
it 'calls Gitlab::Experimentation.enabled? with the name of the experiment and an experimentation_subject_index of nil' do
expect(Gitlab::Experimentation).to receive(:enabled?).with(:test_experiment, nil)
controller.experiment_enabled?(:test_experiment)
end
end
context 'cookie is present' do
before do
cookies.permanent.signed[:experimentation_subject_id] = 'abcd-1234'
get :index
end
it 'calls Gitlab::Experimentation.enabled? with the name of the experiment and an experimentation_subject_index of the modulo 100 of the hex value of the uuid' do
# 'abcd1234'.hex % 100 = 76
expect(Gitlab::Experimentation).to receive(:enabled?).with(:test_experiment, 76)
controller.experiment_enabled?(:test_experiment)
end
end
end
end
describe Gitlab::Experimentation do
before do
stub_const('Gitlab::Experimentation::EXPERIMENTS', {
test_experiment: {
feature_toggle: feature_toggle,
environment: environment,
enabled_ratio: enabled_ratio
}
})
stub_feature_flags(feature_toggle => true)
end
let(:feature_toggle) { :test_experiment_toggle }
let(:environment) { Rails.env.test? }
let(:enabled_ratio) { 0.1 }
describe '.enabled?' do
subject { described_class.enabled?(:test_experiment, experimentation_subject_index) }
let(:experimentation_subject_index) { 9 }
context 'feature toggle is enabled, we are on the right environment and we are selected' do
it { is_expected.to be_truthy }
end
describe 'experiment is not defined' do
it 'returns false' do
expect(described_class.enabled?(:missing_experiment, experimentation_subject_index)).to be_falsey
end
end
describe 'feature toggle' do
context 'feature toggle is not set' do
let(:feature_toggle) { nil }
it { is_expected.to be_truthy }
end
context 'feature toggle is not set, but a feature with the experiment key as name does exist' do
before do
stub_feature_flags(test_experiment: false)
end
let(:feature_toggle) { nil }
it { is_expected.to be_falsey }
end
context 'feature toggle is disabled' do
before do
stub_feature_flags(feature_toggle => false)
end
it { is_expected.to be_falsey }
end
end
describe 'environment' do
context 'environment is not set' do
let(:environment) { nil }
it { is_expected.to be_truthy }
end
context 'we are on the wrong environment' do
let(:environment) { ::Gitlab.com? }
it { is_expected.to be_falsey }
end
end
describe 'enabled ratio' do
context 'enabled ratio is not set' do
let(:enabled_ratio) { nil }
it { is_expected.to be_falsey }
end
context 'experimentation_subject_index is not set' do
let(:experimentation_subject_index) { nil }
it { is_expected.to be_falsey }
end
context 'experimentation_subject_index is an empty string' do
let(:experimentation_subject_index) { '' }
it { is_expected.to be_falsey }
end
context 'experimentation_subject_index outside enabled ratio' do
let(:experimentation_subject_index) { 11 }
it { is_expected.to be_falsey }
end
end
end
end
...@@ -19,6 +19,8 @@ describe Projects::AfterImportService do ...@@ -19,6 +19,8 @@ describe Projects::AfterImportService do
allow(housekeeping_service) allow(housekeeping_service)
.to receive(:execute).and_yield .to receive(:execute).and_yield
expect(housekeeping_service).to receive(:increment!)
end end
it 'performs housekeeping' do it 'performs housekeeping' do
......
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