OpenProject is the leading open source project management software.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 
openproject/spec/models/changeset_spec.rb

409 lines
13 KiB

#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) 2012-2021 the OpenProject GmbH
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See COPYRIGHT and LICENSE files for more details.
#++
require 'spec_helper'
describe Changeset, type: :model do
let(:email) { 'bob@bobbit.org' }
with_virtual_subversion_repository do
let(:changeset) do
FactoryBot.build(:changeset,
repository: repository,
revision: '1',
committer: email,
comments: 'Initial commit')
end
end
shared_examples_for 'valid changeset' do
it { expect(changeset.revision).to eq('1') }
it { expect(changeset.committer).to eq(email) }
it { expect(changeset.comments).to eq('Initial commit') }
describe 'journal' do
let(:journal) { changeset.journals.first }
it { expect(journal.user).to eq(journal_user) }
it { expect(journal.notes).to eq(changeset.comments) }
end
end
describe 'empty comment' do
it 'should comments empty' do
changeset.comments = ''
expect(changeset.save).to eq true
expect(changeset.comments).to eq ''
if changeset.comments.respond_to?(:force_encoding)
assert_equal 'UTF-8', changeset.comments.encoding.to_s
end
end
it 'should comments nil' do
changeset.comments = nil
expect(changeset.save).to eq true
expect(changeset.comments).to eq ''
if changeset.comments.respond_to?(:force_encoding)
assert_equal 'UTF-8', changeset.comments.encoding.to_s
end
end
end
describe 'stripping commit' do
let(:comment) { 'This is a looooooooooooooong comment' + (' ' * 80 + "\n") * 5 }
with_virtual_subversion_repository do
let(:changeset) do
FactoryBot.build(:changeset,
repository: repository,
revision: '1',
committer: email,
comments: comment)
end
end
it 'should for changeset comments strip' do
expect(changeset.save).to eq true
expect(comment).to_not eq changeset.comments
expect(changeset.comments).to eq 'This is a looooooooooooooong comment'
end
end
describe 'mapping' do
let!(:user) { FactoryBot.create :user, login: 'jsmith', mail: 'jsmith@somenet.foo' }
let!(:repository) { FactoryBot.create(:repository_subversion) }
it 'should manual user mapping' do
c = Changeset.create! repository: repository,
committer: 'foo',
committed_on: Time.now,
revision: 100,
comments: 'Committed by foo.'
expect(c.user).to be_nil
repository.committer_ids = { 'foo' => user.id }
expect(c.reload.user).to eq user
# committer is now mapped
c = Changeset.create! repository: repository,
committer: 'foo',
committed_on: Time.now,
revision: 101,
comments: 'Another commit by foo.'
expect(c.user).to eq user
end
it 'should auto user mapping by username' do
c = Changeset.create! repository: repository,
committer: 'jsmith',
committed_on: Time.now,
revision: 100,
comments: 'Committed by john.'
expect(c.user).to eq user
end
it 'should auto user mapping by email' do
c = Changeset.create! repository: repository,
committer: 'john <jsmith@somenet.foo>',
committed_on: Time.now,
revision: 100,
comments: 'Committed by john.'
expect(c.user).to eq user
end
end
describe '#scan_comment_for_work_package_ids',
with_settings: {
commit_fix_done_ratio: '90',
commit_ref_keywords: 'refs , references, IssueID',
commit_fix_keywords: 'fixes , closes',
default_language: 'en'
} do
let!(:user) { FactoryBot.create :admin, login: 'dlopper' }
let!(:open_status) { FactoryBot.create :status }
let!(:closed_status) { FactoryBot.create :closed_status }
let!(:other_work_package) { FactoryBot.create :work_package, status: open_status }
let(:comments) { "Some fix made, fixes ##{work_package.id} and fixes ##{other_work_package.id}" }
with_virtual_subversion_repository do
let!(:work_package) do
FactoryBot.create :work_package,
project: repository.project,
status: open_status
end
let(:changeset) do
FactoryBot.create(:changeset,
repository: repository,
revision: '123',
committer: user.login,
comments: comments)
end
end
before do
# choosing a status to apply to fix issues
allow(Setting).to receive(:commit_fix_status_id).and_return closed_status.id
end
describe 'with any matching', with_settings: { commit_ref_keywords: '*' } do
describe 'reference with brackets' do
let(:comments) { "[##{work_package.id}] Worked on this work_package" }
it 'should reference' do
changeset.scan_comment_for_work_package_ids
work_package.reload
expect(work_package.changesets).to eq [changeset]
end
end
describe 'reference at line start' do
let(:comments) { "##{work_package.id} Worked on this work_package" }
it 'should reference' do
changeset.scan_comment_for_work_package_ids
work_package.reload
expect(work_package.changesets).to eq [changeset]
end
end
end
describe 'non matching ref' do
let(:comments) { "Some fix ignores ##{work_package.id}" }
it 'should reference the work package id' do
changeset.scan_comment_for_work_package_ids
work_package.reload
expect(work_package.changesets).to eq []
end
end
describe 'with timelogs' do
let!(:activity) { FactoryBot.create :activity, is_default: true }
before do
repository.project.enabled_module_names += ['costs']
repository.project.save!
end
it 'should ref keywords any with timelog' do
allow(Setting).to receive(:commit_ref_keywords).and_return '*'
allow(Setting).to receive(:commit_logtime_enabled?).and_return true
{
'2' => 2.0,
'2h' => 2.0,
'2hours' => 2.0,
'15m' => 0.25,
'15min' => 0.25,
'3h15' => 3.25,
'2h15m' => 2.25,
'2h15min' => 2.25,
'2:15' => 2.25,
'2.25' => 2.25,
'1.25h' => 1.25,
'0,75' => 0.75,
'1,25h' => 1.25
}.each do |syntax, expected_hours|
c = Changeset.new repository: repository,
committed_on: 24.hours.ago,
comments: "Worked on this work_package ##{work_package.id} @#{syntax}",
revision: '520',
user: user
expect { c.scan_comment_for_work_package_ids }
.to change { TimeEntry.count }.by(1)
expect(c.work_package_ids).to eq [work_package.id]
time = TimeEntry.order(Arel.sql('id DESC')).first
assert_equal work_package.id, time.work_package_id
assert_equal work_package.project_id, time.project_id
assert_equal user.id, time.user_id
expect(time.hours).to eq expected_hours
expect(time.spent_on).to eq Date.yesterday
expect(time.activity.is_default).to eq true
expect(time.comments).to include 'r520'
end
end
context 'with a second work package' do
let!(:work_package2) { FactoryBot.create :work_package, project: repository.project, status: open_status }
it 'should ref keywords closing with timelog' do
allow(Setting).to receive(:commit_fix_status_id).and_return closed_status.id
allow(Setting).to receive(:commit_ref_keywords).and_return '*'
allow(Setting).to receive(:commit_fix_keywords).and_return 'fixes , closes'
allow(Setting).to receive(:commit_logtime_enabled?).and_return true
c = Changeset.new repository: repository,
committed_on: Time.now,
comments: "This is a comment. Fixes ##{work_package.id} @4.5, ##{work_package2.id} @1",
revision: '520',
user: user
expect { c.scan_comment_for_work_package_ids }
.to change { TimeEntry.count }.by(2)
expect(c.work_package_ids).to match_array [work_package.id, work_package2.id]
work_package.reload
work_package2.reload
expect(work_package).to be_closed
expect(work_package2).to be_closed
times = TimeEntry.order(Arel.sql('id desc')).limit(2)
expect(times.map(&:work_package_id)).to match_array [work_package.id, work_package2.id]
end
end
end
it 'should reference the work package id' do
# make sure work package 1 is not already closed
expect(work_package.status.is_closed?).to eq false
changeset.scan_comment_for_work_package_ids
work_package.reload
expect(work_package.changesets).to eq [changeset]
expect(work_package.status).to eq closed_status
expect(work_package.done_ratio).to eq 90
# issue change
journal = work_package.journals.last
expect(journal.user).to eq user
expect(journal.notes).to eq 'Applied in changeset r123.'
# Expect other work package to be unchanged
# due to other project
other_work_package.reload
expect(other_work_package.changesets).to eq []
end
describe 'with work package in parent project' do
let(:parent) { FactoryBot.create :project }
let!(:work_package) { FactoryBot.create :work_package, project: parent, status: open_status }
before do
repository.project.parent = parent
repository.project.save!
end
it 'can reference it' do
# make sure work package 1 is not already closed
expect(work_package.status.is_closed?).to eq false
changeset.scan_comment_for_work_package_ids
work_package.reload
expect(work_package.changesets).to eq [changeset]
# Expect other work package to be unchanged
# due to other project
other_work_package.reload
expect(other_work_package.changesets).to eq []
end
end
describe 'with work package in sub project' do
let(:sub) { FactoryBot.create :project }
let!(:work_package) { FactoryBot.create :work_package, project: sub, status: open_status }
before do
sub.parent = repository.project
sub.save!
repository.project.reload
sub.reload
end
it 'can reference it' do
# make sure work package 1 is not already closed
expect(work_package.status.is_closed?).to eq false
changeset.scan_comment_for_work_package_ids
work_package.reload
expect(work_package.changesets).to eq [changeset]
# Expect other work package to be unchanged
# due to other project
other_work_package.reload
expect(other_work_package.changesets).to eq []
end
end
end
describe 'assign_openproject user' do
describe 'w/o user' do
before do
changeset.save!
end
it_behaves_like 'valid changeset' do
let(:journal_user) { User.anonymous }
end
end
describe 'with user is committer' do
let!(:committer) { FactoryBot.create(:user, login: email) }
before do
changeset.save!
end
it_behaves_like 'valid changeset' do
let(:journal_user) { committer }
end
end
describe 'current user is not committer' do
let(:current_user) { FactoryBot.create(:user) }
let!(:committer) { FactoryBot.create(:user, login: email) }
before do
allow(User).to receive(:current).and_return current_user
changeset.save!
end
it_behaves_like 'valid changeset' do
let(:journal_user) { committer }
end
end
end
end