diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2019-10-18 11:11:44 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2019-10-18 11:11:44 +0000 |
commit | 25989ab7ef1a444ed2abd5479f176d58e1d9462a (patch) | |
tree | 271bb24f3c7178f320cb9de0be0833a285327d09 /spec | |
parent | 9bbb32b29703f3ce33dd35d5101145774b793a6d (diff) | |
download | gitlab-ce-25989ab7ef1a444ed2abd5479f176d58e1d9462a.tar.gz |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec')
118 files changed, 1172 insertions, 155 deletions
diff --git a/spec/controllers/projects/issues_controller_spec.rb b/spec/controllers/projects/issues_controller_spec.rb index 2edc0aa5536..c9558abab33 100644 --- a/spec/controllers/projects/issues_controller_spec.rb +++ b/spec/controllers/projects/issues_controller_spec.rb @@ -1180,6 +1180,7 @@ describe Projects::IssuesController do name: emoji_name }) end + let(:emoji_name) { 'thumbsup' } it "toggles the award emoji" do diff --git a/spec/controllers/projects/notes_controller_spec.rb b/spec/controllers/projects/notes_controller_spec.rb index 4db77921f24..3ab191c0032 100644 --- a/spec/controllers/projects/notes_controller_spec.rb +++ b/spec/controllers/projects/notes_controller_spec.rb @@ -713,6 +713,7 @@ describe Projects::NotesController do end subject { post(:toggle_award_emoji, params: request_params.merge(name: emoji_name)) } + let(:emoji_name) { 'thumbsup' } it "toggles the award emoji" do diff --git a/spec/controllers/projects/serverless/functions_controller_spec.rb b/spec/controllers/projects/serverless/functions_controller_spec.rb index 9f1ef3a4be8..eccc8e1d5de 100644 --- a/spec/controllers/projects/serverless/functions_controller_spec.rb +++ b/spec/controllers/projects/serverless/functions_controller_spec.rb @@ -107,26 +107,50 @@ describe Projects::Serverless::FunctionsController do end end - context 'valid data', :use_clean_rails_memory_store_caching do - before do - stub_kubeclient_service_pods - stub_reactive_cache(knative_services_finder, - { - services: kube_knative_services_body(namespace: namespace.namespace, name: cluster.project.name)["items"], - pods: kube_knative_pods_body(cluster.project.name, namespace.namespace)["items"] - }, - *knative_services_finder.cache_args) + context 'with valid data', :use_clean_rails_memory_store_caching do + shared_examples 'GET #show with valid data' do + it 'has a valid function name' do + get :show, params: params({ format: :json, environment_id: "*", id: cluster.project.name }) + expect(response).to have_gitlab_http_status(200) + + expect(json_response).to include( + "name" => project.name, + "url" => "http://#{project.name}.#{namespace.namespace}.example.com", + "podcount" => 1 + ) + end end - it 'has a valid function name' do - get :show, params: params({ format: :json, environment_id: "*", id: cluster.project.name }) - expect(response).to have_gitlab_http_status(200) + context 'on Knative 0.5' do + before do + stub_kubeclient_service_pods + stub_reactive_cache(knative_services_finder, + { + services: kube_knative_services_body( + legacy_knative: true, + namespace: namespace.namespace, + name: cluster.project.name + )["items"], + pods: kube_knative_pods_body(cluster.project.name, namespace.namespace)["items"] + }, + *knative_services_finder.cache_args) + end - expect(json_response).to include( - "name" => project.name, - "url" => "http://#{project.name}.#{namespace.namespace}.example.com", - "podcount" => 1 - ) + include_examples 'GET #show with valid data' + end + + context 'on Knative 0.6 or 0.7' do + before do + stub_kubeclient_service_pods + stub_reactive_cache(knative_services_finder, + { + services: kube_knative_services_body(namespace: namespace.namespace, name: cluster.project.name)["items"], + pods: kube_knative_pods_body(cluster.project.name, namespace.namespace)["items"] + }, + *knative_services_finder.cache_args) + end + + include_examples 'GET #show with valid data' end end end @@ -141,38 +165,60 @@ describe Projects::Serverless::FunctionsController do end describe 'GET #index with data', :use_clean_rails_memory_store_caching do - before do - stub_kubeclient_service_pods - stub_reactive_cache(knative_services_finder, - { - services: kube_knative_services_body(namespace: namespace.namespace, name: cluster.project.name)["items"], - pods: kube_knative_pods_body(cluster.project.name, namespace.namespace)["items"] - }, - *knative_services_finder.cache_args) + shared_examples 'GET #index with data' do + it 'has data' do + get :index, params: params({ format: :json }) + + expect(response).to have_gitlab_http_status(200) + + expect(json_response).to match({ + "knative_installed" => "checking", + "functions" => [ + a_hash_including( + "name" => project.name, + "url" => "http://#{project.name}.#{namespace.namespace}.example.com" + ) + ] + }) + end + + it 'has data in html' do + get :index, params: params + + expect(response).to have_gitlab_http_status(200) + end end - it 'has data' do - get :index, params: params({ format: :json }) - - expect(response).to have_gitlab_http_status(200) - - expect(json_response).to match( - { - "knative_installed" => "checking", - "functions" => [ - a_hash_including( - "name" => project.name, - "url" => "http://#{project.name}.#{namespace.namespace}.example.com" - ) - ] - } - ) + context 'on Knative 0.5' do + before do + stub_kubeclient_service_pods + stub_reactive_cache(knative_services_finder, + { + services: kube_knative_services_body( + legacy_knative: true, + namespace: namespace.namespace, + name: cluster.project.name + )["items"], + pods: kube_knative_pods_body(cluster.project.name, namespace.namespace)["items"] + }, + *knative_services_finder.cache_args) + end + + include_examples 'GET #index with data' end - it 'has data in html' do - get :index, params: params + context 'on Knative 0.6 or 0.7' do + before do + stub_kubeclient_service_pods + stub_reactive_cache(knative_services_finder, + { + services: kube_knative_services_body(namespace: namespace.namespace, name: cluster.project.name)["items"], + pods: kube_knative_pods_body(cluster.project.name, namespace.namespace)["items"] + }, + *knative_services_finder.cache_args) + end - expect(response).to have_gitlab_http_status(200) + include_examples 'GET #index with data' end end end diff --git a/spec/controllers/projects/settings/ci_cd_controller_spec.rb b/spec/controllers/projects/settings/ci_cd_controller_spec.rb index 93507b58910..c67e7f7dadd 100644 --- a/spec/controllers/projects/settings/ci_cd_controller_spec.rb +++ b/spec/controllers/projects/settings/ci_cd_controller_spec.rb @@ -78,6 +78,7 @@ describe Projects::Settings::CiCdController do describe 'PUT #reset_registration_token' do subject { put :reset_registration_token, params: { namespace_id: project.namespace, project_id: project } } + it 'resets runner registration token' do expect { subject }.to change { project.reload.runners_token } end diff --git a/spec/factories/gitaly/commit.rb b/spec/factories/gitaly/commit.rb index 954b5338846..ef5301db770 100644 --- a/spec/factories/gitaly/commit.rb +++ b/spec/factories/gitaly/commit.rb @@ -12,6 +12,7 @@ FactoryBot.define do Google::Protobuf::RepeatedField.new(:string, ids) end subject { "My commit" } + body { subject + "\nMy body" } author { build(:gitaly_commit_author) } committer { build(:gitaly_commit_author) } diff --git a/spec/factories/issues.rb b/spec/factories/issues.rb index 70f480a3bcb..46910078ee5 100644 --- a/spec/factories/issues.rb +++ b/spec/factories/issues.rb @@ -12,7 +12,7 @@ FactoryBot.define do end trait :opened do - state { :opened } + state_id { Issue.available_states[:opened] } end trait :locked do @@ -20,10 +20,14 @@ FactoryBot.define do end trait :closed do - state { :closed } + state_id { Issue.available_states[:closed] } closed_at { Time.now } end + after(:build) do |issue, evaluator| + issue.state_id = Issue.available_states[evaluator.state] + end + factory :closed_issue, traits: [:closed] factory :reopened_issue, traits: [:opened] diff --git a/spec/factories/merge_requests.rb b/spec/factories/merge_requests.rb index 28a3f76d485..d16e0c10671 100644 --- a/spec/factories/merge_requests.rb +++ b/spec/factories/merge_requests.rb @@ -40,7 +40,7 @@ FactoryBot.define do end trait :merged do - state { :merged } + state_id { MergeRequest.available_states[:merged] } end trait :merged_target do @@ -57,7 +57,7 @@ FactoryBot.define do end trait :closed do - state { :closed } + state_id { MergeRequest.available_states[:closed] } end trait :closed_last_month do @@ -69,7 +69,7 @@ FactoryBot.define do end trait :opened do - state { :opened } + state_id { MergeRequest.available_states[:opened] } end trait :invalid do @@ -78,7 +78,7 @@ FactoryBot.define do end trait :locked do - state { :locked } + state_id { MergeRequest.available_states[:locked] } end trait :simple do @@ -186,6 +186,10 @@ FactoryBot.define do end end + after(:build) do |merge_request, evaluator| + merge_request.state_id = MergeRequest.available_states[evaluator.state] + end + after(:create) do |merge_request, evaluator| merge_request.cache_merge_request_closes_issues! end diff --git a/spec/features/groups/issues_spec.rb b/spec/features/groups/issues_spec.rb index 0cb24ef856b..5d87c9d7be8 100644 --- a/spec/features/groups/issues_spec.rb +++ b/spec/features/groups/issues_spec.rb @@ -93,6 +93,7 @@ describe 'Group issues page' do end it 'shows projects only with issues feature enabled', :js do + find('.empty-state .js-lazy-loaded') find('.new-project-item-link').click page.within('.select2-results') do diff --git a/spec/features/security/group/internal_access_spec.rb b/spec/features/security/group/internal_access_spec.rb index d6575ec9de1..a182b6b9d57 100644 --- a/spec/features/security/group/internal_access_spec.rb +++ b/spec/features/security/group/internal_access_spec.rb @@ -16,6 +16,7 @@ describe 'Internal Group access' do describe "Group should be internal" do describe '#internal?' do subject { group.internal? } + it { is_expected.to be_truthy } end end diff --git a/spec/features/security/group/private_access_spec.rb b/spec/features/security/group/private_access_spec.rb index 2dc863a6e73..5e3e9824aaa 100644 --- a/spec/features/security/group/private_access_spec.rb +++ b/spec/features/security/group/private_access_spec.rb @@ -16,6 +16,7 @@ describe 'Private Group access' do describe "Group should be private" do describe '#private?' do subject { group.private? } + it { is_expected.to be_truthy } end end diff --git a/spec/features/security/group/public_access_spec.rb b/spec/features/security/group/public_access_spec.rb index 4066a19fce2..efc84205980 100644 --- a/spec/features/security/group/public_access_spec.rb +++ b/spec/features/security/group/public_access_spec.rb @@ -16,6 +16,7 @@ describe 'Public Group access' do describe "Group should be public" do describe '#public?' do subject { group.public? } + it { is_expected.to be_truthy } end end diff --git a/spec/features/security/project/internal_access_spec.rb b/spec/features/security/project/internal_access_spec.rb index d089fa718d2..768b883a90e 100644 --- a/spec/features/security/project/internal_access_spec.rb +++ b/spec/features/security/project/internal_access_spec.rb @@ -14,6 +14,7 @@ describe "Internal Project Access" do describe "Project should be internal" do describe '#internal?' do subject { project.internal? } + it { is_expected.to be_truthy } end end diff --git a/spec/features/security/project/private_access_spec.rb b/spec/features/security/project/private_access_spec.rb index b868cd595cb..c2d44c05a22 100644 --- a/spec/features/security/project/private_access_spec.rb +++ b/spec/features/security/project/private_access_spec.rb @@ -14,6 +14,7 @@ describe "Private Project Access" do describe "Project should be private" do describe '#private?' do subject { project.private? } + it { is_expected.to be_truthy } end end diff --git a/spec/features/security/project/public_access_spec.rb b/spec/features/security/project/public_access_spec.rb index 8db2f2d69e5..19f01257713 100644 --- a/spec/features/security/project/public_access_spec.rb +++ b/spec/features/security/project/public_access_spec.rb @@ -14,6 +14,7 @@ describe "Public Project Access" do describe "Project should be public" do describe '#public?' do subject { project.public? } + it { is_expected.to be_truthy } end end diff --git a/spec/finders/clusters/knative_services_finder_spec.rb b/spec/finders/clusters/knative_services_finder_spec.rb index 159724b3c1f..7ad64cc3bca 100644 --- a/spec/finders/clusters/knative_services_finder_spec.rb +++ b/spec/finders/clusters/knative_services_finder_spec.rb @@ -77,6 +77,7 @@ describe Clusters::KnativeServicesFinder do describe '#knative_detected' do subject { finder.knative_detected } + before do synchronous_reactive_cache(finder) end diff --git a/spec/finders/concerns/finder_with_cross_project_access_spec.rb b/spec/finders/concerns/finder_with_cross_project_access_spec.rb index 7f6190f96e0..6ba98b79176 100644 --- a/spec/finders/concerns/finder_with_cross_project_access_spec.rb +++ b/spec/finders/concerns/finder_with_cross_project_access_spec.rb @@ -24,6 +24,7 @@ describe FinderWithCrossProjectAccess do let(:user) { create(:user) } subject(:finder) { finder_class.new(user) } + let!(:result) { create(:issue) } before do diff --git a/spec/fixtures/lib/gitlab/import_export/project.group.json b/spec/fixtures/lib/gitlab/import_export/project.group.json index 66f5bb4c87b..47faf271cca 100644 --- a/spec/fixtures/lib/gitlab/import_export/project.group.json +++ b/spec/fixtures/lib/gitlab/import_export/project.group.json @@ -129,7 +129,7 @@ "updated_at": "2017-08-15T18:37:40.807Z", "branch_name": null, "description": "Quam totam fuga numquam in eveniet.", - "state": "opened", + "state": "closed", "iid": 2, "updated_by_id": 1, "confidential": false, diff --git a/spec/frontend/monitoring/components/date_time_picker/date_time_picker_input_spec.js b/spec/frontend/monitoring/components/date_time_picker/date_time_picker_input_spec.js new file mode 100644 index 00000000000..1315e1226a4 --- /dev/null +++ b/spec/frontend/monitoring/components/date_time_picker/date_time_picker_input_spec.js @@ -0,0 +1,66 @@ +import { mount } from '@vue/test-utils'; +import DateTimePickerInput from '~/monitoring/components/date_time_picker/date_time_picker_input.vue'; + +const inputLabel = 'This is a label'; +const inputValue = 'something'; + +describe('DateTimePickerInput', () => { + let wrapper; + + const createComponent = (propsData = {}) => { + wrapper = mount(DateTimePickerInput, { + propsData: { + state: null, + value: '', + label: '', + ...propsData, + }, + sync: false, + }); + }; + + afterEach(() => { + wrapper.destroy(); + }); + + it('renders label above the input', () => { + createComponent({ + label: inputLabel, + }); + + expect(wrapper.find('.gl-form-group label').text()).toBe(inputLabel); + }); + + it('renders the same `ID` for input and `for` for label', () => { + createComponent({ label: inputLabel }); + + expect(wrapper.find('.gl-form-group label').attributes('for')).toBe( + wrapper.find('input').attributes('id'), + ); + }); + + it('renders valid input in gray color instead of green', () => { + createComponent({ + state: true, + }); + + expect(wrapper.find('input').classes('is-valid')).toBe(false); + }); + + it('renders invalid input in red color', () => { + createComponent({ + state: false, + }); + + expect(wrapper.find('input').classes('is-invalid')).toBe(true); + }); + + it('input event is emitted when focus is lost', () => { + createComponent(); + jest.spyOn(wrapper.vm, '$emit'); + wrapper.find('input').setValue(inputValue); + wrapper.find('input').trigger('blur'); + + expect(wrapper.vm.$emit).toHaveBeenCalledWith('input', inputValue); + }); +}); diff --git a/spec/frontend/monitoring/components/date_time_picker/date_time_picker_spec.js b/spec/frontend/monitoring/components/date_time_picker/date_time_picker_spec.js new file mode 100644 index 00000000000..be544435671 --- /dev/null +++ b/spec/frontend/monitoring/components/date_time_picker/date_time_picker_spec.js @@ -0,0 +1,157 @@ +import { mount } from '@vue/test-utils'; +import DateTimePicker from '~/monitoring/components/date_time_picker/date_time_picker.vue'; +import { timeWindows } from '~/monitoring/constants'; + +const timeWindowsCount = Object.keys(timeWindows).length; +const selectedTimeWindow = { + start: '2019-10-10T07:00:00.000Z', + end: '2019-10-13T07:00:00.000Z', +}; +const selectedTimeWindowText = `3 days`; + +describe('DateTimePicker', () => { + let dateTimePicker; + + const dropdownToggle = () => dateTimePicker.find('.dropdown-toggle'); + const dropdownMenu = () => dateTimePicker.find('.dropdown-menu'); + const applyButtonElement = () => dateTimePicker.find('button[variant="success"]').element; + const cancelButtonElement = () => dateTimePicker.find('button.btn-secondary').element; + const fillInputAndBlur = (input, val) => { + dateTimePicker.find(input).setValue(val); + dateTimePicker.find(input).trigger('blur'); + }; + + const createComponent = props => { + dateTimePicker = mount(DateTimePicker, { + propsData: { + timeWindows, + selectedTimeWindow, + ...props, + }, + sync: false, + }); + }; + + afterEach(() => { + dateTimePicker.destroy(); + }); + + it('renders dropdown toggle button with selected text', done => { + createComponent(); + dateTimePicker.vm.$nextTick(() => { + expect(dropdownToggle().text()).toBe(selectedTimeWindowText); + done(); + }); + }); + + it('renders dropdown with 2 custom time range inputs', () => { + createComponent(); + dateTimePicker.vm.$nextTick(() => { + expect(dateTimePicker.findAll('input').length).toBe(2); + }); + }); + + it('renders inputs with h/m/s truncated if its all 0s', done => { + createComponent({ + selectedTimeWindow: { + start: '2019-10-10T00:00:00.000Z', + end: '2019-10-14T00:10:00.000Z', + }, + }); + dateTimePicker.vm.$nextTick(() => { + expect(dateTimePicker.find('#custom-time-from').element.value).toBe('2019-10-10'); + expect(dateTimePicker.find('#custom-time-to').element.value).toBe('2019-10-14 00:10:00'); + done(); + }); + }); + + it(`renders dropdown with ${timeWindowsCount} items in quick range`, done => { + createComponent(); + dropdownToggle().trigger('click'); + dateTimePicker.vm.$nextTick(() => { + expect(dateTimePicker.findAll('.dropdown-item').length).toBe(timeWindowsCount); + done(); + }); + }); + + it(`renders dropdown with correct quick range item selected`, done => { + createComponent(); + dropdownToggle().trigger('click'); + dateTimePicker.vm.$nextTick(() => { + expect(dateTimePicker.find('.dropdown-item.active').text()).toBe(selectedTimeWindowText); + + expect(dateTimePicker.find('.dropdown-item.active svg').isVisible()).toBe(true); + done(); + }); + }); + + it('renders a disabled apply button on load', () => { + createComponent(); + + expect(applyButtonElement().getAttribute('disabled')).toBe('disabled'); + }); + + it('displays inline error message if custom time range inputs are invalid', done => { + createComponent(); + fillInputAndBlur('#custom-time-from', '2019-10-01abc'); + fillInputAndBlur('#custom-time-to', '2019-10-10abc'); + + dateTimePicker.vm.$nextTick(() => { + expect(dateTimePicker.findAll('.invalid-feedback').length).toBe(2); + done(); + }); + }); + + it('keeps apply button disabled with invalid custom time range inputs', done => { + createComponent(); + fillInputAndBlur('#custom-time-from', '2019-10-01abc'); + fillInputAndBlur('#custom-time-to', '2019-09-19'); + + dateTimePicker.vm.$nextTick(() => { + expect(applyButtonElement().getAttribute('disabled')).toBe('disabled'); + done(); + }); + }); + + it('enables apply button with valid custom time range inputs', done => { + createComponent(); + fillInputAndBlur('#custom-time-from', '2019-10-01'); + fillInputAndBlur('#custom-time-to', '2019-10-19'); + + dateTimePicker.vm.$nextTick(() => { + expect(applyButtonElement().getAttribute('disabled')).toBeNull(); + done(); + }); + }); + + it('returns an object when apply is clicked', done => { + createComponent(); + fillInputAndBlur('#custom-time-from', '2019-10-01'); + fillInputAndBlur('#custom-time-to', '2019-10-19'); + + dateTimePicker.vm.$nextTick(() => { + jest.spyOn(dateTimePicker.vm, '$emit'); + applyButtonElement().click(); + + expect(dateTimePicker.vm.$emit).toHaveBeenCalledWith('onApply', { + end: '2019-10-19T00:00:00Z', + start: '2019-10-01T00:00:00Z', + }); + done(); + }); + }); + + it('hides the popover with cancel button', done => { + createComponent(); + dropdownToggle().trigger('click'); + + dateTimePicker.vm.$nextTick(() => { + cancelButtonElement().click(); + + dateTimePicker.vm.$nextTick(() => { + expect(dropdownMenu().classes('show')).toBe(false); + done(); + }); + }); + }); +}); diff --git a/spec/helpers/merge_requests_helper_spec.rb b/spec/helpers/merge_requests_helper_spec.rb index 193390d2f2c..695d1520897 100644 --- a/spec/helpers/merge_requests_helper_spec.rb +++ b/spec/helpers/merge_requests_helper_spec.rb @@ -39,6 +39,7 @@ describe MergeRequestsHelper do let(:forked_project) { fork_project(project) } let(:merge_request) { create(:merge_request, source_project: forked_project, target_project: project) } subject { format_mr_branch_names(merge_request) } + let(:source_title) { "#{forked_project.full_path}:#{merge_request.source_branch}" } let(:target_title) { "#{project.full_path}:#{merge_request.target_branch}" } diff --git a/spec/javascripts/monitoring/components/dashboard_spec.js b/spec/javascripts/monitoring/components/dashboard_spec.js index 1b2b01d1c8c..75df2ce3103 100644 --- a/spec/javascripts/monitoring/components/dashboard_spec.js +++ b/spec/javascripts/monitoring/components/dashboard_spec.js @@ -4,7 +4,6 @@ import { GlToast } from '@gitlab/ui'; import VueDraggable from 'vuedraggable'; import MockAdapter from 'axios-mock-adapter'; import Dashboard from '~/monitoring/components/dashboard.vue'; -import { timeWindows, timeWindowsKeyNames } from '~/monitoring/constants'; import * as types from '~/monitoring/stores/mutation_types'; import { createStore } from '~/monitoring/stores'; import axios from '~/lib/utils/axios_utils'; @@ -37,6 +36,12 @@ const propsData = { validateQueryPath: '', }; +const resetSpy = spy => { + if (spy) { + spy.calls.reset(); + } +}; + export default propsData; describe('Dashboard', () => { @@ -96,10 +101,15 @@ describe('Dashboard', () => { }); describe('requests information to the server', () => { + let spy; beforeEach(() => { mock.onGet(mockApiEndpoint).reply(200, metricsGroupsAPIResponse); }); + afterEach(() => { + resetSpy(spy); + }); + it('shows up a loading state', done => { component = new DashboardComponent({ el: document.querySelector('.prometheus-graphs'), @@ -272,7 +282,7 @@ describe('Dashboard', () => { }); }); - it('renders the time window dropdown with a set of options', done => { + it('renders the datetimepicker dropdown', done => { component = new DashboardComponent({ el: document.querySelector('.prometheus-graphs'), propsData: { @@ -282,17 +292,9 @@ describe('Dashboard', () => { }, store, }); - const numberOfTimeWindows = Object.keys(timeWindows).length; setTimeout(() => { - const timeWindowDropdown = component.$el.querySelector('.js-time-window-dropdown'); - const timeWindowDropdownEls = component.$el.querySelectorAll( - '.js-time-window-dropdown .dropdown-item', - ); - - expect(timeWindowDropdown).not.toBeNull(); - expect(timeWindowDropdownEls.length).toEqual(numberOfTimeWindows); - + expect(component.$el.querySelector('.js-time-window-dropdown')).not.toBeNull(); done(); }); }); @@ -355,7 +357,7 @@ describe('Dashboard', () => { }); }); - it('defaults to the eight hours time window for non valid url parameters', done => { + it('shows an error message if invalid url parameters are passed', done => { spyOnDependency(Dashboard, 'getParameterValues').and.returnValue([ '<script>alert("XSS")</script>', ]); @@ -366,9 +368,11 @@ describe('Dashboard', () => { store, }); - Vue.nextTick(() => { - expect(component.selectedTimeWindowKey).toEqual(timeWindowsKeyNames.eightHours); + spy = spyOn(component, 'showInvalidDateError'); + component.$mount(); + component.$nextTick(() => { + expect(component.showInvalidDateError).toHaveBeenCalled(); done(); }); }); diff --git a/spec/javascripts/monitoring/utils_spec.js b/spec/javascripts/monitoring/utils_spec.js index 7030156931f..512dd2a0eb3 100644 --- a/spec/javascripts/monitoring/utils_spec.js +++ b/spec/javascripts/monitoring/utils_spec.js @@ -1,4 +1,13 @@ -import { getTimeDiff, getTimeWindow, graphDataValidatorForValues } from '~/monitoring/utils'; +import { + getTimeDiff, + getTimeWindow, + graphDataValidatorForValues, + isDateTimePickerInputValid, + truncateZerosInDateTime, + stringToISODate, + ISODateToString, + isValidDate, +} from '~/monitoring/utils'; import { timeWindows, timeWindowsKeyNames } from '~/monitoring/constants'; import { graphDataPrometheusQuery, graphDataPrometheusQueryRange } from './mock_data'; @@ -57,7 +66,7 @@ describe('getTimeWindow', () => { end: '2019-10-01T21:27:47.000Z', }, ], - expected: timeWindowsKeyNames.eightHours, + expected: null, }, { args: [ @@ -66,7 +75,7 @@ describe('getTimeWindow', () => { end: '', }, ], - expected: timeWindowsKeyNames.eightHours, + expected: null, }, { args: [ @@ -75,11 +84,11 @@ describe('getTimeWindow', () => { end: null, }, ], - expected: timeWindowsKeyNames.eightHours, + expected: null, }, { args: [{}], - expected: timeWindowsKeyNames.eightHours, + expected: null, }, ].forEach(({ args, expected }) => { it(`returns "${expected}" with args=${JSON.stringify(args)}`, () => { @@ -111,3 +120,190 @@ describe('graphDataValidatorForValues', () => { expect(validGraphData).toBe(true); }); }); + +describe('stringToISODate', () => { + ['', 'null', undefined, 'abc'].forEach(input => { + it(`throws error for invalid input like ${input}`, done => { + try { + stringToISODate(input); + } catch (e) { + expect(e).toBeDefined(); + done(); + } + }); + }); + [ + { + input: '2019-09-09 01:01:01', + output: '2019-09-09T01:01:01Z', + }, + { + input: '2019-09-09 00:00:00', + output: '2019-09-09T00:00:00Z', + }, + { + input: '2019-09-09 23:59:59', + output: '2019-09-09T23:59:59Z', + }, + { + input: '2019-09-09', + output: '2019-09-09T00:00:00Z', + }, + ].forEach(({ input, output }) => { + it(`returns ${output} from ${input}`, () => { + expect(stringToISODate(input)).toBe(output); + }); + }); +}); + +describe('ISODateToString', () => { + [ + { + input: new Date('2019-09-09T00:00:00.000Z'), + output: '2019-09-09 00:00:00', + }, + { + input: new Date('2019-09-09T07:00:00.000Z'), + output: '2019-09-09 07:00:00', + }, + ].forEach(({ input, output }) => { + it(`ISODateToString return ${output} for ${input}`, () => { + expect(ISODateToString(input)).toBe(output); + }); + }); +}); + +describe('truncateZerosInDateTime', () => { + [ + { + input: '', + output: '', + }, + { + input: '2019-10-10', + output: '2019-10-10', + }, + { + input: '2019-10-10 00:00:01', + output: '2019-10-10 00:00:01', + }, + { + input: '2019-10-10 00:00:00', + output: '2019-10-10', + }, + ].forEach(({ input, output }) => { + it(`truncateZerosInDateTime return ${output} for ${input}`, () => { + expect(truncateZerosInDateTime(input)).toBe(output); + }); + }); +}); + +describe('isValidDate', () => { + [ + { + input: '2019-09-09T00:00:00.000Z', + output: true, + }, + { + input: '2019-09-09T000:00.000Z', + output: false, + }, + { + input: 'a2019-09-09T000:00.000Z', + output: false, + }, + { + input: '2019-09-09T', + output: false, + }, + { + input: '2019-09-09', + output: true, + }, + { + input: '2019-9-9', + output: true, + }, + { + input: '2019-9-', + output: true, + }, + { + input: '2019--', + output: false, + }, + { + input: '2019', + output: true, + }, + { + input: '', + output: false, + }, + { + input: null, + output: false, + }, + ].forEach(({ input, output }) => { + it(`isValidDate return ${output} for ${input}`, () => { + expect(isValidDate(input)).toBe(output); + }); + }); +}); + +describe('isDateTimePickerInputValid', () => { + [ + { + input: null, + output: false, + }, + { + input: '', + output: false, + }, + { + input: 'xxxx-xx-xx', + output: false, + }, + { + input: '9999-99-19', + output: false, + }, + { + input: '2019-19-23', + output: false, + }, + { + input: '2019-09-23', + output: true, + }, + { + input: '2019-09-23 x', + output: false, + }, + { + input: '2019-09-29 0:0:0', + output: false, + }, + { + input: '2019-09-29 00:00:00', + output: true, + }, + { + input: '2019-09-29 24:24:24', + output: false, + }, + { + input: '2019-09-29 23:24:24', + output: true, + }, + { + input: '2019-09-29 23:24:24 ', + output: false, + }, + ].forEach(({ input, output }) => { + it(`returns ${output} for ${input}`, () => { + expect(isDateTimePickerInputValid(input)).toBe(output); + }); + }); +}); diff --git a/spec/lib/backup/files_spec.rb b/spec/lib/backup/files_spec.rb index e903eada62d..b75f3bafeef 100644 --- a/spec/lib/backup/files_spec.rb +++ b/spec/lib/backup/files_spec.rb @@ -24,6 +24,7 @@ describe Backup::Files do describe '#restore' do subject { described_class.new('registry', '/var/gitlab-registry') } + let(:timestamp) { Time.utc(2017, 3, 22) } around do |example| diff --git a/spec/lib/banzai/filter/project_reference_filter_spec.rb b/spec/lib/banzai/filter/project_reference_filter_spec.rb index 927d226c400..d0b4542d503 100644 --- a/spec/lib/banzai/filter/project_reference_filter_spec.rb +++ b/spec/lib/banzai/filter/project_reference_filter_spec.rb @@ -15,6 +15,7 @@ describe Banzai::Filter::ProjectReferenceFilter do let(:project) { create(:project, :public) } subject { project } + let(:subject_name) { "project" } let(:reference) { get_reference(project) } diff --git a/spec/lib/banzai/filter/user_reference_filter_spec.rb b/spec/lib/banzai/filter/user_reference_filter_spec.rb index 6bc87d245f5..a09aeb7d7f6 100644 --- a/spec/lib/banzai/filter/user_reference_filter_spec.rb +++ b/spec/lib/banzai/filter/user_reference_filter_spec.rb @@ -12,6 +12,7 @@ describe Banzai::Filter::UserReferenceFilter do let(:project) { create(:project, :public) } let(:user) { create(:user) } subject { user } + let(:subject_name) { "user" } let(:reference) { get_reference(user) } diff --git a/spec/lib/banzai/reference_parser/commit_parser_spec.rb b/spec/lib/banzai/reference_parser/commit_parser_spec.rb index b44ae67e430..eac1cf16a8f 100644 --- a/spec/lib/banzai/reference_parser/commit_parser_spec.rb +++ b/spec/lib/banzai/reference_parser/commit_parser_spec.rb @@ -8,6 +8,7 @@ describe Banzai::ReferenceParser::CommitParser do let(:project) { create(:project, :public) } let(:user) { create(:user) } subject { described_class.new(Banzai::RenderContext.new(project, user)) } + let(:link) { empty_html_link } describe '#nodes_visible_to_user' do diff --git a/spec/lib/banzai/reference_parser/commit_range_parser_spec.rb b/spec/lib/banzai/reference_parser/commit_range_parser_spec.rb index da853233018..78b337466aa 100644 --- a/spec/lib/banzai/reference_parser/commit_range_parser_spec.rb +++ b/spec/lib/banzai/reference_parser/commit_range_parser_spec.rb @@ -8,6 +8,7 @@ describe Banzai::ReferenceParser::CommitRangeParser do let(:project) { create(:project, :public) } let(:user) { create(:user) } subject { described_class.new(Banzai::RenderContext.new(project, user)) } + let(:link) { empty_html_link } describe '#nodes_visible_to_user' do diff --git a/spec/lib/banzai/reference_parser/external_issue_parser_spec.rb b/spec/lib/banzai/reference_parser/external_issue_parser_spec.rb index 0f29a95bdcc..9343d52e44b 100644 --- a/spec/lib/banzai/reference_parser/external_issue_parser_spec.rb +++ b/spec/lib/banzai/reference_parser/external_issue_parser_spec.rb @@ -8,6 +8,7 @@ describe Banzai::ReferenceParser::ExternalIssueParser do let(:project) { create(:project, :public) } let(:user) { create(:user) } subject { described_class.new(Banzai::RenderContext.new(project, user)) } + let(:link) { empty_html_link } describe '#nodes_visible_to_user' do diff --git a/spec/lib/banzai/reference_parser/label_parser_spec.rb b/spec/lib/banzai/reference_parser/label_parser_spec.rb index cf8adb57ffc..8b66a891e69 100644 --- a/spec/lib/banzai/reference_parser/label_parser_spec.rb +++ b/spec/lib/banzai/reference_parser/label_parser_spec.rb @@ -9,6 +9,7 @@ describe Banzai::ReferenceParser::LabelParser do let(:user) { create(:user) } let(:label) { create(:label, project: project) } subject { described_class.new(Banzai::RenderContext.new(project, user)) } + let(:link) { empty_html_link } describe '#nodes_visible_to_user' do diff --git a/spec/lib/banzai/reference_parser/merge_request_parser_spec.rb b/spec/lib/banzai/reference_parser/merge_request_parser_spec.rb index 1561dabcdbf..cb65893aea0 100644 --- a/spec/lib/banzai/reference_parser/merge_request_parser_spec.rb +++ b/spec/lib/banzai/reference_parser/merge_request_parser_spec.rb @@ -9,6 +9,7 @@ describe Banzai::ReferenceParser::MergeRequestParser do let(:project) { create(:project, :public) } let(:merge_request) { create(:merge_request, source_project: project) } subject { described_class.new(Banzai::RenderContext.new(merge_request.target_project, user)) } + let(:link) { empty_html_link } describe '#nodes_visible_to_user' do diff --git a/spec/lib/banzai/reference_parser/milestone_parser_spec.rb b/spec/lib/banzai/reference_parser/milestone_parser_spec.rb index 006f8e37690..25ba41dd8a0 100644 --- a/spec/lib/banzai/reference_parser/milestone_parser_spec.rb +++ b/spec/lib/banzai/reference_parser/milestone_parser_spec.rb @@ -9,6 +9,7 @@ describe Banzai::ReferenceParser::MilestoneParser do let(:user) { create(:user) } let(:milestone) { create(:milestone, project: project) } subject { described_class.new(Banzai::RenderContext.new(project, user)) } + let(:link) { empty_html_link } describe '#nodes_visible_to_user' do diff --git a/spec/lib/banzai/reference_parser/project_parser_spec.rb b/spec/lib/banzai/reference_parser/project_parser_spec.rb index e4936aa9e57..356dde1e9c2 100644 --- a/spec/lib/banzai/reference_parser/project_parser_spec.rb +++ b/spec/lib/banzai/reference_parser/project_parser_spec.rb @@ -8,6 +8,7 @@ describe Banzai::ReferenceParser::ProjectParser do let(:project) { create(:project, :public) } let(:user) { create(:user) } subject { described_class.new(Banzai::RenderContext.new(project, user)) } + let(:link) { empty_html_link } describe '#referenced_by' do diff --git a/spec/lib/banzai/reference_parser/snippet_parser_spec.rb b/spec/lib/banzai/reference_parser/snippet_parser_spec.rb index 528f79ed020..05dc1cb4d2d 100644 --- a/spec/lib/banzai/reference_parser/snippet_parser_spec.rb +++ b/spec/lib/banzai/reference_parser/snippet_parser_spec.rb @@ -12,6 +12,7 @@ describe Banzai::ReferenceParser::SnippetParser do let(:project_member) { create(:user) } subject { described_class.new(Banzai::RenderContext.new(project, user)) } + let(:link) { empty_html_link } def visible_references(snippet_visibility, user = nil) diff --git a/spec/lib/banzai/reference_parser/user_parser_spec.rb b/spec/lib/banzai/reference_parser/user_parser_spec.rb index a5b4e59a3a1..931fb1e3953 100644 --- a/spec/lib/banzai/reference_parser/user_parser_spec.rb +++ b/spec/lib/banzai/reference_parser/user_parser_spec.rb @@ -9,6 +9,7 @@ describe Banzai::ReferenceParser::UserParser do let(:user) { create(:user) } let(:project) { create(:project, :public, group: group, creator: user) } subject { described_class.new(Banzai::RenderContext.new(project, user)) } + let(:link) { empty_html_link } describe '#referenced_by' do diff --git a/spec/lib/gitlab/bitbucket_import/importer_spec.rb b/spec/lib/gitlab/bitbucket_import/importer_spec.rb index 3d0d3f91859..7f7a285c453 100644 --- a/spec/lib/gitlab/bitbucket_import/importer_spec.rb +++ b/spec/lib/gitlab/bitbucket_import/importer_spec.rb @@ -308,8 +308,8 @@ describe Gitlab::BitbucketImport::Importer do importer.execute - expect(project.issues.where(state: "closed").size).to eq(5) - expect(project.issues.where(state: "opened").size).to eq(2) + expect(project.issues.where(state_id: Issue.available_states[:closed]).size).to eq(5) + expect(project.issues.where(state_id: Issue.available_states[:opened]).size).to eq(2) end describe 'wiki import' do diff --git a/spec/lib/gitlab/ci/build/artifacts/metadata/entry_spec.rb b/spec/lib/gitlab/ci/build/artifacts/metadata/entry_spec.rb index 24d17eb0fb3..73c3cad88bc 100644 --- a/spec/lib/gitlab/ci/build/artifacts/metadata/entry_spec.rb +++ b/spec/lib/gitlab/ci/build/artifacts/metadata/entry_spec.rb @@ -34,27 +34,32 @@ describe Gitlab::Ci::Build::Artifacts::Metadata::Entry do describe '#basename' do subject { |example| path(example).basename } + it { is_expected.to eq 'absolute_path' } end end describe 'path/dir_1/', path: 'path/dir_1/' do subject { |example| path(example) } + it { is_expected.to have_parent } it { is_expected.to be_directory } describe '#basename' do subject { |example| path(example).basename } + it { is_expected.to eq 'dir_1/' } end describe '#name' do subject { |example| path(example).name } + it { is_expected.to eq 'dir_1' } end describe '#parent' do subject { |example| path(example).parent } + it { is_expected.to eq entry('path/') } end @@ -102,21 +107,25 @@ describe Gitlab::Ci::Build::Artifacts::Metadata::Entry do describe '#nodes' do subject { |example| path(example).nodes } + it { is_expected.to eq 2 } end describe '#exists?' do subject { |example| path(example).exists? } + it { is_expected.to be true } end describe '#empty?' do subject { |example| path(example).empty? } + it { is_expected.to be false } end describe '#total_size' do subject { |example| path(example).total_size } + it { is_expected.to eq(30) } end end @@ -124,10 +133,12 @@ describe Gitlab::Ci::Build::Artifacts::Metadata::Entry do describe 'empty path', path: '' do subject { |example| path(example) } + it { is_expected.not_to have_parent } describe '#children' do subject { |example| path(example).children } + it { expect(subject.count).to eq 3 } end end @@ -135,6 +146,7 @@ describe Gitlab::Ci::Build::Artifacts::Metadata::Entry do describe 'path/dir_1/subdir/subfile', path: 'path/dir_1/subdir/subfile' do describe '#nodes' do subject { |example| path(example).nodes } + it { is_expected.to eq 4 } end @@ -153,11 +165,13 @@ describe Gitlab::Ci::Build::Artifacts::Metadata::Entry do describe 'non-existent/', path: 'non-existent/' do describe '#empty?' do subject { |example| path(example).empty? } + it { is_expected.to be true } end describe '#exists?' do subject { |example| path(example).exists? } + it { is_expected.to be false } end end @@ -165,6 +179,7 @@ describe Gitlab::Ci::Build::Artifacts::Metadata::Entry do describe 'another_directory/', path: 'another_directory/' do describe '#empty?' do subject { |example| path(example).empty? } + it { is_expected.to be true } end end diff --git a/spec/lib/gitlab/ci/build/artifacts/metadata_spec.rb b/spec/lib/gitlab/ci/build/artifacts/metadata_spec.rb index ff189c4701e..bfa65c66b33 100644 --- a/spec/lib/gitlab/ci/build/artifacts/metadata_spec.rb +++ b/spec/lib/gitlab/ci/build/artifacts/metadata_spec.rb @@ -76,21 +76,25 @@ describe Gitlab::Ci::Build::Artifacts::Metadata do describe '#to_entry' do subject { metadata('').to_entry } + it { is_expected.to be_an_instance_of(Gitlab::Ci::Build::Artifacts::Metadata::Entry) } end describe '#full_version' do subject { metadata('').full_version } + it { is_expected.to eq 'GitLab Build Artifacts Metadata 0.0.1' } end describe '#version' do subject { metadata('').version } + it { is_expected.to eq '0.0.1' } end describe '#errors' do subject { metadata('').errors } + it { is_expected.to eq({}) } end end diff --git a/spec/lib/gitlab/ci/config/entry/cache_spec.rb b/spec/lib/gitlab/ci/config/entry/cache_spec.rb index 4cb63168ec7..9aab3664e1c 100644 --- a/spec/lib/gitlab/ci/config/entry/cache_spec.rb +++ b/spec/lib/gitlab/ci/config/entry/cache_spec.rb @@ -69,6 +69,7 @@ describe Gitlab::Ci::Config::Entry::Cache do context 'when entry value is not correct' do describe '#errors' do subject { entry.errors } + context 'when is not a hash' do let(:config) { 'ls' } diff --git a/spec/lib/gitlab/ci/config/entry/coverage_spec.rb b/spec/lib/gitlab/ci/config/entry/coverage_spec.rb index 48d0864cfca..877e3ec6216 100644 --- a/spec/lib/gitlab/ci/config/entry/coverage_spec.rb +++ b/spec/lib/gitlab/ci/config/entry/coverage_spec.rb @@ -11,11 +11,13 @@ describe Gitlab::Ci::Config::Entry::Coverage do describe '#errors' do subject { entry.errors } + it { is_expected.to include(/coverage config must be a regular expression/) } end describe '#valid?' do subject { entry } + it { is_expected.not_to be_valid } end end @@ -25,16 +27,19 @@ describe Gitlab::Ci::Config::Entry::Coverage do describe '#value' do subject { entry.value } + it { is_expected.to eq(config[1...-1]) } end describe '#errors' do subject { entry.errors } + it { is_expected.to be_empty } end describe '#valid?' do subject { entry } + it { is_expected.to be_valid } end end @@ -44,11 +49,13 @@ describe Gitlab::Ci::Config::Entry::Coverage do describe '#errors' do subject { entry.errors } + it { is_expected.to include(/coverage config must be a regular expression/) } end describe '#valid?' do subject { entry } + it { is_expected.not_to be_valid } end end diff --git a/spec/lib/gitlab/cleanup/project_uploads_spec.rb b/spec/lib/gitlab/cleanup/project_uploads_spec.rb index 7bad788e44e..5787cce7d20 100644 --- a/spec/lib/gitlab/cleanup/project_uploads_spec.rb +++ b/spec/lib/gitlab/cleanup/project_uploads_spec.rb @@ -4,6 +4,7 @@ require 'spec_helper' describe Gitlab::Cleanup::ProjectUploads do subject { described_class.new(logger: logger) } + let(:logger) { double(:logger) } before do diff --git a/spec/lib/gitlab/danger/teammate_spec.rb b/spec/lib/gitlab/danger/teammate_spec.rb index 1e9cf0017be..bd1c2b10dc8 100644 --- a/spec/lib/gitlab/danger/teammate_spec.rb +++ b/spec/lib/gitlab/danger/teammate_spec.rb @@ -8,6 +8,7 @@ require 'gitlab/danger/teammate' describe Gitlab::Danger::Teammate do subject { described_class.new(options.stringify_keys) } + let(:options) { { username: 'luigi', projects: projects, role: role } } let(:projects) { { project => capabilities } } let(:role) { 'Engineer, Manage' } diff --git a/spec/lib/gitlab/discussions_diff/file_collection_spec.rb b/spec/lib/gitlab/discussions_diff/file_collection_spec.rb index 6ef1e41450f..a13727b62ea 100644 --- a/spec/lib/gitlab/discussions_diff/file_collection_spec.rb +++ b/spec/lib/gitlab/discussions_diff/file_collection_spec.rb @@ -40,6 +40,14 @@ describe Gitlab::DiscussionsDiff::FileCollection do subject.load_highlight end + it 'does not write cache for empty mapping' do + allow(subject).to receive(:highlighted_lines_by_ids).and_return([]) + + expect(Gitlab::DiscussionsDiff::HighlightCache).not_to receive(:write_multiple) + + subject.load_highlight + end + it 'does not write cache for resolved notes' do diff_note_a.update_column(:resolved_at, Time.now) diff --git a/spec/lib/gitlab/downtime_check_spec.rb b/spec/lib/gitlab/downtime_check_spec.rb index 56ad49d528f..5a5e34961a4 100644 --- a/spec/lib/gitlab/downtime_check_spec.rb +++ b/spec/lib/gitlab/downtime_check_spec.rb @@ -4,6 +4,7 @@ require 'spec_helper' describe Gitlab::DowntimeCheck do subject { described_class.new } + let(:path) { 'foo.rb' } describe '#check' do diff --git a/spec/lib/gitlab/email/message/repository_push_spec.rb b/spec/lib/gitlab/email/message/repository_push_spec.rb index 84c5b38127e..b57764bceef 100644 --- a/spec/lib/gitlab/email/message/repository_push_spec.rb +++ b/spec/lib/gitlab/email/message/repository_push_spec.rb @@ -28,90 +28,107 @@ describe Gitlab::Email::Message::RepositoryPush do describe '#project' do subject { message.project } + it { is_expected.to eq project } it { is_expected.to be_an_instance_of Project } end describe '#project_namespace' do subject { message.project_namespace } + it { is_expected.to eq group } it { is_expected.to be_kind_of Namespace } end describe '#project_name_with_namespace' do subject { message.project_name_with_namespace } + it { is_expected.to eq "#{group.name} / #{project.path}" } end describe '#author' do subject { message.author } + it { is_expected.to eq author } it { is_expected.to be_an_instance_of User } end describe '#author_name' do subject { message.author_name } + it { is_expected.to eq 'Author' } end describe '#commits' do subject { message.commits } + it { is_expected.to be_kind_of Array } it { is_expected.to all(be_instance_of Commit) } end describe '#diffs' do subject { message.diffs } + it { is_expected.to all(be_an_instance_of Gitlab::Diff::File) } end describe '#diffs_count' do subject { message.diffs_count } + it { is_expected.to eq raw_compare.diffs.size } end describe '#compare' do subject { message.compare } + it { is_expected.to be_an_instance_of Compare } end describe '#compare_timeout' do subject { message.compare_timeout } + it { is_expected.to eq raw_compare.diffs.overflow? } end describe '#reverse_compare?' do subject { message.reverse_compare? } + it { is_expected.to eq false } end describe '#disable_diffs?' do subject { message.disable_diffs? } + it { is_expected.to eq false } end describe '#send_from_committer_email?' do subject { message.send_from_committer_email? } + it { is_expected.to eq true } end describe '#action_name' do subject { message.action_name } + it { is_expected.to eq 'pushed to' } end describe '#ref_name' do subject { message.ref_name } + it { is_expected.to eq 'master' } end describe '#ref_type' do subject { message.ref_type } + it { is_expected.to eq 'branch' } end describe '#target_url' do subject { message.target_url } + it { is_expected.to include 'compare' } it { is_expected.to include compare.commits.first.parents.first.id } it { is_expected.to include compare.commits.last.id } @@ -119,6 +136,7 @@ describe Gitlab::Email::Message::RepositoryPush do describe '#subject' do subject { message.subject } + it { is_expected.to include "[Git][#{project.full_path}]" } it { is_expected.to include "#{compare.commits.length} commits" } it { is_expected.to include compare.commits.first.message.split("\n").first } diff --git a/spec/lib/gitlab/experimentation_spec.rb b/spec/lib/gitlab/experimentation_spec.rb index 4d473731f39..2e5fd16d370 100644 --- a/spec/lib/gitlab/experimentation_spec.rb +++ b/spec/lib/gitlab/experimentation_spec.rb @@ -75,6 +75,7 @@ describe Gitlab::Experimentation do 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 diff --git a/spec/lib/gitlab/gfm/uploads_rewriter_spec.rb b/spec/lib/gitlab/gfm/uploads_rewriter_spec.rb index d24f5c45107..eef3b9de476 100644 --- a/spec/lib/gitlab/gfm/uploads_rewriter_spec.rb +++ b/spec/lib/gitlab/gfm/uploads_rewriter_spec.rb @@ -84,11 +84,13 @@ describe Gitlab::Gfm::UploadsRewriter do describe '#needs_rewrite?' do subject { rewriter.needs_rewrite? } + it { is_expected.to eq true } end describe '#files' do subject { rewriter.files } + it { is_expected.to be_an(Array) } end end diff --git a/spec/lib/gitlab/git/branch_spec.rb b/spec/lib/gitlab/git/branch_spec.rb index 0764e525ede..02ef7b92538 100644 --- a/spec/lib/gitlab/git/branch_spec.rb +++ b/spec/lib/gitlab/git/branch_spec.rb @@ -44,6 +44,7 @@ describe Gitlab::Git::Branch, :seed_helper do describe '#size' do subject { super().size } + it { is_expected.to eq(SeedRepo::Repo::BRANCHES.size) } end diff --git a/spec/lib/gitlab/git/commit_spec.rb b/spec/lib/gitlab/git/commit_spec.rb index 3f0e6b34291..23651e3d7f2 100644 --- a/spec/lib/gitlab/git/commit_spec.rb +++ b/spec/lib/gitlab/git/commit_spec.rb @@ -174,6 +174,7 @@ describe Gitlab::Git::Commit, :seed_helper do describe '#id' do subject { super().id } + it { is_expected.to eq(SeedRepo::LastCommit::ID) } end end @@ -183,6 +184,7 @@ describe Gitlab::Git::Commit, :seed_helper do describe '#id' do subject { super().id } + it { is_expected.to eq(SeedRepo::Commit::ID) } end end @@ -192,6 +194,7 @@ describe Gitlab::Git::Commit, :seed_helper do describe '#id' do subject { super().id } + it { is_expected.to eq(SeedRepo::BigCommit::ID) } end end @@ -544,11 +547,13 @@ describe Gitlab::Git::Commit, :seed_helper do describe '#id' do subject { super().id } + it { is_expected.to eq(sample_commit_hash[:id])} end describe '#message' do subject { super().message } + it { is_expected.to eq(sample_commit_hash[:message])} end end @@ -558,16 +563,19 @@ describe Gitlab::Git::Commit, :seed_helper do describe '#additions' do subject { super().additions } + it { is_expected.to eq(11) } end describe '#deletions' do subject { super().deletions } + it { is_expected.to eq(6) } end describe '#total' do subject { super().total } + it { is_expected.to eq(17) } end end @@ -596,6 +604,7 @@ describe Gitlab::Git::Commit, :seed_helper do describe '#keys' do subject { super().keys.sort } + it { is_expected.to match(sample_commit_hash.keys.sort) } end end diff --git a/spec/lib/gitlab/git/diff_collection_spec.rb b/spec/lib/gitlab/git/diff_collection_spec.rb index ded173c49ef..ce45d6e24ba 100644 --- a/spec/lib/gitlab/git/diff_collection_spec.rb +++ b/spec/lib/gitlab/git/diff_collection_spec.rb @@ -10,6 +10,7 @@ describe Gitlab::Git::DiffCollection, :seed_helper do expanded: expanded ) end + let(:iterator) { MutatingConstantIterator.new(file_count, fake_diff(line_length, line_count)) } let(:file_count) { 0 } let(:line_length) { 1 } @@ -21,6 +22,7 @@ describe Gitlab::Git::DiffCollection, :seed_helper do describe '#to_a' do subject { super().to_a } + it { is_expected.to be_kind_of ::Array } end @@ -52,16 +54,19 @@ describe Gitlab::Git::DiffCollection, :seed_helper do describe '#overflow?' do subject { super().overflow? } + it { is_expected.to be_falsey } end describe '#empty?' do subject { super().empty? } + it { is_expected.to be_falsey } end describe '#real_size' do subject { super().real_size } + it { is_expected.to eq('3') } end @@ -76,6 +81,7 @@ describe Gitlab::Git::DiffCollection, :seed_helper do describe '#line_count' do subject { super().line_count } + it { is_expected.to eq file_count * line_count } end @@ -84,16 +90,19 @@ describe Gitlab::Git::DiffCollection, :seed_helper do describe '#overflow?' do subject { super().overflow? } + it { is_expected.to be_falsey } end describe '#empty?' do subject { super().empty? } + it { is_expected.to be_falsey } end describe '#real_size' do subject { super().real_size } + it { is_expected.to eq('3') } end @@ -108,6 +117,7 @@ describe Gitlab::Git::DiffCollection, :seed_helper do describe '#line_count' do subject { super().line_count } + it { is_expected.to eq file_count * line_count } end end @@ -118,21 +128,25 @@ describe Gitlab::Git::DiffCollection, :seed_helper do describe '#overflow?' do subject { super().overflow? } + it { is_expected.to be_truthy } end describe '#empty?' do subject { super().empty? } + it { is_expected.to be_falsey } end describe '#real_size' do subject { super().real_size } + it { is_expected.to eq('0+') } end describe '#line_count' do subject { super().line_count } + it { is_expected.to eq 1000 } end @@ -143,21 +157,25 @@ describe Gitlab::Git::DiffCollection, :seed_helper do describe '#overflow?' do subject { super().overflow? } + it { is_expected.to be_falsey } end describe '#empty?' do subject { super().empty? } + it { is_expected.to be_falsey } end describe '#real_size' do subject { super().real_size } + it { is_expected.to eq('3') } end describe '#line_count' do subject { super().line_count } + it { is_expected.to eq file_count * line_count } end @@ -174,21 +192,25 @@ describe Gitlab::Git::DiffCollection, :seed_helper do describe '#overflow?' do subject { super().overflow? } + it { is_expected.to be_truthy } end describe '#empty?' do subject { super().empty? } + it { is_expected.to be_falsey } end describe '#real_size' do subject { super().real_size } + it { is_expected.to eq('10+') } end describe '#line_count' do subject { super().line_count } + it { is_expected.to eq 10 } end @@ -199,21 +221,25 @@ describe Gitlab::Git::DiffCollection, :seed_helper do describe '#overflow?' do subject { super().overflow? } + it { is_expected.to be_falsey } end describe '#empty?' do subject { super().empty? } + it { is_expected.to be_falsey } end describe '#real_size' do subject { super().real_size } + it { is_expected.to eq('11') } end describe '#line_count' do subject { super().line_count } + it { is_expected.to eq file_count * line_count } end @@ -226,21 +252,25 @@ describe Gitlab::Git::DiffCollection, :seed_helper do describe '#overflow?' do subject { super().overflow? } + it { is_expected.to be_truthy } end describe '#empty?' do subject { super().empty? } + it { is_expected.to be_falsey } end describe '#real_size' do subject { super().real_size } + it { is_expected.to eq('3+') } end describe '#line_count' do subject { super().line_count } + it { is_expected.to eq 120 } end @@ -251,21 +281,25 @@ describe Gitlab::Git::DiffCollection, :seed_helper do describe '#overflow?' do subject { super().overflow? } + it { is_expected.to be_falsey } end describe '#empty?' do subject { super().empty? } + it { is_expected.to be_falsey } end describe '#real_size' do subject { super().real_size } + it { is_expected.to eq('11') } end describe '#line_count' do subject { super().line_count } + it { is_expected.to eq file_count * line_count } end @@ -282,21 +316,25 @@ describe Gitlab::Git::DiffCollection, :seed_helper do describe '#overflow?' do subject { super().overflow? } + it { is_expected.to be_falsey } end describe '#empty?' do subject { super().empty? } + it { is_expected.to be_falsey } end describe '#real_size' do subject { super().real_size } + it { is_expected.to eq('10') } end describe '#line_count' do subject { super().line_count } + it { is_expected.to eq file_count * line_count } end @@ -310,21 +348,25 @@ describe Gitlab::Git::DiffCollection, :seed_helper do describe '#overflow?' do subject { super().overflow? } + it { is_expected.to be_truthy } end describe '#empty?' do subject { super().empty? } + it { is_expected.to be_falsey } end describe '#real_size' do subject { super().real_size } + it { is_expected.to eq('9+') } end describe '#line_count' do subject { super().line_count } + it { is_expected.to eq file_count * line_count } end @@ -335,21 +377,25 @@ describe Gitlab::Git::DiffCollection, :seed_helper do describe '#overflow?' do subject { super().overflow? } + it { is_expected.to be_falsey } end describe '#empty?' do subject { super().empty? } + it { is_expected.to be_falsey } end describe '#real_size' do subject { super().real_size } + it { is_expected.to eq('10') } end describe '#line_count' do subject { super().line_count } + it { is_expected.to eq file_count * line_count } end @@ -363,26 +409,31 @@ describe Gitlab::Git::DiffCollection, :seed_helper do describe '#overflow?' do subject { super().overflow? } + it { is_expected.to be_falsey } end describe '#empty?' do subject { super().empty? } + it { is_expected.to be_truthy } end describe '#size' do subject { super().size } + it { is_expected.to eq(0) } end describe '#real_size' do subject { super().real_size } + it { is_expected.to eq('0')} end describe '#line_count' do subject { super().line_count } + it { is_expected.to eq 0 } end end diff --git a/spec/lib/gitlab/git/repository_spec.rb b/spec/lib/gitlab/git/repository_spec.rb index 04a648a0da0..44c41da7560 100644 --- a/spec/lib/gitlab/git/repository_spec.rb +++ b/spec/lib/gitlab/git/repository_spec.rb @@ -93,6 +93,7 @@ describe Gitlab::Git::Repository, :seed_helper do describe '#last' do subject { super().last } + it { is_expected.to eq("v1.2.1") } end it { is_expected.to include("v1.0.0") } @@ -215,11 +216,13 @@ describe Gitlab::Git::Repository, :seed_helper do describe '#first' do subject { super().first } + it { is_expected.to eq('feature') } end describe '#last' do subject { super().last } + it { is_expected.to eq('v1.2.1') } end end diff --git a/spec/lib/gitlab/gitlab_import/client_spec.rb b/spec/lib/gitlab/gitlab_import/client_spec.rb index 22ad88e28cb..0f1745fcc02 100644 --- a/spec/lib/gitlab/gitlab_import/client_spec.rb +++ b/spec/lib/gitlab/gitlab_import/client_spec.rb @@ -52,6 +52,7 @@ describe Gitlab::GitlabImport::Client do describe '#projects' do subject(:method) { :projects } + let(:args) { [] } let(:element_list) { build_list(:project, 2) } @@ -67,6 +68,7 @@ describe Gitlab::GitlabImport::Client do describe '#issues' do subject(:method) { :issues } + let(:args) { [1] } let(:element_list) { build_list(:issue, 2) } @@ -82,6 +84,7 @@ describe Gitlab::GitlabImport::Client do describe '#issue_comments' do subject(:method) { :issue_comments } + let(:args) { [1, 1] } let(:element_list) { build_list(:note_on_issue, 2) } diff --git a/spec/lib/gitlab/graphs/commits_spec.rb b/spec/lib/gitlab/graphs/commits_spec.rb index 530d4a981bf..09654e0439e 100644 --- a/spec/lib/gitlab/graphs/commits_spec.rb +++ b/spec/lib/gitlab/graphs/commits_spec.rb @@ -11,12 +11,14 @@ describe Gitlab::Graphs::Commits do describe '#commit_per_day' do context 'when range is only commits from today' do subject { described_class.new([commit2, commit1]).commit_per_day } + it { is_expected.to eq 2 } end end context 'when range is only commits from today' do subject { described_class.new([commit2, commit1]) } + describe '#commit_per_day' do it { expect(subject.commit_per_day).to eq 2 } end @@ -28,6 +30,7 @@ describe Gitlab::Graphs::Commits do context 'with commits from yesterday and today' do subject { described_class.new([commit2, commit1_yesterday]) } + describe '#commit_per_day' do it { expect(subject.commit_per_day).to eq 1.0 } end diff --git a/spec/lib/gitlab/health_checks/gitaly_check_spec.rb b/spec/lib/gitlab/health_checks/gitaly_check_spec.rb index 99d10312c15..36e2fd04aeb 100644 --- a/spec/lib/gitlab/health_checks/gitaly_check_spec.rb +++ b/spec/lib/gitlab/health_checks/gitaly_check_spec.rb @@ -30,6 +30,7 @@ describe Gitlab::HealthChecks::GitalyCheck do describe '#metrics' do subject { described_class.metrics } + let(:server) { double(storage: 'default', read_writeable?: up) } before do diff --git a/spec/lib/gitlab/health_checks/simple_check_shared.rb b/spec/lib/gitlab/health_checks/simple_check_shared.rb index c3d55a11909..03a7cf249cf 100644 --- a/spec/lib/gitlab/health_checks/simple_check_shared.rb +++ b/spec/lib/gitlab/health_checks/simple_check_shared.rb @@ -1,6 +1,7 @@ shared_context 'simple_check' do |metrics_prefix, check_name, success_result| describe '#metrics' do subject { described_class.metrics } + context 'Check is passing' do before do allow(described_class).to receive(:check).and_return success_result @@ -34,6 +35,7 @@ shared_context 'simple_check' do |metrics_prefix, check_name, success_result| describe '#readiness' do subject { described_class.readiness } + context 'Check returns ok' do before do allow(described_class).to receive(:check).and_return success_result diff --git a/spec/lib/gitlab/hook_data/issue_builder_spec.rb b/spec/lib/gitlab/hook_data/issue_builder_spec.rb index 6013fb78bc7..ebd7feb0055 100644 --- a/spec/lib/gitlab/hook_data/issue_builder_spec.rb +++ b/spec/lib/gitlab/hook_data/issue_builder_spec.rb @@ -26,7 +26,7 @@ describe Gitlab::HookData::IssueBuilder do duplicated_to_id project_id relative_position - state + state_id time_estimate title updated_at @@ -41,6 +41,7 @@ describe Gitlab::HookData::IssueBuilder do expect(data).to include(:human_time_estimate) expect(data).to include(:human_total_time_spent) expect(data).to include(:assignee_ids) + expect(data).to include(:state) expect(data).to include('labels' => [label.hook_attrs]) end diff --git a/spec/lib/gitlab/import_export/all_models.yml b/spec/lib/gitlab/import_export/all_models.yml index 1efd7bf5c71..4fd61383c6b 100644 --- a/spec/lib/gitlab/import_export/all_models.yml +++ b/spec/lib/gitlab/import_export/all_models.yml @@ -25,6 +25,7 @@ issues: - epic - designs - design_versions +- description_versions - prometheus_alerts - prometheus_alert_events - self_managed_prometheus_alert_events @@ -132,6 +133,7 @@ merge_requests: - blocks_as_blockee - blocking_merge_requests - blocked_merge_requests +- description_versions external_pull_requests: - project merge_request_diff: diff --git a/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb b/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb index 218031784cb..676973ff5e7 100644 --- a/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb +++ b/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb @@ -434,6 +434,11 @@ describe Gitlab::ImportExport::ProjectTreeRestorer do labels: 0, milestones: 0, first_issue_labels: 1 + + it 'restores issue states' do + expect(project.issues.with_state(:closed).count).to eq(1) + expect(project.issues.with_state(:opened).count).to eq(1) + end end context 'with existing group models' do diff --git a/spec/lib/gitlab/import_export/relation_factory_spec.rb b/spec/lib/gitlab/import_export/relation_factory_spec.rb index 51b2fd06b46..a23e68a8f00 100644 --- a/spec/lib/gitlab/import_export/relation_factory_spec.rb +++ b/spec/lib/gitlab/import_export/relation_factory_spec.rb @@ -85,7 +85,7 @@ describe Gitlab::ImportExport::RelationFactory do class FooModel include ActiveModel::Model - def initialize(params) + def initialize(params = {}) params.each { |key, value| send("#{key}=", value) } end diff --git a/spec/lib/gitlab/metrics/samplers/puma_sampler_spec.rb b/spec/lib/gitlab/metrics/samplers/puma_sampler_spec.rb index b8add3c1324..1097d26c320 100644 --- a/spec/lib/gitlab/metrics/samplers/puma_sampler_spec.rb +++ b/spec/lib/gitlab/metrics/samplers/puma_sampler_spec.rb @@ -4,6 +4,7 @@ require 'spec_helper' describe Gitlab::Metrics::Samplers::PumaSampler do subject { described_class.new(5) } + let(:null_metric) { double('null_metric', set: nil, observe: nil) } before do diff --git a/spec/lib/gitlab/phabricator_import/worker_state_spec.rb b/spec/lib/gitlab/phabricator_import/worker_state_spec.rb index b6f2524a9d0..51514dd0ffd 100644 --- a/spec/lib/gitlab/phabricator_import/worker_state_spec.rb +++ b/spec/lib/gitlab/phabricator_import/worker_state_spec.rb @@ -4,6 +4,7 @@ require 'spec_helper' describe Gitlab::PhabricatorImport::WorkerState, :clean_gitlab_redis_shared_state do subject(:state) { described_class.new('weird-project-id') } + let(:key) { 'phabricator-import/jobs/project-weird-project-id/job-count' } describe '#add_job' do diff --git a/spec/lib/gitlab/reference_extractor_spec.rb b/spec/lib/gitlab/reference_extractor_spec.rb index f6ace0d8bf5..6bc9b6365d1 100644 --- a/spec/lib/gitlab/reference_extractor_spec.rb +++ b/spec/lib/gitlab/reference_extractor_spec.rb @@ -259,6 +259,7 @@ describe Gitlab::ReferenceExtractor do describe '.references_pattern' do subject { described_class.references_pattern } + it { is_expected.to be_kind_of Regexp } end diff --git a/spec/lib/gitlab/request_context_spec.rb b/spec/lib/gitlab/request_context_spec.rb index a744f48da1f..cde12d4b310 100644 --- a/spec/lib/gitlab/request_context_spec.rb +++ b/spec/lib/gitlab/request_context_spec.rb @@ -5,6 +5,7 @@ require 'spec_helper' describe Gitlab::RequestContext do describe '#client_ip' do subject { described_class.client_ip } + let(:app) { -> (env) {} } let(:env) { Hash.new } diff --git a/spec/lib/gitlab/shell_spec.rb b/spec/lib/gitlab/shell_spec.rb index ff9e31ec346..a17e9a31212 100644 --- a/spec/lib/gitlab/shell_spec.rb +++ b/spec/lib/gitlab/shell_spec.rb @@ -396,6 +396,7 @@ describe Gitlab::Shell do describe 'namespace actions' do subject { described_class.new } + let(:storage) { Gitlab.config.repositories.storages.keys.first } describe '#add_namespace' do diff --git a/spec/lib/gitlab/sidekiq_middleware/memory_killer_spec.rb b/spec/lib/gitlab/sidekiq_middleware/memory_killer_spec.rb index bf3bc8e1add..b5be43ec96c 100644 --- a/spec/lib/gitlab/sidekiq_middleware/memory_killer_spec.rb +++ b/spec/lib/gitlab/sidekiq_middleware/memory_killer_spec.rb @@ -4,6 +4,7 @@ require 'spec_helper' describe Gitlab::SidekiqMiddleware::MemoryKiller do subject { described_class.new } + let(:pid) { 999 } let(:worker) { double(:worker, class: ProjectCacheWorker) } diff --git a/spec/lib/gitlab/utils/override_spec.rb b/spec/lib/gitlab/utils/override_spec.rb index 5855c4374a9..e2776efac85 100644 --- a/spec/lib/gitlab/utils/override_spec.rb +++ b/spec/lib/gitlab/utils/override_spec.rb @@ -151,6 +151,7 @@ describe Gitlab::Utils::Override do context 'when subject is a module, and class is prepending it' do subject { extension } + let(:klass) { prepending_class } it_behaves_like 'checking as intended' @@ -158,6 +159,7 @@ describe Gitlab::Utils::Override do context 'when subject is a module, and class is including it' do subject { extension } + let(:klass) { including_class } it_behaves_like 'checking as intended, nothing was overridden' @@ -177,6 +179,7 @@ describe Gitlab::Utils::Override do context 'when subject is a module, and class is prepending it' do subject { extension } + let(:klass) { prepending_class } it_behaves_like 'nothing happened' @@ -184,6 +187,7 @@ describe Gitlab::Utils::Override do context 'when subject is a module, and class is including it' do subject { extension } + let(:klass) { including_class } it 'does not complain when it is overriding something' do @@ -215,6 +219,7 @@ describe Gitlab::Utils::Override do context 'when subject is a module, and class is prepending it' do subject { extension } + let(:klass) { prepending_class_methods } it_behaves_like 'checking as intended' @@ -222,6 +227,7 @@ describe Gitlab::Utils::Override do context 'when subject is a module, and class is extending it' do subject { extension } + let(:klass) { extending_class_methods } it_behaves_like 'checking as intended, nothing was overridden' diff --git a/spec/lib/google_api/cloud_platform/client_spec.rb b/spec/lib/google_api/cloud_platform/client_spec.rb index 91b076c31d6..0f7f57095df 100644 --- a/spec/lib/google_api/cloud_platform/client_spec.rb +++ b/spec/lib/google_api/cloud_platform/client_spec.rb @@ -54,6 +54,7 @@ describe GoogleApi::CloudPlatform::Client do describe '#projects_zones_clusters_get' do subject { client.projects_zones_clusters_get(spy, spy, spy) } + let(:gke_cluster) { double } before do @@ -160,6 +161,7 @@ describe GoogleApi::CloudPlatform::Client do describe '#projects_zones_operations' do subject { client.projects_zones_operations(spy, spy, spy) } + let(:operation) { double } before do diff --git a/spec/lib/json_web_token/token_spec.rb b/spec/lib/json_web_token/token_spec.rb index 916d11ce0ed..ca587a6ebcd 100644 --- a/spec/lib/json_web_token/token_spec.rb +++ b/spec/lib/json_web_token/token_spec.rb @@ -16,6 +16,7 @@ describe JSONWebToken::Token do context 'embeds default payload' do subject { token.payload } + let(:default) { token.send(:default_payload) } it { is_expected.to include(default) } diff --git a/spec/lib/omni_auth/strategies/jwt_spec.rb b/spec/lib/omni_auth/strategies/jwt_spec.rb index bdf3ea6be98..a8c565aa705 100644 --- a/spec/lib/omni_auth/strategies/jwt_spec.rb +++ b/spec/lib/omni_auth/strategies/jwt_spec.rb @@ -8,6 +8,7 @@ describe OmniAuth::Strategies::Jwt do context '#decoded' do subject { described_class.new({}) } + let(:timestamp) { Time.now.to_i } let(:jwt_config) { Devise.omniauth_configs[:jwt] } let(:claims) do diff --git a/spec/lib/uploaded_file_spec.rb b/spec/lib/uploaded_file_spec.rb index 2cb4727bd4b..2bbbd67b13c 100644 --- a/spec/lib/uploaded_file_spec.rb +++ b/spec/lib/uploaded_file_spec.rb @@ -72,16 +72,6 @@ describe UploadedFile do end end - context 'when only remote id is specified' do - let(:params) do - { 'file.remote_id' => 'remote_id' } - end - - it "raises an error" do - expect { subject }.to raise_error(UploadedFile::InvalidPathError, /file is invalid/) - end - end - context 'when verifying allowed paths' do let(:params) do { 'file.path' => temp_file.path } @@ -120,6 +110,52 @@ describe UploadedFile do end end + describe '.initialize' do + context 'when no size is provided' do + it 'determine size from local path' do + file = described_class.new(temp_file.path) + + expect(file.size).to eq(temp_file.size) + end + + it 'raises an exception if is a remote file' do + expect do + described_class.new(nil, remote_id: 'id') + end.to raise_error(UploadedFile::UnknownSizeError, 'Unable to determine file size') + end + end + + context 'when size is a number' do + let_it_be(:size) { 1.gigabyte } + + it 'is overridden by the size of the local file' do + file = described_class.new(temp_file.path, size: size) + + expect(file.size).to eq(temp_file.size) + end + + it 'is respected if is a remote file' do + file = described_class.new(nil, remote_id: 'id', size: size) + + expect(file.size).to eq(size) + end + end + + context 'when size is a string' do + it 'is converted to a number' do + file = described_class.new(nil, remote_id: 'id', size: '1') + + expect(file.size).to eq(1) + end + + it 'raises an exception if does not represent a number' do + expect do + described_class.new(nil, remote_id: 'id', size: 'not a number') + end.to raise_error(UploadedFile::UnknownSizeError, 'Unable to determine file size') + end + end + end + describe '#sanitize_filename' do it { expect(described_class.new(temp_file.path).sanitize_filename('spaced name')).to eq('spaced_name') } it { expect(described_class.new(temp_file.path).sanitize_filename('#$%^&')).to eq('_____') } diff --git a/spec/models/ci/build_spec.rb b/spec/models/ci/build_spec.rb index da8ea00b2f6..058305bc04e 100644 --- a/spec/models/ci/build_spec.rb +++ b/spec/models/ci/build_spec.rb @@ -574,6 +574,7 @@ describe Ci::Build do describe '#artifacts_metadata?' do subject { build.artifacts_metadata? } + context 'artifacts metadata does not exist' do it { is_expected.to be_falsy } end @@ -586,6 +587,7 @@ describe Ci::Build do describe '#artifacts_expire_in' do subject { build.artifacts_expire_in } + it { is_expected.to be_nil } context 'when artifacts_expire_at is specified' do @@ -1265,6 +1267,7 @@ describe Ci::Build do describe '#erasable?' do subject { build.erasable? } + it { is_expected.to be_truthy } end diff --git a/spec/models/ci/runner_spec.rb b/spec/models/ci/runner_spec.rb index 70ff3cf5dc4..ac438f7d473 100644 --- a/spec/models/ci/runner_spec.rb +++ b/spec/models/ci/runner_spec.rb @@ -686,11 +686,13 @@ describe Ci::Runner do describe '#has_tags?' do context 'when runner has tags' do subject { create(:ci_runner, tag_list: ['tag']) } + it { is_expected.to have_tags } end context 'when runner does not have tags' do subject { create(:ci_runner, tag_list: []) } + it { is_expected.not_to have_tags } end end diff --git a/spec/models/concerns/noteable_spec.rb b/spec/models/concerns/noteable_spec.rb index 929b5f52c7c..f823ac0165f 100644 --- a/spec/models/concerns/noteable_spec.rb +++ b/spec/models/concerns/noteable_spec.rb @@ -6,6 +6,7 @@ describe Noteable do let!(:active_diff_note1) { create(:diff_note_on_merge_request) } let(:project) { active_diff_note1.project } subject { active_diff_note1.noteable } + let!(:active_diff_note2) { create(:diff_note_on_merge_request, project: project, noteable: subject, in_reply_to: active_diff_note1) } let!(:active_diff_note3) { create(:diff_note_on_merge_request, project: project, noteable: subject, position: active_position2) } let!(:outdated_diff_note1) { create(:diff_note_on_merge_request, project: project, noteable: subject, position: outdated_position) } diff --git a/spec/models/concerns/token_authenticatable_spec.rb b/spec/models/concerns/token_authenticatable_spec.rb index 51e28974ae0..43b894b5957 100644 --- a/spec/models/concerns/token_authenticatable_spec.rb +++ b/spec/models/concerns/token_authenticatable_spec.rb @@ -17,6 +17,7 @@ describe User, 'TokenAuthenticatable' do describe 'ensures authentication token' do subject { create(:user).send(token_field) } + it { is_expected.to be_a String } end end diff --git a/spec/models/deploy_keys_project_spec.rb b/spec/models/deploy_keys_project_spec.rb index c137444763b..1dbae78a01d 100644 --- a/spec/models/deploy_keys_project_spec.rb +++ b/spec/models/deploy_keys_project_spec.rb @@ -16,6 +16,7 @@ describe DeployKeysProject do describe "Destroying" do let(:project) { create(:project) } subject { create(:deploy_keys_project, project: project) } + let(:deploy_key) { subject.deploy_key } context "when the deploy key is only used by this project" do diff --git a/spec/models/description_version_spec.rb b/spec/models/description_version_spec.rb new file mode 100644 index 00000000000..5ec34c0cde4 --- /dev/null +++ b/spec/models/description_version_spec.rb @@ -0,0 +1,29 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe DescriptionVersion do + describe 'associations' do + it { is_expected.to belong_to :issue } + it { is_expected.to belong_to :merge_request } + end + + describe 'validations' do + describe 'exactly_one_issuable' do + using RSpec::Parameterized::TableSyntax + + subject { described_class.new(issue_id: issue_id, merge_request_id: merge_request_id).valid? } + + where(:issue_id, :merge_request_id, :valid?) do + nil | 1 | true + 1 | nil | true + nil | nil | false + 1 | 1 | false + end + + with_them do + it { is_expected.to eq(valid?) } + end + end + end +end diff --git a/spec/models/event_spec.rb b/spec/models/event_spec.rb index 62663c247d1..ff2e1aa047e 100644 --- a/spec/models/event_spec.rb +++ b/spec/models/event_spec.rb @@ -100,26 +100,31 @@ describe Event do describe '#membership_changed?' do context "created" do subject { build(:event, :created).membership_changed? } + it { is_expected.to be_falsey } end context "updated" do subject { build(:event, :updated).membership_changed? } + it { is_expected.to be_falsey } end context "expired" do subject { build(:event, :expired).membership_changed? } + it { is_expected.to be_truthy } end context "left" do subject { build(:event, :left).membership_changed? } + it { is_expected.to be_truthy } end context "joined" do subject { build(:event, :joined).membership_changed? } + it { is_expected.to be_truthy } end end diff --git a/spec/models/gpg_signature_spec.rb b/spec/models/gpg_signature_spec.rb index dd18c8842ab..a780b8bfdf5 100644 --- a/spec/models/gpg_signature_spec.rb +++ b/spec/models/gpg_signature_spec.rb @@ -20,6 +20,7 @@ RSpec.describe GpgSignature do describe 'validation' do subject { described_class.new } + it { is_expected.to validate_presence_of(:commit_sha) } it { is_expected.to validate_presence_of(:project_id) } it { is_expected.to validate_presence_of(:gpg_key_primary_keyid) } diff --git a/spec/models/issue_spec.rb b/spec/models/issue_spec.rb index 9c58d307c4c..18a1a30eee5 100644 --- a/spec/models/issue_spec.rb +++ b/spec/models/issue_spec.rb @@ -138,7 +138,10 @@ describe Issue do end it 'changes the state to closed' do - expect { issue.close }.to change { issue.state }.from('opened').to('closed') + open_state = described_class.available_states[:opened] + closed_state = described_class.available_states[:closed] + + expect { issue.close }.to change { issue.state_id }.from(open_state).to(closed_state) end end @@ -155,7 +158,7 @@ describe Issue do end it 'changes the state to opened' do - expect { issue.reopen }.to change { issue.state }.from('closed').to('opened') + expect { issue.reopen }.to change { issue.state_id }.from(described_class.available_states[:closed]).to(described_class.available_states[:opened]) end end @@ -277,6 +280,7 @@ describe Issue do context 'checking destination project also' do subject { issue.can_move?(user, to_project) } + let(:to_project) { create(:project) } context 'destination project allowed' do @@ -899,4 +903,6 @@ describe Issue do let(:default_params) { { project: project } } end end + + it_behaves_like 'versioned description' end diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb index b146c767f82..ad79bee8801 100644 --- a/spec/models/merge_request_spec.rb +++ b/spec/models/merge_request_spec.rb @@ -470,7 +470,7 @@ describe MergeRequest do commit = double('commit1', safe_message: "Fixes #{issue.to_reference}") allow(subject).to receive(:commits).and_return([commit]) - allow(subject).to receive(:state).and_return("closed") + allow(subject).to receive(:state_id).and_return(described_class.available_states[:closed]) expect { subject.cache_merge_request_closes_issues!(subject.author) }.not_to change(subject.merge_requests_closing_issues, :count) end @@ -479,7 +479,7 @@ describe MergeRequest do issue = create :issue, project: subject.project commit = double('commit1', safe_message: "Fixes #{issue.to_reference}") allow(subject).to receive(:commits).and_return([commit]) - allow(subject).to receive(:state).and_return("merged") + allow(subject).to receive(:state_id).and_return(described_class.available_states[:merged]) expect { subject.cache_merge_request_closes_issues!(subject.author) }.not_to change(subject.merge_requests_closing_issues, :count) end @@ -541,6 +541,7 @@ describe MergeRequest do context 'with diffs' do subject { create(:merge_request, :with_diffs) } + it 'returns the sha of the source branch last commit' do expect(subject.source_branch_sha).to eq(last_branch_commit.sha) end @@ -548,6 +549,7 @@ describe MergeRequest do context 'without diffs' do subject { create(:merge_request, :without_diffs) } + it 'returns the sha of the source branch last commit' do expect(subject.source_branch_sha).to eq(last_branch_commit.sha) end @@ -570,6 +572,7 @@ describe MergeRequest do context 'when the merge request is being created' do subject { build(:merge_request, source_branch: nil, compare_commits: []) } + it 'returns nil' do expect(subject.source_branch_sha).to be_nil end @@ -2072,7 +2075,7 @@ describe MergeRequest do end it 'refuses to enqueue a job if the MR is not open' do - merge_request.update_column(:state, 'foo') + merge_request.update_column(:state_id, 5) expect(RebaseWorker).not_to receive(:perform_async) @@ -2495,6 +2498,7 @@ describe MergeRequest do describe "#diff_refs" do context "with diffs" do subject { create(:merge_request, :with_diffs) } + let(:expected_diff_refs) do Gitlab::Diff::DiffRefs.new( base_sha: subject.merge_request_diff.base_commit_sha, @@ -2567,32 +2571,32 @@ describe MergeRequest do describe '#merge_ongoing?' do it 'returns true when the merge request is locked' do - merge_request = build_stubbed(:merge_request, state: :locked) + merge_request = build_stubbed(:merge_request, state_id: described_class.available_states[:locked]) expect(merge_request.merge_ongoing?).to be(true) end it 'returns true when merge_id, MR is not merged and it has no running job' do - merge_request = build_stubbed(:merge_request, state: :open, merge_jid: 'foo') + merge_request = build_stubbed(:merge_request, state_id: described_class.available_states[:opened], merge_jid: 'foo') allow(Gitlab::SidekiqStatus).to receive(:running?).with('foo') { true } expect(merge_request.merge_ongoing?).to be(true) end it 'returns false when merge_jid is nil' do - merge_request = build_stubbed(:merge_request, state: :open, merge_jid: nil) + merge_request = build_stubbed(:merge_request, state_id: described_class.available_states[:opened], merge_jid: nil) expect(merge_request.merge_ongoing?).to be(false) end it 'returns false if MR is merged' do - merge_request = build_stubbed(:merge_request, state: :merged, merge_jid: 'foo') + merge_request = build_stubbed(:merge_request, state_id: described_class.available_states[:merged], merge_jid: 'foo') expect(merge_request.merge_ongoing?).to be(false) end it 'returns false if there is no merge job running' do - merge_request = build_stubbed(:merge_request, state: :open, merge_jid: 'foo') + merge_request = build_stubbed(:merge_request, state_id: described_class.available_states[:opened], merge_jid: 'foo') allow(Gitlab::SidekiqStatus).to receive(:running?).with('foo') { false } expect(merge_request.merge_ongoing?).to be(false) @@ -2726,7 +2730,7 @@ describe MergeRequest do context 'closed MR' do before do - merge_request.update_attribute(:state, :closed) + merge_request.update_attribute(:state_id, described_class.available_states[:closed]) end it 'is not mergeable' do @@ -2840,6 +2844,7 @@ describe MergeRequest do describe '#merge_request_diff_for' do subject { create(:merge_request, importing: true) } + let!(:merge_request_diff1) { subject.merge_request_diffs.create(head_commit_sha: '6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9') } let!(:merge_request_diff2) { subject.merge_request_diffs.create(head_commit_sha: nil) } let!(:merge_request_diff3) { subject.merge_request_diffs.create(head_commit_sha: '5937ac0a7beb003549fc5fd26fc247adbce4a52e') } @@ -2870,6 +2875,7 @@ describe MergeRequest do describe '#version_params_for' do subject { create(:merge_request, importing: true) } + let(:project) { subject.project } let!(:merge_request_diff1) { subject.merge_request_diffs.create(head_commit_sha: '6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9') } let!(:merge_request_diff2) { subject.merge_request_diffs.create(head_commit_sha: nil) } @@ -3331,4 +3337,6 @@ describe MergeRequest do it { expect(query).to contain_exactly(merge_request1, merge_request2) } end + + it_behaves_like 'versioned description' end diff --git a/spec/models/note_spec.rb b/spec/models/note_spec.rb index 8a47b8c206b..4c320b4b145 100644 --- a/spec/models/note_spec.rb +++ b/spec/models/note_spec.rb @@ -55,11 +55,13 @@ describe Note do context 'when noteable and note project are the same' do subject { create(:note) } + it { is_expected.to be_valid } end context 'when project is missing for a project related note' do subject { build(:note, project: nil, noteable: build_stubbed(:issue)) } + it { is_expected.to be_invalid } end @@ -741,6 +743,7 @@ describe Note do describe '#to_discussion' do subject { create(:discussion_note_on_merge_request) } + let!(:note2) { create(:discussion_note_on_merge_request, project: subject.project, noteable: subject.noteable, in_reply_to: subject) } it "returns a discussion with just this note" do @@ -808,6 +811,7 @@ describe Note do context 'for a note' do context 'when part of a discussion' do subject { create(:discussion_note_on_issue) } + let(:note) { create(:discussion_note_on_issue, in_reply_to: subject) } it 'checks if the note is in reply to the other discussion' do @@ -821,6 +825,7 @@ describe Note do context 'when not part of a discussion' do subject { create(:note) } + let(:note) { create(:note, in_reply_to: subject) } it 'checks if the note is in reply to the other noteable' do @@ -835,6 +840,7 @@ describe Note do context 'for a discussion' do context 'when part of the same discussion' do subject { create(:diff_note_on_merge_request) } + let(:note) { create(:diff_note_on_merge_request, in_reply_to: subject) } it 'returns true' do @@ -844,6 +850,7 @@ describe Note do context 'when not part of the same discussion' do subject { create(:diff_note_on_merge_request) } + let(:note) { create(:diff_note_on_merge_request) } it 'returns false' do @@ -855,6 +862,7 @@ describe Note do context 'for a noteable' do context 'when a comment on the same noteable' do subject { create(:note) } + let(:note) { create(:note, in_reply_to: subject) } it 'returns true' do @@ -864,6 +872,7 @@ describe Note do context 'when not a comment on the same noteable' do subject { create(:note) } + let(:note) { create(:note) } it 'returns false' do @@ -887,6 +896,7 @@ describe Note do context 'when not part of a discussion' do subject { create(:note) } + let(:note) { create(:note, in_reply_to: subject) } it 'returns the noteable' do diff --git a/spec/models/pages_domain_spec.rb b/spec/models/pages_domain_spec.rb index 2e7b2b88432..4b65bf032d1 100644 --- a/spec/models/pages_domain_spec.rb +++ b/spec/models/pages_domain_spec.rb @@ -293,11 +293,13 @@ describe PagesDomain do describe "#https?" do context "when a certificate is present" do subject { build(:pages_domain) } + it { is_expected.to be_https } end context "when no certificate is present" do subject { build(:pages_domain, :without_certificate) } + it { is_expected.not_to be_https } end end diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index a5048d78363..9f3313e67b5 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -5014,6 +5014,7 @@ describe Project do describe '#git_objects_poolable?' do subject { project } + context 'when not using hashed storage' do let(:project) { create(:project, :legacy_storage, :public, :repository) } diff --git a/spec/models/project_wiki_spec.rb b/spec/models/project_wiki_spec.rb index b089544c810..31d1d1fd7d1 100644 --- a/spec/models/project_wiki_spec.rb +++ b/spec/models/project_wiki_spec.rb @@ -109,6 +109,7 @@ describe ProjectWiki do context "when the wiki repository is empty" do describe '#empty?' do subject { super().empty? } + it { is_expected.to be_truthy } end end @@ -121,6 +122,7 @@ describe ProjectWiki do describe '#empty?' do subject { super().empty? } + it { is_expected.to be_falsey } it 'only instantiates a Wiki page once' do diff --git a/spec/models/resource_label_event_spec.rb b/spec/models/resource_label_event_spec.rb index f4023dcb95a..f51041c9ddc 100644 --- a/spec/models/resource_label_event_spec.rb +++ b/spec/models/resource_label_event_spec.rb @@ -4,6 +4,7 @@ require 'spec_helper' RSpec.describe ResourceLabelEvent, type: :model do subject { build(:resource_label_event, issue: issue) } + let(:issue) { create(:issue) } let(:merge_request) { create(:merge_request) } diff --git a/spec/models/system_note_metadata_spec.rb b/spec/models/system_note_metadata_spec.rb index bcd3c03f947..801f139355b 100644 --- a/spec/models/system_note_metadata_spec.rb +++ b/spec/models/system_note_metadata_spec.rb @@ -5,6 +5,7 @@ require 'spec_helper' describe SystemNoteMetadata do describe 'associations' do it { is_expected.to belong_to(:note) } + it { is_expected.to belong_to(:description_version) } end describe 'validation' do diff --git a/spec/models/timelog_spec.rb b/spec/models/timelog_spec.rb index 28fc82f2a32..7321a458817 100644 --- a/spec/models/timelog_spec.rb +++ b/spec/models/timelog_spec.rb @@ -4,6 +4,7 @@ require 'spec_helper' RSpec.describe Timelog do subject { build(:timelog) } + let(:issue) { create(:issue) } let(:merge_request) { create(:merge_request) } diff --git a/spec/models/user_interacted_project_spec.rb b/spec/models/user_interacted_project_spec.rb index 47d919c1d12..b96ff08e22d 100644 --- a/spec/models/user_interacted_project_spec.rb +++ b/spec/models/user_interacted_project_spec.rb @@ -5,6 +5,7 @@ require 'spec_helper' describe UserInteractedProject do describe '.track' do subject { described_class.track(event) } + let(:event) { build(:event) } Event::ACTIONS.each do |action| diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index 24e66fe14c3..240c917e7cf 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -2170,6 +2170,7 @@ describe User do describe "#contributed_projects" do subject { create(:user) } + let!(:project1) { create(:project) } let!(:project2) { fork_project(project3) } let!(:project3) { create(:project) } diff --git a/spec/policies/identity_provider_policy_spec.rb b/spec/policies/identity_provider_policy_spec.rb index 2520469d4e7..52b6d2c89ba 100644 --- a/spec/policies/identity_provider_policy_spec.rb +++ b/spec/policies/identity_provider_policy_spec.rb @@ -4,6 +4,7 @@ require 'spec_helper' describe IdentityProviderPolicy do subject(:policy) { described_class.new(user, provider) } + let(:user) { User.new } let(:provider) { :a_provider } diff --git a/spec/policies/merge_request_policy_spec.rb b/spec/policies/merge_request_policy_spec.rb index 87205f56589..af4c9703eb4 100644 --- a/spec/policies/merge_request_policy_spec.rb +++ b/spec/policies/merge_request_policy_spec.rb @@ -53,21 +53,25 @@ describe MergeRequestPolicy do describe 'the author' do subject { author } + it_behaves_like 'a denied user' end describe 'a guest' do subject { guest } + it_behaves_like 'a denied user' end describe 'a developer' do subject { developer } + it_behaves_like 'a denied user' end describe 'any other user' do subject { non_team_member } + it_behaves_like 'a denied user' end end @@ -82,11 +86,13 @@ describe MergeRequestPolicy do describe 'a non-team-member' do subject { non_team_member } + it_behaves_like 'a denied user' end describe 'a developer' do subject { developer } + it_behaves_like 'a user with access' end end diff --git a/spec/presenters/conversational_development_index/metric_presenter_spec.rb b/spec/presenters/conversational_development_index/metric_presenter_spec.rb index 81eb05a9a6b..b8b68a676e6 100644 --- a/spec/presenters/conversational_development_index/metric_presenter_spec.rb +++ b/spec/presenters/conversational_development_index/metric_presenter_spec.rb @@ -2,6 +2,7 @@ require 'spec_helper' describe ConversationalDevelopmentIndex::MetricPresenter do subject { described_class.new(metric) } + let(:metric) { build(:conversational_development_index_metric) } describe '#cards' do diff --git a/spec/requests/api/commit_statuses_spec.rb b/spec/requests/api/commit_statuses_spec.rb index 1be8883bd3c..6cb02ba2f6b 100644 --- a/spec/requests/api/commit_statuses_spec.rb +++ b/spec/requests/api/commit_statuses_spec.rb @@ -125,25 +125,55 @@ describe API::CommitStatuses do let(:post_url) { "/projects/#{project.id}/statuses/#{sha}" } context 'developer user' do - %w[pending running success failed canceled].each do |status| - context "for #{status}" do - context 'uses only required parameters' do - it 'creates commit status' do - post api(post_url, developer), params: { state: status } + context 'uses only required parameters' do + %w[pending running success failed canceled].each do |status| + context "for #{status}" do + context 'when pipeline for sha does not exists' do + it 'creates commit status' do + post api(post_url, developer), params: { state: status } + + expect(response).to have_gitlab_http_status(201) + expect(json_response['sha']).to eq(commit.id) + expect(json_response['status']).to eq(status) + expect(json_response['name']).to eq('default') + expect(json_response['ref']).not_to be_empty + expect(json_response['target_url']).to be_nil + expect(json_response['description']).to be_nil + + if status == 'failed' + expect(CommitStatus.find(json_response['id'])).to be_api_failure + end + end + end + end + end + + context 'when pipeline already exists for the specified sha' do + let!(:pipeline) { create(:ci_pipeline, project: project, sha: sha, ref: 'ref') } + let(:params) { { state: 'pending' } } + + shared_examples_for 'creates a commit status for the existing pipeline' do + it do + expect do + post api(post_url, developer), params: params + end.not_to change { Ci::Pipeline.count } + + job = pipeline.statuses.find_by_name(json_response['name']) expect(response).to have_gitlab_http_status(201) - expect(json_response['sha']).to eq(commit.id) - expect(json_response['status']).to eq(status) - expect(json_response['name']).to eq('default') - expect(json_response['ref']).not_to be_empty - expect(json_response['target_url']).to be_nil - expect(json_response['description']).to be_nil - - if status == 'failed' - expect(CommitStatus.find(json_response['id'])).to be_api_failure - end + expect(job.status).to eq('pending') end end + + it_behaves_like 'creates a commit status for the existing pipeline' + + context 'with pipeline for merge request' do + let!(:merge_request) { create(:merge_request, :with_detached_merge_request_pipeline, source_project: project) } + let!(:pipeline) { merge_request.all_pipelines.last } + let(:sha) { pipeline.sha } + + it_behaves_like 'creates a commit status for the existing pipeline' + end end end diff --git a/spec/requests/api/graphql/namespace/projects_spec.rb b/spec/requests/api/graphql/namespace/projects_spec.rb index 815e9531ecf..2a95b99572f 100644 --- a/spec/requests/api/graphql/namespace/projects_spec.rb +++ b/spec/requests/api/graphql/namespace/projects_spec.rb @@ -67,6 +67,7 @@ describe 'getting projects' do context 'when the namespace is a user' do subject { user.namespace } + let(:include_subgroups) { false } it_behaves_like 'a graphql namespace' diff --git a/spec/requests/api/pages/internal_access_spec.rb b/spec/requests/api/pages/internal_access_spec.rb index c41eabe0a48..28abe1a8456 100644 --- a/spec/requests/api/pages/internal_access_spec.rb +++ b/spec/requests/api/pages/internal_access_spec.rb @@ -27,6 +27,7 @@ describe "Internal Project Pages Access" do describe "Project should be internal" do describe '#internal?' do subject { project.internal? } + it { is_expected.to be_truthy } end end diff --git a/spec/requests/api/pages/private_access_spec.rb b/spec/requests/api/pages/private_access_spec.rb index c647537038e..6af441caf74 100644 --- a/spec/requests/api/pages/private_access_spec.rb +++ b/spec/requests/api/pages/private_access_spec.rb @@ -27,6 +27,7 @@ describe "Private Project Pages Access" do describe "Project should be private" do describe '#private?' do subject { project.private? } + it { is_expected.to be_truthy } end end diff --git a/spec/requests/api/pages/public_access_spec.rb b/spec/requests/api/pages/public_access_spec.rb index 16cc5697f30..d99224eca5b 100644 --- a/spec/requests/api/pages/public_access_spec.rb +++ b/spec/requests/api/pages/public_access_spec.rb @@ -27,6 +27,7 @@ describe "Public Project Pages Access" do describe "Project should be public" do describe '#public?' do subject { project.public? } + it { is_expected.to be_truthy } end end diff --git a/spec/requests/api/runner_spec.rb b/spec/requests/api/runner_spec.rb index bc3a04420f9..70a95663aea 100644 --- a/spec/requests/api/runner_spec.rb +++ b/spec/requests/api/runner_spec.rb @@ -3,6 +3,7 @@ require 'spec_helper' describe API::Runner, :clean_gitlab_redis_shared_state do include StubGitlabCalls include RedisHelpers + include WorkhorseHelpers let(:registration_token) { 'abcdefg123456' } @@ -1395,7 +1396,7 @@ describe API::Runner, :clean_gitlab_redis_shared_state do expect(response).to have_gitlab_http_status(200) expect(response.content_type.to_s).to eq(Gitlab::Workhorse::INTERNAL_API_CONTENT_TYPE) - expect(json_response['TempPath']).to eq(JobArtifactUploader.workhorse_local_upload_path) + expect(json_response).not_to have_key('TempPath') expect(json_response['RemoteObject']).to have_key('ID') expect(json_response['RemoteObject']).to have_key('GetURL') expect(json_response['RemoteObject']).to have_key('StoreURL') @@ -1562,15 +1563,16 @@ describe API::Runner, :clean_gitlab_redis_shared_state do let!(:fog_connection) do stub_artifacts_object_storage(direct_upload: true) end - - before do + let(:object) do fog_connection.directories.new(key: 'artifacts').files.create( key: 'tmp/uploads/12312300', body: 'content' ) + end + let(:file_upload) { fog_to_uploaded_file(object) } - upload_artifacts(file_upload, headers_with_token, - { 'file.remote_id' => remote_id }) + before do + upload_artifacts(file_upload, headers_with_token, 'file.remote_id' => remote_id) end context 'when valid remote_id is used' do @@ -1804,12 +1806,13 @@ describe API::Runner, :clean_gitlab_redis_shared_state do end def upload_artifacts(file, headers = {}, params = {}) - params = params.merge({ - 'file.path' => file.path, - 'file.name' => file.original_filename - }) - - post api("/jobs/#{job.id}/artifacts"), params: params, headers: headers + workhorse_finalize( + api("/jobs/#{job.id}/artifacts"), + method: :post, + file_key: :file, + params: params.merge(file: file), + headers: headers + ) end end diff --git a/spec/requests/lfs_http_spec.rb b/spec/requests/lfs_http_spec.rb index ae34f7d1f87..62b9ee1d361 100644 --- a/spec/requests/lfs_http_spec.rb +++ b/spec/requests/lfs_http_spec.rb @@ -4,6 +4,7 @@ require 'spec_helper' describe 'Git LFS API and storage' do include LfsHttpHelpers include ProjectForksHelper + include WorkhorseHelpers set(:project) { create(:project, :repository) } set(:other_project) { create(:project, :repository) } @@ -933,7 +934,7 @@ describe 'Git LFS API and storage' do it_behaves_like 'a valid response' do it 'responds with status 200, location of LFS remote store and object details' do - expect(json_response['TempPath']).to eq(LfsObjectUploader.workhorse_local_upload_path) + expect(json_response).not_to have_key('TempPath') expect(json_response['RemoteObject']).to have_key('ID') expect(json_response['RemoteObject']).to have_key('GetURL') expect(json_response['RemoteObject']).to have_key('StoreURL') @@ -992,10 +993,17 @@ describe 'Git LFS API and storage' do stub_lfs_object_storage(direct_upload: true) end + let(:tmp_object) do + fog_connection.directories.new(key: 'lfs-objects').files.create( + key: 'tmp/uploads/12312300', + body: 'content' + ) + end + ['123123', '../../123123'].each do |remote_id| context "with invalid remote_id: #{remote_id}" do subject do - put_finalize(with_tempfile: true, args: { + put_finalize(remote_object: tmp_object, args: { 'file.remote_id' => remote_id }) end @@ -1009,15 +1017,8 @@ describe 'Git LFS API and storage' do end context 'with valid remote_id' do - before do - fog_connection.directories.new(key: 'lfs-objects').files.create( - key: 'tmp/uploads/12312300', - body: 'content' - ) - end - subject do - put_finalize(with_tempfile: true, args: { + put_finalize(remote_object: tmp_object, args: { 'file.remote_id' => '12312300', 'file.name' => 'name' }) @@ -1027,6 +1028,10 @@ describe 'Git LFS API and storage' do subject expect(response).to have_gitlab_http_status(200) + + object = LfsObject.find_by_oid(sample_oid) + expect(object).to be_present + expect(object.file.read).to eq(tmp_object.body) end it 'schedules migration of file to object storage' do @@ -1268,28 +1273,31 @@ describe 'Git LFS API and storage' do put authorize_url(project, sample_oid, sample_size), params: {}, headers: authorize_headers end - def put_finalize(lfs_tmp = lfs_tmp_file, with_tempfile: false, verified: true, args: {}) - upload_path = LfsObjectUploader.workhorse_local_upload_path - file_path = upload_path + '/' + lfs_tmp if lfs_tmp + def put_finalize(lfs_tmp = lfs_tmp_file, with_tempfile: false, verified: true, remote_object: nil, args: {}) + uploaded_file = nil if with_tempfile + upload_path = LfsObjectUploader.workhorse_local_upload_path + file_path = upload_path + '/' + lfs_tmp if lfs_tmp + FileUtils.mkdir_p(upload_path) FileUtils.touch(file_path) - end - - extra_args = { - 'file.path' => file_path, - 'file.name' => File.basename(file_path) - } - put_finalize_with_args(args.merge(extra_args).compact, verified: verified) - end + uploaded_file = UploadedFile.new(file_path, filename: File.basename(file_path)) + elsif remote_object + uploaded_file = fog_to_uploaded_file(remote_object) + end - def put_finalize_with_args(args, verified:) finalize_headers = headers finalize_headers.merge!(workhorse_internal_api_request_header) if verified - put objects_url(project, sample_oid, sample_size), params: args, headers: finalize_headers + workhorse_finalize( + objects_url(project, sample_oid, sample_size), + method: :put, + file_key: :file, + params: args.merge(file: uploaded_file), + headers: finalize_headers + ) end def lfs_tmp_file diff --git a/spec/rubocop/cop/migration/add_timestamps_spec.rb b/spec/rubocop/cop/migration/add_timestamps_spec.rb index fae0177d5f5..33f1bb85af8 100644 --- a/spec/rubocop/cop/migration/add_timestamps_spec.rb +++ b/spec/rubocop/cop/migration/add_timestamps_spec.rb @@ -9,6 +9,7 @@ describe RuboCop::Cop::Migration::AddTimestamps do include CopHelper subject(:cop) { described_class.new } + let(:migration_with_add_timestamps) do %q( class Users < ActiveRecord::Migration[4.2] diff --git a/spec/rubocop/cop/migration/timestamps_spec.rb b/spec/rubocop/cop/migration/timestamps_spec.rb index 1812818692a..cafe255dc9a 100644 --- a/spec/rubocop/cop/migration/timestamps_spec.rb +++ b/spec/rubocop/cop/migration/timestamps_spec.rb @@ -9,6 +9,7 @@ describe RuboCop::Cop::Migration::Timestamps do include CopHelper subject(:cop) { described_class.new } + let(:migration_with_timestamps) do %q( class Users < ActiveRecord::Migration[4.2] diff --git a/spec/rubocop/cop/scalability/file_uploads_spec.rb b/spec/rubocop/cop/scalability/file_uploads_spec.rb index 2a94fde5ba2..a35d423581c 100644 --- a/spec/rubocop/cop/scalability/file_uploads_spec.rb +++ b/spec/rubocop/cop/scalability/file_uploads_spec.rb @@ -10,6 +10,7 @@ describe RuboCop::Cop::Scalability::FileUploads do include ExpectOffense subject(:cop) { described_class.new } + let(:message) { 'Do not upload files without workhorse acceleration. Please refer to https://docs.gitlab.com/ee/development/uploads.html' } context 'with required params' do diff --git a/spec/serializers/cluster_basic_entity_spec.rb b/spec/serializers/cluster_basic_entity_spec.rb index ab5d54fca16..be03ee91784 100644 --- a/spec/serializers/cluster_basic_entity_spec.rb +++ b/spec/serializers/cluster_basic_entity_spec.rb @@ -5,6 +5,7 @@ require 'spec_helper' describe ClusterBasicEntity do describe '#as_json' do subject { described_class.new(cluster, request: request).as_json } + let(:maintainer) { create(:user) } let(:developer) { create(:user) } let(:current_user) { maintainer } diff --git a/spec/serializers/pipeline_serializer_spec.rb b/spec/serializers/pipeline_serializer_spec.rb index 9762c83ed6a..ce5264ec8bb 100644 --- a/spec/serializers/pipeline_serializer_spec.rb +++ b/spec/serializers/pipeline_serializer_spec.rb @@ -139,6 +139,7 @@ describe PipelineSerializer do describe 'number of queries when preloaded' do subject { serializer.represent(resource, preload: true) } + let(:resource) { Ci::Pipeline.all } before do diff --git a/spec/services/git/tag_hooks_service_spec.rb b/spec/services/git/tag_hooks_service_spec.rb index c97d4d38b1c..abb5b9b130b 100644 --- a/spec/services/git/tag_hooks_service_spec.rb +++ b/spec/services/git/tag_hooks_service_spec.rb @@ -58,6 +58,7 @@ describe Git::TagHooksService, :service do describe 'Push data' do shared_examples_for 'tag push data expectations' do subject(:push_data) { service.send(:push_data) } + it 'has expected push data attributes' do is_expected.to match a_hash_including( object_kind: 'tag_push', diff --git a/spec/services/merge_requests/create_from_issue_service_spec.rb b/spec/services/merge_requests/create_from_issue_service_spec.rb index 07e0218e1df..51a5c51f6c3 100644 --- a/spec/services/merge_requests/create_from_issue_service_spec.rb +++ b/spec/services/merge_requests/create_from_issue_service_spec.rb @@ -13,6 +13,7 @@ describe MergeRequests::CreateFromIssueService do let(:custom_source_branch) { 'custom-source-branch' } subject(:service) { described_class.new(project, user, service_params) } + subject(:service_with_custom_source_branch) { described_class.new(project, user, branch_name: custom_source_branch, **service_params) } before do diff --git a/spec/services/note_summary_spec.rb b/spec/services/note_summary_spec.rb index e59731207a5..aa4e41f4d8c 100644 --- a/spec/services/note_summary_spec.rb +++ b/spec/services/note_summary_spec.rb @@ -46,5 +46,17 @@ describe NoteSummary do it 'returns metadata hash' do expect(create_note_summary.metadata).to eq(action: 'icon', commit_count: 5) end + + context 'description action and noteable has saved_description_version' do + before do + noteable.saved_description_version = 1 + end + + subject { described_class.new(noteable, project, user, 'note', action: 'description') } + + it 'sets the description_version metadata' do + expect(subject.metadata).to include(description_version: 1) + end + end end end diff --git a/spec/services/projects/housekeeping_service_spec.rb b/spec/services/projects/housekeeping_service_spec.rb index f651db70cbd..c99054d9fd5 100644 --- a/spec/services/projects/housekeeping_service_spec.rb +++ b/spec/services/projects/housekeeping_service_spec.rb @@ -4,6 +4,7 @@ require 'spec_helper' describe Projects::HousekeepingService do subject { described_class.new(project) } + set(:project) { create(:project, :repository) } before do diff --git a/spec/services/system_notes/issuables_service_spec.rb b/spec/services/system_notes/issuables_service_spec.rb index a914b34cb23..5023abad4cd 100644 --- a/spec/services/system_notes/issuables_service_spec.rb +++ b/spec/services/system_notes/issuables_service_spec.rb @@ -212,6 +212,15 @@ describe ::SystemNotes::IssuablesService do it 'sets the note text' do expect(subject.note).to eq('changed the description') end + + it 'associates the related description version' do + noteable.update!(description: 'New description') + + description_version_id = subject.system_note_metadata.description_version_id + + expect(description_version_id).not_to be_nil + expect(description_version_id).to eq(noteable.saved_description_version.id) + end end end diff --git a/spec/support/helpers/kubernetes_helpers.rb b/spec/support/helpers/kubernetes_helpers.rb index da743e586f5..e74dbca4f93 100644 --- a/spec/support/helpers/kubernetes_helpers.rb +++ b/spec/support/helpers/kubernetes_helpers.rb @@ -319,10 +319,10 @@ module KubernetesHelpers } end - def kube_knative_services_body(**options) + def kube_knative_services_body(legacy_knative: false, **options) { "kind" => "List", - "items" => [kube_service(options)] + "items" => [legacy_knative ? knative_05_service(options) : kube_service(options)] } end @@ -421,6 +421,27 @@ module KubernetesHelpers } end + def knative_05_service(name: "kubetest", namespace: "default", domain: "example.com") + { + "metadata" => { + "creationTimestamp" => "2018-11-21T06:16:33Z", + "name" => name, + "namespace" => namespace, + "selfLink" => "/apis/serving.knative.dev/v1alpha1/namespaces/#{namespace}/services/#{name}" + }, + "spec" => { + "generation" => 2 + }, + "status" => { + "domain" => "#{name}.#{namespace}.#{domain}", + "domainInternal" => "#{name}.#{namespace}.svc.cluster.local", + "latestCreatedRevisionName" => "#{name}-00002", + "latestReadyRevisionName" => "#{name}-00002", + "observedGeneration" => 2 + } + } + end + def kube_service_full(name: "kubetest", namespace: "kube-ns", domain: "example.com") { "metadata" => { diff --git a/spec/support/helpers/workhorse_helpers.rb b/spec/support/helpers/workhorse_helpers.rb index 40007a14b85..e0fba191deb 100644 --- a/spec/support/helpers/workhorse_helpers.rb +++ b/spec/support/helpers/workhorse_helpers.rb @@ -22,16 +22,40 @@ module WorkhorseHelpers # workhorse_post_with_file will transform file_key inside params as if it was disk accelerated by workhorse def workhorse_post_with_file(url, file_key:, params:) + workhorse_request_with_file(:post, url, + file_key: file_key, + params: params, + env: { 'CONTENT_TYPE' => 'multipart/form-data' }, + send_rewritten_field: true + ) + end + + # workhorse_finalize will transform file_key inside params as if it was the finalize call of an inline object storage upload. + # note that based on the content of the params it can simulate a disc acceleration or an object storage upload + def workhorse_finalize(url, method: :post, file_key:, params:, headers: {}) + workhorse_request_with_file(method, url, + file_key: file_key, + params: params, + extra_headers: headers, + send_rewritten_field: false + ) + end + + def workhorse_request_with_file(method, url, file_key:, params:, env: {}, extra_headers: {}, send_rewritten_field:) workhorse_params = params.dup file = workhorse_params.delete(file_key) - workhorse_params.merge!(workhorse_disk_accelerated_file_params(file_key, file)) + workhorse_params = workhorse_disk_accelerated_file_params(file_key, file).merge(workhorse_params) + + headers = if send_rewritten_field + workhorse_rewritten_fields_header(file_key => file.path) + else + {} + end + + headers.merge!(extra_headers) - post(url, - params: workhorse_params, - headers: workhorse_rewritten_fields_header(file_key => file.path), - env: { 'CONTENT_TYPE' => 'multipart/form-data' } - ) + process(method, url, params: workhorse_params, headers: headers, env: env) end private @@ -45,9 +69,24 @@ module WorkhorseHelpers end def workhorse_disk_accelerated_file_params(key, file) + return {} unless file + { "#{key}.name" => file.original_filename, - "#{key}.path" => file.path - } + "#{key}.size" => file.size + }.tap do |params| + params["#{key}.path"] = file.path if file.path + params["#{key}.remote_id"] = file.remote_id if file.respond_to?(:remote_id) && file.remote_id + end + end + + def fog_to_uploaded_file(file) + filename = File.basename(file.key) + + UploadedFile.new(nil, + filename: filename, + remote_id: filename, + size: file.content_length + ) end end diff --git a/spec/support/redis/redis_shared_examples.rb b/spec/support/redis/redis_shared_examples.rb index 7e47cdae866..97a23f02b3e 100644 --- a/spec/support/redis/redis_shared_examples.rb +++ b/spec/support/redis/redis_shared_examples.rb @@ -90,6 +90,7 @@ RSpec.shared_examples "redis_shared_examples" do describe '._raw_config' do subject { described_class._raw_config } + let(:config_file_name) { '/var/empty/doesnotexist' } it 'is frozen' do diff --git a/spec/support/shared_examples/controllers/uploads_actions_shared_examples.rb b/spec/support/shared_examples/controllers/uploads_actions_shared_examples.rb index 0b4ab9941fc..c24418b2f90 100644 --- a/spec/support/shared_examples/controllers/uploads_actions_shared_examples.rb +++ b/spec/support/shared_examples/controllers/uploads_actions_shared_examples.rb @@ -338,7 +338,7 @@ shared_examples 'handle uploads authorize' do it_behaves_like 'a valid response' do it 'responds with status 200, location of uploads remote store and object details' do - expect(json_response['TempPath']).to eq(uploader_class.workhorse_local_upload_path) + expect(json_response).not_to have_key('TempPath') expect(json_response['RemoteObject']).to have_key('ID') expect(json_response['RemoteObject']).to have_key('GetURL') expect(json_response['RemoteObject']).to have_key('StoreURL') diff --git a/spec/support/shared_examples/models/cluster_application_status_shared_examples.rb b/spec/support/shared_examples/models/cluster_application_status_shared_examples.rb index f95b612eb70..a6653f89377 100644 --- a/spec/support/shared_examples/models/cluster_application_status_shared_examples.rb +++ b/spec/support/shared_examples/models/cluster_application_status_shared_examples.rb @@ -111,6 +111,7 @@ shared_examples 'cluster application status specs' do |application_name| describe '#make_errored' do subject { create(application_name, :installing) } + let(:reason) { 'some errors' } it 'is errored' do diff --git a/spec/support/shared_examples/versioned_description_shared_examples.rb b/spec/support/shared_examples/versioned_description_shared_examples.rb new file mode 100644 index 00000000000..59124af19ec --- /dev/null +++ b/spec/support/shared_examples/versioned_description_shared_examples.rb @@ -0,0 +1,65 @@ +# frozen_string_literal: true + +RSpec.shared_examples 'versioned description' do + describe 'associations' do + it { is_expected.to have_many(:description_versions) } + end + + describe 'save_description_version' do + let(:factory_name) { described_class.name.underscore.to_sym } + let!(:model) { create(factory_name, description: 'Original description') } + + context 'when feature is enabled' do + before do + stub_feature_flags(save_description_versions: true) + end + + context 'when description was changed' do + before do + model.update!(description: 'New description') + end + + it 'saves the old and new description for the first update' do + expect(model.description_versions.first.description).to eq('Original description') + expect(model.description_versions.last.description).to eq('New description') + end + + it 'only saves the new description for subsequent updates' do + expect { model.update!(description: 'Another description') }.to change { model.description_versions.count }.by(1) + + expect(model.description_versions.last.description).to eq('Another description') + end + + it 'sets the new description version to `saved_description_version`' do + expect(model.saved_description_version).to eq(model.description_versions.last) + end + + it 'clears `saved_description_version` after another save that does not change description' do + model.save! + + expect(model.saved_description_version).to be_nil + end + end + + context 'when description was not changed' do + it 'does not save any description version' do + expect { model.save! }.not_to change { model.description_versions.count } + + expect(model.saved_description_version).to be_nil + end + end + end + + context 'when feature is disabled' do + before do + stub_feature_flags(save_description_versions: false) + end + + it 'does not save any description version' do + expect { model.update!(description: 'New description') }.not_to change { model.description_versions.count } + + expect(model.saved_description_version).to be_nil + end + end + end +end diff --git a/spec/tasks/gitlab/check_rake_spec.rb b/spec/tasks/gitlab/check_rake_spec.rb index 1469143d2ac..b3c8ca03aec 100644 --- a/spec/tasks/gitlab/check_rake_spec.rb +++ b/spec/tasks/gitlab/check_rake_spec.rb @@ -19,6 +19,7 @@ describe 'check.rake' do describe 'gitlab:check rake task' do subject { run_rake_task('gitlab:check') } + let(:name) { 'GitLab subtasks' } it_behaves_like 'system check rake task' @@ -26,6 +27,7 @@ describe 'check.rake' do describe 'gitlab:gitlab_shell:check rake task' do subject { run_rake_task('gitlab:gitlab_shell:check') } + let(:name) { 'GitLab Shell' } it_behaves_like 'system check rake task' @@ -33,6 +35,7 @@ describe 'check.rake' do describe 'gitlab:gitaly:check rake task' do subject { run_rake_task('gitlab:gitaly:check') } + let(:name) { 'Gitaly' } it_behaves_like 'system check rake task' @@ -40,6 +43,7 @@ describe 'check.rake' do describe 'gitlab:sidekiq:check rake task' do subject { run_rake_task('gitlab:sidekiq:check') } + let(:name) { 'Sidekiq' } it_behaves_like 'system check rake task' @@ -47,6 +51,7 @@ describe 'check.rake' do describe 'gitlab:incoming_email:check rake task' do subject { run_rake_task('gitlab:incoming_email:check') } + let(:name) { 'Incoming Email' } it_behaves_like 'system check rake task' @@ -56,6 +61,7 @@ describe 'check.rake' do include LdapHelpers subject { run_rake_task('gitlab:ldap:check') } + let(:name) { 'LDAP' } it_behaves_like 'system check rake task' diff --git a/spec/workers/every_sidekiq_worker_spec.rb b/spec/workers/every_sidekiq_worker_spec.rb index 8fddd8540ef..b7ba4d61723 100644 --- a/spec/workers/every_sidekiq_worker_spec.rb +++ b/spec/workers/every_sidekiq_worker_spec.rb @@ -35,4 +35,32 @@ describe 'Every Sidekiq worker' do expect(config_queues).to include(queue).or(include(queue_namespace)) end end + + describe "feature category declarations" do + let(:feature_categories) do + YAML.load_file(Rails.root.join('config', 'feature_categories.yml')).map(&:to_sym).to_set + end + + # All Sidekiq worker classes should declare a valid `feature_category` + # or explicitely be excluded with the `feature_category_not_owned!` annotation. + # Please see doc/development/sidekiq_style_guide.md#Feature-Categorization for more details. + it 'has a feature_category or feature_category_not_owned! attribute', :aggregate_failures do + Gitlab::SidekiqConfig.workers.each do |worker| + expect(worker.get_feature_category).to be_a(Symbol), "expected #{worker.inspect} to declare a feature_category or feature_category_not_owned!" + end + end + + # All Sidekiq worker classes should declare a valid `feature_category`. + # The category should match a value in `config/feature_categories.yml`. + # Please see doc/development/sidekiq_style_guide.md#Feature-Categorization for more details. + it 'has a feature_category that maps to a value in feature_categories.yml', :aggregate_failures do + workers_with_feature_categories = Gitlab::SidekiqConfig.workers + .select(&:get_feature_category) + .reject(&:feature_category_not_owned?) + + workers_with_feature_categories.each do |worker| + expect(feature_categories).to include(worker.get_feature_category), "expected #{worker.inspect} to declare a valid feature_category, but got #{worker.get_feature_category}" + end + end + end end diff --git a/spec/workers/hashed_storage/migrator_worker_spec.rb b/spec/workers/hashed_storage/migrator_worker_spec.rb index a318cdd003e..12c1a26104e 100644 --- a/spec/workers/hashed_storage/migrator_worker_spec.rb +++ b/spec/workers/hashed_storage/migrator_worker_spec.rb @@ -4,6 +4,7 @@ require 'spec_helper' describe HashedStorage::MigratorWorker do subject(:worker) { described_class.new } + let(:projects) { create_list(:project, 2, :legacy_storage, :empty_repo) } let(:ids) { projects.map(&:id) } diff --git a/spec/workers/hashed_storage/rollbacker_worker_spec.rb b/spec/workers/hashed_storage/rollbacker_worker_spec.rb index 4055f380978..5fcb1adf9ae 100644 --- a/spec/workers/hashed_storage/rollbacker_worker_spec.rb +++ b/spec/workers/hashed_storage/rollbacker_worker_spec.rb @@ -4,6 +4,7 @@ require 'spec_helper' describe HashedStorage::RollbackerWorker do subject(:worker) { described_class.new } + let(:projects) { create_list(:project, 2, :empty_repo) } let(:ids) { projects.map(&:id) } |