kanbanworkflowstimelinescrumrubyroadmapproject-planningproject-managementopenprojectangularissue-trackerifcgantt-chartganttbug-trackerboardsbcf
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.
348 lines
13 KiB
348 lines
13 KiB
12 years ago
|
#-- copyright
|
||
|
# OpenProject is a project management system.
|
||
|
#
|
||
|
# Copyright (C) 2012-2013 the OpenProject Team
|
||
|
#
|
||
|
# This program is free software; you can redistribute it and/or
|
||
|
# modify it under the terms of the GNU General Public License version 3.
|
||
|
#
|
||
|
# See doc/COPYRIGHT.rdoc for more details.
|
||
|
#++
|
||
|
|
||
12 years ago
|
require File.expand_path('../../spec_helper', __FILE__)
|
||
12 years ago
|
|
||
11 years ago
|
describe WorkPackage do
|
||
11 years ago
|
let(:project) { FactoryGirl.create(:project_with_types) }
|
||
|
let(:user) { FactoryGirl.create(:user) }
|
||
12 years ago
|
|
||
11 years ago
|
before do
|
||
|
FactoryGirl.create :priority, is_default: true
|
||
|
FactoryGirl.create :default_issue_status
|
||
|
end
|
||
|
|
||
12 years ago
|
describe '- Relations ' do
|
||
|
describe '#project' do
|
||
|
it 'can read the project w/ the help of the belongs_to association' do
|
||
|
project = FactoryGirl.create(:project)
|
||
11 years ago
|
planning_element = FactoryGirl.create(:work_package,
|
||
11 years ago
|
:project_id => project.id)
|
||
12 years ago
|
|
||
|
planning_element.reload
|
||
|
|
||
|
planning_element.project.should == project
|
||
|
end
|
||
|
|
||
|
it 'can read the responsible w/ the help of the belongs_to association' do
|
||
|
user = FactoryGirl.create(:user)
|
||
11 years ago
|
planning_element = FactoryGirl.create(:work_package,
|
||
11 years ago
|
:responsible_id => user.id)
|
||
12 years ago
|
|
||
|
planning_element.reload
|
||
|
|
||
|
planning_element.responsible.should == user
|
||
|
end
|
||
|
|
||
11 years ago
|
it 'can read the type w/ the help of the belongs_to association' do
|
||
11 years ago
|
type = project.types.first
|
||
11 years ago
|
planning_element = FactoryGirl.create(:work_package,
|
||
11 years ago
|
:type_id => type.id,
|
||
|
:project => project)
|
||
12 years ago
|
|
||
|
planning_element.reload
|
||
|
|
||
11 years ago
|
planning_element.type.should == type
|
||
12 years ago
|
end
|
||
|
|
||
|
it 'can read the planning_element_status w/ the help of the belongs_to association' do
|
||
11 years ago
|
status = FactoryGirl.create(:issue_status)
|
||
|
work_package = FactoryGirl.create(:work_package,
|
||
|
:status_id => status.id)
|
||
12 years ago
|
|
||
11 years ago
|
work_package.reload
|
||
12 years ago
|
|
||
11 years ago
|
work_package.status.should == status
|
||
12 years ago
|
end
|
||
|
end
|
||
|
end
|
||
|
|
||
|
describe '- Validations ' do
|
||
|
let(:attributes) {
|
||
11 years ago
|
{:subject => 'workpackage No. 1',
|
||
12 years ago
|
:start_date => Date.today,
|
||
12 years ago
|
:due_date => Date.today + 2.weeks,
|
||
11 years ago
|
:project_id => project.id,
|
||
|
:type => project.types.first,
|
||
|
:author => user
|
||
|
}
|
||
12 years ago
|
}
|
||
|
|
||
11 years ago
|
it { WorkPackage.new.tap { |pe| pe.send(:assign_attributes, attributes, :without_protection => true) }.should be_valid }
|
||
12 years ago
|
|
||
12 years ago
|
describe 'subject' do
|
||
|
it 'is invalid w/o a subject' do
|
||
|
attributes[:subject] = nil
|
||
11 years ago
|
planning_element = WorkPackage.new.tap { |pe| pe.send(:assign_attributes, attributes, :without_protection => true) }
|
||
12 years ago
|
|
||
|
planning_element.should_not be_valid
|
||
|
|
||
12 years ago
|
planning_element.errors[:subject].should be_present
|
||
|
planning_element.errors[:subject].should == ["can't be blank"]
|
||
12 years ago
|
end
|
||
|
|
||
12 years ago
|
it 'is invalid w/ a subject longer than 255 characters' do
|
||
|
attributes[:subject] = "A" * 500
|
||
11 years ago
|
planning_element = WorkPackage.new.tap { |pe| pe.send(:assign_attributes, attributes, :without_protection => true) }
|
||
12 years ago
|
|
||
|
planning_element.should_not be_valid
|
||
|
|
||
12 years ago
|
planning_element.errors[:subject].should be_present
|
||
|
planning_element.errors[:subject].should == ["is too long (maximum is 255 characters)"]
|
||
12 years ago
|
end
|
||
|
end
|
||
|
|
||
|
describe 'start_date' do
|
||
12 years ago
|
it 'is valid w/o a start_date' do
|
||
12 years ago
|
attributes[:start_date] = nil
|
||
11 years ago
|
planning_element = WorkPackage.new.tap { |pe| pe.send(:assign_attributes, attributes, :without_protection => true) }
|
||
12 years ago
|
|
||
12 years ago
|
planning_element.should be_valid
|
||
12 years ago
|
|
||
12 years ago
|
planning_element.errors[:start_date].should_not be_present
|
||
12 years ago
|
end
|
||
|
end
|
||
|
|
||
12 years ago
|
describe 'due_date' do
|
||
12 years ago
|
it 'is valid w/o a due_date' do
|
||
12 years ago
|
attributes[:due_date] = nil
|
||
11 years ago
|
planning_element = WorkPackage.new.tap { |pe| pe.send(:assign_attributes, attributes, :without_protection => true) }
|
||
12 years ago
|
|
||
12 years ago
|
planning_element.should be_valid
|
||
12 years ago
|
|
||
12 years ago
|
planning_element.errors[:due_date].should_not be_present
|
||
12 years ago
|
end
|
||
|
|
||
12 years ago
|
it 'is invalid if start_date is after due_date' do
|
||
12 years ago
|
attributes[:start_date] = Date.today
|
||
12 years ago
|
attributes[:due_date] = Date.today - 1.week
|
||
11 years ago
|
planning_element = WorkPackage.new.tap { |pe| pe.send(:assign_attributes, attributes, :without_protection => true) }
|
||
12 years ago
|
|
||
|
planning_element.should_not be_valid
|
||
|
|
||
12 years ago
|
planning_element.errors[:due_date].should be_present
|
||
11 years ago
|
planning_element.errors[:due_date].should == ["must be greater than start date"]
|
||
12 years ago
|
end
|
||
|
|
||
12 years ago
|
it 'is invalid if planning_element is milestone and due_date is not on start_date' do
|
||
11 years ago
|
attributes[:type] = FactoryGirl.build(:type, :is_milestone => true)
|
||
12 years ago
|
attributes[:start_date] = Date.today
|
||
12 years ago
|
attributes[:due_date] = Date.today + 1.week
|
||
11 years ago
|
planning_element = WorkPackage.new.tap { |pe| pe.send(:assign_attributes, attributes, :without_protection => true) }
|
||
12 years ago
|
|
||
|
planning_element.should_not be_valid
|
||
|
|
||
12 years ago
|
planning_element.errors[:due_date].should be_present
|
||
|
planning_element.errors[:due_date].should == ["is not on start date, although this is required for milestones"]
|
||
12 years ago
|
end
|
||
|
end
|
||
|
|
||
|
describe 'project' do
|
||
|
it 'is invalid w/o a project' do
|
||
|
attributes[:project_id] = nil
|
||
11 years ago
|
planning_element = WorkPackage.new.tap { |pe| pe.send(:assign_attributes, attributes, :without_protection => true) }
|
||
12 years ago
|
|
||
|
planning_element.should_not be_valid
|
||
|
|
||
|
planning_element.errors[:project].should be_present
|
||
11 years ago
|
planning_element.errors[:project].should == ["can't be blank"]
|
||
12 years ago
|
end
|
||
|
end
|
||
|
|
||
|
describe 'parent' do
|
||
11 years ago
|
let (:de_message){ "darf kein Meilenstein sein"}
|
||
|
let (:en_message){ "cannot be a milestone"}
|
||
|
after(:each) do
|
||
|
#proper reset of the locale after the test
|
||
|
I18n.locale = "en"
|
||
|
end
|
||
|
|
||
12 years ago
|
it 'is invalid if parent is_milestone' do
|
||
11 years ago
|
["en","de"].each do |locale|
|
||
|
I18n.with_locale(locale) do
|
||
|
parent = WorkPackage.new.tap do |pe|
|
||
|
pe.send(:assign_attributes, attributes.merge(:type => FactoryGirl.build(:type, :is_milestone => true)), :without_protection => true)
|
||
|
end
|
||
12 years ago
|
|
||
11 years ago
|
attributes[:parent] = parent
|
||
|
planning_element = WorkPackage.new.tap { |pe| pe.send(:assign_attributes, attributes, :without_protection => true) }
|
||
|
|
||
|
planning_element.should_not be_valid
|
||
|
|
||
|
planning_element.errors[:parent].should be_present
|
||
|
planning_element.errors[:parent].should == [self.send("#{I18n.locale}_message")]
|
||
|
end
|
||
|
|
||
|
end
|
||
12 years ago
|
|
||
|
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
|
||
|
describe 'derived attributes' do
|
||
|
before do
|
||
11 years ago
|
@pe1 = FactoryGirl.create(:work_package, :project_id => project.id)
|
||
|
@pe11 = FactoryGirl.create(:work_package, :project_id => project.id, :parent_id => @pe1.id)
|
||
|
@pe12 = FactoryGirl.create(:work_package, :project_id => project.id, :parent_id => @pe1.id)
|
||
12 years ago
|
end
|
||
|
|
||
|
describe 'start_date' do
|
||
|
it 'equals the minimum start date of all children' do
|
||
12 years ago
|
@pe11.reload
|
||
12 years ago
|
@pe11.update_attributes(:start_date => Date.new(2000, 01, 20), :due_date => Date.new(2001, 01, 20))
|
||
12 years ago
|
@pe12.reload
|
||
12 years ago
|
@pe12.update_attributes(:start_date => Date.new(2000, 03, 20), :due_date => Date.new(2001, 03, 20))
|
||
12 years ago
|
|
||
|
@pe1.reload
|
||
|
@pe1.start_date.should == @pe11.start_date
|
||
|
end
|
||
|
end
|
||
|
|
||
12 years ago
|
describe 'due_date' do
|
||
12 years ago
|
it 'equals the maximum end date of all children' do
|
||
12 years ago
|
@pe11.reload
|
||
12 years ago
|
@pe11.update_attributes(:start_date => Date.new(2000, 01, 20), :due_date => Date.new(2001, 01, 20))
|
||
12 years ago
|
@pe12.reload
|
||
12 years ago
|
@pe12.update_attributes(:start_date => Date.new(2000, 03, 20), :due_date => Date.new(2001, 03, 20))
|
||
12 years ago
|
|
||
|
@pe1.reload
|
||
12 years ago
|
@pe1.due_date.should == @pe12.due_date
|
||
12 years ago
|
end
|
||
|
end
|
||
|
end
|
||
|
|
||
|
describe 'journal' do
|
||
|
let(:responsible) { FactoryGirl.create(:user) }
|
||
11 years ago
|
let(:type) { project.types.first } # The type-validation, that now lives on work-package is more
|
||
|
# strict than the previous validation on the planning-element
|
||
|
# it also checks, that the type is available for the project the pe lives in.
|
||
11 years ago
|
let(:pe_status) { FactoryGirl.create(:issue_status) }
|
||
12 years ago
|
|
||
11 years ago
|
let(:pe) { FactoryGirl.create(:work_package,
|
||
12 years ago
|
:subject => "Plan A",
|
||
12 years ago
|
:author => responsible,
|
||
12 years ago
|
:description => "This won't work out",
|
||
|
:start_date => Date.new(2012, 1, 24),
|
||
12 years ago
|
:due_date => Date.new(2012, 1, 31),
|
||
12 years ago
|
:project_id => project.id,
|
||
|
:responsible_id => responsible.id,
|
||
11 years ago
|
:type_id => type.id,
|
||
11 years ago
|
:status_id => pe_status.id
|
||
11 years ago
|
) }
|
||
12 years ago
|
|
||
|
it "has an initial journal, so that it's creation shows up in activity" do
|
||
|
pe.journals.size.should == 1
|
||
|
|
||
|
changes = pe.journals.first.changed_data.to_hash
|
||
|
|
||
11 years ago
|
changes.size.should == 11
|
||
11 years ago
|
|
||
|
changes.should include(:subject)
|
||
|
changes.should include(:author_id)
|
||
|
changes.should include(:description)
|
||
|
changes.should include(:start_date)
|
||
|
changes.should include(:due_date)
|
||
11 years ago
|
changes.should include(:done_ratio)
|
||
11 years ago
|
changes.should include(:status_id)
|
||
|
changes.should include(:priority_id)
|
||
11 years ago
|
changes.should include(:project_id)
|
||
|
changes.should include(:responsible_id)
|
||
|
changes.should include(:type_id)
|
||
12 years ago
|
end
|
||
|
|
||
|
it 'stores updates in journals' do
|
||
12 years ago
|
pe.reload
|
||
12 years ago
|
pe.update_attribute(:due_date, Date.new(2012, 2, 1))
|
||
12 years ago
|
|
||
|
pe.journals.size.should == 2
|
||
|
changes = pe.journals.last.changed_data.to_hash
|
||
|
|
||
|
changes.size.should == 1
|
||
|
|
||
11 years ago
|
changes.should include(:due_date)
|
||
12 years ago
|
|
||
11 years ago
|
changes[:due_date].first.should == Date.new(2012, 1, 31)
|
||
|
changes[:due_date].last.should == Date.new(2012, 2, 1)
|
||
12 years ago
|
end
|
||
|
|
||
11 years ago
|
describe 'workpackage hierarchies' do
|
||
|
let(:child_pe) { FactoryGirl.create(:work_package,
|
||
11 years ago
|
:parent_id => pe.id,
|
||
|
:subject => "Plan B",
|
||
|
:description => "This will work out",
|
||
|
# interval is the same as parent, so that
|
||
|
# dates are not updated
|
||
|
:start_date => Date.new(2012, 1, 24),
|
||
|
:due_date => Date.new(2012, 1, 31),
|
||
|
:project_id => project.id,
|
||
|
:responsible_id => responsible.id
|
||
|
) }
|
||
12 years ago
|
|
||
11 years ago
|
it 'creates a journal in the parent when end date is changed indirectly' do
|
||
12 years ago
|
child_pe # trigger creation of child and parent
|
||
|
|
||
|
# sanity check
|
||
|
child_pe.journals.size.should == 1
|
||
11 years ago
|
pe.journals.size.should == 2
|
||
12 years ago
|
|
||
|
# update child
|
||
12 years ago
|
child_pe.reload
|
||
12 years ago
|
child_pe.update_attribute(:start_date, Date.new(2012, 1, 1))
|
||
|
|
||
|
# reload parent to avoid stale journal caches
|
||
|
pe.reload
|
||
|
|
||
11 years ago
|
pe.journals.size.should == 3
|
||
12 years ago
|
changes = pe.journals.last.changed_data.to_hash
|
||
|
|
||
|
changes.size.should == 1
|
||
11 years ago
|
changes.should include(:start_date)
|
||
12 years ago
|
end
|
||
12 years ago
|
|
||
12 years ago
|
end
|
||
|
end
|
||
|
|
||
12 years ago
|
describe 'acts as paranoid trash' do
|
||
12 years ago
|
before(:each) do
|
||
11 years ago
|
@pe1 = FactoryGirl.create(:work_package,
|
||
12 years ago
|
:project_id => project.id,
|
||
|
:start_date => Date.new(2011, 1, 1),
|
||
12 years ago
|
:due_date => Date.new(2011, 2, 1),
|
||
12 years ago
|
:subject => "Numero Uno")
|
||
12 years ago
|
end
|
||
|
|
||
12 years ago
|
it 'should delete the object permanantly when using destroy' do
|
||
|
@pe1.destroy
|
||
12 years ago
|
|
||
11 years ago
|
WorkPackage.without_deleted.find_by_id(@pe1.id).should be_nil
|
||
|
WorkPackage.find_by_id(@pe1.id).should be_nil
|
||
12 years ago
|
end
|
||
|
|
||
|
it 'destroys all child elements' do
|
||
11 years ago
|
pe1 = FactoryGirl.create(:work_package, :project_id => project.id)
|
||
|
pe11 = FactoryGirl.create(:work_package, :project_id => project.id, :parent_id => pe1.id)
|
||
|
pe12 = FactoryGirl.create(:work_package, :project_id => project.id, :parent_id => pe1.id)
|
||
|
pe121 = FactoryGirl.create(:work_package, :project_id => project.id, :parent_id => pe12.id)
|
||
|
pe2 = FactoryGirl.create(:work_package, :project_id => project.id)
|
||
12 years ago
|
|
||
12 years ago
|
pe1.destroy
|
||
12 years ago
|
|
||
|
[pe1, pe11, pe12, pe121].each do |pe|
|
||
11 years ago
|
WorkPackage.without_deleted.find_by_id(pe.id).should be_nil
|
||
|
WorkPackage.find_by_id(pe.id).should be_nil
|
||
12 years ago
|
end
|
||
|
|
||
11 years ago
|
WorkPackage.without_deleted.find_by_id(pe2.id).should == pe2
|
||
12 years ago
|
end
|
||
|
end
|
||
|
end
|