Redmine 4.1.7

This commit is contained in:
Manuel Cillero 2023-07-07 08:08:27 +02:00
parent 55458d3479
commit 3ca3c37487
103 changed files with 2426 additions and 431 deletions

View file

@ -152,6 +152,19 @@ class AttachmentTest < ActiveSupport::TestCase
end
end
def test_extension_update_should_be_validated_against_denied_extensions
with_settings :attachment_extensions_denied => "txt, png" do
a = Attachment.new(:container => Issue.find(1),
:file => mock_file_with_options(:original_filename => "test.jpeg"),
:author => User.find(1))
assert_save a
b = Attachment.find(a.id)
b.filename = "test.png"
assert !b.save
end
end
def test_valid_extension_should_be_case_insensitive
with_settings :attachment_extensions_allowed => "txt, Png" do
assert Attachment.valid_extension?(".pnG")
@ -235,6 +248,23 @@ class AttachmentTest < ActiveSupport::TestCase
assert_not_equal a1.diskfile, a2.diskfile
end
def test_identical_attachments_created_in_same_transaction_should_not_end_up_unreadable
attachments = []
Project.transaction do
3.times do
a = Attachment.create!(
:container => Issue.find(1), :author => User.find(1),
:file => mock_file(:filename => 'foo', :content => 'abcde')
)
attachments << a
end
end
attachments.each do |a|
assert a.readable?
end
assert_equal 1, attachments.map(&:diskfile).uniq.size
end
def test_filename_should_be_basenamed
a = Attachment.new(:file => mock_file(:original_filename => "path/to/the/file"))
assert_equal 'file', a.filename

View file

@ -241,6 +241,16 @@ class IssueSubtaskingTest < ActiveSupport::TestCase
end
end
def test_done_ratio_of_parent_with_completed_children_should_not_be_99
with_settings :parent_issue_done_ratio => 'derived' do
parent = Issue.generate!
parent.generate_child!(:estimated_hours => 8.0, :done_ratio => 100)
parent.generate_child!(:estimated_hours => 8.1, :done_ratio => 100)
# (8.0 * 100 + 8.1 * 100) / (8.0 + 8.1) => 99.99999999999999
assert_equal 100, parent.reload.done_ratio
end
end
def test_changing_parent_should_update_previous_parent_done_ratio
with_settings :parent_issue_done_ratio => 'derived' do
first_parent = Issue.generate!

View file

@ -898,6 +898,23 @@ class IssueTest < ActiveSupport::TestCase
assert_equal Date.parse('2012-07-14'), issue.due_date
end
def test_safe_attributes_notes_should_check_add_issue_notes_permission
# With add_issue_notes permission
user = User.find(2)
issue = Issue.new(:project => Project.find(1))
issue.init_journal(user)
issue.send :safe_attributes=, {'notes' => 'note'}, user
assert_equal 'note', issue.notes
# Without add_issue_notes permission
Role.find(1).remove_permission!(:add_issue_notes)
issue = Issue.new(:project => Project.find(1))
user.reload
issue.init_journal(user)
issue.send :safe_attributes=, {'notes' => 'note'}, user
assert_equal '', issue.notes
end
def test_safe_attributes_should_accept_target_tracker_enabled_fields
source = Tracker.find(1)
source.core_fields = []
@ -1459,6 +1476,23 @@ class IssueTest < ActiveSupport::TestCase
assert_equal [3, nil], copy.children.map(&:assigned_to_id)
end
def test_copy_should_not_add_attachments_to_journal
set_tmp_attachments_directory
issue = Issue.generate!
copy = Issue.new
copy.init_journal User.find(1)
copy.copy_from issue
copy.project = issue.project
copy.save_attachments(
{ 'p0' => {'file' => mock_file_with_options(:original_filename => 'upload')} }
)
assert copy.save
assert j = copy.journals.last
assert_equal 1, j.details.size
assert_equal 'relation', j.details[0].property
end
def test_should_not_call_after_project_change_on_creation
issue = Issue.new(:project_id => 1, :tracker_id => 1, :status_id => 1,
:subject => 'Test', :author_id => 1)

View file

@ -20,6 +20,7 @@
require File.expand_path('../../../../test_helper', __FILE__)
class Redmine::CipheringTest < ActiveSupport::TestCase
fixtures :auth_sources
def test_password_should_be_encrypted
Redmine::Configuration.with 'database_cipher_key' => 'secret' do
@ -106,4 +107,12 @@ class Redmine::CipheringTest < ActiveSupport::TestCase
assert_equal 'bar', r.read_attribute(:password)
end
end
def test_encrypt_all_and_decrypt_all_should_skip_validation
auth_source = auth_sources(:auth_sources_001)
# validator checks if AuthSource#host is present
auth_source.update_column(:host, nil)
assert AuthSource.encrypt_all(:account_password)
assert AuthSource.decrypt_all(:account_password)
end
end

View file

@ -44,6 +44,12 @@ class Redmine::Helpers::GanttHelperTest < Redmine::HelperTest
def gantt_start
@gantt.date_from
end
private :gantt_start
def gantt_end
@gantt.date_to
end
private :gantt_end
# Creates a Gantt chart for a 4 week span
def create_gantt(project=Project.generate!, options={})
@ -354,6 +360,26 @@ class Redmine::Helpers::GanttHelperTest < Redmine::HelperTest
assert_select 'div.task_todo[style*="left:28px"]', 1
end
test "#line todo line should appear if it ends on the leftmost date in the gantt" do
create_gantt
[gantt_start - 1, gantt_start].each do |start_date|
@output_buffer = @gantt.line(start_date, gantt_start, 30, false, 'line', :format => :html, :zoom => 4)
# the leftmost date (Date.today - 14 days)
assert_select 'div.task_todo[style*="left:0px"]', 1, @output_buffer
assert_select 'div.task_todo[style*="width:2px"]', 1, @output_buffer
end
end
test "#line todo line should appear if it starts on the rightmost date in the gantt" do
create_gantt
[gantt_end, gantt_end + 1].each do |end_date|
@output_buffer = @gantt.line(gantt_end, end_date, 30, false, 'line', :format => :html, :zoom => 4)
# the rightmost date (Date.today + 14 days)
assert_select 'div.task_todo[style*="left:112px"]', 1, @output_buffer
assert_select 'div.task_todo[style*="width:2px"]', 1, @output_buffer
end
end
test "#line todo line should be the total width" do
create_gantt
@output_buffer = @gantt.line(today - 7, today + 7, 30, false, 'line', :format => :html, :zoom => 4)
@ -424,6 +450,9 @@ class Redmine::Helpers::GanttHelperTest < Redmine::HelperTest
@output_buffer = @gantt.line(today - 7, today + 7, 30, true, 'line', :format => :html, :zoom => 4)
assert_select "div.starting", 1
assert_select 'div.starting[style*="left:28px"]', 1
# starting marker on the leftmost boundary of the gantt
@output_buffer = @gantt.line(gantt_start, today + 7, 30, true, 'line', :format => :html, :zoom => 4)
assert_select 'div.starting[style*="left:0px"]', 1
end
test "#line starting marker should not appear if the start date is before gantt start date" do
@ -437,6 +466,9 @@ class Redmine::Helpers::GanttHelperTest < Redmine::HelperTest
@output_buffer = @gantt.line(today - 7, today + 7, 30, true, 'line', :format => :html, :zoom => 4)
assert_select "div.ending", 1
assert_select 'div.ending[style*="left:88px"]', 1
# ending marker on the rightmost boundary of the gantt
@output_buffer = @gantt.line(today - 7, gantt_end, 30, true, 'line', :format => :html, :zoom => 4)
assert_select 'div.ending[style*="left:116px"]', 1
end
test "#line ending marker should not appear if the end date is before gantt start date" do

View file

@ -23,7 +23,8 @@ class Redmine::ProjectJumpBoxTest < ActiveSupport::TestCase
fixtures :users, :projects, :user_preferences
def setup
@user = User.find_by_login 'dlopper'
@user = User.find_by_login 'jsmith'
User.current = @user
@ecookbook = Project.find 'ecookbook'
@onlinestore = Project.find 'onlinestore'
end
@ -142,4 +143,16 @@ class Redmine::ProjectJumpBoxTest < ActiveSupport::TestCase
assert_equal @onlinestore, pjb.recently_used_projects.first
assert_equal @ecookbook, pjb.recently_used_projects.last
end
def test_recents_list_should_include_only_visible_projects
@user = User.find_by_login 'dlopper'
User.current = @user
pjb = Redmine::ProjectJumpBox.new @user
pjb.project_used @ecookbook
pjb.project_used @onlinestore
assert_equal 1, pjb.recently_used_projects.size
assert_equal @ecookbook, pjb.recently_used_projects.first
end
end

View file

@ -1005,6 +1005,18 @@ class MailHandlerTest < ActiveSupport::TestCase
end
end
def test_reply_to_an_issue_without_permission
set_tmp_attachments_directory
# "add_issue_notes" permission is explicit required to allow users to add notes
# "edit_issue" permission no longer includes the "add_issue_notes" permission
Role.all.each {|r| r.remove_permission! :add_issue_notes}
assert_no_difference 'Issue.count' do
assert_no_difference 'Journal.count' do
assert_not submit_email('ticket_reply_with_status.eml')
end
end
end
def test_reply_to_a_nonexitent_journal
journal_id = Issue.find(2).journals.last.id
Journal.destroy(journal_id)
@ -1056,6 +1068,13 @@ class MailHandlerTest < ActiveSupport::TestCase
end
end
def test_reply_to_a_topic_without_permission
Role.all.each {|r| r.remove_permission! :add_messages}
assert_no_difference('Message.count') do
assert_not submit_email('message_reply_by_subject.eml')
end
end
def test_should_convert_tags_of_html_only_emails
with_settings :text_formatting => 'textile' do
issue = submit_email('ticket_html_only.eml', :issue => {:project => 'ecookbook'})

View file

@ -344,6 +344,13 @@ class ProjectTest < ActiveSupport::TestCase
assert_equal parent.children.sort_by(&:name), parent.children.to_a
end
def test_validate_custom_field_values_of_project
User.current = User.find(3)
ProjectCustomField.generate!(:name => 'CustomFieldTest', :field_format => 'int', :is_required => true, :visible => false, :role_ids => [1])
p = Project.new(:name => 'Project test', :identifier => 'project-t')
assert p.save!
end
def test_set_parent_should_update_issue_fixed_version_associations_when_a_fixed_version_is_moved_out_of_the_hierarchy
# Parent issue with a hierarchy project's fixed version
parent_issue = Issue.find(1)

View file

@ -1126,6 +1126,21 @@ class QueryTest < ActiveSupport::TestCase
assert_equal [1, 3, 7, 8], find_issues_with_query(query).map(&:id).uniq.sort
end
def test_filter_on_fixed_version_status_respects_sharing
issue = Issue.generate!(:project_id => 1, :fixed_version_id => 7)
filter_name = "fixed_version.status"
query = IssueQuery.new(:name => '_', :project => Project.find(1))
assert_include filter_name, query.available_filters.keys
query.filters = {filter_name => {:operator => '=', :values => ['open']}}
assert_include issue, find_issues_with_query(query)
query = IssueQuery.new(:name => '_', :project => Project.find(1))
query.filters = {filter_name => {:operator => '=', :values => ['closed']}}
assert_not_includes find_issues_with_query(query), issue
end
def test_filter_on_version_custom_field
field = IssueCustomField.generate!(:field_format => 'version', :is_filter => true)
issue = Issue.generate!(:project_id => 1, :tracker_id => 1, :custom_field_values => {field.id.to_s => '2'})
@ -1451,6 +1466,19 @@ class QueryTest < ActiveSupport::TestCase
end
end
def test_available_filters_as_json_should_not_include_duplicate_assigned_to_id_values
set_language_if_valid 'en'
user = User.find_by_login 'dlopper'
with_current_user User.find(1) do
q = IssueQuery.new
q.filters = {"assigned_to_id" => {:operator => '=', :values => user.id.to_s}}
filters = q.available_filters_as_json
assert_not_include [user.name, user.id.to_s], filters['assigned_to_id']['values']
assert_include [user.name, user.id.to_s, 'active'], filters['assigned_to_id']['values']
end
end
def test_available_filters_as_json_should_include_missing_author_id_values
user = User.generate!
with_current_user User.find(1) do
@ -1630,6 +1658,16 @@ class QueryTest < ActiveSupport::TestCase
assert !q.sortable_columns['cf_1']
end
def test_sortable_should_return_false_for_multi_custom_field
field = CustomField.find(1)
field.update_attribute :multiple, true
q = IssueQuery.new
field_column = q.available_columns.detect {|c| c.name==:cf_1}
assert !field_column.sortable?
end
def test_default_sort
q = IssueQuery.new
assert_equal [['id', 'desc']], q.sort_criteria
@ -1800,6 +1838,37 @@ class QueryTest < ActiveSupport::TestCase
assert_include "cf_#{field.id}".to_sym, q.available_totalable_columns.map(&:name)
end
def test_available_totalable_columns_should_sort_in_position_order_for_custom_field
ProjectCustomField.delete_all
cf_pos3 = ProjectCustomField.generate!(:position => 3, :is_for_all => true, :field_format => 'int')
cf_pos4 = ProjectCustomField.generate!(:position => 4, :is_for_all => true, :field_format => 'float')
cf_pos1 = ProjectCustomField.generate!(:position => 1, :is_for_all => true, :field_format => 'float')
cf_pos2 = ProjectCustomField.generate!(:position => 2, :is_for_all => true, :field_format => 'int')
q = ProjectQuery.new
custom_field_columns = q.available_totalable_columns.select{|column| column.is_a?(QueryCustomFieldColumn)}
assert_equal [cf_pos1, cf_pos2, cf_pos3, cf_pos4], custom_field_columns.collect(&:custom_field)
IssueCustomField.delete_all
cf_pos3 = IssueCustomField.generate!(:position => 3, :is_for_all => true, :field_format => 'int')
cf_pos4 = IssueCustomField.generate!(:position => 4, :is_for_all => true, :field_format => 'float')
cf_pos1 = IssueCustomField.generate!(:position => 1, :is_for_all => true, :field_format => 'float')
cf_pos2 = IssueCustomField.generate!(:position => 2, :is_for_all => true, :field_format => 'int')
q = IssueQuery.new
custom_field_columns = q.available_totalable_columns.select{|column| column.is_a?(QueryCustomFieldColumn)}
assert_equal [cf_pos1, cf_pos2, cf_pos3, cf_pos4], custom_field_columns.collect(&:custom_field)
ProjectCustomField.delete_all
IssueCustomField.delete_all
TimeEntryCustomField.delete_all
cf_pos3 = TimeEntryCustomField.generate!(:position => 3, :is_for_all => true, :field_format => 'int')
cf_pos4 = TimeEntryCustomField.generate!(:position => 4, :is_for_all => true, :field_format => 'float')
cf_pos1 = TimeEntryCustomField.generate!(:position => 1, :is_for_all => true, :field_format => 'float')
cf_pos2 = TimeEntryCustomField.generate!(:position => 2, :is_for_all => true, :field_format => 'int')
q = TimeEntryQuery.new
custom_field_columns = q.available_totalable_columns.select{|column| column.is_a?(QueryCustomFieldColumn)}
assert_equal [cf_pos1, cf_pos2, cf_pos3, cf_pos4], custom_field_columns.collect(&:custom_field)
end
def test_total_for_estimated_hours
Issue.delete_all
Issue.generate!(:estimated_hours => 5.5)
@ -2370,6 +2439,17 @@ class QueryTest < ActiveSupport::TestCase
ActiveRecord::Base.default_timezone = :local # restore Redmine default
end
def test_project_statement_with_closed_subprojects
project = Project.find(1)
project.descendants.each(&:close)
with_settings :display_subprojects_issues => '1' do
query = IssueQuery.new(:name => '_', :project => project)
statement = query.project_statement
assert_equal "projects.lft >= #{project.lft} AND projects.rgt <= #{project.rgt}", statement
end
end
def test_filter_on_subprojects
query = IssueQuery.new(:name => '_', :project => Project.find(1))
filter_name = "subproject_id"

View file

@ -0,0 +1,75 @@
# frozen_string_literal: true
# Redmine - project management software
# Copyright (C) 2006-2020 Jean-Philippe Lang
#
# 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.
require File.expand_path('../../test_helper', __FILE__)
class TimeEntryCustomFieldTest < ActiveSupport::TestCase
include Redmine::I18n
fixtures :roles
def setup
User.current = nil
end
def test_custom_field_with_visible_set_to_false_should_validate_roles
set_language_if_valid 'en'
field = TimeEntryCustomField.new(:name => 'Field', :field_format => 'string', :visible => false)
assert !field.save
assert_include "Roles cannot be blank", field.errors.full_messages
field.role_ids = [1, 2]
assert field.save
end
def test_changing_visible_to_true_should_clear_roles
field = TimeEntryCustomField.create!(:name => 'Field', :field_format => 'string', :visible => false, :role_ids => [1, 2])
assert_equal 2, field.roles.count
field.visible = true
field.save!
assert_equal 0, field.roles.count
end
def test_safe_attributes_should_include_only_custom_fields_visible_to_user
cf1 = TimeEntryCustomField.create!(:name => 'Visible field',
:field_format => 'string',
:visible => false, :role_ids => [1])
cf2 = TimeEntryCustomField.create!(:name => 'Non visible field',
:field_format => 'string',
:visible => false, :role_ids => [3])
user = User.find(2)
time_entry = TimeEntry.new(:issue_id => 1)
time_entry.send :safe_attributes=, {'custom_field_values' => {
cf1.id.to_s => 'value1',
cf2.id.to_s => 'value2'
}}, user
assert_equal 'value1', time_entry.custom_field_value(cf1)
assert_nil time_entry.custom_field_value(cf2)
time_entry.send :safe_attributes=, {'custom_fields' => [
{'id' => cf1.id.to_s, 'value' => 'valuea'},
{'id' => cf2.id.to_s, 'value' => 'valueb'}
]}, user
assert_equal 'valuea', time_entry.custom_field_value(cf1)
assert_nil time_entry.custom_field_value(cf2)
end
end

View file

@ -29,7 +29,8 @@ class TimeEntryTest < ActiveSupport::TestCase
:journals, :journal_details,
:issue_categories, :enumerations,
:groups_users,
:enabled_modules
:enabled_modules,
:custom_fields, :custom_fields_projects, :custom_values
def setup
User.current = nil