summaryrefslogtreecommitdiff
path: root/spec
diff options
context:
space:
mode:
Diffstat (limited to 'spec')
-rw-r--r--spec/controllers/projects/boards/issues_controller_spec.rb1
-rw-r--r--spec/features/boards/boards_spec.rb18
-rw-r--r--spec/features/boards/issue_ordering_spec.rb166
-rw-r--r--spec/features/boards/sidebar_spec.rb4
-rw-r--r--spec/fixtures/api/schemas/issue.json1
-rw-r--r--spec/javascripts/boards/boards_store_spec.js75
-rw-r--r--spec/javascripts/boards/issue_spec.js16
-rw-r--r--spec/javascripts/boards/list_spec.js3
-rw-r--r--spec/lib/gitlab/git/repository_spec.rb26
-rw-r--r--spec/lib/gitlab/import_export/safe_model_attributes.yml1
-rw-r--r--spec/lib/gitlab/middleware/go_spec.rb95
-rw-r--r--spec/models/concerns/relative_positioning_spec.rb104
-rw-r--r--spec/models/repository_spec.rb29
-rw-r--r--spec/requests/api/commits_spec.rb94
-rw-r--r--spec/requests/api/jobs_spec.rb76
-rw-r--r--spec/services/boards/issues/list_service_spec.rb26
-rw-r--r--spec/services/boards/issues/move_service_spec.rb18
-rw-r--r--spec/services/issues/update_service_spec.rb16
18 files changed, 713 insertions, 56 deletions
diff --git a/spec/controllers/projects/boards/issues_controller_spec.rb b/spec/controllers/projects/boards/issues_controller_spec.rb
index 3d0533cb516..15667e8d4b1 100644
--- a/spec/controllers/projects/boards/issues_controller_spec.rb
+++ b/spec/controllers/projects/boards/issues_controller_spec.rb
@@ -43,6 +43,7 @@ describe Projects::Boards::IssuesController do
expect(response).to match_response_schema('issues')
expect(parsed_response.length).to eq 2
+ expect(development.issues.map(&:relative_position)).not_to include(nil)
end
end
diff --git a/spec/features/boards/boards_spec.rb b/spec/features/boards/boards_spec.rb
index e247bfa2980..ecc356f2505 100644
--- a/spec/features/boards/boards_spec.rb
+++ b/spec/features/boards/boards_spec.rb
@@ -71,16 +71,16 @@ describe 'Issue Boards', feature: true, js: true do
let!(:list1) { create(:list, board: board, label: planning, position: 0) }
let!(:list2) { create(:list, board: board, label: development, position: 1) }
- let!(:confidential_issue) { create(:labeled_issue, :confidential, project: project, author: user, labels: [planning]) }
- let!(:issue1) { create(:labeled_issue, project: project, assignee: user, labels: [planning]) }
- let!(:issue2) { create(:labeled_issue, project: project, author: user2, labels: [planning]) }
- let!(:issue3) { create(:labeled_issue, project: project, labels: [planning]) }
- let!(:issue4) { create(:labeled_issue, project: project, labels: [planning]) }
- let!(:issue5) { create(:labeled_issue, project: project, labels: [planning], milestone: milestone) }
- let!(:issue6) { create(:labeled_issue, project: project, labels: [planning, development]) }
- let!(:issue7) { create(:labeled_issue, project: project, labels: [development]) }
+ let!(:confidential_issue) { create(:labeled_issue, :confidential, project: project, author: user, labels: [planning], relative_position: 9) }
+ let!(:issue1) { create(:labeled_issue, project: project, assignee: user, labels: [planning], relative_position: 8) }
+ let!(:issue2) { create(:labeled_issue, project: project, author: user2, labels: [planning], relative_position: 7) }
+ let!(:issue3) { create(:labeled_issue, project: project, labels: [planning], relative_position: 6) }
+ let!(:issue4) { create(:labeled_issue, project: project, labels: [planning], relative_position: 5) }
+ let!(:issue5) { create(:labeled_issue, project: project, labels: [planning], milestone: milestone, relative_position: 4) }
+ let!(:issue6) { create(:labeled_issue, project: project, labels: [planning, development], relative_position: 3) }
+ let!(:issue7) { create(:labeled_issue, project: project, labels: [development], relative_position: 2) }
let!(:issue8) { create(:closed_issue, project: project) }
- let!(:issue9) { create(:labeled_issue, project: project, labels: [planning, testing, bug, accepting]) }
+ let!(:issue9) { create(:labeled_issue, project: project, labels: [planning, testing, bug, accepting], relative_position: 1) }
before do
visit namespace_project_board_path(project.namespace, project, board)
diff --git a/spec/features/boards/issue_ordering_spec.rb b/spec/features/boards/issue_ordering_spec.rb
new file mode 100644
index 00000000000..c50155a6d14
--- /dev/null
+++ b/spec/features/boards/issue_ordering_spec.rb
@@ -0,0 +1,166 @@
+require 'rails_helper'
+
+describe 'Issue Boards', :feature, :js do
+ include WaitForVueResource
+ include DragTo
+
+ let(:project) { create(:empty_project, :public) }
+ let(:board) { create(:board, project: project) }
+ let(:user) { create(:user) }
+ let(:label) { create(:label, project: project) }
+ let!(:list1) { create(:list, board: board, label: label, position: 0) }
+ let!(:issue1) { create(:labeled_issue, project: project, title: 'testing 1', labels: [label], relative_position: 3) }
+ let!(:issue2) { create(:labeled_issue, project: project, title: 'testing 2', labels: [label], relative_position: 2) }
+ let!(:issue3) { create(:labeled_issue, project: project, title: 'testing 3', labels: [label], relative_position: 1) }
+
+ before do
+ project.team << [user, :master]
+
+ login_as(user)
+ end
+
+ context 'un-ordered issues' do
+ let!(:issue4) { create(:labeled_issue, project: project, labels: [label]) }
+
+ before do
+ visit namespace_project_board_path(project.namespace, project, board)
+ wait_for_vue_resource
+
+ expect(page).to have_selector('.board', count: 2)
+ end
+
+ it 'has un-ordered issue as last issue' do
+ page.within(first('.board')) do
+ expect(all('.card').last).to have_content(issue4.title)
+ end
+ end
+
+ it 'moves un-ordered issue to top of list' do
+ drag(from_index: 3, to_index: 0)
+
+ page.within(first('.board')) do
+ expect(first('.card')).to have_content(issue4.title)
+ end
+ end
+ end
+
+ context 'ordering in list' do
+ before do
+ visit namespace_project_board_path(project.namespace, project, board)
+ wait_for_vue_resource
+
+ expect(page).to have_selector('.board', count: 2)
+ end
+
+ it 'moves from middle to top' do
+ drag(from_index: 1, to_index: 0)
+
+ wait_for_vue_resource
+
+ expect(first('.card')).to have_content(issue2.title)
+ end
+
+ it 'moves from middle to bottom' do
+ drag(from_index: 1, to_index: 2)
+
+ wait_for_vue_resource
+
+ expect(all('.card').last).to have_content(issue2.title)
+ end
+
+ it 'moves from top to bottom' do
+ drag(from_index: 0, to_index: 2)
+
+ wait_for_vue_resource
+
+ expect(all('.card').last).to have_content(issue3.title)
+ end
+
+ it 'moves from bottom to top' do
+ drag(from_index: 2, to_index: 0)
+
+ wait_for_vue_resource
+
+ expect(first('.card')).to have_content(issue1.title)
+ end
+
+ it 'moves from top to middle' do
+ drag(from_index: 0, to_index: 1)
+
+ wait_for_vue_resource
+
+ expect(first('.card')).to have_content(issue2.title)
+ end
+
+ it 'moves from bottom to middle' do
+ drag(from_index: 2, to_index: 1)
+
+ wait_for_vue_resource
+
+ expect(all('.card').last).to have_content(issue2.title)
+ end
+ end
+
+ context 'ordering when changing list' do
+ let(:label2) { create(:label, project: project) }
+ let!(:list2) { create(:list, board: board, label: label2, position: 1) }
+ let!(:issue4) { create(:labeled_issue, project: project, title: 'testing 1', labels: [label2], relative_position: 3.0) }
+ let!(:issue5) { create(:labeled_issue, project: project, title: 'testing 2', labels: [label2], relative_position: 2.0) }
+ let!(:issue6) { create(:labeled_issue, project: project, title: 'testing 3', labels: [label2], relative_position: 1.0) }
+
+ before do
+ visit namespace_project_board_path(project.namespace, project, board)
+ wait_for_vue_resource
+
+ expect(page).to have_selector('.board', count: 3)
+ end
+
+ it 'moves to top of another list' do
+ drag(list_from_index: 0, list_to_index: 1)
+
+ wait_for_vue_resource
+
+ expect(first('.board')).to have_selector('.card', count: 2)
+ expect(all('.board')[1]).to have_selector('.card', count: 4)
+
+ page.within(all('.board')[1]) do
+ expect(first('.card')).to have_content(issue3.title)
+ end
+ end
+
+ it 'moves to bottom of another list' do
+ drag(list_from_index: 0, list_to_index: 1, to_index: 2)
+
+ wait_for_vue_resource
+
+ expect(first('.board')).to have_selector('.card', count: 2)
+ expect(all('.board')[1]).to have_selector('.card', count: 4)
+
+ page.within(all('.board')[1]) do
+ expect(all('.card').last).to have_content(issue3.title)
+ end
+ end
+
+ it 'moves to index of another list' do
+ drag(list_from_index: 0, list_to_index: 1, to_index: 1)
+
+ wait_for_vue_resource
+
+ expect(first('.board')).to have_selector('.card', count: 2)
+ expect(all('.board')[1]).to have_selector('.card', count: 4)
+
+ page.within(all('.board')[1]) do
+ expect(all('.card')[1]).to have_content(issue3.title)
+ end
+ end
+ end
+
+ def drag(selector: '.board-list', list_from_index: 0, from_index: 0, to_index: 0, list_to_index: 0)
+ drag_to(selector: selector,
+ scrollable: '#board-app',
+ list_from_index: list_from_index,
+ from_index: from_index,
+ to_index: to_index,
+ list_to_index: list_to_index)
+ end
+end
diff --git a/spec/features/boards/sidebar_spec.rb b/spec/features/boards/sidebar_spec.rb
index 59e87b3f69c..3332e07ec31 100644
--- a/spec/features/boards/sidebar_spec.rb
+++ b/spec/features/boards/sidebar_spec.rb
@@ -11,8 +11,8 @@ describe 'Issue Boards', feature: true, js: true do
let!(:bug) { create(:label, project: project, name: 'Bug') }
let!(:regression) { create(:label, project: project, name: 'Regression') }
let!(:stretch) { create(:label, project: project, name: 'Stretch') }
- let!(:issue1) { create(:labeled_issue, project: project, assignee: user, milestone: milestone, labels: [development]) }
- let!(:issue2) { create(:labeled_issue, project: project, labels: [development, stretch]) }
+ let!(:issue1) { create(:labeled_issue, project: project, assignee: user, milestone: milestone, labels: [development], relative_position: 2) }
+ let!(:issue2) { create(:labeled_issue, project: project, labels: [development, stretch], relative_position: 1) }
let(:board) { create(:board, project: project) }
let!(:list) { create(:list, board: board, label: development, position: 0) }
let(:card) { first('.board').first('.card') }
diff --git a/spec/fixtures/api/schemas/issue.json b/spec/fixtures/api/schemas/issue.json
index 8e19cee5440..21c078e0f44 100644
--- a/spec/fixtures/api/schemas/issue.json
+++ b/spec/fixtures/api/schemas/issue.json
@@ -11,6 +11,7 @@
"title": { "type": "string" },
"confidential": { "type": "boolean" },
"due_date": { "type": ["date", "null"] },
+ "relative_position": { "type": "integer" },
"labels": {
"type": "array",
"items": {
diff --git a/spec/javascripts/boards/boards_store_spec.js b/spec/javascripts/boards/boards_store_spec.js
index 9dd741a680b..49a2ca4a78f 100644
--- a/spec/javascripts/boards/boards_store_spec.js
+++ b/spec/javascripts/boards/boards_store_spec.js
@@ -5,6 +5,7 @@
/* global Cookies */
/* global listObj */
/* global listObjDuplicate */
+/* global ListIssue */
require('~/lib/utils/url_utility');
require('~/boards/models/issue');
@@ -14,6 +15,7 @@ require('~/boards/models/user');
require('~/boards/services/board_service');
require('~/boards/stores/boards_store');
require('./mock_data');
+require('es6-promise').polyfill();
describe('Store', () => {
beforeEach(() => {
@@ -21,6 +23,10 @@ describe('Store', () => {
gl.boardService = new BoardService('/test/issue-boards/board', '', '1');
gl.issueBoards.BoardsStore.create();
+ spyOn(gl.boardService, 'moveIssue').and.callFake(() => new Promise((resolve) => {
+ resolve();
+ }));
+
Cookies.set('issue_board_welcome_hidden', 'false', {
expires: 365 * 10,
path: ''
@@ -154,5 +160,74 @@ describe('Store', () => {
done();
}, 0);
});
+
+ it('moves issue to top of another list', (done) => {
+ const listOne = gl.issueBoards.BoardsStore.addList(listObj);
+ const listTwo = gl.issueBoards.BoardsStore.addList(listObjDuplicate);
+
+ expect(gl.issueBoards.BoardsStore.state.lists.length).toBe(2);
+
+ setTimeout(() => {
+ listOne.issues[0].id = 2;
+
+ expect(listOne.issues.length).toBe(1);
+ expect(listTwo.issues.length).toBe(1);
+
+ gl.issueBoards.BoardsStore.moveIssueToList(listOne, listTwo, listOne.findIssue(2), 0);
+
+ expect(listOne.issues.length).toBe(0);
+ expect(listTwo.issues.length).toBe(2);
+ expect(listTwo.issues[0].id).toBe(2);
+ expect(gl.boardService.moveIssue).toHaveBeenCalledWith(2, listOne.id, listTwo.id, null, 1);
+
+ done();
+ }, 0);
+ });
+
+ it('moves issue to bottom of another list', (done) => {
+ const listOne = gl.issueBoards.BoardsStore.addList(listObj);
+ const listTwo = gl.issueBoards.BoardsStore.addList(listObjDuplicate);
+
+ expect(gl.issueBoards.BoardsStore.state.lists.length).toBe(2);
+
+ setTimeout(() => {
+ listOne.issues[0].id = 2;
+
+ expect(listOne.issues.length).toBe(1);
+ expect(listTwo.issues.length).toBe(1);
+
+ gl.issueBoards.BoardsStore.moveIssueToList(listOne, listTwo, listOne.findIssue(2), 1);
+
+ expect(listOne.issues.length).toBe(0);
+ expect(listTwo.issues.length).toBe(2);
+ expect(listTwo.issues[1].id).toBe(2);
+ expect(gl.boardService.moveIssue).toHaveBeenCalledWith(2, listOne.id, listTwo.id, 1, null);
+
+ done();
+ }, 0);
+ });
+
+ it('moves issue in list', (done) => {
+ const issue = new ListIssue({
+ title: 'Testing',
+ iid: 2,
+ confidential: false,
+ labels: []
+ });
+ const list = gl.issueBoards.BoardsStore.addList(listObj);
+
+ setTimeout(() => {
+ list.addIssue(issue);
+
+ expect(list.issues.length).toBe(2);
+
+ gl.issueBoards.BoardsStore.moveIssueInList(list, issue, 0, 1, [1, 2]);
+
+ expect(list.issues[0].id).toBe(2);
+ expect(gl.boardService.moveIssue).toHaveBeenCalledWith(2, null, null, 1, null);
+
+ done();
+ });
+ });
});
});
diff --git a/spec/javascripts/boards/issue_spec.js b/spec/javascripts/boards/issue_spec.js
index aab4d9c501e..c96dfe94a4a 100644
--- a/spec/javascripts/boards/issue_spec.js
+++ b/spec/javascripts/boards/issue_spec.js
@@ -79,4 +79,20 @@ describe('Issue model', () => {
issue.removeLabels([issue.labels[0], issue.labels[1]]);
expect(issue.labels.length).toBe(0);
});
+
+ it('sets position to infinity if no position is stored', () => {
+ expect(issue.position).toBe(Infinity);
+ });
+
+ it('sets position', () => {
+ const relativePositionIssue = new ListIssue({
+ title: 'Testing',
+ iid: 1,
+ confidential: false,
+ relative_position: 1,
+ labels: []
+ });
+
+ expect(relativePositionIssue.position).toBe(1);
+ });
});
diff --git a/spec/javascripts/boards/list_spec.js b/spec/javascripts/boards/list_spec.js
index c8a18af7198..d49d3af33d9 100644
--- a/spec/javascripts/boards/list_spec.js
+++ b/spec/javascripts/boards/list_spec.js
@@ -103,6 +103,7 @@ describe('List model', () => {
listDup.updateIssueLabel(list, issue);
- expect(gl.boardService.moveIssue).toHaveBeenCalledWith(issue.id, list.id, listDup.id);
+ expect(gl.boardService.moveIssue)
+ .toHaveBeenCalledWith(issue.id, list.id, listDup.id, undefined, undefined);
});
});
diff --git a/spec/lib/gitlab/git/repository_spec.rb b/spec/lib/gitlab/git/repository_spec.rb
index 3f11f0a4516..bc139d5ef28 100644
--- a/spec/lib/gitlab/git/repository_spec.rb
+++ b/spec/lib/gitlab/git/repository_spec.rb
@@ -824,6 +824,32 @@ describe Gitlab::Git::Repository, seed_helper: true do
it { is_expected.to eq(17) }
end
+ describe '#count_commits' do
+ context 'with after timestamp' do
+ it 'returns the number of commits after timestamp' do
+ options = { ref: 'master', limit: nil, after: Time.iso8601('2013-03-03T20:15:01+00:00') }
+
+ expect(repository.count_commits(options)).to eq(25)
+ end
+ end
+
+ context 'with before timestamp' do
+ it 'returns the number of commits after timestamp' do
+ options = { ref: 'feature', limit: nil, before: Time.iso8601('2015-03-03T20:15:01+00:00') }
+
+ expect(repository.count_commits(options)).to eq(9)
+ end
+ end
+
+ context 'with path' do
+ it 'returns the number of commits with path ' do
+ options = { ref: 'master', limit: nil, path: "encoding" }
+
+ expect(repository.count_commits(options)).to eq(2)
+ end
+ end
+ end
+
describe "branch_names_contains" do
subject { repository.branch_names_contains(SeedRepo::LastCommit::ID) }
diff --git a/spec/lib/gitlab/import_export/safe_model_attributes.yml b/spec/lib/gitlab/import_export/safe_model_attributes.yml
index 3bd1f335a89..c718e792461 100644
--- a/spec/lib/gitlab/import_export/safe_model_attributes.yml
+++ b/spec/lib/gitlab/import_export/safe_model_attributes.yml
@@ -21,6 +21,7 @@ Issue:
- milestone_id
- weight
- time_estimate
+- relative_position
Event:
- id
- target_type
diff --git a/spec/lib/gitlab/middleware/go_spec.rb b/spec/lib/gitlab/middleware/go_spec.rb
index fd3769d75b5..c2ab015d5cb 100644
--- a/spec/lib/gitlab/middleware/go_spec.rb
+++ b/spec/lib/gitlab/middleware/go_spec.rb
@@ -15,16 +15,93 @@ describe Gitlab::Middleware::Go, lib: true do
end
describe 'when go-get=1' do
- it 'returns a document' do
- env = { 'rack.input' => '',
- 'QUERY_STRING' => 'go-get=1',
- 'PATH_INFO' => '/group/project/path' }
- resp = middleware.call(env)
- expect(resp[0]).to eq(200)
- expect(resp[1]['Content-Type']).to eq('text/html')
- expected_body = "<!DOCTYPE html><html><head><meta content='#{Gitlab.config.gitlab.host}/group/project git http://#{Gitlab.config.gitlab.host}/group/project.git' name='go-import'></head></html>\n"
- expect(resp[2].body).to eq([expected_body])
+ let(:current_user) { nil }
+
+ context 'with simple 2-segment project path' do
+ let!(:project) { create(:project, :private) }
+
+ context 'with subpackages' do
+ let(:path) { "#{project.full_path}/subpackage" }
+
+ it 'returns the full project path' do
+ expect_response_with_path(go, project.full_path)
+ end
+ end
+
+ context 'without subpackages' do
+ let(:path) { project.full_path }
+
+ it 'returns the full project path' do
+ expect_response_with_path(go, project.full_path)
+ end
+ end
+ end
+
+ context 'with a nested project path' do
+ let(:group) { create(:group, :nested) }
+ let!(:project) { create(:project, :public, namespace: group) }
+
+ shared_examples 'a nested project' do
+ context 'when the project is public' do
+ it 'returns the full project path' do
+ expect_response_with_path(go, project.full_path)
+ end
+ end
+
+ context 'when the project is private' do
+ before do
+ project.update_attribute(:visibility_level, Project::PRIVATE)
+ end
+
+ context 'with access to the project' do
+ let(:current_user) { project.creator }
+
+ before do
+ project.team.add_master(current_user)
+ end
+
+ it 'returns the full project path' do
+ expect_response_with_path(go, project.full_path)
+ end
+ end
+
+ context 'without access to the project' do
+ it 'returns the 2-segment group path' do
+ expect_response_with_path(go, group.full_path)
+ end
+ end
+ end
+ end
+
+ context 'with subpackages' do
+ let(:path) { "#{project.full_path}/subpackage" }
+
+ it_behaves_like 'a nested project'
+ end
+
+ context 'without subpackages' do
+ let(:path) { project.full_path }
+
+ it_behaves_like 'a nested project'
+ end
end
end
+
+ def go
+ env = {
+ 'rack.input' => '',
+ 'QUERY_STRING' => 'go-get=1',
+ 'PATH_INFO' => "/#{path}",
+ 'warden' => double(authenticate: current_user)
+ }
+ middleware.call(env)
+ end
+
+ def expect_response_with_path(response, path)
+ expect(response[0]).to eq(200)
+ expect(response[1]['Content-Type']).to eq('text/html')
+ expected_body = "<!DOCTYPE html><html><head><meta content='#{Gitlab.config.gitlab.host}/#{path} git http://#{Gitlab.config.gitlab.host}/#{path}.git' name='go-import'></head></html>\n"
+ expect(response[2].body).to eq([expected_body])
+ end
end
end
diff --git a/spec/models/concerns/relative_positioning_spec.rb b/spec/models/concerns/relative_positioning_spec.rb
new file mode 100644
index 00000000000..69906382545
--- /dev/null
+++ b/spec/models/concerns/relative_positioning_spec.rb
@@ -0,0 +1,104 @@
+require 'spec_helper'
+
+describe Issue, 'RelativePositioning' do
+ let(:project) { create(:empty_project) }
+ let(:issue) { create(:issue, project: project) }
+ let(:issue1) { create(:issue, project: project) }
+ let(:new_issue) { create(:issue, project: project) }
+
+ before do
+ [issue, issue1].each do |issue|
+ issue.move_to_end && issue.save
+ end
+ end
+
+ describe '#min_relative_position' do
+ it 'returns maximum position' do
+ expect(issue.min_relative_position).to eq issue.relative_position
+ end
+ end
+
+ describe '#max_relative_position' do
+ it 'returns maximum position' do
+ expect(issue.max_relative_position).to eq issue1.relative_position
+ end
+ end
+
+ describe '#prev_relative_position' do
+ it 'returns previous position if there is an issue above' do
+ expect(issue1.prev_relative_position).to eq issue.relative_position
+ end
+
+ it 'returns minimum position if there is no issue above' do
+ expect(issue.prev_relative_position).to eq RelativePositioning::MIN_POSITION
+ end
+ end
+
+ describe '#next_relative_position' do
+ it 'returns next position if there is an issue below' do
+ expect(issue.next_relative_position).to eq issue1.relative_position
+ end
+
+ it 'returns next position if there is no issue below' do
+ expect(issue1.next_relative_position).to eq RelativePositioning::MAX_POSITION
+ end
+ end
+
+ describe '#move_before' do
+ it 'moves issue before' do
+ [issue1, issue].each(&:move_to_end)
+
+ issue.move_before(issue1)
+
+ expect(issue.relative_position).to be < issue1.relative_position
+ end
+ end
+
+ describe '#move_after' do
+ it 'moves issue after' do
+ [issue, issue1].each(&:move_to_end)
+
+ issue.move_after(issue1)
+
+ expect(issue.relative_position).to be > issue1.relative_position
+ end
+ end
+
+ describe '#move_to_end' do
+ it 'moves issue to the end' do
+ new_issue.move_to_end
+
+ expect(new_issue.relative_position).to be > issue1.relative_position
+ end
+ end
+
+ describe '#move_between' do
+ it 'positions issue between two other' do
+ new_issue.move_between(issue, issue1)
+
+ expect(new_issue.relative_position).to be > issue.relative_position
+ expect(new_issue.relative_position).to be < issue1.relative_position
+ end
+
+ it 'positions issue between on top' do
+ new_issue.move_between(nil, issue)
+
+ expect(new_issue.relative_position).to be < issue.relative_position
+ end
+
+ it 'positions issue between to end' do
+ new_issue.move_between(issue1, nil)
+
+ expect(new_issue.relative_position).to be > issue1.relative_position
+ end
+
+ it 'positions issues even when after and before positions are the same' do
+ issue1.update relative_position: issue.relative_position
+
+ new_issue.move_between(issue, issue1)
+
+ expect(new_issue.relative_position).to be > issue.relative_position
+ expect(issue.relative_position).to be < issue1.relative_position
+ end
+ end
+end
diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb
index eb992e1354e..274e4f00a0a 100644
--- a/spec/models/repository_spec.rb
+++ b/spec/models/repository_spec.rb
@@ -1042,7 +1042,7 @@ describe Repository, models: true do
it 'expires the cache for all branches' do
expect(cache).to receive(:expire).
- at_least(repository.branches.length).
+ at_least(repository.branches.length * 2).
times
repository.expire_branch_cache
@@ -1050,14 +1050,14 @@ describe Repository, models: true do
it 'expires the cache for all branches when the root branch is given' do
expect(cache).to receive(:expire).
- at_least(repository.branches.length).
+ at_least(repository.branches.length * 2).
times
repository.expire_branch_cache(repository.root_ref)
end
it 'expires the cache for a specific branch' do
- expect(cache).to receive(:expire).once
+ expect(cache).to receive(:expire).twice
repository.expire_branch_cache('foo')
end
@@ -1742,6 +1742,29 @@ describe Repository, models: true do
end
end
+ describe '#commit_count_for_ref' do
+ let(:project) { create :empty_project }
+
+ context 'with a non-existing repository' do
+ it 'returns 0' do
+ expect(project.repository.commit_count_for_ref('master')).to eq(0)
+ end
+ end
+
+ context 'with empty repository' do
+ it 'returns 0' do
+ project.create_repository
+ expect(project.repository.commit_count_for_ref('master')).to eq(0)
+ end
+ end
+
+ context 'when searching for the root ref' do
+ it 'returns the same count as #commit_count' do
+ expect(repository.commit_count_for_ref(repository.root_ref)).to eq(repository.commit_count)
+ end
+ end
+ end
+
describe '#cache_method_output', caching: true do
context 'with a non-existing repository' do
let(:value) do
diff --git a/spec/requests/api/commits_spec.rb b/spec/requests/api/commits_spec.rb
index 5190fcca2d1..585449e62b6 100644
--- a/spec/requests/api/commits_spec.rb
+++ b/spec/requests/api/commits_spec.rb
@@ -19,6 +19,7 @@ describe API::Commits, api: true do
it "returns project commits" do
commit = project.repository.commit
+
get api("/projects/#{project.id}/repository/commits", user)
expect(response).to have_http_status(200)
@@ -27,6 +28,16 @@ describe API::Commits, api: true do
expect(json_response.first['committer_name']).to eq(commit.committer_name)
expect(json_response.first['committer_email']).to eq(commit.committer_email)
end
+
+ it 'include correct pagination headers' do
+ commit_count = project.repository.count_commits(ref: 'master').to_s
+
+ get api("/projects/#{project.id}/repository/commits", user)
+
+ expect(response).to include_pagination_headers
+ expect(response.headers['X-Total']).to eq(commit_count)
+ expect(response.headers['X-Page']).to eql('1')
+ end
end
context "unauthorized user" do
@@ -39,14 +50,26 @@ describe API::Commits, api: true do
context "since optional parameter" do
it "returns project commits since provided parameter" do
commits = project.repository.commits("master")
- since = commits.second.created_at
+ after = commits.second.created_at
- get api("/projects/#{project.id}/repository/commits?since=#{since.utc.iso8601}", user)
+ get api("/projects/#{project.id}/repository/commits?since=#{after.utc.iso8601}", user)
expect(json_response.size).to eq 2
expect(json_response.first["id"]).to eq(commits.first.id)
expect(json_response.second["id"]).to eq(commits.second.id)
end
+
+ it 'include correct pagination headers' do
+ commits = project.repository.commits("master")
+ after = commits.second.created_at
+ commit_count = project.repository.count_commits(ref: 'master', after: after).to_s
+
+ get api("/projects/#{project.id}/repository/commits?since=#{after.utc.iso8601}", user)
+
+ expect(response).to include_pagination_headers
+ expect(response.headers['X-Total']).to eq(commit_count)
+ expect(response.headers['X-Page']).to eql('1')
+ end
end
context "until optional parameter" do
@@ -65,6 +88,18 @@ describe API::Commits, api: true do
expect(json_response.first["id"]).to eq(commits.second.id)
expect(json_response.second["id"]).to eq(commits.third.id)
end
+
+ it 'include correct pagination headers' do
+ commits = project.repository.commits("master")
+ before = commits.second.created_at
+ commit_count = project.repository.count_commits(ref: 'master', before: before).to_s
+
+ get api("/projects/#{project.id}/repository/commits?until=#{before.utc.iso8601}", user)
+
+ expect(response).to include_pagination_headers
+ expect(response.headers['X-Total']).to eq(commit_count)
+ expect(response.headers['X-Page']).to eql('1')
+ end
end
context "invalid xmlschema date parameters" do
@@ -79,11 +114,66 @@ describe API::Commits, api: true do
context "path optional parameter" do
it "returns project commits matching provided path parameter" do
path = 'files/ruby/popen.rb'
+ commit_count = project.repository.count_commits(ref: 'master', path: path).to_s
get api("/projects/#{project.id}/repository/commits?path=#{path}", user)
expect(json_response.size).to eq(3)
expect(json_response.first["id"]).to eq("570e7b2abdd848b95f2f578043fc23bd6f6fd24d")
+ expect(response).to include_pagination_headers
+ expect(response.headers['X-Total']).to eq(commit_count)
+ end
+
+ it 'include correct pagination headers' do
+ path = 'files/ruby/popen.rb'
+ commit_count = project.repository.count_commits(ref: 'master', path: path).to_s
+
+ get api("/projects/#{project.id}/repository/commits?path=#{path}", user)
+
+ expect(response).to include_pagination_headers
+ expect(response.headers['X-Total']).to eq(commit_count)
+ expect(response.headers['X-Page']).to eql('1')
+ end
+ end
+
+ context 'with pagination params' do
+ let(:page) { 1 }
+ let(:per_page) { 5 }
+ let(:ref_name) { 'master' }
+ let!(:request) do
+ get api("/projects/#{project.id}/repository/commits?page=#{page}&per_page=#{per_page}&ref_name=#{ref_name}", user)
+ end
+
+ it 'returns correct headers' do
+ commit_count = project.repository.count_commits(ref: ref_name).to_s
+
+ expect(response).to include_pagination_headers
+ expect(response.headers['X-Total']).to eq(commit_count)
+ expect(response.headers['X-Page']).to eq('1')
+ expect(response.headers['Link']).to match(/page=1&per_page=5/)
+ expect(response.headers['Link']).to match(/page=2&per_page=5/)
+ end
+
+ context 'viewing the first page' do
+ it 'returns the first 5 commits' do
+ commit = project.repository.commit
+
+ expect(json_response.size).to eq(per_page)
+ expect(json_response.first['id']).to eq(commit.id)
+ expect(response.headers['X-Page']).to eq('1')
+ end
+ end
+
+ context 'viewing the third page' do
+ let(:page) { 3 }
+
+ it 'returns the third 5 commits' do
+ commit = project.repository.commits('HEAD', offset: (page - 1) * per_page).first
+
+ expect(json_response.size).to eq(per_page)
+ expect(json_response.first['id']).to eq(commit.id)
+ expect(response.headers['X-Page']).to eq('3')
+ end
end
end
end
diff --git a/spec/requests/api/jobs_spec.rb b/spec/requests/api/jobs_spec.rb
index a4d27734cc2..9450701064b 100644
--- a/spec/requests/api/jobs_spec.rb
+++ b/spec/requests/api/jobs_spec.rb
@@ -51,7 +51,7 @@ describe API::Jobs, api: true do
end
context 'filter project with array of scope elements' do
- let(:query) { { 'scope[0]' => 'pending', 'scope[1]' => 'running' } }
+ let(:query) { { scope: %w(pending running) } }
it do
expect(response).to have_http_status(200)
@@ -60,7 +60,7 @@ describe API::Jobs, api: true do
end
context 'respond 400 when scope contains invalid state' do
- let(:query) { { 'scope[0]' => 'unknown', 'scope[1]' => 'running' } }
+ let(:query) { { scope: %w(unknown running) } }
it { expect(response).to have_http_status(400) }
end
@@ -75,6 +75,78 @@ describe API::Jobs, api: true do
end
end
+ describe 'GET /projects/:id/pipelines/:pipeline_id/jobs' do
+ let(:query) { Hash.new }
+
+ before do
+ get api("/projects/#{project.id}/pipelines/#{pipeline.id}/jobs", api_user), query
+ end
+
+ context 'authorized user' do
+ it 'returns pipeline jobs' do
+ expect(response).to have_http_status(200)
+ expect(response).to include_pagination_headers
+ expect(json_response).to be_an Array
+ end
+
+ it 'returns correct values' do
+ expect(json_response).not_to be_empty
+ expect(json_response.first['commit']['id']).to eq project.commit.id
+ end
+
+ it 'returns pipeline data' do
+ json_build = json_response.first
+
+ expect(json_build['pipeline']).not_to be_empty
+ expect(json_build['pipeline']['id']).to eq build.pipeline.id
+ expect(json_build['pipeline']['ref']).to eq build.pipeline.ref
+ expect(json_build['pipeline']['sha']).to eq build.pipeline.sha
+ expect(json_build['pipeline']['status']).to eq build.pipeline.status
+ end
+
+ context 'filter jobs with one scope element' do
+ let(:query) { { 'scope' => 'pending' } }
+
+ it do
+ expect(response).to have_http_status(200)
+ expect(json_response).to be_an Array
+ end
+ end
+
+ context 'filter jobs with array of scope elements' do
+ let(:query) { { scope: %w(pending running) } }
+
+ it do
+ expect(response).to have_http_status(200)
+ expect(json_response).to be_an Array
+ end
+ end
+
+ context 'respond 400 when scope contains invalid state' do
+ let(:query) { { scope: %w(unknown running) } }
+
+ it { expect(response).to have_http_status(400) }
+ end
+
+ context 'jobs in different pipelines' do
+ let!(:pipeline2) { create(:ci_empty_pipeline, project: project) }
+ let!(:build2) { create(:ci_build, pipeline: pipeline2) }
+
+ it 'excludes jobs from other pipelines' do
+ json_response.each { |job| expect(job['pipeline']['id']).to eq(pipeline.id) }
+ end
+ end
+ end
+
+ context 'unauthorized user' do
+ let(:api_user) { nil }
+
+ it 'does not return jobs' do
+ expect(response).to have_http_status(401)
+ end
+ end
+ end
+
describe 'GET /projects/:id/jobs/:job_id' do
before do
get api("/projects/#{project.id}/jobs/#{build.id}", api_user)
diff --git a/spec/services/boards/issues/list_service_spec.rb b/spec/services/boards/issues/list_service_spec.rb
index 305278843f5..01baedc4761 100644
--- a/spec/services/boards/issues/list_service_spec.rb
+++ b/spec/services/boards/issues/list_service_spec.rb
@@ -43,32 +43,6 @@ describe Boards::Issues::ListService, services: true do
described_class.new(project, user, params).execute
end
- context 'sets default order to priority' do
- it 'returns opened issues when list id is missing' do
- params = { board_id: board.id }
-
- issues = described_class.new(project, user, params).execute
-
- expect(issues).to eq [opened_issue2, reopened_issue1, opened_issue1]
- end
-
- it 'returns closed issues when listing issues from Done' do
- params = { board_id: board.id, id: done.id }
-
- issues = described_class.new(project, user, params).execute
-
- expect(issues).to eq [closed_issue4, closed_issue2, closed_issue3, closed_issue1]
- end
-
- it 'returns opened issues that have label list applied when listing issues from a label list' do
- params = { board_id: board.id, id: list1.id }
-
- issues = described_class.new(project, user, params).execute
-
- expect(issues).to eq [list1_issue3, list1_issue1, list1_issue2]
- end
- end
-
context 'with list that does not belong to the board' do
it 'raises an error' do
list = create(:list)
diff --git a/spec/services/boards/issues/move_service_spec.rb b/spec/services/boards/issues/move_service_spec.rb
index 77f75167b3d..727ea04ea5c 100644
--- a/spec/services/boards/issues/move_service_spec.rb
+++ b/spec/services/boards/issues/move_service_spec.rb
@@ -78,8 +78,10 @@ describe Boards::Issues::MoveService, services: true do
end
context 'when moving to same list' do
- let(:issue) { create(:labeled_issue, project: project, labels: [bug, development]) }
- let(:params) { { board_id: board1.id, from_list_id: list1.id, to_list_id: list1.id } }
+ let(:issue) { create(:labeled_issue, project: project, labels: [bug, development]) }
+ let(:issue1) { create(:labeled_issue, project: project, labels: [bug, development]) }
+ let(:issue2) { create(:labeled_issue, project: project, labels: [bug, development]) }
+ let(:params) { { board_id: board1.id, from_list_id: list1.id, to_list_id: list1.id } }
it 'returns false' do
expect(described_class.new(project, user, params).execute(issue)).to eq false
@@ -90,6 +92,18 @@ describe Boards::Issues::MoveService, services: true do
expect(issue.reload.labels).to contain_exactly(bug, development)
end
+
+ it 'sorts issues' do
+ [issue, issue1, issue2].each do |issue|
+ issue.move_to_end && issue.save!
+ end
+
+ params.merge!(move_after_iid: issue1.iid, move_before_iid: issue2.iid)
+
+ described_class.new(project, user, params).execute(issue)
+
+ expect(issue.relative_position).to be_between(issue1.relative_position, issue2.relative_position)
+ end
end
end
end
diff --git a/spec/services/issues/update_service_spec.rb b/spec/services/issues/update_service_spec.rb
index d83b09fd32c..fa472f3e2c3 100644
--- a/spec/services/issues/update_service_spec.rb
+++ b/spec/services/issues/update_service_spec.rb
@@ -58,6 +58,22 @@ describe Issues::UpdateService, services: true do
expect(issue.due_date).to eq Date.tomorrow
end
+ it 'sorts issues as specified by parameters' do
+ issue1 = create(:issue, project: project, assignee_id: user3.id)
+ issue2 = create(:issue, project: project, assignee_id: user3.id)
+
+ [issue, issue1, issue2].each do |issue|
+ issue.move_to_end
+ issue.save
+ end
+
+ opts[:move_between_iids] = [issue1.iid, issue2.iid]
+
+ update_issue(opts)
+
+ expect(issue.relative_position).to be_between(issue1.relative_position, issue2.relative_position)
+ end
+
context 'when current user cannot admin issues in the project' do
let(:guest) { create(:user) }
before do