diff options
author | Phil Hughes <me@iamphill.com> | 2018-04-23 08:54:02 +0100 |
---|---|---|
committer | Phil Hughes <me@iamphill.com> | 2018-04-23 08:54:02 +0100 |
commit | e1a701c79ac993cb44c7d51ac2540a93774a5ab6 (patch) | |
tree | 83fb00931c96fa6406ef9b7bb509f695264174c9 /spec | |
parent | ca3f7cf0e3e41206e2f557c49b5bf26d4b9cad0f (diff) | |
parent | c212908aad9b32352653dfe9ca966f148c8dfc1a (diff) | |
download | gitlab-ce-e1a701c79ac993cb44c7d51ac2540a93774a5ab6.tar.gz |
Merge branch 'master' into ide-temp-file-folder-fixes
Diffstat (limited to 'spec')
26 files changed, 441 insertions, 112 deletions
diff --git a/spec/controllers/projects/jobs_controller_spec.rb b/spec/controllers/projects/jobs_controller_spec.rb index f677cec3408..b9a979044fe 100644 --- a/spec/controllers/projects/jobs_controller_spec.rb +++ b/spec/controllers/projects/jobs_controller_spec.rb @@ -190,10 +190,7 @@ describe Projects::JobsController do expect(response).to have_gitlab_http_status(:ok) expect(json_response['id']).to eq job.id expect(json_response['status']).to eq job.status - end - - it 'returns no job log message' do - expect(json_response['html']).to eq('No job log') + expect(json_response['html']).to be_nil end end diff --git a/spec/controllers/projects/repositories_controller_spec.rb b/spec/controllers/projects/repositories_controller_spec.rb index c3b71458e38..a102a3a3c8c 100644 --- a/spec/controllers/projects/repositories_controller_spec.rb +++ b/spec/controllers/projects/repositories_controller_spec.rb @@ -40,6 +40,30 @@ describe Projects::RepositoriesController do expect(response.header[Gitlab::Workhorse::SEND_DATA_HEADER]).to start_with("git-archive:") end + it 'handles legacy queries with the ref specified as ref in params' do + get :archive, namespace_id: project.namespace, project_id: project, ref: 'feature', format: 'zip' + + expect(response).to have_gitlab_http_status(200) + expect(assigns(:ref)).to eq('feature') + expect(response.header[Gitlab::Workhorse::SEND_DATA_HEADER]).to start_with("git-archive:") + end + + it 'handles legacy queries with the ref specified as id in params' do + get :archive, namespace_id: project.namespace, project_id: project, id: 'feature', format: 'zip' + + expect(response).to have_gitlab_http_status(200) + expect(assigns(:ref)).to eq('feature') + expect(response.header[Gitlab::Workhorse::SEND_DATA_HEADER]).to start_with("git-archive:") + end + + it 'prioritizes the id param over the ref param when both are specified' do + get :archive, namespace_id: project.namespace, project_id: project, id: 'feature', ref: 'feature_conflict', format: 'zip' + + expect(response).to have_gitlab_http_status(200) + expect(assigns(:ref)).to eq('feature') + expect(response.header[Gitlab::Workhorse::SEND_DATA_HEADER]).to start_with("git-archive:") + end + context "when the service raises an error" do before do allow(Gitlab::Workhorse).to receive(:send_git_archive).and_raise("Archive failed") diff --git a/spec/features/boards/new_issue_spec.rb b/spec/features/boards/new_issue_spec.rb index 6769acb7c9c..e880f0096c1 100644 --- a/spec/features/boards/new_issue_spec.rb +++ b/spec/features/boards/new_issue_spec.rb @@ -63,6 +63,13 @@ describe 'Issue Boards new issue', :js do page.within(first('.board .issue-count-badge-count')) do expect(page).to have_content('1') end + + page.within(first('.card')) do + issue = project.issues.find_by_title('bug') + + expect(page).to have_content(issue.to_reference) + expect(page).to have_link(issue.title, href: issue_path(issue)) + end end it 'shows sidebar when creating new issue' do diff --git a/spec/features/issues/todo_spec.rb b/spec/features/issues/todo_spec.rb index 8e6493bbd93..4a44ec302fc 100644 --- a/spec/features/issues/todo_spec.rb +++ b/spec/features/issues/todo_spec.rb @@ -14,7 +14,7 @@ feature 'Manually create a todo item from issue', :js do it 'creates todo when clicking button' do page.within '.issuable-sidebar' do click_button 'Add todo' - expect(page).to have_content 'Mark done' + expect(page).to have_content 'Mark todo as done' end page.within '.header-content .todos-count' do @@ -31,7 +31,7 @@ feature 'Manually create a todo item from issue', :js do it 'marks a todo as done' do page.within '.issuable-sidebar' do click_button 'Add todo' - click_button 'Mark done' + click_button 'Mark todo as done' end expect(page).to have_selector('.todos-count', visible: false) diff --git a/spec/features/merge_request/user_resolves_diff_notes_and_discussions_resolve_spec.rb b/spec/features/merge_request/user_resolves_diff_notes_and_discussions_resolve_spec.rb index b4ad4b64d8e..0fd2840c426 100644 --- a/spec/features/merge_request/user_resolves_diff_notes_and_discussions_resolve_spec.rb +++ b/spec/features/merge_request/user_resolves_diff_notes_and_discussions_resolve_spec.rb @@ -5,7 +5,7 @@ describe 'Merge request > User resolves diff notes and discussions', :js do let(:user) { project.creator } let(:guest) { create(:user) } let(:merge_request) { create(:merge_request_with_diffs, source_project: project, author: user, title: "Bug NS-04") } - let!(:note) { create(:diff_note_on_merge_request, project: project, noteable: merge_request) } + let!(:note) { create(:diff_note_on_merge_request, project: project, noteable: merge_request, note: "| Markdown | Table |\n|-------|---------|\n| first | second |") } let(:path) { "files/ruby/popen.rb" } let(:position) do Gitlab::Diff::Position.new( @@ -111,6 +111,15 @@ describe 'Merge request > User resolves diff notes and discussions', :js do expect(page.find(".line-holder-placeholder")).to be_visible expect(page.find(".timeline-content #note_#{note.id}")).to be_visible end + + it 'renders tables in lazy-loaded resolved diff dicussions' do + find(".timeline-content .discussion[data-discussion-id='#{note.discussion_id}'] .discussion-toggle-button").click + + wait_for_requests + + expect(page.find(".timeline-content #note_#{note.id}")).not_to have_css(".line_holder") + expect(page.find(".timeline-content #note_#{note.id}")).to have_css("tr", count: 2) + end end describe 'side-by-side view' do diff --git a/spec/helpers/issuables_helper_spec.rb b/spec/helpers/issuables_helper_spec.rb index 4224cea4652..7b59fde999d 100644 --- a/spec/helpers/issuables_helper_spec.rb +++ b/spec/helpers/issuables_helper_spec.rb @@ -22,11 +22,15 @@ describe IssuablesHelper do end describe '#issuable_labels_tooltip' do - it 'returns label text' do + it 'returns label text with no labels' do + expect(issuable_labels_tooltip([])).to eq("Labels") + end + + it 'returns label text with labels within max limit' do expect(issuable_labels_tooltip([label])).to eq(label.title) end - it 'returns label text' do + it 'returns label text with labels exceeding max limit' do expect(issuable_labels_tooltip([label, label2], limit: 1)).to eq("#{label.title}, and 1 more") end end diff --git a/spec/helpers/milestones_helper_spec.rb b/spec/helpers/milestones_helper_spec.rb index 70b4a89cb86..f5185cb2857 100644 --- a/spec/helpers/milestones_helper_spec.rb +++ b/spec/helpers/milestones_helper_spec.rb @@ -83,58 +83,4 @@ describe MilestonesHelper do end end end - - describe '#milestone_remaining_days' do - around do |example| - Timecop.freeze(Time.utc(2017, 3, 17)) { example.run } - end - - context 'when less than 31 days remaining' do - let(:milestone_remaining) { milestone_remaining_days(build_stubbed(:milestone, due_date: 12.days.from_now.utc)) } - - it 'returns days remaining' do - expect(milestone_remaining).to eq("<strong>12</strong> days remaining") - end - end - - context 'when less than 1 year and more than 30 days remaining' do - let(:milestone_remaining) { milestone_remaining_days(build_stubbed(:milestone, due_date: 2.months.from_now.utc)) } - - it 'returns months remaining' do - expect(milestone_remaining).to eq("<strong>2</strong> months remaining") - end - end - - context 'when more than 1 year remaining' do - let(:milestone_remaining) { milestone_remaining_days(build_stubbed(:milestone, due_date: (1.year.from_now + 2.days).utc)) } - - it 'returns years remaining' do - expect(milestone_remaining).to eq("<strong>1</strong> year remaining") - end - end - - context 'when milestone is expired' do - let(:milestone_remaining) { milestone_remaining_days(build_stubbed(:milestone, due_date: 2.days.ago.utc)) } - - it 'returns "Past due"' do - expect(milestone_remaining).to eq("<strong>Past due</strong>") - end - end - - context 'when milestone has start_date in the future' do - let(:milestone_remaining) { milestone_remaining_days(build_stubbed(:milestone, start_date: 2.days.from_now.utc)) } - - it 'returns "Upcoming"' do - expect(milestone_remaining).to eq("<strong>Upcoming</strong>") - end - end - - context 'when milestone has start_date in the past' do - let(:milestone_remaining) { milestone_remaining_days(build_stubbed(:milestone, start_date: 2.days.ago.utc)) } - - it 'returns days elapsed' do - expect(milestone_remaining).to eq("<strong>2</strong> days elapsed") - end - end - end end diff --git a/spec/javascripts/collapsed_sidebar_todo_spec.js b/spec/javascripts/collapsed_sidebar_todo_spec.js index 2abf52a1676..8427e8a0ba7 100644 --- a/spec/javascripts/collapsed_sidebar_todo_spec.js +++ b/spec/javascripts/collapsed_sidebar_todo_spec.js @@ -85,7 +85,7 @@ describe('Issuable right sidebar collapsed todo toggle', () => { setTimeout(() => { expect( document.querySelector('.issuable-sidebar-header .js-issuable-todo').textContent.trim(), - ).toBe('Mark done'); + ).toBe('Mark todo as done'); done(); }); @@ -97,7 +97,7 @@ describe('Issuable right sidebar collapsed todo toggle', () => { setTimeout(() => { expect( document.querySelector('.js-issuable-todo.sidebar-collapsed-icon').getAttribute('data-original-title'), - ).toBe('Mark done'); + ).toBe('Mark todo as done'); done(); }); @@ -128,13 +128,13 @@ describe('Issuable right sidebar collapsed todo toggle', () => { .catch(done.fail); }); - it('updates aria-label to mark done', (done) => { + it('updates aria-label to mark todo as done', (done) => { document.querySelector('.js-issuable-todo.sidebar-collapsed-icon').click(); setTimeout(() => { expect( document.querySelector('.js-issuable-todo.sidebar-collapsed-icon').getAttribute('aria-label'), - ).toBe('Mark done'); + ).toBe('Mark todo as done'); done(); }); @@ -147,7 +147,7 @@ describe('Issuable right sidebar collapsed todo toggle', () => { .then(() => { expect( document.querySelector('.js-issuable-todo.sidebar-collapsed-icon').getAttribute('aria-label'), - ).toBe('Mark done'); + ).toBe('Mark todo as done'); document.querySelector('.js-issuable-todo.sidebar-collapsed-icon').click(); }) diff --git a/spec/javascripts/ide/components/repo_editor_spec.js b/spec/javascripts/ide/components/repo_editor_spec.js index fc585647fbd..b06a6c62a1c 100644 --- a/spec/javascripts/ide/components/repo_editor_spec.js +++ b/spec/javascripts/ide/components/repo_editor_spec.js @@ -222,7 +222,7 @@ describe('RepoEditor', () => { vm.setupEditor(); expect(vm.editor.onPositionChange).toHaveBeenCalled(); - expect(vm.model.events.size).toBe(1); + expect(vm.model.events.size).toBe(2); }); it('updates state when model content changed', done => { diff --git a/spec/javascripts/ide/lib/common/model_spec.js b/spec/javascripts/ide/lib/common/model_spec.js index 553a7a3746b..7a6c22b6d27 100644 --- a/spec/javascripts/ide/lib/common/model_spec.js +++ b/spec/javascripts/ide/lib/common/model_spec.js @@ -83,13 +83,6 @@ describe('Multi-file editor library model', () => { }); describe('onChange', () => { - it('caches event by path', () => { - model.onChange(() => {}); - - expect(model.events.size).toBe(1); - expect(model.events.keys().next().value).toBe(model.file.key); - }); - it('calls callback on change', done => { const spy = jasmine.createSpy(); model.onChange(spy); @@ -132,5 +125,15 @@ describe('Multi-file editor library model', () => { jasmine.anything(), ); }); + + it('calls onDispose callback', () => { + const disposeSpy = jasmine.createSpy(); + + model.onDispose(disposeSpy); + + model.dispose(); + + expect(disposeSpy).toHaveBeenCalled(); + }); }); }); diff --git a/spec/javascripts/ide/lib/decorations/controller_spec.js b/spec/javascripts/ide/lib/decorations/controller_spec.js index aec325e26a9..e1c4ca570b6 100644 --- a/spec/javascripts/ide/lib/decorations/controller_spec.js +++ b/spec/javascripts/ide/lib/decorations/controller_spec.js @@ -117,4 +117,33 @@ describe('Multi-file editor library decorations controller', () => { expect(controller.editorDecorations.size).toBe(0); }); }); + + describe('hasDecorations', () => { + it('returns true when decorations are cached', () => { + controller.addDecorations(model, 'key', [{ decoration: 'decorationValue' }]); + + expect(controller.hasDecorations(model)).toBe(true); + }); + + it('returns false when no model decorations exist', () => { + expect(controller.hasDecorations(model)).toBe(false); + }); + }); + + describe('removeDecorations', () => { + beforeEach(() => { + controller.addDecorations(model, 'key', [{ decoration: 'decorationValue' }]); + controller.decorate(model); + }); + + it('removes cached decorations', () => { + expect(controller.decorations.size).not.toBe(0); + expect(controller.editorDecorations.size).not.toBe(0); + + controller.removeDecorations(model); + + expect(controller.decorations.size).toBe(0); + expect(controller.editorDecorations.size).toBe(0); + }); + }); }); diff --git a/spec/javascripts/ide/lib/diff/controller_spec.js b/spec/javascripts/ide/lib/diff/controller_spec.js index ff73240734e..fd8ab3b4f1d 100644 --- a/spec/javascripts/ide/lib/diff/controller_spec.js +++ b/spec/javascripts/ide/lib/diff/controller_spec.js @@ -3,10 +3,7 @@ import monacoLoader from '~/ide/monaco_loader'; import editor from '~/ide/lib/editor'; import ModelManager from '~/ide/lib/common/model_manager'; import DecorationsController from '~/ide/lib/decorations/controller'; -import DirtyDiffController, { - getDiffChangeType, - getDecorator, -} from '~/ide/lib/diff/controller'; +import DirtyDiffController, { getDiffChangeType, getDecorator } from '~/ide/lib/diff/controller'; import { computeDiff } from '~/ide/lib/diff/diff'; import { file } from '../../helpers'; @@ -90,6 +87,14 @@ describe('Multi-file editor library dirty diff controller', () => { expect(model.onChange).toHaveBeenCalled(); }); + it('adds dispose event callback', () => { + spyOn(model, 'onDispose'); + + controller.attachModel(model); + + expect(model.onDispose).toHaveBeenCalled(); + }); + it('calls throttledComputeDiff on change', () => { spyOn(controller, 'throttledComputeDiff'); @@ -99,6 +104,12 @@ describe('Multi-file editor library dirty diff controller', () => { expect(controller.throttledComputeDiff).toHaveBeenCalled(); }); + + it('caches model', () => { + controller.attachModel(model); + + expect(controller.models.has(model.url)).toBe(true); + }); }); describe('computeDiff', () => { @@ -116,14 +127,22 @@ describe('Multi-file editor library dirty diff controller', () => { }); describe('reDecorate', () => { - it('calls decorations controller decorate', () => { + it('calls computeDiff when no decorations are cached', () => { + spyOn(controller, 'computeDiff'); + + controller.reDecorate(model); + + expect(controller.computeDiff).toHaveBeenCalledWith(model); + }); + + it('calls decorate when decorations are cached', () => { spyOn(controller.decorationsController, 'decorate'); + controller.decorationsController.decorations.set(model.url, 'test'); + controller.reDecorate(model); - expect(controller.decorationsController.decorate).toHaveBeenCalledWith( - model, - ); + expect(controller.decorationsController.decorate).toHaveBeenCalledWith(model); }); }); @@ -133,16 +152,15 @@ describe('Multi-file editor library dirty diff controller', () => { controller.decorate({ data: { changes: [], path: model.path } }); - expect( - controller.decorationsController.addDecorations, - ).toHaveBeenCalledWith(model, 'dirtyDiff', jasmine.anything()); + expect(controller.decorationsController.addDecorations).toHaveBeenCalledWith( + model, + 'dirtyDiff', + jasmine.anything(), + ); }); it('adds decorations into editor', () => { - const spy = spyOn( - controller.decorationsController.editor.instance, - 'deltaDecorations', - ); + const spy = spyOn(controller.decorationsController.editor.instance, 'deltaDecorations'); controller.decorate({ data: { changes: computeDiff('123', '1234'), path: model.path }, @@ -181,16 +199,22 @@ describe('Multi-file editor library dirty diff controller', () => { }); it('removes worker event listener', () => { - spyOn( - controller.dirtyDiffWorker, - 'removeEventListener', - ).and.callThrough(); + spyOn(controller.dirtyDiffWorker, 'removeEventListener').and.callThrough(); controller.dispose(); - expect( - controller.dirtyDiffWorker.removeEventListener, - ).toHaveBeenCalledWith('message', jasmine.anything()); + expect(controller.dirtyDiffWorker.removeEventListener).toHaveBeenCalledWith( + 'message', + jasmine.anything(), + ); + }); + + it('clears cached models', () => { + controller.attachModel(model); + + model.dispose(); + + expect(controller.models.size).toBe(0); }); }); }); diff --git a/spec/javascripts/issue_spec.js b/spec/javascripts/issue_spec.js index f37426a72d4..047ecab27db 100644 --- a/spec/javascripts/issue_spec.js +++ b/spec/javascripts/issue_spec.js @@ -92,6 +92,7 @@ describe('Issue', function() { function mockCanCreateBranch(canCreateBranch) { mock.onGet(/(.*)\/can_create_branch$/).reply(200, { can_create_branch: canCreateBranch, + suggested_branch_name: 'foo-99', }); } diff --git a/spec/lib/gitlab/gitaly_client/repository_service_spec.rb b/spec/lib/gitlab/gitaly_client/repository_service_spec.rb index 074323d47d2..ecd8657c406 100644 --- a/spec/lib/gitlab/gitaly_client/repository_service_spec.rb +++ b/spec/lib/gitlab/gitaly_client/repository_service_spec.rb @@ -156,4 +156,15 @@ describe Gitlab::GitalyClient::RepositoryService do client.calculate_checksum end end + + describe '#create_from_snapshot' do + it 'sends a create_repository_from_snapshot message' do + expect_any_instance_of(Gitaly::RepositoryService::Stub) + .to receive(:create_repository_from_snapshot) + .with(gitaly_request_with_path(storage_name, relative_path), kind_of(Hash)) + .and_return(double) + + client.create_from_snapshot('http://example.com?wiki=1', 'Custom xyz') + end + end end diff --git a/spec/lib/gitlab/import_export/safe_model_attributes.yml b/spec/lib/gitlab/import_export/safe_model_attributes.yml index f84a777a27f..05790bb5fe1 100644 --- a/spec/lib/gitlab/import_export/safe_model_attributes.yml +++ b/spec/lib/gitlab/import_export/safe_model_attributes.yml @@ -537,12 +537,6 @@ ProjectCustomAttribute: - project_id - key - value -LfsFileLock: -- id -- path -- user_id -- project_id -- created_at Badge: - id - link_url diff --git a/spec/lib/gitlab/workhorse_spec.rb b/spec/lib/gitlab/workhorse_spec.rb index d64ea72e346..e732b089d44 100644 --- a/spec/lib/gitlab/workhorse_spec.rb +++ b/spec/lib/gitlab/workhorse_spec.rb @@ -482,4 +482,26 @@ describe Gitlab::Workhorse do }.deep_stringify_keys) end end + + describe '.send_git_snapshot' do + let(:url) { 'http://example.com' } + + subject(:request) { described_class.send_git_snapshot(repository) } + + it 'sets the header correctly' do + key, command, params = decode_workhorse_header(request) + + expect(key).to eq("Gitlab-Workhorse-Send-Data") + expect(command).to eq('git-snapshot') + expect(params).to eq( + 'GitalyServer' => { + 'address' => Gitlab::GitalyClient.address(project.repository_storage), + 'token' => Gitlab::GitalyClient.token(project.repository_storage) + }, + 'GetSnapshotRequest' => Gitaly::GetSnapshotRequest.new( + repository: repository.gitaly_repository + ).to_json + ) + end + end end diff --git a/spec/models/concerns/uniquify_spec.rb b/spec/models/concerns/uniquify_spec.rb index 914730718e7..6cd2de6dcce 100644 --- a/spec/models/concerns/uniquify_spec.rb +++ b/spec/models/concerns/uniquify_spec.rb @@ -22,6 +22,15 @@ describe Uniquify do expect(result).to eq('test_string2') end + it 'allows to pass an initial value for the counter' do + start_counting_from = 2 + uniquify = described_class.new(start_counting_from) + + result = uniquify.string('test_string') { |s| s == 'test_string' } + + expect(result).to eq('test_string2') + end + it 'allows passing in a base function that defines the location of the counter' do result = uniquify.string(-> (counter) { "test_#{counter}_string" }) do |s| s == 'test__string' diff --git a/spec/models/deployment_spec.rb b/spec/models/deployment_spec.rb index ac30cd27e0c..aee70bcfb29 100644 --- a/spec/models/deployment_spec.rb +++ b/spec/models/deployment_spec.rb @@ -16,6 +16,15 @@ describe Deployment do it { is_expected.to validate_presence_of(:ref) } it { is_expected.to validate_presence_of(:sha) } + describe 'modules' do + it_behaves_like 'AtomicInternalId' do + let(:internal_id_attribute) { :iid } + let(:instance) { build(:deployment) } + let(:scope_attrs) { { project: instance.project } } + let(:usage) { :deployments } + end + end + describe 'after_create callbacks' do let(:environment) { create(:environment) } let(:store) { Gitlab::EtagCaching::Store.new } diff --git a/spec/models/issue_spec.rb b/spec/models/issue_spec.rb index 11154291368..128acf83686 100644 --- a/spec/models/issue_spec.rb +++ b/spec/models/issue_spec.rb @@ -376,6 +376,48 @@ describe Issue do end end + describe '#suggested_branch_name' do + let(:repository) { double } + + subject { build(:issue) } + + before do + allow(subject.project).to receive(:repository).and_return(repository) + end + + context '#to_branch_name does not exists' do + before do + allow(repository).to receive(:branch_exists?).and_return(false) + end + + it 'returns #to_branch_name' do + expect(subject.suggested_branch_name).to eq(subject.to_branch_name) + end + end + + context '#to_branch_name exists not ending with -index' do + before do + allow(repository).to receive(:branch_exists?).and_return(true) + allow(repository).to receive(:branch_exists?).with(/#{subject.to_branch_name}-\d/).and_return(false) + end + + it 'returns #to_branch_name ending with -2' do + expect(subject.suggested_branch_name).to eq("#{subject.to_branch_name}-2") + end + end + + context '#to_branch_name exists ending with -index' do + before do + allow(repository).to receive(:branch_exists?).and_return(true) + allow(repository).to receive(:branch_exists?).with("#{subject.to_branch_name}-3").and_return(false) + end + + it 'returns #to_branch_name ending with max index + 1' do + expect(subject.suggested_branch_name).to eq("#{subject.to_branch_name}-3") + end + end + end + describe '#has_related_branch?' do let(:issue) { create(:issue, title: "Blue Bell Knoll") } subject { issue.has_related_branch? } @@ -425,6 +467,27 @@ describe Issue do end end + describe '#can_be_worked_on?' do + let(:project) { build(:project) } + subject { build(:issue, :opened, project: project) } + + context 'is closed' do + subject { build(:issue, :closed) } + + it { is_expected.not_to be_can_be_worked_on } + end + + context 'project is forked' do + before do + allow(project).to receive(:forked?).and_return(true) + end + + it { is_expected.not_to be_can_be_worked_on } + end + + it { is_expected.to be_can_be_worked_on } + end + describe '#participants' do context 'using a public project' do let(:project) { create(:project, :public) } diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb index f73f44ca0ad..becb146422e 100644 --- a/spec/models/merge_request_spec.rb +++ b/spec/models/merge_request_spec.rb @@ -17,11 +17,17 @@ describe MergeRequest do describe 'modules' do subject { described_class } - it { is_expected.to include_module(NonatomicInternalId) } it { is_expected.to include_module(Issuable) } it { is_expected.to include_module(Referable) } it { is_expected.to include_module(Sortable) } it { is_expected.to include_module(Taskable) } + + it_behaves_like 'AtomicInternalId' do + let(:internal_id_attribute) { :iid } + let(:instance) { build(:merge_request) } + let(:scope_attrs) { { project: instance.target_project } } + let(:usage) { :merge_requests } + end end describe 'validation' do diff --git a/spec/models/milestone_spec.rb b/spec/models/milestone_spec.rb index 47f4a792e5c..4bb9717d33e 100644 --- a/spec/models/milestone_spec.rb +++ b/spec/models/milestone_spec.rb @@ -1,6 +1,26 @@ require 'spec_helper' describe Milestone do + describe 'modules' do + context 'with a project' do + it_behaves_like 'AtomicInternalId' do + let(:internal_id_attribute) { :iid } + let(:instance) { build(:milestone, project: build(:project), group: nil) } + let(:scope_attrs) { { project: instance.project } } + let(:usage) { :milestones } + end + end + + context 'with a group' do + it_behaves_like 'AtomicInternalId' do + let(:internal_id_attribute) { :iid } + let(:instance) { build(:milestone, project: nil, group: build(:group)) } + let(:scope_attrs) { { namespace: instance.group } } + let(:usage) { :milestones } + end + end + end + describe "Validation" do before do allow(subject).to receive(:set_iid).and_return(false) @@ -96,7 +116,9 @@ describe Milestone do allow(milestone).to receive(:due_date).and_return(Date.today.prev_year) end - it { expect(milestone.expired?).to be_truthy } + it 'returns true when due_date is in the past' do + expect(milestone.expired?).to be_truthy + end end context "not expired" do @@ -104,17 +126,19 @@ describe Milestone do allow(milestone).to receive(:due_date).and_return(Date.today.next_year) end - it { expect(milestone.expired?).to be_falsey } + it 'returns false when due_date is in the future' do + expect(milestone.expired?).to be_falsey + end end end describe '#upcoming?' do - it 'returns true' do + it 'returns true when start_date is in the future' do milestone = build(:milestone, start_date: Time.now + 1.month) expect(milestone.upcoming?).to be_truthy end - it 'returns false' do + it 'returns false when start_date is in the past' do milestone = build(:milestone, start_date: Date.today.prev_year) expect(milestone.upcoming?).to be_falsey end diff --git a/spec/models/note_spec.rb b/spec/models/note_spec.rb index 86962cd8d61..6a6c71e6c82 100644 --- a/spec/models/note_spec.rb +++ b/spec/models/note_spec.rb @@ -91,6 +91,23 @@ describe Note do it "keeps the commit around" do expect(note.project.repository.kept_around?(commit.id)).to be_truthy end + + it 'does not generate N+1 queries for participants', :request_store do + def retrieve_participants + commit.notes_with_associations.map(&:participants).to_a + end + + # Project authorization checks are cached, establish a baseline + retrieve_participants + + control_count = ActiveRecord::QueryRecorder.new do + retrieve_participants + end + + create(:note_on_commit, project: note.project, note: 'another note', noteable_id: commit.id) + + expect { retrieve_participants }.not_to exceed_query_limit(control_count) + end end describe 'authorization' do diff --git a/spec/requests/api/project_snapshots_spec.rb b/spec/requests/api/project_snapshots_spec.rb new file mode 100644 index 00000000000..07a920f8d28 --- /dev/null +++ b/spec/requests/api/project_snapshots_spec.rb @@ -0,0 +1,51 @@ +require 'spec_helper' + +describe API::ProjectSnapshots do + include WorkhorseHelpers + + let(:project) { create(:project) } + let(:admin) { create(:admin) } + + describe 'GET /projects/:id/snapshot' do + def expect_snapshot_response_for(repository) + type, params = workhorse_send_data + + expect(type).to eq('git-snapshot') + expect(params).to eq( + 'GitalyServer' => { + 'address' => Gitlab::GitalyClient.address(repository.project.repository_storage), + 'token' => Gitlab::GitalyClient.token(repository.project.repository_storage) + }, + 'GetSnapshotRequest' => Gitaly::GetSnapshotRequest.new( + repository: repository.gitaly_repository + ).to_json + ) + end + + it 'returns authentication error as project owner' do + get api("/projects/#{project.id}/snapshot", project.owner) + + expect(response).to have_gitlab_http_status(403) + end + + it 'returns authentication error as unauthenticated user' do + get api("/projects/#{project.id}/snapshot", nil) + + expect(response).to have_gitlab_http_status(401) + end + + it 'requests project repository raw archive as administrator' do + get api("/projects/#{project.id}/snapshot", admin), wiki: '0' + + expect(response).to have_gitlab_http_status(200) + expect_snapshot_response_for(project.repository) + end + + it 'requests wiki repository raw archive as administrator' do + get api("/projects/#{project.id}/snapshot", admin), wiki: '1' + + expect(response).to have_gitlab_http_status(200) + expect_snapshot_response_for(project.wiki.repository) + end + end +end diff --git a/spec/serializers/entity_date_helper_spec.rb b/spec/serializers/entity_date_helper_spec.rb index b9cc2f64831..36da8d33a44 100644 --- a/spec/serializers/entity_date_helper_spec.rb +++ b/spec/serializers/entity_date_helper_spec.rb @@ -32,6 +32,7 @@ describe EntityDateHelper do end it 'converts 86560 seconds' do + Rails.logger.debug date_helper_class.inspect expect(date_helper_class.distance_of_time_as_hash(86560)).to eq(days: 1, mins: 2, seconds: 40) end @@ -42,4 +43,58 @@ describe EntityDateHelper do it 'converts 986760 seconds' do expect(date_helper_class.distance_of_time_as_hash(986760)).to eq(days: 11, hours: 10, mins: 6) end + + describe '#remaining_days_in_words' do + around do |example| + Timecop.freeze(Time.utc(2017, 3, 17)) { example.run } + end + + context 'when less than 31 days remaining' do + let(:milestone_remaining) { date_helper_class.remaining_days_in_words(build_stubbed(:milestone, due_date: 12.days.from_now.utc)) } + + it 'returns days remaining' do + expect(milestone_remaining).to eq("<strong>12</strong> days remaining") + end + end + + context 'when less than 1 year and more than 30 days remaining' do + let(:milestone_remaining) { date_helper_class.remaining_days_in_words(build_stubbed(:milestone, due_date: 2.months.from_now.utc)) } + + it 'returns months remaining' do + expect(milestone_remaining).to eq("<strong>2</strong> months remaining") + end + end + + context 'when more than 1 year remaining' do + let(:milestone_remaining) { date_helper_class.remaining_days_in_words(build_stubbed(:milestone, due_date: (1.year.from_now + 2.days).utc)) } + + it 'returns years remaining' do + expect(milestone_remaining).to eq("<strong>1</strong> year remaining") + end + end + + context 'when milestone is expired' do + let(:milestone_remaining) { date_helper_class.remaining_days_in_words(build_stubbed(:milestone, due_date: 2.days.ago.utc)) } + + it 'returns "Past due"' do + expect(milestone_remaining).to eq("<strong>Past due</strong>") + end + end + + context 'when milestone has start_date in the future' do + let(:milestone_remaining) { date_helper_class.remaining_days_in_words(build_stubbed(:milestone, start_date: 2.days.from_now.utc)) } + + it 'returns "Upcoming"' do + expect(milestone_remaining).to eq("<strong>Upcoming</strong>") + end + end + + context 'when milestone has start_date in the past' do + let(:milestone_remaining) { date_helper_class.remaining_days_in_words(build_stubbed(:milestone, start_date: 2.days.ago.utc)) } + + it 'returns days elapsed' do + expect(milestone_remaining).to eq("<strong>2</strong> days elapsed") + end + end + end end diff --git a/spec/services/projects/create_from_template_service_spec.rb b/spec/services/projects/create_from_template_service_spec.rb index 609d678caea..d40e6f1449d 100644 --- a/spec/services/projects/create_from_template_service_spec.rb +++ b/spec/services/projects/create_from_template_service_spec.rb @@ -7,7 +7,7 @@ describe Projects::CreateFromTemplateService do path: user.to_param, template_name: 'rails', description: 'project description', - visibility_level: Gitlab::VisibilityLevel::PRIVATE + visibility_level: Gitlab::VisibilityLevel::PUBLIC } end @@ -24,7 +24,23 @@ describe Projects::CreateFromTemplateService do expect(project).to be_saved expect(project.scheduled?).to be(true) - expect(project.description).to match('project description') - expect(project.visibility_level).to eq(Gitlab::VisibilityLevel::PRIVATE) + end + + context 'the result project' do + before do + Sidekiq::Testing.inline! do + @project = subject.execute + end + + @project.reload + end + + it 'overrides template description' do + expect(@project.description).to match('project description') + end + + it 'overrides template visibility_level' do + expect(@project.visibility_level).to eq(Gitlab::VisibilityLevel::PUBLIC) + end end end diff --git a/spec/support/shared_examples/models/atomic_internal_id_spec.rb b/spec/support/shared_examples/models/atomic_internal_id_spec.rb index 144af4fc475..6a6e13418a9 100644 --- a/spec/support/shared_examples/models/atomic_internal_id_spec.rb +++ b/spec/support/shared_examples/models/atomic_internal_id_spec.rb @@ -19,6 +19,14 @@ shared_examples_for 'AtomicInternalId' do it { is_expected.to validate_numericality_of(internal_id_attribute) } end + describe 'Creating an instance' do + subject { instance.save! } + + it 'saves a new instance properly' do + expect { subject }.not_to raise_error + end + end + describe 'internal id generation' do subject { instance.save! } |