diff options
Diffstat (limited to 'spec/services')
28 files changed, 801 insertions, 723 deletions
diff --git a/spec/services/auth/container_registry_authentication_service_spec.rb b/spec/services/auth/container_registry_authentication_service_spec.rb index 9128280eb5a..290eeae828e 100644 --- a/spec/services/auth/container_registry_authentication_service_spec.rb +++ b/spec/services/auth/container_registry_authentication_service_spec.rb @@ -172,7 +172,7 @@ describe Auth::ContainerRegistryAuthenticationService do end let(:current_params) do - { scope: "repository:#{project.path_with_namespace}:*" } + { scope: "repository:#{project.full_path}:*" } end it_behaves_like 'an inaccessible' @@ -200,7 +200,7 @@ describe Auth::ContainerRegistryAuthenticationService do end let(:current_params) do - { scope: "repository:#{project.path_with_namespace}:*" } + { scope: "repository:#{project.full_path}:*" } end it_behaves_like 'an inaccessible' @@ -239,7 +239,7 @@ describe Auth::ContainerRegistryAuthenticationService do end let(:current_params) do - { scope: "repository:#{project.path_with_namespace}:*" } + { scope: "repository:#{project.full_path}:*" } end it_behaves_like 'an inaccessible' @@ -270,7 +270,7 @@ describe Auth::ContainerRegistryAuthenticationService do context 'disallow anyone to delete images' do let(:current_params) do - { scope: "repository:#{project.path_with_namespace}:*" } + { scope: "repository:#{project.full_path}:*" } end it_behaves_like 'an inaccessible' @@ -311,7 +311,7 @@ describe Auth::ContainerRegistryAuthenticationService do context 'disallow anyone to delete images' do let(:current_params) do - { scope: "repository:#{project.path_with_namespace}:*" } + { scope: "repository:#{project.full_path}:*" } end it_behaves_like 'an inaccessible' @@ -323,7 +323,7 @@ describe Auth::ContainerRegistryAuthenticationService do context 'disallow anyone to pull or push images' do let(:current_user) { create(:user, external: true) } let(:current_params) do - { scope: "repository:#{project.path_with_namespace}:pull,push" } + { scope: "repository:#{project.full_path}:pull,push" } end it_behaves_like 'an inaccessible' @@ -333,7 +333,7 @@ describe Auth::ContainerRegistryAuthenticationService do context 'disallow anyone to delete images' do let(:current_user) { create(:user, external: true) } let(:current_params) do - { scope: "repository:#{project.path_with_namespace}:*" } + { scope: "repository:#{project.full_path}:*" } end it_behaves_like 'an inaccessible' @@ -359,7 +359,7 @@ describe Auth::ContainerRegistryAuthenticationService do context 'allow to delete images' do let(:current_params) do - { scope: "repository:#{current_project.path_with_namespace}:*" } + { scope: "repository:#{current_project.full_path}:*" } end it_behaves_like 'a deletable' do @@ -398,7 +398,7 @@ describe Auth::ContainerRegistryAuthenticationService do context 'disallow to delete images' do let(:current_params) do - { scope: "repository:#{current_project.path_with_namespace}:*" } + { scope: "repository:#{current_project.full_path}:*" } end it_behaves_like 'an inaccessible' do diff --git a/spec/services/boards/create_service_spec.rb b/spec/services/boards/create_service_spec.rb index db51a524e79..a715261cd6c 100644 --- a/spec/services/boards/create_service_spec.rb +++ b/spec/services/boards/create_service_spec.rb @@ -2,34 +2,20 @@ require 'spec_helper' describe Boards::CreateService do describe '#execute' do - let(:project) { create(:project) } + context 'when board parent is a project' do + let(:parent) { create(:project) } - subject(:service) { described_class.new(project, double) } + subject(:service) { described_class.new(parent, double) } - context 'when project does not have a board' do - it 'creates a new board' do - expect { service.execute }.to change(Board, :count).by(1) - end - - it 'creates the default lists' do - board = service.execute - - expect(board.lists.size).to eq 2 - expect(board.lists.first).to be_backlog - expect(board.lists.last).to be_closed - end + it_behaves_like 'boards create service' end - context 'when project has a board' do - before do - create(:board, project: project) - end + context 'when board parent is a group' do + let(:parent) { create(:group) } - it 'does not create a new board' do - expect(service).to receive(:can_create_board?) { false } + subject(:service) { described_class.new(parent, double) } - expect { service.execute }.not_to change(project.boards, :count) - end + it_behaves_like 'boards create service' end end end diff --git a/spec/services/boards/issues/list_service_spec.rb b/spec/services/boards/issues/list_service_spec.rb index ff5733b7064..b4efa3e44b6 100644 --- a/spec/services/boards/issues/list_service_spec.rb +++ b/spec/services/boards/issues/list_service_spec.rb @@ -2,98 +2,103 @@ require 'spec_helper' describe Boards::Issues::ListService do describe '#execute' do - let(:user) { create(:user) } - let(:project) { create(:project) } - let(:board) { create(:board, project: project) } - - let(:bug) { create(:label, project: project, name: 'Bug') } - let(:development) { create(:label, project: project, name: 'Development') } - let(:testing) { create(:label, project: project, name: 'Testing') } - let(:p1) { create(:label, title: 'P1', project: project, priority: 1) } - let(:p2) { create(:label, title: 'P2', project: project, priority: 2) } - let(:p3) { create(:label, title: 'P3', project: project, priority: 3) } - - let!(:backlog) { create(:backlog_list, board: board) } - let!(:list1) { create(:list, board: board, label: development, position: 0) } - let!(:list2) { create(:list, board: board, label: testing, position: 1) } - let!(:closed) { create(:closed_list, board: board) } - - let!(:opened_issue1) { create(:labeled_issue, project: project, labels: [bug]) } - let!(:opened_issue2) { create(:labeled_issue, project: project, labels: [p2]) } - let!(:reopened_issue1) { create(:issue, :opened, project: project) } - - let!(:list1_issue1) { create(:labeled_issue, project: project, labels: [p2, development]) } - let!(:list1_issue2) { create(:labeled_issue, project: project, labels: [development]) } - let!(:list1_issue3) { create(:labeled_issue, project: project, labels: [development, p1]) } - let!(:list2_issue1) { create(:labeled_issue, project: project, labels: [testing]) } - - let!(:closed_issue1) { create(:labeled_issue, :closed, project: project, labels: [bug]) } - let!(:closed_issue2) { create(:labeled_issue, :closed, project: project, labels: [p3]) } - let!(:closed_issue3) { create(:issue, :closed, project: project) } - let!(:closed_issue4) { create(:labeled_issue, :closed, project: project, labels: [p1]) } - let!(:closed_issue5) { create(:labeled_issue, :closed, project: project, labels: [development]) } - - before do - project.add_developer(user) - end - - it 'delegates search to IssuesFinder' do - params = { board_id: board.id, id: list1.id } - - expect_any_instance_of(IssuesFinder).to receive(:execute).once.and_call_original + context 'when parent is a project' do + let(:user) { create(:user) } + let(:project) { create(:project) } + let(:board) { create(:board, project: project) } + + let(:m1) { create(:milestone, project: project) } + let(:m2) { create(:milestone, project: project) } + + let(:bug) { create(:label, project: project, name: 'Bug') } + let(:development) { create(:label, project: project, name: 'Development') } + let(:testing) { create(:label, project: project, name: 'Testing') } + let(:p1) { create(:label, title: 'P1', project: project, priority: 1) } + let(:p2) { create(:label, title: 'P2', project: project, priority: 2) } + let(:p3) { create(:label, title: 'P3', project: project, priority: 3) } + + let!(:backlog) { create(:backlog_list, board: board) } + let!(:list1) { create(:list, board: board, label: development, position: 0) } + let!(:list2) { create(:list, board: board, label: testing, position: 1) } + let!(:closed) { create(:closed_list, board: board) } + + let!(:opened_issue1) { create(:labeled_issue, project: project, milestone: m1, title: 'Issue 1', labels: [bug]) } + let!(:opened_issue2) { create(:labeled_issue, project: project, milestone: m2, title: 'Issue 2', labels: [p2]) } + let!(:reopened_issue1) { create(:issue, :opened, project: project, title: 'Issue 3' ) } + + let!(:list1_issue1) { create(:labeled_issue, project: project, milestone: m1, labels: [p2, development]) } + let!(:list1_issue2) { create(:labeled_issue, project: project, milestone: m2, labels: [development]) } + let!(:list1_issue3) { create(:labeled_issue, project: project, milestone: m1, labels: [development, p1]) } + let!(:list2_issue1) { create(:labeled_issue, project: project, milestone: m1, labels: [testing]) } + + let!(:closed_issue1) { create(:labeled_issue, :closed, project: project, labels: [bug]) } + let!(:closed_issue2) { create(:labeled_issue, :closed, project: project, labels: [p3]) } + let!(:closed_issue3) { create(:issue, :closed, project: project) } + let!(:closed_issue4) { create(:labeled_issue, :closed, project: project, labels: [p1]) } + let!(:closed_issue5) { create(:labeled_issue, :closed, project: project, labels: [development]) } + + let(:parent) { project } + + before do + project.add_developer(user) + end - described_class.new(project, user, params).execute + it_behaves_like 'issues list service' end - context 'issues are ordered by 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 + context 'when parent is a group' do + let(:user) { create(:user) } + let(:group) { create(:group) } + let(:project) { create(:project, :empty_repo, namespace: group) } + let(:project1) { create(:project, :empty_repo, namespace: group) } + let(:board) { create(:board, group: group) } - it 'returns opened issues when listing issues from Backlog' do - params = { board_id: board.id, id: backlog.id } + let(:m1) { create(:milestone, group: group) } + let(:m2) { create(:milestone, group: group) } - issues = described_class.new(project, user, params).execute + let(:bug) { create(:group_label, group: group, name: 'Bug') } + let(:development) { create(:group_label, group: group, name: 'Development') } + let(:testing) { create(:group_label, group: group, name: 'Testing') } - expect(issues).to eq [opened_issue2, reopened_issue1, opened_issue1] - end + let(:p1) { create(:group_label, title: 'P1', group: group) } + let(:p2) { create(:group_label, title: 'P2', group: group) } + let(:p3) { create(:group_label, title: 'P3', group: group) } - it 'returns closed issues when listing issues from Closed' do - params = { board_id: board.id, id: closed.id } + let(:p1_project) { create(:label, title: 'P1_project', project: project, priority: 1) } + let(:p2_project) { create(:label, title: 'P2_project', project: project, priority: 2) } + let(:p3_project) { create(:label, title: 'P3_project', project: project, priority: 3) } - issues = described_class.new(project, user, params).execute + let(:p1_project1) { create(:label, title: 'P1_project1', project: project1, priority: 1) } + let(:p2_project1) { create(:label, title: 'P2_project1', project: project1, priority: 2) } + let(:p3_project1) { create(:label, title: 'P3_project1', project: project1, priority: 3) } - expect(issues).to eq [closed_issue4, closed_issue2, closed_issue5, closed_issue3, closed_issue1] - end + let!(:backlog) { create(:backlog_list, board: board) } + let!(:list1) { create(:list, board: board, label: development, position: 0) } + let!(:list2) { create(:list, board: board, label: testing, position: 1) } + let!(:closed) { create(:closed_list, board: board) } - 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 } + let!(:opened_issue1) { create(:labeled_issue, project: project, milestone: m1, title: 'Issue 1', labels: [bug]) } + let!(:opened_issue2) { create(:labeled_issue, project: project, milestone: m2, title: 'Issue 2', labels: [p2, p2_project]) } + let!(:reopened_issue1) { create(:issue, state: 'opened', project: project, title: 'Issue 3', closed_at: Time.now ) } - issues = described_class.new(project, user, params).execute + let!(:list1_issue1) { create(:labeled_issue, project: project, milestone: m1, labels: [p2, p2_project, development]) } + let!(:list1_issue2) { create(:labeled_issue, project: project, milestone: m2, labels: [development]) } + let!(:list1_issue3) { create(:labeled_issue, project: project1, milestone: m1, labels: [development, p1, p1_project1]) } + let!(:list2_issue1) { create(:labeled_issue, project: project1, milestone: m1, labels: [testing]) } - expect(issues).to eq [list1_issue3, list1_issue1, list1_issue2] - end - end + let!(:closed_issue1) { create(:labeled_issue, :closed, project: project, labels: [bug]) } + let!(:closed_issue2) { create(:labeled_issue, :closed, project: project, labels: [p3, p3_project]) } + let!(:closed_issue3) { create(:issue, :closed, project: project1) } + let!(:closed_issue4) { create(:labeled_issue, :closed, project: project1, labels: [p1, p1_project1]) } + let!(:closed_issue5) { create(:labeled_issue, :closed, project: project1, labels: [development]) } - context 'with list that does not belong to the board' do - it 'raises an error' do - list = create(:list) - service = described_class.new(project, user, board_id: board.id, id: list.id) + let(:parent) { group } - expect { service.execute }.to raise_error(ActiveRecord::RecordNotFound) + before do + group.add_developer(user) end - end - - context 'with invalid list id' do - it 'raises an error' do - service = described_class.new(project, user, board_id: board.id, id: nil) - expect { service.execute }.to raise_error(ActiveRecord::RecordNotFound) - end + it_behaves_like 'issues list service' end end end diff --git a/spec/services/boards/issues/move_service_spec.rb b/spec/services/boards/issues/move_service_spec.rb index 280e411683e..0a6b6d880d3 100644 --- a/spec/services/boards/issues/move_service_spec.rb +++ b/spec/services/boards/issues/move_service_spec.rb @@ -2,108 +2,53 @@ require 'spec_helper' describe Boards::Issues::MoveService do describe '#execute' do - let(:user) { create(:user) } - let(:project) { create(:project) } - let(:board1) { create(:board, project: project) } - - let(:bug) { create(:label, project: project, name: 'Bug') } - let(:development) { create(:label, project: project, name: 'Development') } - let(:testing) { create(:label, project: project, name: 'Testing') } - - let!(:list1) { create(:list, board: board1, label: development, position: 0) } - let!(:list2) { create(:list, board: board1, label: testing, position: 1) } - let!(:closed) { create(:closed_list, board: board1) } - - before do - project.add_developer(user) - end - - context 'when moving an issue between lists' 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: list2.id } } - - it 'delegates the label changes to Issues::UpdateService' do - expect_any_instance_of(Issues::UpdateService).to receive(:execute).with(issue).once - - described_class.new(project, user, params).execute(issue) - end - - it 'removes the label from the list it came from and adds the label of the list it goes to' do - described_class.new(project, user, params).execute(issue) - - expect(issue.reload.labels).to contain_exactly(bug, testing) - end - end - - context 'when moving to closed' do + context 'when parent is a project' do + let(:user) { create(:user) } + let(:project) { create(:project) } + let(:board1) { create(:board, project: project) } let(:board2) { create(:board, project: project) } + + let(:bug) { create(:label, project: project, name: 'Bug') } + let(:development) { create(:label, project: project, name: 'Development') } + let(:testing) { create(:label, project: project, name: 'Testing') } let(:regression) { create(:label, project: project, name: 'Regression') } - let!(:list3) { create(:list, board: board2, label: regression, position: 1) } - let(:issue) { create(:labeled_issue, project: project, labels: [bug, development, testing, regression]) } - let(:params) { { board_id: board1.id, from_list_id: list2.id, to_list_id: closed.id } } + let!(:list1) { create(:list, board: board1, label: development, position: 0) } + let!(:list2) { create(:list, board: board1, label: testing, position: 1) } + let!(:closed) { create(:closed_list, board: board1) } - it 'delegates the close proceedings to Issues::CloseService' do - expect_any_instance_of(Issues::CloseService).to receive(:execute).with(issue).once + let(:parent) { project } - described_class.new(project, user, params).execute(issue) + before do + parent.add_developer(user) end - it 'removes all list-labels from project boards and close the issue' do - described_class.new(project, user, params).execute(issue) - issue.reload - - expect(issue.labels).to contain_exactly(bug) - expect(issue).to be_closed - end + it_behaves_like 'issues move service' end - context 'when moving from closed' do - let(:issue) { create(:labeled_issue, :closed, project: project, labels: [bug]) } - let(:params) { { board_id: board1.id, from_list_id: closed.id, to_list_id: list2.id } } - - it 'delegates the re-open proceedings to Issues::ReopenService' do - expect_any_instance_of(Issues::ReopenService).to receive(:execute).with(issue).once - - described_class.new(project, user, params).execute(issue) - end - - it 'adds the label of the list it goes to and reopen the issue' do - described_class.new(project, user, params).execute(issue) - issue.reload - - expect(issue.labels).to contain_exactly(bug, testing) - expect(issue).to be_opened - end - end + context 'when parent is a group' do + let(:user) { create(:user) } + let(:group) { create(:group) } + let(:project) { create(:project, namespace: group) } + let(:board1) { create(:board, group: group) } + let(:board2) { create(:board, group: group) } - context 'when moving to same list' do - 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 } } + let(:bug) { create(:group_label, group: group, name: 'Bug') } + let(:development) { create(:group_label, group: group, name: 'Development') } + let(:testing) { create(:group_label, group: group, name: 'Testing') } + let(:regression) { create(:group_label, group: group, name: 'Regression') } - it 'returns false' do - expect(described_class.new(project, user, params).execute(issue)).to eq false - end + let!(:list1) { create(:list, board: board1, label: development, position: 0) } + let!(:list2) { create(:list, board: board1, label: testing, position: 1) } + let!(:closed) { create(:closed_list, board: board1) } - it 'keeps issues labels' do - described_class.new(project, user, params).execute(issue) + let(:parent) { group } - expect(issue.reload.labels).to contain_exactly(bug, development) + before do + parent.add_developer(user) end - it 'sorts issues' do - [issue, issue1, issue2].each do |issue| - issue.move_to_end && issue.save! - end - - params.merge!(move_after_id: issue1.id, move_before_id: issue2.id) - - described_class.new(project, user, params).execute(issue) - - expect(issue.relative_position).to be_between(issue1.relative_position, issue2.relative_position) - end + it_behaves_like 'issues move service' end end end diff --git a/spec/services/boards/list_service_spec.rb b/spec/services/boards/list_service_spec.rb index 1d0be99fb35..7518e9e9b75 100644 --- a/spec/services/boards/list_service_spec.rb +++ b/spec/services/boards/list_service_spec.rb @@ -2,36 +2,20 @@ require 'spec_helper' describe Boards::ListService do describe '#execute' do - let(:project) { create(:project) } + context 'when board parent is a project' do + let(:parent) { create(:project) } - subject(:service) { described_class.new(project, double) } + subject(:service) { described_class.new(parent, double) } - context 'when project does not have a board' do - it 'creates a new project board' do - expect { service.execute }.to change(project.boards, :count).by(1) - end - - it 'delegates the project board creation to Boards::CreateService' do - expect_any_instance_of(Boards::CreateService).to receive(:execute).once - - service.execute - end + it_behaves_like 'boards list service' end - context 'when project has a board' do - before do - create(:board, project: project) - end - - it 'does not create a new board' do - expect { service.execute }.not_to change(project.boards, :count) - end - end + context 'when board parent is a group' do + let(:parent) { create(:group) } - it 'returns project boards' do - board = create(:board, project: project) + subject(:service) { described_class.new(parent, double) } - expect(service.execute).to match_array [board] + it_behaves_like 'boards list service' end end end diff --git a/spec/services/boards/lists/create_service_spec.rb b/spec/services/boards/lists/create_service_spec.rb index d5322e1bb21..7d3f5f86deb 100644 --- a/spec/services/boards/lists/create_service_spec.rb +++ b/spec/services/boards/lists/create_service_spec.rb @@ -2,62 +2,77 @@ require 'spec_helper' describe Boards::Lists::CreateService do describe '#execute' do - let(:project) { create(:project) } - let(:board) { create(:board, project: project) } - let(:user) { create(:user) } - let(:label) { create(:label, project: project, name: 'in-progress') } + shared_examples 'creating board lists' do + let(:user) { create(:user) } - subject(:service) { described_class.new(project, user, label_id: label.id) } + subject(:service) { described_class.new(parent, user, label_id: label.id) } - before do - project.add_developer(user) - end + before do + parent.add_developer(user) + end - context 'when board lists is empty' do - it 'creates a new list at beginning of the list' do - list = service.execute(board) + context 'when board lists is empty' do + it 'creates a new list at beginning of the list' do + list = service.execute(board) - expect(list.position).to eq 0 + expect(list.position).to eq 0 + end end - end - context 'when board lists has the done list' do - it 'creates a new list at beginning of the list' do - list = service.execute(board) + context 'when board lists has the done list' do + it 'creates a new list at beginning of the list' do + list = service.execute(board) - expect(list.position).to eq 0 + expect(list.position).to eq 0 + end end - end - context 'when board lists has labels lists' do - it 'creates a new list at end of the lists' do - create(:list, board: board, position: 0) - create(:list, board: board, position: 1) + context 'when board lists has labels lists' do + it 'creates a new list at end of the lists' do + create(:list, board: board, position: 0) + create(:list, board: board, position: 1) - list = service.execute(board) + list = service.execute(board) - expect(list.position).to eq 2 + expect(list.position).to eq 2 + end end - end - context 'when board lists has label and done lists' do - it 'creates a new list at end of the label lists' do - list1 = create(:list, board: board, position: 0) + context 'when board lists has label and done lists' do + it 'creates a new list at end of the label lists' do + list1 = create(:list, board: board, position: 0) - list2 = service.execute(board) + list2 = service.execute(board) - expect(list1.reload.position).to eq 0 - expect(list2.reload.position).to eq 1 + expect(list1.reload.position).to eq 0 + expect(list2.reload.position).to eq 1 + end end - end - context 'when provided label does not belongs to the project' do - it 'raises an error' do - label = create(:label, name: 'in-development') - service = described_class.new(project, user, label_id: label.id) + context 'when provided label does not belongs to the parent' do + it 'raises an error' do + label = create(:label, name: 'in-development') + service = described_class.new(parent, user, label_id: label.id) - expect { service.execute(board) }.to raise_error(ActiveRecord::RecordNotFound) + expect { service.execute(board) }.to raise_error(ActiveRecord::RecordNotFound) + end end end + + context 'when board parent is a project' do + let(:parent) { create(:project) } + let(:board) { create(:board, project: parent) } + let(:label) { create(:label, project: parent, name: 'in-progress') } + + it_behaves_like 'creating board lists' + end + + context 'when board parent is a group' do + let(:parent) { create(:group) } + let(:board) { create(:board, group: parent) } + let(:label) { create(:group_label, group: parent, name: 'in-progress') } + + it_behaves_like 'creating board lists' + end end end diff --git a/spec/services/boards/lists/destroy_service_spec.rb b/spec/services/boards/lists/destroy_service_spec.rb index bd98625b44f..3c4eb6b3fc5 100644 --- a/spec/services/boards/lists/destroy_service_spec.rb +++ b/spec/services/boards/lists/destroy_service_spec.rb @@ -2,37 +2,24 @@ require 'spec_helper' describe Boards::Lists::DestroyService do describe '#execute' do - let(:project) { create(:project) } - let(:board) { create(:board, project: project) } - let(:user) { create(:user) } + context 'when board parent is a project' do + let(:project) { create(:project) } + let(:board) { create(:board, project: project) } + let(:user) { create(:user) } - context 'when list type is label' do - it 'removes list from board' do - list = create(:list, board: board) - service = described_class.new(project, user) + let(:parent) { project } - expect { service.execute(list) }.to change(board.lists, :count).by(-1) - end - - it 'decrements position of higher lists' do - development = create(:list, board: board, position: 0) - review = create(:list, board: board, position: 1) - staging = create(:list, board: board, position: 2) - closed = board.closed_list - - described_class.new(project, user).execute(development) - - expect(review.reload.position).to eq 0 - expect(staging.reload.position).to eq 1 - expect(closed.reload.position).to be_nil - end + it_behaves_like 'lists destroy service' end - it 'does not remove list from board when list type is closed' do - list = board.closed_list - service = described_class.new(project, user) + context 'when board parent is a group' do + let(:group) { create(:group) } + let(:board) { create(:board, group: group) } + let(:user) { create(:user) } + + let(:parent) { group } - expect { service.execute(list) }.not_to change(board.lists, :count) + it_behaves_like 'lists destroy service' end end end diff --git a/spec/services/boards/lists/list_service_spec.rb b/spec/services/boards/lists/list_service_spec.rb index b189857e4f4..24e04eed642 100644 --- a/spec/services/boards/lists/list_service_spec.rb +++ b/spec/services/boards/lists/list_service_spec.rb @@ -1,33 +1,25 @@ require 'spec_helper' describe Boards::Lists::ListService do - let(:project) { create(:project) } - let(:board) { create(:board, project: project) } - let(:label) { create(:label, project: project) } - let!(:list) { create(:list, board: board, label: label) } - let(:service) { described_class.new(project, double) } - describe '#execute' do - context 'when the board has a backlog list' do - let!(:backlog_list) { create(:backlog_list, board: board) } - - it 'does not create a backlog list' do - expect { service.execute(board) }.not_to change(board.lists, :count) - end + context 'when board parent is a project' do + let(:project) { create(:project) } + let(:board) { create(:board, project: project) } + let(:label) { create(:label, project: project) } + let!(:list) { create(:list, board: board, label: label) } + let(:service) { described_class.new(project, double) } - it "returns board's lists" do - expect(service.execute(board)).to eq [backlog_list, list, board.closed_list] - end + it_behaves_like 'lists list service' end - context 'when the board does not have a backlog list' do - it 'creates a backlog list' do - expect { service.execute(board) }.to change(board.lists, :count).by(1) - end + context 'when board parent is a group' do + let(:group) { create(:group) } + let(:board) { create(:board, group: group) } + let(:label) { create(:group_label, group: group) } + let!(:list) { create(:list, board: board, label: label) } + let(:service) { described_class.new(group, double) } - it "returns board's lists" do - expect(service.execute(board)).to eq [board.backlog_list, list, board.closed_list] - end + it_behaves_like 'lists list service' end end end diff --git a/spec/services/boards/lists/move_service_spec.rb b/spec/services/boards/lists/move_service_spec.rb index a9d218bad49..16dfb2ae6af 100644 --- a/spec/services/boards/lists/move_service_spec.rb +++ b/spec/services/boards/lists/move_service_spec.rb @@ -2,100 +2,24 @@ require 'spec_helper' describe Boards::Lists::MoveService do describe '#execute' do - let(:project) { create(:project) } - let(:board) { create(:board, project: project) } - let(:user) { create(:user) } + context 'when board parent is a project' do + let(:project) { create(:project) } + let(:board) { create(:board, project: project) } + let(:user) { create(:user) } - let!(:planning) { create(:list, board: board, position: 0) } - let!(:development) { create(:list, board: board, position: 1) } - let!(:review) { create(:list, board: board, position: 2) } - let!(:staging) { create(:list, board: board, position: 3) } - let!(:closed) { create(:closed_list, board: board) } + let(:parent) { project } - context 'when list type is set to label' do - it 'keeps position of lists when new position is nil' do - service = described_class.new(project, user, position: nil) - - service.execute(planning) - - expect(current_list_positions).to eq [0, 1, 2, 3] - end - - it 'keeps position of lists when new positon is equal to old position' do - service = described_class.new(project, user, position: planning.position) - - service.execute(planning) - - expect(current_list_positions).to eq [0, 1, 2, 3] - end - - it 'keeps position of lists when new positon is negative' do - service = described_class.new(project, user, position: -1) - - service.execute(planning) - - expect(current_list_positions).to eq [0, 1, 2, 3] - end - - it 'keeps position of lists when new positon is equal to number of labels lists' do - service = described_class.new(project, user, position: board.lists.label.size) - - service.execute(planning) - - expect(current_list_positions).to eq [0, 1, 2, 3] - end - - it 'keeps position of lists when new positon is greater than number of labels lists' do - service = described_class.new(project, user, position: board.lists.label.size + 1) - - service.execute(planning) - - expect(current_list_positions).to eq [0, 1, 2, 3] - end - - it 'increments position of intermediate lists when new positon is equal to first position' do - service = described_class.new(project, user, position: 0) - - service.execute(staging) - - expect(current_list_positions).to eq [1, 2, 3, 0] - end - - it 'decrements position of intermediate lists when new positon is equal to last position' do - service = described_class.new(project, user, position: board.lists.label.last.position) - - service.execute(planning) - - expect(current_list_positions).to eq [3, 0, 1, 2] - end - - it 'decrements position of intermediate lists when new position is greater than old position' do - service = described_class.new(project, user, position: 2) - - service.execute(planning) - - expect(current_list_positions).to eq [2, 0, 1, 3] - end - - it 'increments position of intermediate lists when new position is lower than old position' do - service = described_class.new(project, user, position: 1) - - service.execute(staging) - - expect(current_list_positions).to eq [0, 2, 3, 1] - end + it_behaves_like 'lists move service' end - it 'keeps position of lists when list type is closed' do - service = described_class.new(project, user, position: 2) + context 'when board parent is a group' do + let(:group) { create(:group) } + let(:board) { create(:board, group: group) } + let(:user) { create(:user) } - service.execute(closed) + let(:parent) { group } - expect(current_list_positions).to eq [0, 1, 2, 3] + it_behaves_like 'lists move service' end end - - def current_list_positions - [planning, development, review, staging].map { |list| list.reload.position } - end end diff --git a/spec/services/chat_names/find_user_service_spec.rb b/spec/services/chat_names/find_user_service_spec.rb index 79aaac3aeb6..5734b10109a 100644 --- a/spec/services/chat_names/find_user_service_spec.rb +++ b/spec/services/chat_names/find_user_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe ChatNames::FindUserService do +describe ChatNames::FindUserService, :clean_gitlab_redis_shared_state do describe '#execute' do let(:service) { create(:service) } @@ -13,21 +13,30 @@ describe ChatNames::FindUserService do context 'when existing user is requested' do let(:params) { { team_id: chat_name.team_id, user_id: chat_name.chat_id } } - it 'returns the existing user' do - is_expected.to eq(user) + it 'returns the existing chat_name' do + is_expected.to eq(chat_name) end - it 'updates when last time chat name was used' do + it 'updates the last used timestamp if one is not already set' do expect(chat_name.last_used_at).to be_nil subject - initial_last_used = chat_name.reload.last_used_at - expect(initial_last_used).to be_present + expect(chat_name.reload.last_used_at).to be_present + end + + it 'only updates an existing timestamp once within a certain time frame' do + service = described_class.new(service, params) + + expect(chat_name.last_used_at).to be_nil + + service.execute + + time = chat_name.reload.last_used_at - Timecop.travel(2.days.from_now) { described_class.new(service, params).execute } + service.execute - expect(chat_name.reload.last_used_at).to be > initial_last_used + expect(chat_name.reload.last_used_at).to eq(time) end end diff --git a/spec/services/ci/create_trace_artifact_service_spec.rb b/spec/services/ci/create_trace_artifact_service_spec.rb deleted file mode 100644 index 8c5e8e438c7..00000000000 --- a/spec/services/ci/create_trace_artifact_service_spec.rb +++ /dev/null @@ -1,63 +0,0 @@ -require 'spec_helper' - -describe Ci::CreateTraceArtifactService do - describe '#execute' do - subject { described_class.new(nil, nil).execute(job) } - - context 'when the job does not have trace artifact' do - context 'when the job has a trace file' do - let!(:job) { create(:ci_build, :trace_live) } - let!(:legacy_path) { job.trace.read { |stream| return stream.path } } - let!(:legacy_checksum) { Digest::SHA256.file(legacy_path).hexdigest } - let(:new_path) { job.job_artifacts_trace.file.path } - let(:new_checksum) { Digest::SHA256.file(new_path).hexdigest } - - it { expect(File.exist?(legacy_path)).to be_truthy } - - it 'creates trace artifact' do - expect { subject }.to change { Ci::JobArtifact.count }.by(1) - - expect(File.exist?(legacy_path)).to be_falsy - expect(File.exist?(new_path)).to be_truthy - expect(new_checksum).to eq(legacy_checksum) - expect(job.job_artifacts_trace.file.exists?).to be_truthy - expect(job.job_artifacts_trace.file.filename).to eq('job.log') - end - - context 'when failed to create trace artifact record' do - before do - # When ActiveRecord error happens - allow_any_instance_of(Ci::JobArtifact).to receive(:save).and_return(false) - allow_any_instance_of(Ci::JobArtifact).to receive_message_chain(:errors, :full_messages) - .and_return("Error") - - subject rescue nil - - job.reload - end - - it 'keeps legacy trace and removes trace artifact' do - expect(File.exist?(legacy_path)).to be_truthy - expect(job.job_artifacts_trace).to be_nil - end - end - end - - context 'when the job does not have a trace file' do - let!(:job) { create(:ci_build) } - - it 'does not create trace artifact' do - expect { subject }.not_to change { Ci::JobArtifact.count } - end - end - end - - context 'when the job has already had trace artifact' do - let!(:job) { create(:ci_build, :trace_artifact) } - - it 'does not create trace artifact' do - expect { subject }.not_to change { Ci::JobArtifact.count } - end - end - end -end diff --git a/spec/services/clusters/applications/check_ingress_ip_address_service_spec.rb b/spec/services/clusters/applications/check_ingress_ip_address_service_spec.rb new file mode 100644 index 00000000000..bf038595a4d --- /dev/null +++ b/spec/services/clusters/applications/check_ingress_ip_address_service_spec.rb @@ -0,0 +1,73 @@ +require 'spec_helper' + +describe Clusters::Applications::CheckIngressIpAddressService do + let(:application) { create(:clusters_applications_ingress, :installed) } + let(:service) { described_class.new(application) } + let(:kubeclient) { double(::Kubeclient::Client, get_service: kube_service) } + let(:ingress) { [{ ip: '111.222.111.222' }] } + let(:exclusive_lease) { instance_double(Gitlab::ExclusiveLease, try_obtain: true) } + + let(:kube_service) do + ::Kubeclient::Resource.new( + { + status: { + loadBalancer: { + ingress: ingress + } + } + } + ) + end + + subject { service.execute } + + before do + allow(application.cluster).to receive(:kubeclient).and_return(kubeclient) + allow(Gitlab::ExclusiveLease) + .to receive(:new) + .with("check_ingress_ip_address_service:#{application.id}", timeout: 15.seconds.to_i) + .and_return(exclusive_lease) + end + + describe '#execute' do + context 'when the ingress ip address is available' do + it 'updates the external_ip for the app' do + subject + + expect(application.external_ip).to eq('111.222.111.222') + end + end + + context 'when the ingress ip address is not available' do + let(:ingress) { nil } + + it 'does not error' do + subject + end + end + + context 'when the exclusive lease cannot be obtained' do + before do + allow(exclusive_lease) + .to receive(:try_obtain) + .and_return(false) + end + + it 'does not call kubeclient' do + subject + + expect(kubeclient).not_to have_received(:get_service) + end + end + + context 'when there is already an external_ip' do + let(:application) { create(:clusters_applications_ingress, :installed, external_ip: '001.111.002.111') } + + it 'does not call kubeclient' do + subject + + expect(kubeclient).not_to have_received(:get_service) + end + end + end +end diff --git a/spec/services/members/approve_access_request_service_spec.rb b/spec/services/members/approve_access_request_service_spec.rb index b3018169a1c..7076571b753 100644 --- a/spec/services/members/approve_access_request_service_spec.rb +++ b/spec/services/members/approve_access_request_service_spec.rb @@ -1,70 +1,56 @@ require 'spec_helper' describe Members::ApproveAccessRequestService do - let(:user) { create(:user) } - let(:access_requester) { create(:user) } let(:project) { create(:project, :public, :access_requestable) } let(:group) { create(:group, :public, :access_requestable) } + let(:current_user) { create(:user) } + let(:access_requester_user) { create(:user) } + let(:access_requester) { source.requesters.find_by!(user_id: access_requester_user.id) } let(:opts) { {} } shared_examples 'a service raising ActiveRecord::RecordNotFound' do it 'raises ActiveRecord::RecordNotFound' do - expect { described_class.new(source, user, params).execute(opts) }.to raise_error(ActiveRecord::RecordNotFound) + expect { described_class.new(current_user).execute(access_requester, opts) }.to raise_error(ActiveRecord::RecordNotFound) end end shared_examples 'a service raising Gitlab::Access::AccessDeniedError' do it 'raises Gitlab::Access::AccessDeniedError' do - expect { described_class.new(source, user, params).execute(opts) }.to raise_error(Gitlab::Access::AccessDeniedError) + expect { described_class.new(current_user).execute(access_requester, opts) }.to raise_error(Gitlab::Access::AccessDeniedError) end end shared_examples 'a service approving an access request' do it 'succeeds' do - expect { described_class.new(source, user, params).execute(opts) }.to change { source.requesters.count }.by(-1) + expect { described_class.new(current_user).execute(access_requester, opts) }.to change { source.requesters.count }.by(-1) end it 'returns a <Source>Member' do - member = described_class.new(source, user, params).execute(opts) + member = described_class.new(current_user).execute(access_requester, opts) expect(member).to be_a "#{source.class}Member".constantize expect(member.requested_at).to be_nil end context 'with a custom access level' do - let(:params2) { params.merge(user_id: access_requester.id, access_level: Gitlab::Access::MASTER) } - it 'returns a ProjectMember with the custom access level' do - member = described_class.new(source, user, params2).execute(opts) + member = described_class.new(current_user, access_level: Gitlab::Access::MASTER).execute(access_requester, opts) - expect(member.access_level).to eq Gitlab::Access::MASTER + expect(member.access_level).to eq(Gitlab::Access::MASTER) end end end - context 'when no access requester are found' do - let(:params) { { user_id: 42 } } - - it_behaves_like 'a service raising ActiveRecord::RecordNotFound' do - let(:source) { project } - end - - it_behaves_like 'a service raising ActiveRecord::RecordNotFound' do - let(:source) { group } - end - end - context 'when an access requester is found' do before do - project.request_access(access_requester) - group.request_access(access_requester) + project.request_access(access_requester_user) + group.request_access(access_requester_user) end - let(:params) { { user_id: access_requester.id } } context 'when current user is nil' do let(:user) { nil } - context 'and :force option is not given' do + context 'and :ldap option is not given' do it_behaves_like 'a service raising Gitlab::Access::AccessDeniedError' do let(:source) { project } end @@ -74,8 +60,8 @@ describe Members::ApproveAccessRequestService do end end - context 'and :force option is false' do - let(:opts) { { force: false } } + context 'and :skip_authorization option is false' do + let(:opts) { { skip_authorization: false } } it_behaves_like 'a service raising Gitlab::Access::AccessDeniedError' do let(:source) { project } @@ -86,8 +72,8 @@ describe Members::ApproveAccessRequestService do end end - context 'and :force option is true' do - let(:opts) { { force: true } } + context 'and :skip_authorization option is true' do + let(:opts) { { skip_authorization: true } } it_behaves_like 'a service approving an access request' do let(:source) { project } @@ -97,18 +83,6 @@ describe Members::ApproveAccessRequestService do let(:source) { group } end end - - context 'and :force param is true' do - let(:params) { { user_id: access_requester.id, force: true } } - - it_behaves_like 'a service raising Gitlab::Access::AccessDeniedError' do - let(:source) { project } - end - - it_behaves_like 'a service raising Gitlab::Access::AccessDeniedError' do - let(:source) { group } - end - end end context 'when current user cannot approve access request to the project' do @@ -123,8 +97,8 @@ describe Members::ApproveAccessRequestService do context 'when current user can approve access request to the project' do before do - project.add_master(user) - group.add_owner(user) + project.add_master(current_user) + group.add_owner(current_user) end it_behaves_like 'a service approving an access request' do @@ -134,14 +108,6 @@ describe Members::ApproveAccessRequestService do it_behaves_like 'a service approving an access request' do let(:source) { group } end - - context 'when given a :id' do - let(:params) { { id: project.requesters.find_by!(user_id: access_requester.id).id } } - - it_behaves_like 'a service approving an access request' do - let(:source) { project } - end - end end end end diff --git a/spec/services/members/authorized_destroy_service_spec.rb b/spec/services/members/authorized_destroy_service_spec.rb deleted file mode 100644 index 9cf6f64a078..00000000000 --- a/spec/services/members/authorized_destroy_service_spec.rb +++ /dev/null @@ -1,110 +0,0 @@ -require 'spec_helper' - -describe Members::AuthorizedDestroyService do - let(:member_user) { create(:user) } - let(:project) { create(:project, :public) } - let(:group) { create(:group, :public) } - let(:group_project) { create(:project, :public, group: group) } - - def number_of_assigned_issuables(user) - Issue.assigned_to(user).count + MergeRequest.assigned_to(user).count - end - - context 'Invited users' do - # Regression spec for issue: https://gitlab.com/gitlab-org/gitlab-ce/issues/32504 - it 'destroys invited project member' do - project.add_developer(member_user) - - member = create :project_member, :invited, project: project - - expect { described_class.new(member, member_user).execute } - .to change { Member.count }.from(3).to(2) - end - - it "doesn't destroy invited project member notification_settings" do - project.add_developer(member_user) - - member = create :project_member, :invited, project: project - - expect { described_class.new(member, member_user).execute } - .not_to change { NotificationSetting.count } - end - - it 'destroys invited group member' do - group.add_developer(member_user) - - member = create :group_member, :invited, group: group - - expect { described_class.new(member, member_user).execute } - .to change { Member.count }.from(2).to(1) - end - - it "doesn't destroy invited group member notification_settings" do - group.add_developer(member_user) - - member = create :group_member, :invited, group: group - - expect { described_class.new(member, member_user).execute } - .not_to change { NotificationSetting.count } - end - end - - context 'Requested user' do - it "doesn't destroy member notification_settings" do - member = create(:project_member, user: member_user, requested_at: Time.now) - - expect { described_class.new(member, member_user).execute } - .not_to change { NotificationSetting.count } - end - end - - context 'Group member' do - let(:member) { group.members.find_by(user_id: member_user.id) } - - before do - group.add_developer(member_user) - end - - it "unassigns issues and merge requests" do - issue = create :issue, project: group_project, assignees: [member_user] - create :issue, assignees: [member_user] - merge_request = create :merge_request, target_project: group_project, source_project: group_project, assignee: member_user - create :merge_request, target_project: project, source_project: project, assignee: member_user - - expect { described_class.new(member, member_user).execute } - .to change { number_of_assigned_issuables(member_user) }.from(4).to(2) - - expect(issue.reload.assignee_ids).to be_empty - expect(merge_request.reload.assignee_id).to be_nil - end - - it 'destroys member notification_settings' do - group.add_developer(member_user) - member = group.members.find_by(user_id: member_user.id) - - expect { described_class.new(member, member_user).execute } - .to change { member_user.notification_settings.count }.by(-1) - end - end - - context 'Project member' do - let(:member) { project.members.find_by(user_id: member_user.id) } - - before do - project.add_developer(member_user) - end - - it "unassigns issues and merge requests" do - create :issue, project: project, assignees: [member_user] - create :merge_request, target_project: project, source_project: project, assignee: member_user - - expect { described_class.new(member, member_user).execute } - .to change { number_of_assigned_issuables(member_user) }.from(2).to(0) - end - - it 'destroys member notification_settings' do - expect { described_class.new(member, member_user).execute } - .to change { member_user.notification_settings.count }.by(-1) - end - end -end diff --git a/spec/services/members/create_service_spec.rb b/spec/services/members/create_service_spec.rb index 6bd4718e780..1831c62d788 100644 --- a/spec/services/members/create_service_spec.rb +++ b/spec/services/members/create_service_spec.rb @@ -11,7 +11,7 @@ describe Members::CreateService do it 'adds user to members' do params = { user_ids: project_user.id.to_s, access_level: Gitlab::Access::GUEST } - result = described_class.new(project, user, params).execute + result = described_class.new(user, params).execute(project) expect(result[:status]).to eq(:success) expect(project.users).to include project_user @@ -19,7 +19,7 @@ describe Members::CreateService do it 'adds no user to members' do params = { user_ids: '', access_level: Gitlab::Access::GUEST } - result = described_class.new(project, user, params).execute + result = described_class.new(user, params).execute(project) expect(result[:status]).to eq(:error) expect(result[:message]).to be_present @@ -30,7 +30,7 @@ describe Members::CreateService do user_ids = 1.upto(101).to_a.join(',') params = { user_ids: user_ids, access_level: Gitlab::Access::GUEST } - result = described_class.new(project, user, params).execute + result = described_class.new(user, params).execute(project) expect(result[:status]).to eq(:error) expect(result[:message]).to be_present diff --git a/spec/services/members/destroy_service_spec.rb b/spec/services/members/destroy_service_spec.rb index 91152df3ad9..36b6e5a701e 100644 --- a/spec/services/members/destroy_service_spec.rb +++ b/spec/services/members/destroy_service_spec.rb @@ -1,112 +1,226 @@ require 'spec_helper' describe Members::DestroyService do - let(:user) { create(:user) } + let(:current_user) { create(:user) } let(:member_user) { create(:user) } - let(:project) { create(:project, :public) } let(:group) { create(:group, :public) } + let(:group_project) { create(:project, :public, group: group) } + let(:opts) { {} } shared_examples 'a service raising ActiveRecord::RecordNotFound' do it 'raises ActiveRecord::RecordNotFound' do - expect { described_class.new(source, user, params).execute }.to raise_error(ActiveRecord::RecordNotFound) + expect { described_class.new(current_user).execute(member) }.to raise_error(ActiveRecord::RecordNotFound) end end shared_examples 'a service raising Gitlab::Access::AccessDeniedError' do it 'raises Gitlab::Access::AccessDeniedError' do - expect { described_class.new(source, user, params).execute }.to raise_error(Gitlab::Access::AccessDeniedError) + expect { described_class.new(current_user).execute(member) }.to raise_error(Gitlab::Access::AccessDeniedError) end end shared_examples 'a service destroying a member' do it 'destroys the member' do - expect { described_class.new(source, user, params).execute }.to change { source.members.count }.by(-1) + expect { described_class.new(current_user).execute(member, opts) }.to change { member.source.members_and_requesters.count }.by(-1) end - context 'when the given member is an access requester' do - before do - source.members.find_by(user_id: member_user).destroy - source.update_attributes(request_access_enabled: true) - source.request_access(member_user) + it 'destroys member notification_settings' do + if member_user.notification_settings.any? + expect { described_class.new(current_user).execute(member, opts) } + .to change { member_user.notification_settings.count }.by(-1) + else + expect { described_class.new(current_user).execute(member, opts) } + .not_to change { member_user.notification_settings.count } end - let(:access_requester) { source.requesters.find_by(user_id: member_user) } + end + end - it_behaves_like 'a service raising ActiveRecord::RecordNotFound' + shared_examples 'a service destroying a member with access' do + it_behaves_like 'a service destroying a member' - %i[requesters all].each do |scope| - context "and #{scope} scope is passed" do - it 'destroys the access requester' do - expect { described_class.new(source, user, params).execute(scope) }.to change { source.requesters.count }.by(-1) - end + it 'invalidates cached counts for todos and assigned issues and merge requests', :aggregate_failures do + create(:issue, project: group_project, assignees: [member_user]) + create(:merge_request, source_project: group_project, assignee: member_user) + create(:todo, :pending, project: group_project, user: member_user) + create(:todo, :done, project: group_project, user: member_user) - it 'calls Member#after_decline_request' do - expect_any_instance_of(NotificationService).to receive(:decline_access_request).with(access_requester) + expect(member_user.assigned_open_merge_requests_count).to be(1) + expect(member_user.assigned_open_issues_count).to be(1) + expect(member_user.todos_pending_count).to be(1) + expect(member_user.todos_done_count).to be(1) - described_class.new(source, user, params).execute(scope) - end + described_class.new(current_user).execute(member, opts) - context 'when current user is the member' do - it 'does not call Member#after_decline_request' do - expect_any_instance_of(NotificationService).not_to receive(:decline_access_request).with(access_requester) + expect(member_user.assigned_open_merge_requests_count).to be(0) + expect(member_user.assigned_open_issues_count).to be(0) + expect(member_user.todos_pending_count).to be(0) + expect(member_user.todos_done_count).to be(0) + end + end - described_class.new(source, member_user, params).execute(scope) - end - end - end + shared_examples 'a service destroying an access requester' do + it_behaves_like 'a service destroying a member' + + it 'calls Member#after_decline_request' do + expect_any_instance_of(NotificationService).to receive(:decline_access_request).with(member) + + described_class.new(current_user).execute(member) + end + + context 'when current user is the member' do + it 'does not call Member#after_decline_request' do + expect_any_instance_of(NotificationService).not_to receive(:decline_access_request).with(member) + + described_class.new(member_user).execute(member) end end end - context 'when no member are found' do - let(:params) { { user_id: 42 } } + context 'with a member with access' do + before do + group_project.update_attribute(:visibility_level, Gitlab::VisibilityLevel::PRIVATE) + group.update_attribute(:visibility_level, Gitlab::VisibilityLevel::PRIVATE) + end + + context 'when current user cannot destroy the given member' do + context 'with a project member' do + let(:member) { group_project.members.find_by(user_id: member_user.id) } + + before do + group_project.add_developer(member_user) + end + + it_behaves_like 'a service raising Gitlab::Access::AccessDeniedError' - it_behaves_like 'a service raising ActiveRecord::RecordNotFound' do - let(:source) { project } + it_behaves_like 'a service destroying a member with access' do + let(:opts) { { skip_authorization: true } } + end + end + + context 'with a group member' do + let(:member) { group.members.find_by(user_id: member_user.id) } + + before do + group.add_developer(member_user) + end + + it_behaves_like 'a service raising Gitlab::Access::AccessDeniedError' + + it_behaves_like 'a service destroying a member with access' do + let(:opts) { { skip_authorization: true } } + end + end end - it_behaves_like 'a service raising ActiveRecord::RecordNotFound' do - let(:source) { group } + context 'when current user can destroy the given member' do + before do + group_project.add_master(current_user) + group.add_owner(current_user) + end + + context 'with a project member' do + let(:member) { group_project.members.find_by(user_id: member_user.id) } + + before do + group_project.add_developer(member_user) + end + + it_behaves_like 'a service destroying a member with access' + end + + context 'with a group member' do + let(:member) { group.members.find_by(user_id: member_user.id) } + + before do + group.add_developer(member_user) + end + + it_behaves_like 'a service destroying a member with access' + end end end - context 'when a member is found' do + context 'with an access requester' do before do - project.add_developer(member_user) - group.add_developer(member_user) + group_project.update_attributes(request_access_enabled: true) + group.update_attributes(request_access_enabled: true) + group_project.request_access(member_user) + group.request_access(member_user) end - let(:params) { { user_id: member_user.id } } - context 'when current user cannot destroy the given member' do + context 'when current user cannot destroy the given access requester' do it_behaves_like 'a service raising Gitlab::Access::AccessDeniedError' do - let(:source) { project } + let(:member) { group_project.requesters.find_by(user_id: member_user.id) } + end + + it_behaves_like 'a service destroying a member' do + let(:opts) { { skip_authorization: true } } + let(:member) { group_project.requesters.find_by(user_id: member_user.id) } end it_behaves_like 'a service raising Gitlab::Access::AccessDeniedError' do - let(:source) { group } + let(:member) { group.requesters.find_by(user_id: member_user.id) } + end + + it_behaves_like 'a service destroying a member' do + let(:opts) { { skip_authorization: true } } + let(:member) { group.requesters.find_by(user_id: member_user.id) } end end - context 'when current user can destroy the given member' do + context 'when current user can destroy the given access requester' do before do - project.add_master(user) - group.add_owner(user) + group_project.add_master(current_user) + group.add_owner(current_user) + end + + it_behaves_like 'a service destroying an access requester' do + let(:member) { group_project.requesters.find_by(user_id: member_user.id) } + end + + it_behaves_like 'a service destroying an access requester' do + let(:member) { group.requesters.find_by(user_id: member_user.id) } + end + end + end + + context 'with an invited user' do + let(:project_invited_member) { create(:project_member, :invited, project: group_project) } + let(:group_invited_member) { create(:group_member, :invited, group: group) } + + context 'when current user cannot destroy the given invited user' do + it_behaves_like 'a service raising Gitlab::Access::AccessDeniedError' do + let(:member) { project_invited_member } end it_behaves_like 'a service destroying a member' do - let(:source) { project } + let(:opts) { { skip_authorization: true } } + let(:member) { project_invited_member } + end + + it_behaves_like 'a service raising Gitlab::Access::AccessDeniedError' do + let(:member) { group_invited_member } end it_behaves_like 'a service destroying a member' do - let(:source) { group } + let(:opts) { { skip_authorization: true } } + let(:member) { group_invited_member } end + end - context 'when given a :id' do - let(:params) { { id: project.members.find_by!(user_id: user.id).id } } + context 'when current user can destroy the given invited user' do + before do + group_project.add_master(current_user) + group.add_owner(current_user) + end - it 'destroys the member' do - expect { described_class.new(project, user, params).execute } - .to change { project.members.count }.by(-1) - end + # Regression spec for issue: https://gitlab.com/gitlab-org/gitlab-ce/issues/32504 + it_behaves_like 'a service destroying a member' do + let(:member) { project_invited_member } + end + + it_behaves_like 'a service destroying a member' do + let(:member) { group_invited_member } end end end diff --git a/spec/services/members/request_access_service_spec.rb b/spec/services/members/request_access_service_spec.rb index 0a704bba521..e93ba5a85c0 100644 --- a/spec/services/members/request_access_service_spec.rb +++ b/spec/services/members/request_access_service_spec.rb @@ -5,17 +5,17 @@ describe Members::RequestAccessService do shared_examples 'a service raising Gitlab::Access::AccessDeniedError' do it 'raises Gitlab::Access::AccessDeniedError' do - expect { described_class.new(source, user).execute }.to raise_error(Gitlab::Access::AccessDeniedError) + expect { described_class.new(user).execute(source) }.to raise_error(Gitlab::Access::AccessDeniedError) end end shared_examples 'a service creating a access request' do it 'succeeds' do - expect { described_class.new(source, user).execute }.to change { source.requesters.count }.by(1) + expect { described_class.new(user).execute(source) }.to change { source.requesters.count }.by(1) end it 'returns a <Source>Member' do - member = described_class.new(source, user).execute + member = described_class.new(user).execute(source) expect(member).to be_a "#{source.class}Member".constantize expect(member.requested_at).to be_present diff --git a/spec/services/members/update_service_spec.rb b/spec/services/members/update_service_spec.rb new file mode 100644 index 00000000000..a451272dd1f --- /dev/null +++ b/spec/services/members/update_service_spec.rb @@ -0,0 +1,59 @@ +require 'spec_helper' + +describe Members::UpdateService do + let(:project) { create(:project, :public) } + let(:group) { create(:group, :public) } + let(:current_user) { create(:user) } + let(:member_user) { create(:user) } + let(:permission) { :update } + let(:member) { source.members_and_requesters.find_by!(user_id: member_user.id) } + let(:params) do + { access_level: Gitlab::Access::MASTER } + end + + shared_examples 'a service raising Gitlab::Access::AccessDeniedError' do + it 'raises Gitlab::Access::AccessDeniedError' do + expect { described_class.new(current_user, params).execute(member, permission: permission) } + .to raise_error(Gitlab::Access::AccessDeniedError) + end + end + + shared_examples 'a service updating a member' do + it 'updates the member' do + updated_member = described_class.new(current_user, params).execute(member, permission: permission) + + expect(updated_member).to be_valid + expect(updated_member.access_level).to eq(Gitlab::Access::MASTER) + end + end + + before do + project.add_developer(member_user) + group.add_developer(member_user) + end + + context 'when current user cannot update the given member' do + it_behaves_like 'a service raising Gitlab::Access::AccessDeniedError' do + let(:source) { project } + end + + it_behaves_like 'a service raising Gitlab::Access::AccessDeniedError' do + let(:source) { group } + end + end + + context 'when current user can update the given member' do + before do + project.add_master(current_user) + group.add_owner(current_user) + end + + it_behaves_like 'a service updating a member' do + let(:source) { project } + end + + it_behaves_like 'a service updating a member' do + let(:source) { group } + end + end +end diff --git a/spec/services/merge_requests/build_service_spec.rb b/spec/services/merge_requests/build_service_spec.rb index 3a935d98540..6aed481939e 100644 --- a/spec/services/merge_requests/build_service_spec.rb +++ b/spec/services/merge_requests/build_service_spec.rb @@ -15,8 +15,8 @@ describe MergeRequests::BuildService do let(:target_branch) { 'master' } let(:merge_request) { service.execute } let(:compare) { double(:compare, commits: commits) } - let(:commit_1) { double(:commit_1, safe_message: "Initial commit\n\nCreate the app") } - let(:commit_2) { double(:commit_2, safe_message: 'This is a bad commit message!') } + let(:commit_1) { double(:commit_1, sha: 'f00ba7', safe_message: "Initial commit\n\nCreate the app") } + let(:commit_2) { double(:commit_2, sha: 'f00ba7', safe_message: 'This is a bad commit message!') } let(:commits) { nil } let(:service) do diff --git a/spec/services/merge_requests/create_service_spec.rb b/spec/services/merge_requests/create_service_spec.rb index 5d226f34d2d..44a83c436cb 100644 --- a/spec/services/merge_requests/create_service_spec.rb +++ b/spec/services/merge_requests/create_service_spec.rb @@ -28,6 +28,7 @@ describe MergeRequests::CreateService do it 'creates an MR' do expect(merge_request).to be_valid + expect(merge_request.work_in_progress?).to be(false) expect(merge_request.title).to eq('Awesome merge_request') expect(merge_request.assignee).to be_nil expect(merge_request.merge_params['force_remove_source_branch']).to eq('1') @@ -62,6 +63,40 @@ describe MergeRequests::CreateService do expect(Event.where(attributes).count).to eq(1) end + describe 'when marked with /wip' do + context 'in title and in description' do + let(:opts) do + { + title: 'WIP: Awesome merge_request', + description: "well this is not done yet\n/wip", + source_branch: 'feature', + target_branch: 'master', + assignee: assignee + } + end + + it 'sets MR to WIP' do + expect(merge_request.work_in_progress?).to be(true) + end + end + + context 'in description only' do + let(:opts) do + { + title: 'Awesome merge_request', + description: "well this is not done yet\n/wip", + source_branch: 'feature', + target_branch: 'master', + assignee: assignee + } + end + + it 'sets MR to WIP' do + expect(merge_request.work_in_progress?).to be(true) + end + end + end + context 'when merge request is assigned to someone' do let(:opts) do { diff --git a/spec/services/merge_requests/update_service_spec.rb b/spec/services/merge_requests/update_service_spec.rb index c31259239ee..5279ea6164e 100644 --- a/spec/services/merge_requests/update_service_spec.rb +++ b/spec/services/merge_requests/update_service_spec.rb @@ -1,6 +1,8 @@ require 'spec_helper' describe MergeRequests::UpdateService, :mailer do + include ProjectForksHelper + let(:project) { create(:project, :repository) } let(:user) { create(:user) } let(:user2) { create(:user) } @@ -538,5 +540,40 @@ describe MergeRequests::UpdateService, :mailer do let(:open_issuable) { merge_request } let(:closed_issuable) { create(:closed_merge_request, source_project: project) } end + + context 'setting `allow_maintainer_to_push`' do + let(:target_project) { create(:project, :public) } + let(:source_project) { fork_project(target_project) } + let(:user) { create(:user) } + let(:merge_request) do + create(:merge_request, + source_project: source_project, + source_branch: 'fixes', + target_project: target_project) + end + + before do + allow(ProtectedBranch).to receive(:protected?).with(source_project, 'fixes') { false } + end + + it 'does not allow a maintainer of the target project to set `allow_maintainer_to_push`' do + target_project.add_developer(user) + + update_merge_request(allow_maintainer_to_push: true, title: 'Updated title') + + expect(merge_request.title).to eq('Updated title') + expect(merge_request.allow_maintainer_to_push).to be_falsy + end + + it 'is allowed by a user that can push to the source and can update the merge request' do + merge_request.update!(assignee: user) + source_project.add_developer(user) + + update_merge_request(allow_maintainer_to_push: true, title: 'Updated title') + + expect(merge_request.title).to eq('Updated title') + expect(merge_request.allow_maintainer_to_push).to be_truthy + end + end end end diff --git a/spec/services/notes/create_service_spec.rb b/spec/services/notes/create_service_spec.rb index 0ae26e87154..f5cff66de6d 100644 --- a/spec/services/notes/create_service_spec.rb +++ b/spec/services/notes/create_service_spec.rb @@ -57,32 +57,55 @@ describe Notes::CreateService do end end - describe 'note with commands' do - describe '/close, /label, /assign & /milestone' do - let(:note_text) { %(HELLO\n/close\n/assign @#{user.username}\nWORLD) } + context 'note with commands' do + context 'as a user who can update the target' do + context '/close, /label, /assign & /milestone' do + let(:note_text) { %(HELLO\n/close\n/assign @#{user.username}\nWORLD) } - it 'saves the note and does not alter the note text' do - expect_any_instance_of(Issues::UpdateService).to receive(:execute).and_call_original + it 'saves the note and does not alter the note text' do + expect_any_instance_of(Issues::UpdateService).to receive(:execute).and_call_original - note = described_class.new(project, user, opts.merge(note: note_text)).execute + note = described_class.new(project, user, opts.merge(note: note_text)).execute - expect(note.note).to eq "HELLO\nWORLD" + expect(note.note).to eq "HELLO\nWORLD" + end + end + + context '/merge with sha option' do + let(:note_text) { %(HELLO\n/merge\nWORLD) } + let(:params) { opts.merge(note: note_text, merge_request_diff_head_sha: 'sha') } + + it 'saves the note and exectues merge command' do + note = described_class.new(project, user, params).execute + + expect(note.note).to eq "HELLO\nWORLD" + end end end - describe '/merge with sha option' do - let(:note_text) { %(HELLO\n/merge\nWORLD) } - let(:params) { opts.merge(note: note_text, merge_request_diff_head_sha: 'sha') } + context 'as a user who cannot update the target' do + let(:note_text) { "HELLO\n/todo\n/assign #{user.to_reference}\nWORLD" } + let(:note) { described_class.new(project, user, opts.merge(note: note_text)).execute } - it 'saves the note and exectues merge command' do - note = described_class.new(project, user, params).execute + before do + project.team.find_member(user.id).update!(access_level: Gitlab::Access::GUEST) + end + + it 'applies commands the user can execute' do + expect { note }.to change { user.todos_pending_count }.from(0).to(1) + end + + it 'does not apply commands the user cannot execute' do + expect { note }.not_to change { issue.assignees } + end + it 'saves the note' do expect(note.note).to eq "HELLO\nWORLD" end end end - describe 'personal snippet note' do + context 'personal snippet note' do subject { described_class.new(nil, user, params).execute } let(:snippet) { create(:personal_snippet) } @@ -103,7 +126,7 @@ describe Notes::CreateService do end end - describe 'note with emoji only' do + context 'note with emoji only' do it 'creates regular note' do opts = { note: ':smile: ', diff --git a/spec/services/notes/quick_actions_service_spec.rb b/spec/services/notes/quick_actions_service_spec.rb index 5eafe56c99d..b1e218821d2 100644 --- a/spec/services/notes/quick_actions_service_spec.rb +++ b/spec/services/notes/quick_actions_service_spec.rb @@ -165,31 +165,17 @@ describe Notes::QuickActionsService do let(:note) { create(:note_on_issue, project: project) } - context 'with no current_user' do - it 'returns false' do - expect(described_class.supported?(note, nil)).to be_falsy - end - end - - context 'when current_user cannot update the noteable' do - it 'returns false' do - user = create(:user) - - expect(described_class.supported?(note, user)).to be_falsy - end - end - - context 'when current_user can update the noteable' do + context 'with a note on an issue' do it 'returns true' do - expect(described_class.supported?(note, master)).to be_truthy + expect(described_class.supported?(note)).to be_truthy end + end - context 'with a note on a commit' do - let(:note) { create(:note_on_commit, project: project) } + context 'with a note on a commit' do + let(:note) { create(:note_on_commit, project: project) } - it 'returns false' do - expect(described_class.supported?(note, nil)).to be_falsy - end + it 'returns false' do + expect(described_class.supported?(note)).to be_falsy end end end @@ -201,7 +187,7 @@ describe Notes::QuickActionsService do service = described_class.new(project, master) note = create(:note_on_issue, project: project) - expect(described_class).to receive(:supported?).with(note, master) + expect(described_class).to receive(:supported?).with(note) service.supported?(note) end diff --git a/spec/services/projects/update_pages_service_spec.rb b/spec/services/projects/update_pages_service_spec.rb index bfb86284d86..934106627a9 100644 --- a/spec/services/projects/update_pages_service_spec.rb +++ b/spec/services/projects/update_pages_service_spec.rb @@ -34,6 +34,7 @@ describe Projects::UpdatePagesService do context 'with expiry date' do before do build.artifacts_expire_in = "2 days" + build.save! end it "doesn't delete artifacts" do @@ -105,6 +106,7 @@ describe Projects::UpdatePagesService do context 'with expiry date' do before do build.artifacts_expire_in = "2 days" + build.save! end it "doesn't delete artifacts" do @@ -159,6 +161,20 @@ describe Projects::UpdatePagesService do expect(execute).not_to eq(:success) end + + context 'when timeout happens by DNS error' do + before do + allow_any_instance_of(described_class) + .to receive(:extract_zip_archive!).and_raise(SocketError) + end + + it 'raises an error' do + expect { execute }.to raise_error(SocketError) + + build.reload + expect(build.artifacts?).to eq(true) + end + end end end diff --git a/spec/services/projects/update_service_spec.rb b/spec/services/projects/update_service_spec.rb index a0b97ceead9..d454ac0bda5 100644 --- a/spec/services/projects/update_service_spec.rb +++ b/spec/services/projects/update_service_spec.rb @@ -123,6 +123,49 @@ describe Projects::UpdateService do end end + context 'when we update project but not enabling a wiki' do + it 'does not try to create an empty wiki' do + FileUtils.rm_rf(project.wiki.repository.path) + + result = update_project(project, user, { name: 'test1' }) + + expect(result).to eq({ status: :success }) + expect(project.wiki_repository_exists?).to be false + end + + it 'handles empty project feature attributes' do + project.project_feature.update(wiki_access_level: ProjectFeature::DISABLED) + + result = update_project(project, user, { name: 'test1' }) + + expect(result).to eq({ status: :success }) + expect(project.wiki_repository_exists?).to be false + end + end + + context 'when enabling a wiki' do + it 'creates a wiki' do + project.project_feature.update(wiki_access_level: ProjectFeature::DISABLED) + FileUtils.rm_rf(project.wiki.repository.path) + + result = update_project(project, user, project_feature_attributes: { wiki_access_level: ProjectFeature::ENABLED }) + + expect(result).to eq({ status: :success }) + expect(project.wiki_repository_exists?).to be true + expect(project.wiki_enabled?).to be true + end + + it 'logs an error and creates a metric when wiki can not be created' do + project.project_feature.update(wiki_access_level: ProjectFeature::DISABLED) + + expect_any_instance_of(ProjectWiki).to receive(:wiki).and_raise(ProjectWiki::CouldNotCreateWikiError) + expect_any_instance_of(described_class).to receive(:log_error).with("Could not create wiki for #{project.full_name}") + expect(Gitlab::Metrics).to receive(:counter) + + update_project(project, user, project_feature_attributes: { wiki_access_level: ProjectFeature::ENABLED }) + end + end + context 'when updating a project that contains container images' do before do stub_container_registry_config(enabled: true) diff --git a/spec/services/prometheus/adapter_service_spec.rb b/spec/services/prometheus/adapter_service_spec.rb new file mode 100644 index 00000000000..335fc5844aa --- /dev/null +++ b/spec/services/prometheus/adapter_service_spec.rb @@ -0,0 +1,40 @@ +require 'spec_helper' + +describe Prometheus::AdapterService do + let(:project) { create(:project) } + subject { described_class.new(project) } + + describe '#prometheus_adapter' do + let(:cluster) { create(:cluster, :provided_by_user, environment_scope: '*', projects: [project]) } + + context 'prometheus service can execute queries' do + let(:prometheus_service) { double(:prometheus_service, can_query?: true) } + + before do + allow(project).to receive(:find_or_initialize_service).with('prometheus').and_return prometheus_service + end + + it 'return prometheus service as prometheus adapter' do + expect(subject.prometheus_adapter).to eq(prometheus_service) + end + end + + context "prometheus service can't execute queries" do + let(:prometheus_service) { double(:prometheus_service, can_query?: false) } + + context 'with cluster with prometheus installed' do + let!(:prometheus) { create(:clusters_applications_prometheus, :installed, cluster: cluster) } + + it 'returns application handling all environments' do + expect(subject.prometheus_adapter).to eq(prometheus) + end + end + + context 'with cluster without prometheus installed' do + it 'returns nil' do + expect(subject.prometheus_adapter).to be_nil + end + end + end + end +end diff --git a/spec/services/system_hooks_service_spec.rb b/spec/services/system_hooks_service_spec.rb index c40cd5b7548..51396d34f8f 100644 --- a/spec/services/system_hooks_service_spec.rb +++ b/spec/services/system_hooks_service_spec.rb @@ -30,6 +30,7 @@ describe SystemHooksService do :old_path_with_namespace ) end + it do project.old_path_with_namespace = 'transfered_from_path' expect(event_data(project, :transfer)).to include( @@ -45,18 +46,21 @@ describe SystemHooksService do :owner_name, :owner_email ) end + it do expect(event_data(group, :destroy)).to include( :event_name, :name, :created_at, :updated_at, :path, :group_id, :owner_name, :owner_email ) end + it do expect(event_data(group_member, :create)).to include( :event_name, :created_at, :updated_at, :group_name, :group_path, :group_id, :user_id, :user_username, :user_name, :user_email, :group_access ) end + it do expect(event_data(group_member, :destroy)).to include( :event_name, :created_at, :updated_at, :group_name, :group_path, @@ -70,6 +74,14 @@ describe SystemHooksService do expect(data[:project_visibility]).to eq('private') end + it 'handles nil datetime columns' do + user.update_attributes(created_at: nil, updated_at: nil) + data = event_data(user, :destroy) + + expect(data[:created_at]).to be(nil) + expect(data[:updated_at]).to be(nil) + end + context 'group_rename' do it 'contains old and new path' do allow(group).to receive(:path_was).and_return('old-path') diff --git a/spec/services/system_note_service_spec.rb b/spec/services/system_note_service_spec.rb index 5b5edc1aa0d..a3893188c6e 100644 --- a/spec/services/system_note_service_spec.rb +++ b/spec/services/system_note_service_spec.rb @@ -789,7 +789,7 @@ describe SystemNoteService do object: { url: project_commit_url(project, commit), title: "GitLab: Mentioned on commit - #{commit.title}", - icon: { title: "GitLab", url16x16: "https://gitlab.com/favicon.ico" }, + icon: { title: "GitLab", url16x16: "http://localhost/favicon.ico" }, status: { resolved: false } } ) @@ -815,7 +815,7 @@ describe SystemNoteService do object: { url: project_issue_url(project, issue), title: "GitLab: Mentioned on issue - #{issue.title}", - icon: { title: "GitLab", url16x16: "https://gitlab.com/favicon.ico" }, + icon: { title: "GitLab", url16x16: "http://localhost/favicon.ico" }, status: { resolved: false } } ) @@ -841,7 +841,7 @@ describe SystemNoteService do object: { url: project_snippet_url(project, snippet), title: "GitLab: Mentioned on snippet - #{snippet.title}", - icon: { title: "GitLab", url16x16: "https://gitlab.com/favicon.ico" }, + icon: { title: "GitLab", url16x16: "http://localhost/favicon.ico" }, status: { resolved: false } } ) |