require 'spec_helper' describe Issue do using RSpec::Parameterized::TableSyntax include ExternalAuthorizationServiceHelpers describe 'validations' do subject { build(:issue) } describe 'weight' do it 'is not valid when negative number' do subject.weight = -1 expect(subject).not_to be_valid expect(subject.errors[:weight]).not_to be_empty end it 'is valid when non-negative' do subject.weight = 0 expect(subject).to be_valid subject.weight = 1 expect(subject).to be_valid end end end it_behaves_like 'an editable mentionable with EE-specific mentions' do subject { create(:issue, project: create(:project, :repository)) } let(:backref_text) { "issue #{subject.to_reference}" } let(:set_mentionable_text) { ->(txt) { subject.description = txt } } end describe '#related_issues' do let(:user) { create(:user) } let(:authorized_project) { create(:project) } let(:authorized_project2) { create(:project) } let(:unauthorized_project) { create(:project) } let(:authorized_issue_a) { create(:issue, project: authorized_project) } let(:authorized_issue_b) { create(:issue, project: authorized_project) } let(:authorized_issue_c) { create(:issue, project: authorized_project2) } let(:unauthorized_issue) { create(:issue, project: unauthorized_project) } let!(:issue_link_a) { create(:issue_link, source: authorized_issue_a, target: authorized_issue_b) } let!(:issue_link_b) { create(:issue_link, source: authorized_issue_a, target: unauthorized_issue) } let!(:issue_link_c) { create(:issue_link, source: authorized_issue_a, target: authorized_issue_c) } before do authorized_project.add_developer(user) authorized_project2.add_developer(user) end it 'returns only authorized related issues for given user' do expect(authorized_issue_a.related_issues(user)) .to contain_exactly(authorized_issue_b, authorized_issue_c) end describe 'when a user cannot read cross project' do it 'only returns issues within the same project' do expect(Ability).to receive(:allowed?).with(user, :read_cross_project) .and_return(false) expect(authorized_issue_a.related_issues(user)) .to contain_exactly(authorized_issue_b) end end end describe '#allows_multiple_assignees?' do it 'does not allow multiple assignees without license' do stub_licensed_features(multiple_issue_assignees: false) issue = build(:issue) expect(issue.allows_multiple_assignees?).to be_falsey end it 'does not allow multiple assignees without license' do stub_licensed_features(multiple_issue_assignees: true) issue = build(:issue) expect(issue.allows_multiple_assignees?).to be_truthy end end describe '#sort' do let(:project) { create(:project) } context "by weight" do let!(:issue) { create(:issue, project: project) } let!(:issue2) { create(:issue, weight: 1, project: project) } let!(:issue3) { create(:issue, weight: 2, project: project) } let!(:issue4) { create(:issue, weight: 3, project: project) } it "sorts desc" do issues = project.issues.sort_by_attribute('weight_desc') expect(issues).to eq([issue4, issue3, issue2, issue]) end it "sorts asc" do issues = project.issues.sort_by_attribute('weight_asc') expect(issues).to eq([issue2, issue3, issue4, issue]) end end end describe '#weight' do where(:license_value, :database_value, :expected) do true | 5 | 5 true | nil | nil false | 5 | nil false | nil | nil end with_them do let(:issue) { build(:issue, weight: database_value) } subject { issue.weight } before do stub_licensed_features(issue_weights: license_value) end it { is_expected.to eq(expected) } end end context 'when an external authentication service' do before do enable_external_authorization_service_check end describe '#publicly_visible?' do it 'is `false` when an external authorization service is enabled' do issue = build(:issue, project: build(:project, :public)) expect(issue).not_to be_publicly_visible end end describe '#readable_by?' do it 'checks the external service to determine if an issue is readable by a user' do project = build(:project, :public, external_authorization_classification_label: 'a-label') issue = build(:issue, project: project) user = build(:user) expect(EE::Gitlab::ExternalAuthorization).to receive(:access_allowed?).with(user, 'a-label') { false } expect(issue.readable_by?(user)).to be_falsy end it 'does not check the external webservice for admins' do issue = build(:issue) user = build(:admin) expect(EE::Gitlab::ExternalAuthorization).not_to receive(:access_allowed?) issue.readable_by?(user) end it 'does not check the external webservice for auditors' do issue = build(:issue) user = build(:auditor) expect(EE::Gitlab::ExternalAuthorization).not_to receive(:access_allowed?) issue.readable_by?(user) end end end describe 'relative positioning with group boards' do let(:group) { create(:group) } let!(:board) { create(:board, group: group) } let(:project) { create(:project, namespace: group) } let(:project1) { create(:project, namespace: group) } let(:issue) { create(:issue, project: project) } let(:issue1) { create(:issue, project: project1) } let(:new_issue) { create(:issue, project: project1) } before do [issue, issue1].each do |issue| issue.move_to_end && issue.save 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 nil if there is no issue above' do expect(issue.prev_relative_position).to eq nil 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 nil if there is no issue below' do expect(issue1.next_relative_position).to eq nil 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 '#shift_after?' do it 'returns true' do issue.update(relative_position: issue1.relative_position - 1) expect(issue.shift_after?).to be_truthy end it 'returns false' do issue.update(relative_position: issue1.relative_position - 2) expect(issue.shift_after?).to be_falsey end end describe '#shift_before?' do it 'returns true' do issue.update(relative_position: issue1.relative_position + 1) expect(issue.shift_before?).to be_truthy end it 'returns false' do issue.update(relative_position: issue1.relative_position + 2) expect(issue.shift_before?).to be_falsey 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 it 'positions issues between other two if distance is 1' do issue1.update relative_position: issue.relative_position + 1 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 it 'positions issue in the middle of other two if distance is big enough' do issue.update relative_position: 6000 issue1.update relative_position: 10000 new_issue.move_between(issue, issue1) expect(new_issue.relative_position).to eq(8000) end it 'positions issue closer to the middle if we are at the very top' do issue1.update relative_position: 6000 new_issue.move_between(nil, issue1) expect(new_issue.relative_position).to eq(6000 - RelativePositioning::IDEAL_DISTANCE) end it 'positions issue closer to the middle if we are at the very bottom' do issue.update relative_position: 6000 issue1.update relative_position: nil new_issue.move_between(issue, nil) expect(new_issue.relative_position).to eq(6000 + RelativePositioning::IDEAL_DISTANCE) end it 'positions issue in the middle of other two if distance is not big enough' do issue.update relative_position: 100 issue1.update relative_position: 400 new_issue.move_between(issue, issue1) expect(new_issue.relative_position).to eq(250) end it 'positions issue in the middle of other two is there is no place' do issue.update relative_position: 100 issue1.update relative_position: 101 new_issue.move_between(issue, issue1) expect(new_issue.relative_position).to be_between(issue.relative_position, issue1.relative_position) end it 'uses rebalancing if there is no place' do issue.update relative_position: 100 issue1.update relative_position: 101 issue2 = create(:issue, relative_position: 102, project: project) new_issue.update relative_position: 103 new_issue.move_between(issue1, issue2) new_issue.save! expect(new_issue.relative_position).to be_between(issue1.relative_position, issue2.relative_position) expect(issue.reload.relative_position).not_to eq(100) end it 'positions issue right if we pass none-sequential parameters' do issue.update relative_position: 99 issue1.update relative_position: 101 issue2 = create(:issue, relative_position: 102, project: project) new_issue.update relative_position: 103 new_issue.move_between(issue, issue2) new_issue.save! expect(new_issue.relative_position).to be(100) end end end end