summaryrefslogtreecommitdiffstats
path: root/test/unit
diff options
context:
space:
mode:
Diffstat (limited to 'test/unit')
-rw-r--r--test/unit/changeset_test.rb2
-rw-r--r--test/unit/email_address_test.rb6
-rw-r--r--test/unit/issue_priority_test.rb16
-rw-r--r--test/unit/lib/redmine/field_format/numeric_format_test.rb18
-rw-r--r--test/unit/lib/redmine/field_format/progressbar_format_test.rb132
-rw-r--r--test/unit/lib/redmine/quote_reply_helper_test.rb14
-rw-r--r--test/unit/lib/redmine/reaction_test.rb189
-rw-r--r--test/unit/lib/redmine/scm/adapters/bazaar_adapter_test.rb1
-rw-r--r--test/unit/lib/redmine/scm/adapters/cvs_adapter_test.rb1
-rw-r--r--test/unit/lib/redmine/scm/adapters/git_adapter_test.rb9
-rw-r--r--test/unit/lib/redmine/scm/adapters/mercurial_adapter_test.rb8
-rw-r--r--test/unit/lib/redmine/scm/adapters/subversion_adapter_test.rb1
-rw-r--r--test/unit/lib/redmine/wiki_formatting/common_mark/formatter_test.rb129
-rw-r--r--test/unit/lib/redmine/wiki_formatting/common_mark/sanitization_filter_test.rb38
-rw-r--r--test/unit/lib/redmine/wiki_formatting/html_sanitizer_test.rb20
-rw-r--r--test/unit/lib/redmine/wiki_formatting/macros_test.rb4
-rw-r--r--test/unit/lib/redmine/wiki_formatting/textile_formatter_test.rb12
-rw-r--r--test/unit/member_test.rb35
-rw-r--r--test/unit/project_admin_query_test.rb138
-rw-r--r--test/unit/project_query_test.rb34
-rw-r--r--test/unit/query_test.rb6
-rw-r--r--test/unit/reaction_test.rb118
-rw-r--r--test/unit/repository_bazaar_test.rb1
-rw-r--r--test/unit/repository_cvs_test.rb1
-rw-r--r--test/unit/repository_git_test.rb1
-rw-r--r--test/unit/repository_mercurial_test.rb11
-rw-r--r--test/unit/repository_subversion_test.rb1
-rw-r--r--test/unit/repository_test.rb10
-rw-r--r--test/unit/role_test.rb26
-rw-r--r--test/unit/setting_test.rb4
-rw-r--r--test/unit/time_entry_test.rb12
-rw-r--r--test/unit/user_query_test.rb24
-rw-r--r--test/unit/user_test.rb94
33 files changed, 991 insertions, 125 deletions
diff --git a/test/unit/changeset_test.rb b/test/unit/changeset_test.rb
index ca1e010e3..3ad8b1cbf 100644
--- a/test/unit/changeset_test.rb
+++ b/test/unit/changeset_test.rb
@@ -479,7 +479,7 @@ class ChangesetTest < ActiveSupport::TestCase
end
def test_next_nil
- changeset = Changeset.find_by_revision('10')
+ changeset = Changeset.find_by_revision('11')
assert_nil changeset.next
end
diff --git a/test/unit/email_address_test.rb b/test/unit/email_address_test.rb
index 9d57beb97..923df897a 100644
--- a/test/unit/email_address_test.rb
+++ b/test/unit/email_address_test.rb
@@ -63,6 +63,12 @@ class EmailAddressTest < ActiveSupport::TestCase
end
end
+ def test_domain_in_should_not_raise_exception_when_domain_is_nil
+ assert_nothing_raised do
+ assert_not EmailAddress.domain_in?(nil, 'example.com')
+ end
+ end
+
def test_should_reject_invalid_email
assert_not EmailAddress.new(address: 'invalid,email@example.com').valid?
end
diff --git a/test/unit/issue_priority_test.rb b/test/unit/issue_priority_test.rb
index e076afe67..80dc11e1c 100644
--- a/test/unit/issue_priority_test.rb
+++ b/test/unit/issue_priority_test.rb
@@ -156,4 +156,20 @@ class IssuePriorityTest < ActiveSupport::TestCase
IssuePriority.find_by_position_name('highest').destroy
assert_equal %w(lowest default high2 highest), IssuePriority.active.to_a.sort.map(&:position_name)
end
+
+ def test_high_should_return_false_when_no_default_priority_and_no_active_priorities
+ IssuePriority.update_all(active: false, is_default: false)
+ priority = IssuePriority.order(:position).last # Highest priority
+ assert_nothing_raised do
+ assert_equal false, priority.high?
+ end
+ end
+
+ def test_low_should_return_false_when_no_default_priority_and_no_active_priorities
+ IssuePriority.update_all(active: false, is_default: false)
+ priority = IssuePriority.order(:position).first # Lowest priority
+ assert_nothing_raised do
+ assert_equal false, priority.low?
+ end
+ end
end
diff --git a/test/unit/lib/redmine/field_format/numeric_format_test.rb b/test/unit/lib/redmine/field_format/numeric_format_test.rb
index 2c9ecdc2e..7e5194d9a 100644
--- a/test/unit/lib/redmine/field_format/numeric_format_test.rb
+++ b/test/unit/lib/redmine/field_format/numeric_format_test.rb
@@ -33,13 +33,21 @@ class Redmine::NumericFieldFormatTest < ActionView::TestCase
assert_equal '<a href="http://foo/3" class="external">3</a>', field.format.formatted_custom_value(self, custom_value, true)
end
- def test_float_field_value_should_validate_when_given_with_various_separator
+ def test_float_field_should_normalize_decimal_separator
field = IssueCustomField.generate!(field_format: 'float')
issue = Issue.generate!(tracker: Tracker.find(1), status: IssueStatus.find(1), priority: IssuePriority.find(6))
- to_test = {'en' => '3.33', 'de' => '3,33'}
- to_test.each do |locale, expected|
- with_locale locale do
- assert field.format.validate_single_value(field, expected, issue)
+
+ with_locale 'de' do
+ issue.custom_field_values = { field.id => '3,33' }
+ assert issue.save!
+ assert_equal '3.33', issue.reload.custom_field_values.last.value
+ end
+
+ # Comma decimal separator is not allowed in English locale
+ with_locale 'en' do
+ issue.custom_field_values = { field.id => '3,33' }
+ assert_raise ActiveRecord::RecordInvalid do
+ issue.save!
end
end
end
diff --git a/test/unit/lib/redmine/field_format/progressbar_format_test.rb b/test/unit/lib/redmine/field_format/progressbar_format_test.rb
new file mode 100644
index 000000000..6e0df724d
--- /dev/null
+++ b/test/unit/lib/redmine/field_format/progressbar_format_test.rb
@@ -0,0 +1,132 @@
+# frozen_string_literal: true
+
+# Redmine - project management software
+# Copyright (C) 2006- 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_relative '../../../../test_helper'
+require 'redmine/field_format'
+
+module Redmine::FieldFormat
+ class ProgressbarFormatTest < ActionView::TestCase
+ def setup
+ @field = IssueCustomField.new(name: 'ProgressbarTest', field_format: 'progressbar')
+ @format = Redmine::FieldFormat::ProgressbarFormat.instance
+ end
+
+ def test_validate_invalid_value
+ cv = CustomValue.new(custom_field: @field, value: '120')
+ assert_include ::I18n.t('activerecord.errors.messages.invalid'), @format.validate_custom_value(cv)
+ end
+
+ def test_validate_numericality
+ cv = CustomValue.new(custom_field: @field, value: 'abc')
+ assert_include ::I18n.t('activerecord.errors.messages.not_a_number'), @format.validate_custom_value(cv)
+ end
+
+ def test_cast_value_clamping
+ assert_equal 0, @field.cast_value('-10')
+ assert_equal 0, @field.cast_value('0')
+ assert_equal 50, @field.cast_value('50')
+ assert_equal 100, @field.cast_value('120')
+ end
+
+ def test_empty_value
+ assert_nil @field.cast_value('')
+ end
+
+ def test_totalable_support
+ assert_not @format.totalable_supported?
+ end
+
+ def test_validate_non_numeric_value_should_fail
+ assert_include ::I18n.t('activerecord.errors.messages.not_a_number'),
+ @format.validate_single_value(@field, "abc")
+ end
+
+ def test_validate_negative_value_should_fail
+ assert_include ::I18n.t('activerecord.errors.messages.invalid'),
+ @format.validate_single_value(@field, "-10")
+ end
+
+ def test_validate_value_above_100_should_fail
+ assert_include ::I18n.t('activerecord.errors.messages.invalid'),
+ @format.validate_single_value(@field, "150")
+ end
+
+ def test_validate_valid_value_should_pass
+ assert_empty @format.validate_single_value(@field, "50")
+ assert_empty @format.validate_single_value(@field, "0")
+ assert_empty @format.validate_single_value(@field, "100")
+ end
+
+ def test_validate_blank_value_should_pass
+ assert_empty @format.validate_single_value(@field, "")
+ end
+
+ def test_query_filter_options
+ options = @format.query_filter_options(@field, nil)
+ assert_equal :integer, options[:type]
+ end
+
+ def test_default_ratio_interval_should_be_default_issue_done_ratio_interval
+ @field.save
+ assert_equal 10, @field.ratio_interval
+ end
+
+ def test_ratio_interval
+ @field.update(ratio_interval: 5)
+ assert_equal 5, @field.ratio_interval
+ end
+
+ def test_edit_tag_possible_values_with_ratio_interval
+ [5, 10].each do |ratio_interval|
+ @field.update(ratio_interval: ratio_interval)
+ value = CustomValue.new(custom_field: @field, value: '90')
+
+ tag = @field.format.edit_tag(self, 'id', 'name', value)
+ assert_select_in tag, 'select' do
+ assert_select 'option', 100 / ratio_interval + 1
+ end
+ end
+ end
+
+ def test_bulk_edit_tag_possible_values_with_ratio_interval
+ [5, 10].each do |ratio_interval|
+ @field.update(ratio_interval: ratio_interval)
+ value = CustomValue.new(custom_field: @field, value: '90')
+ objects = [Issue.new, Issue.new]
+
+ tag = @field.format.bulk_edit_tag(self, 'id', 'name', @field, objects, value)
+ assert_select_in tag, 'select' do |select|
+ assert_select select.first, 'option', 100 / ratio_interval + 2
+ end
+ end
+ end
+
+ def test_formatted_value_with_html_true
+ expected = progress_bar(50)
+ formatted = @format.formatted_value(self, @field, 50, Issue.new, true)
+ assert_equal expected, formatted
+ assert formatted.html_safe?
+ end
+
+ def test_formatted_value_with_html_false
+ formatted = @format.formatted_value(self, @field, 50, Issue.new, false)
+ assert_equal '50', formatted
+ end
+ end
+end
diff --git a/test/unit/lib/redmine/quote_reply_helper_test.rb b/test/unit/lib/redmine/quote_reply_helper_test.rb
index 43adb521b..d5d13d4f8 100644
--- a/test/unit/lib/redmine/quote_reply_helper_test.rb
+++ b/test/unit/lib/redmine/quote_reply_helper_test.rb
@@ -23,18 +23,18 @@ class QuoteReplyHelperTest < ActionView::TestCase
include ERB::Util
include Redmine::QuoteReply::Helper
- def test_quote_reply
+ def test_quote_reply_button
with_locale 'en' do
url = quoted_issue_path(issues(:issues_001))
- a_tag = quote_reply(url, '#issue_description_wiki')
- assert_includes a_tag, %|onclick="#{h "quoteReply('/issues/1/quoted', '#issue_description_wiki', 'common_mark'); return false;"}"|
- assert_includes a_tag, %|class="icon icon-comment"|
- assert_not_includes a_tag, 'title='
+ html = quote_reply_button(url: url)
+ assert_select_in html,
+ 'a[data-quote-reply-url-param=?][data-quote-reply-text-formatting-param=?]:not([title])',
+ url, Setting.text_formatting
# When icon_only is true
- a_tag = quote_reply(url, '#issue_description_wiki', icon_only: true)
- assert_includes a_tag, %|title="Quote"|
+ html = quote_reply_button(url: url, icon_only: true)
+ assert_select_in html, 'a.icon-only.icon-quote[title=?]', 'Quote'
end
end
end
diff --git a/test/unit/lib/redmine/reaction_test.rb b/test/unit/lib/redmine/reaction_test.rb
new file mode 100644
index 000000000..f3228a3bd
--- /dev/null
+++ b/test/unit/lib/redmine/reaction_test.rb
@@ -0,0 +1,189 @@
+# frozen_string_literal: true
+
+# Redmine - project management software
+# Copyright (C) 2006- 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_relative '../../../test_helper'
+
+class Redmine::ReactionTest < ActiveSupport::TestCase
+ setup do
+ @user = users(:users_002)
+ @issue = issues(:issues_007)
+ Setting.reactions_enabled = '1'
+ end
+
+ teardown do
+ Setting.clear_cache
+ end
+
+ test 'preload_reaction_details preloads ReactionDetail for all objects in the collection' do
+ User.current = users(:users_002)
+
+ issue1 = issues(:issues_001)
+ issue2 = issues(:issues_002)
+
+ assert_nil issue1.instance_variable_get(:@reaction_detail)
+ assert_nil issue2.instance_variable_get(:@reaction_detail)
+
+ Issue.preload_reaction_details([issue1, issue2])
+
+ expected_issue1_reaction_detail = Reaction::Detail.new(
+ visible_users: [users(:users_003), users(:users_002), users(:users_001)],
+ user_reaction: reactions(:reaction_002)
+ )
+
+ # ReactionDetail is already preloaded, so calling reaction_detail does not execute any query.
+ assert_no_queries do
+ assert_equal expected_issue1_reaction_detail, issue1.reaction_detail
+
+ # Even when an object has no reactions, an empty ReactionDetail is set.
+ assert_equal Reaction::Detail.new(
+ visible_users: [],
+ user_reaction: nil
+ ), issue2.reaction_detail
+ end
+ end
+
+ test 'visible_users in ReactionDetail preloaded by preload_reaction_details does not include non-visible users' do
+ current_user = User.current = User.generate!
+ visible_user = users(:users_002)
+ non_visible_user = User.generate!
+
+ project = Project.generate!
+ role = Role.generate!(users_visibility: 'members_of_visible_projects')
+
+ User.add_to_project(current_user, project, role)
+ User.add_to_project(visible_user, project, roles(:roles_001))
+
+ issue = Issue.generate!(project: project)
+
+ [current_user, visible_user, non_visible_user].each do |user|
+ issue.reactions.create!(user: user)
+ end
+
+ Issue.preload_reaction_details([issue])
+
+ # non_visible_user is not visible to current_user because they do not belong to any project.
+ assert_equal [visible_user, current_user], issue.reaction_detail.visible_users
+ end
+
+ test 'preload_reaction_details does nothing when the reaction feature is disabled' do
+ Setting.reactions_enabled = '0'
+
+ User.current = users(:users_002)
+ news1 = news(:news_001)
+
+ # Stub the Setting to avoid executing queries for retrieving settings,
+ # making it easier to confirm no queries are executed by preload_reaction_details().
+ Setting.stubs(:reactions_enabled?).returns(false)
+
+ assert_no_queries do
+ News.preload_reaction_details([news1])
+ end
+
+ assert_nil news1.instance_variable_get(:@reaction_detail)
+ end
+
+ test 'reaction_detail loads and returns ReactionDetail if it is not preloaded' do
+ message7 = messages(:messages_007)
+
+ User.current = users(:users_002)
+ assert_nil message7.instance_variable_get(:@reaction_detail)
+
+ assert_equal Reaction::Detail.new(
+ visible_users: [users(:users_002)],
+ user_reaction: reactions(:reaction_009)
+ ), message7.reaction_detail
+ end
+
+ test 'load_reaction_detail loads ReactionDetail for the object itself' do
+ comment1 = comments(:comments_001)
+
+ User.current = users(:users_001)
+ assert_nil comment1.instance_variable_get(:@reaction_detail)
+
+ comment1.load_reaction_detail
+
+ assert_equal Reaction::Detail.new(
+ visible_users: [users(:users_002)],
+ user_reaction: nil
+ ), comment1.reaction_detail
+ end
+
+ test 'visible? returns true when reactions are enabled and object is visible to user' do
+ object = issues(:issues_007)
+ user = users(:users_002)
+
+ assert Redmine::Reaction.visible?(object, user)
+ end
+
+ test 'visible? returns false when reactions are disabled' do
+ Setting.reactions_enabled = '0'
+
+ object = issues(:issues_007)
+ user = users(:users_002)
+
+ assert_not Redmine::Reaction.visible?(object, user)
+ end
+
+ test 'visible? returns false when object is not visible to user' do
+ object = issues(:issues_007)
+ user = users(:users_002)
+
+ object.expects(:visible?).with(user).returns(false)
+
+ assert_not Redmine::Reaction.visible?(object, user)
+ end
+
+ test 'editable? returns true for various reactable objects when user is logged in, object is visible, and project is active' do
+ reactable_objects = {
+ issue: issues(:issues_007),
+ message: messages(:messages_001),
+ news: news(:news_001),
+ journal: journals(:journals_001),
+ comment: comments(:comments_002)
+ }
+ user = users(:users_002)
+
+ reactable_objects.each do |type, object|
+ assert Redmine::Reaction.editable?(object, user), "Expected editable? to return true for #{type}"
+ end
+ end
+
+ test 'editable? returns false when user is not logged in' do
+ object = issues(:issues_007)
+ user = User.anonymous
+
+ assert_not Redmine::Reaction.editable?(object, user)
+ end
+
+ test 'editable? returns false when project is inactive' do
+ object = issues(:issues_007)
+ user = users(:users_002)
+ object.project.update!(status: Project::STATUS_ARCHIVED)
+
+ assert_not Redmine::Reaction.editable?(object, user)
+ end
+
+ test 'editable? returns false when project is closed' do
+ object = issues(:issues_007)
+ user = users(:users_002)
+ object.project.update!(status: Project::STATUS_CLOSED)
+
+ assert_not Redmine::Reaction.editable?(object, user)
+ end
+end
diff --git a/test/unit/lib/redmine/scm/adapters/bazaar_adapter_test.rb b/test/unit/lib/redmine/scm/adapters/bazaar_adapter_test.rb
index c0bff9b1f..9d6cd6b32 100644
--- a/test/unit/lib/redmine/scm/adapters/bazaar_adapter_test.rb
+++ b/test/unit/lib/redmine/scm/adapters/bazaar_adapter_test.rb
@@ -27,6 +27,7 @@ class BazaarAdapterTest < ActiveSupport::TestCase
def setup
@adapter = Redmine::Scm::Adapters::BazaarAdapter.
new(File.join(REPOSITORY_PATH, "trunk"))
+ skip "SCM command is unavailable" unless @adapter.class.client_available
end
def test_scm_version
diff --git a/test/unit/lib/redmine/scm/adapters/cvs_adapter_test.rb b/test/unit/lib/redmine/scm/adapters/cvs_adapter_test.rb
index 2ed9dc618..3bfe24997 100644
--- a/test/unit/lib/redmine/scm/adapters/cvs_adapter_test.rb
+++ b/test/unit/lib/redmine/scm/adapters/cvs_adapter_test.rb
@@ -27,6 +27,7 @@ class CvsAdapterTest < ActiveSupport::TestCase
if File.directory?(REPOSITORY_PATH)
def setup
@adapter = Redmine::Scm::Adapters::CvsAdapter.new(MODULE_NAME, REPOSITORY_PATH)
+ skip "SCM command is unavailable" unless @adapter.class.client_available
end
def test_scm_version
diff --git a/test/unit/lib/redmine/scm/adapters/git_adapter_test.rb b/test/unit/lib/redmine/scm/adapters/git_adapter_test.rb
index bf054860a..3f0451601 100644
--- a/test/unit/lib/redmine/scm/adapters/git_adapter_test.rb
+++ b/test/unit/lib/redmine/scm/adapters/git_adapter_test.rb
@@ -42,13 +42,6 @@ class GitAdapterTest < ActiveSupport::TestCase
WINDOWS_SKIP_STR = "TODO: This test fails in Git for Windows above 1.7.10"
def setup
- adapter_class = Redmine::Scm::Adapters::GitAdapter
- assert adapter_class
- assert adapter_class.client_command
- assert_equal true, adapter_class.client_available
- assert_equal true, adapter_class.client_version_above?([1])
- assert_equal true, adapter_class.client_version_above?([1, 0])
-
@adapter =
Redmine::Scm::Adapters::GitAdapter.
new(
@@ -59,6 +52,8 @@ class GitAdapterTest < ActiveSupport::TestCase
'ISO-8859-1'
)
assert @adapter
+ skip "SCM is unavailable" unless @adapter.class.client_available
+
@char_1 = 'Ü'
@str_felix_hex = "Felix Sch\xC3\xA4fer".b
end
diff --git a/test/unit/lib/redmine/scm/adapters/mercurial_adapter_test.rb b/test/unit/lib/redmine/scm/adapters/mercurial_adapter_test.rb
index b4f284103..81741a746 100644
--- a/test/unit/lib/redmine/scm/adapters/mercurial_adapter_test.rb
+++ b/test/unit/lib/redmine/scm/adapters/mercurial_adapter_test.rb
@@ -30,12 +30,6 @@ class MercurialAdapterTest < ActiveSupport::TestCase
if File.directory?(REPOSITORY_PATH)
def setup
- adapter_class = Redmine::Scm::Adapters::MercurialAdapter
- assert adapter_class
- assert adapter_class.client_command
- assert_equal true, adapter_class.client_available
- assert_equal true, adapter_class.client_version_above?([0, 9, 5])
-
@adapter =
Redmine::Scm::Adapters::MercurialAdapter.new(
REPOSITORY_PATH,
@@ -44,6 +38,8 @@ class MercurialAdapterTest < ActiveSupport::TestCase
nil,
'ISO-8859-1'
)
+ skip "SCM command is unavailable" unless @adapter.class.client_available
+
@diff_c_support = true
@char_1 = 'Ü'
@tag_char_1 = 'tag-Ü-00'
diff --git a/test/unit/lib/redmine/scm/adapters/subversion_adapter_test.rb b/test/unit/lib/redmine/scm/adapters/subversion_adapter_test.rb
index fe574a4ff..edc3541d1 100644
--- a/test/unit/lib/redmine/scm/adapters/subversion_adapter_test.rb
+++ b/test/unit/lib/redmine/scm/adapters/subversion_adapter_test.rb
@@ -23,6 +23,7 @@ class SubversionAdapterTest < ActiveSupport::TestCase
if repository_configured?('subversion')
def setup
@adapter = Redmine::Scm::Adapters::SubversionAdapter.new(self.class.subversion_repository_url)
+ skip "SCM command is unavailable" unless @adapter.class.client_available
end
def test_client_version
diff --git a/test/unit/lib/redmine/wiki_formatting/common_mark/formatter_test.rb b/test/unit/lib/redmine/wiki_formatting/common_mark/formatter_test.rb
index 5214a1e00..bb0c5d450 100644
--- a/test/unit/lib/redmine/wiki_formatting/common_mark/formatter_test.rb
+++ b/test/unit/lib/redmine/wiki_formatting/common_mark/formatter_test.rb
@@ -26,71 +26,71 @@ class Redmine::WikiFormatting::CommonMark::FormatterTest < ActionView::TestCase
@formatter = Redmine::WikiFormatting::CommonMark::Formatter
end
- def format(text)
+ def to_html(text)
@formatter.new(text).to_html
end
def test_should_render_hard_breaks
html ="<p>foo<br>\nbar</p>"
- assert_equal html, format("foo\\\nbar")
- assert_equal html, format("foo \nbar")
+ assert_equal html, to_html("foo\\\nbar")
+ assert_equal html, to_html("foo \nbar")
end
def test_should_render_soft_breaks
- assert_equal "<p>foo<br>\nbar</p>", format("foo\nbar")
+ assert_equal "<p>foo<br>\nbar</p>", to_html("foo\nbar")
end
def test_syntax_error_in_image_reference_should_not_raise_exception
- assert format("!>[](foo.png)")
+ assert to_html("!>[](foo.png)")
end
def test_empty_image_should_not_raise_exception
- assert format("![]()")
+ assert to_html("![]()")
end
def test_inline_style
- assert_equal "<p><strong>foo</strong></p>", format("**foo**")
+ assert_equal "<p><strong>foo</strong></p>", to_html("**foo**")
end
def test_not_set_intra_emphasis
- assert_equal "<p>foo_bar_baz</p>", format("foo_bar_baz")
+ assert_equal "<p>foo_bar_baz</p>", to_html("foo_bar_baz")
end
def test_wiki_links_should_be_preserved
text = 'This is a wiki link: [[Foo]]'
- assert_include '[[Foo]]', format(text)
+ assert_include '[[Foo]]', to_html(text)
end
def test_redmine_links_with_double_quotes_should_be_preserved
text = 'This is a redmine link: version:"1.0"'
- assert_include 'version:"1.0"', format(text)
+ assert_include 'version:"1.0"', to_html(text)
end
def test_links_by_id_should_be_preserved
text = "[project#3]"
- assert_equal "<p>#{text}</p>", format(text)
+ assert_equal "<p>#{text}</p>", to_html(text)
end
def test_links_to_users_should_be_preserved
text = "[@login]"
- assert_equal "<p>#{text}</p>", format(text)
+ assert_equal "<p>#{text}</p>", to_html(text)
text = "[user:login]"
- assert_equal "<p>#{text}</p>", format(text)
+ assert_equal "<p>#{text}</p>", to_html(text)
text = "user:user@example.org"
- assert_equal "<p>#{text}</p>", format(text)
+ assert_equal "<p>#{text}</p>", to_html(text)
text = "[user:user@example.org]"
- assert_equal "<p>#{text}</p>", format(text)
+ assert_equal "<p>#{text}</p>", to_html(text)
text = "@user@example.org"
- assert_equal "<p>#{text}</p>", format(text)
+ assert_equal "<p>#{text}</p>", to_html(text)
text = "[@user@example.org]"
- assert_equal "<p>#{text}</p>", format(text)
+ assert_equal "<p>#{text}</p>", to_html(text)
end
def test_files_with_at_should_not_end_up_as_mailto_links
text = "printscreen@2x.png"
- assert_equal "<p>#{text}</p>", format(text)
+ assert_equal "<p>#{text}</p>", to_html(text)
text = "[printscreen@2x.png]"
- assert_equal "<p>#{text}</p>", format(text)
+ assert_equal "<p>#{text}</p>", to_html(text)
end
def test_should_support_syntax_highlight
@@ -100,7 +100,7 @@ class Redmine::WikiFormatting::CommonMark::FormatterTest < ActionView::TestCase
end
~~~
STR
- assert_select_in format(text), 'pre code.ruby.syntaxhl' do
+ assert_select_in to_html(text), 'pre code.ruby.syntaxhl' do
assert_select 'span.k', :text => 'def'
assert_select "[data-language='ruby']"
end
@@ -114,7 +114,7 @@ class Redmine::WikiFormatting::CommonMark::FormatterTest < ActionView::TestCase
~~~
STR
- assert_select_in format(text), 'pre' do
+ assert_select_in to_html(text), 'pre' do
assert_select 'code[class=?]', "c++ syntaxhl"
assert_select 'span.kt', :text => 'int'
assert_select "[data-language=?]", "c++"
@@ -123,12 +123,12 @@ class Redmine::WikiFormatting::CommonMark::FormatterTest < ActionView::TestCase
def test_external_links_should_have_external_css_class
text = 'This is a [link](http://example.net/)'
- assert_equal '<p>This is a <a href="http://example.net/" class="external">link</a></p>', format(text)
+ assert_equal '<p>This is a <a href="http://example.net/" class="external">link</a></p>', to_html(text)
end
def test_locals_links_should_not_have_external_css_class
text = 'This is a [link](/issues)'
- assert_equal '<p>This is a <a href="/issues">link</a></p>', format(text)
+ assert_equal '<p>This is a <a href="/issues">link</a></p>', to_html(text)
end
def test_markdown_should_not_require_surrounded_empty_line
@@ -137,7 +137,7 @@ class Redmine::WikiFormatting::CommonMark::FormatterTest < ActionView::TestCase
* One
* Two
STR
- assert_equal "<p>This is a list:</p>\n<ul>\n<li>One</li>\n<li>Two</li>\n</ul>", format(text)
+ assert_equal "<p>This is a list:</p>\n<ul>\n<li>One</li>\n<li>Two</li>\n</ul>", to_html(text)
end
def test_footnotes
@@ -156,46 +156,46 @@ class Redmine::WikiFormatting::CommonMark::FormatterTest < ActionView::TestCase
</ol>
EXPECTED
- assert_equal expected.gsub(%r{[\r\n\t]}, ''), format(text).gsub(%r{[\r\n\t]}, '').rstrip
+ assert_equal expected.gsub(%r{[\r\n\t]}, ''), to_html(text).gsub(%r{[\r\n\t]}, '').rstrip
end
STR_WITH_PRE = [
# 0
<<~STR.chomp,
# Title
-
+
Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Maecenas sed libero.
STR
# 1
<<~STR.chomp,
## Heading 2
-
+
~~~ruby
def foo
end
~~~
-
+
Morbi facilisis accumsan orci non pharetra.
-
+
~~~ ruby
def foo
end
~~~
-
+
```
Pre Content:
-
+
## Inside pre
-
+
<tag> inside pre block
-
+
Morbi facilisis accumsan orci non pharetra.
```
STR
# 2
<<~STR.chomp,
### Heading 3
-
+
Nulla nunc nisi, egestas in ornare vel, posuere ac libero.
STR
]
@@ -226,18 +226,18 @@ class Redmine::WikiFormatting::CommonMark::FormatterTest < ActionView::TestCase
text = STR_WITH_PRE.join("\n\n")
replacement = "New text"
- assert_equal [STR_WITH_PRE[0..1], "New text"].flatten.join("\n\n"),
+ assert_equal [STR_WITH_PRE[0..1], "New text"].join("\n\n"),
@formatter.new(text).update_section(3, replacement)
end
def test_should_emphasize_text
text = 'This _text_ should be emphasized'
- assert_equal '<p>This <em>text</em> should be emphasized</p>', format(text)
+ assert_equal '<p>This <em>text</em> should be emphasized</p>', to_html(text)
end
def test_should_strike_through_text
text = 'This ~~text~~ should be striked through'
- assert_equal '<p>This <del>text</del> should be striked through</p>', format(text)
+ assert_equal '<p>This <del>text</del> should be striked through</p>', to_html(text)
end
def test_should_autolink_urls_and_emails
@@ -249,13 +249,13 @@ class Redmine::WikiFormatting::CommonMark::FormatterTest < ActionView::TestCase
["www.example.org", '<p><a href="http://www.example.org" class="external">www.example.org</a></p>'],
["user@example.org", '<p><a href="mailto:user@example.org" class="email">user@example.org</a></p>']
].each do |text, html|
- assert_equal html, format(text)
+ assert_equal html, to_html(text)
end
end
def test_should_support_html_tables
text = '<table style="background: red"><tr><td>Cell</td></tr></table>'
- assert_equal '<table><tr><td>Cell</td></tr></table>', format(text)
+ assert_equal '<table><tr><td>Cell</td></tr></table>', to_html(text)
end
def test_should_remove_unsafe_uris
@@ -263,7 +263,7 @@ class Redmine::WikiFormatting::CommonMark::FormatterTest < ActionView::TestCase
['<img src="data:foobar">', '<img>'],
['<a href="javascript:bla">click me</a>', '<p><a>click me</a></p>'],
].each do |text, html|
- assert_equal html, format(text)
+ assert_equal html, to_html(text)
end
end
@@ -274,7 +274,7 @@ class Redmine::WikiFormatting::CommonMark::FormatterTest < ActionView::TestCase
%[sit<br/>amet <style>.foo { color: #fff; }</style> <script>alert("hello world");</script>]
]
].each do |expected, input|
- assert_equal expected, format(input)
+ assert_equal expected, to_html(input)
end
end
@@ -287,7 +287,7 @@ class Redmine::WikiFormatting::CommonMark::FormatterTest < ActionView::TestCase
expected = <<~EXPECTED
<p>Task list:</p>
- <ul class="task-list">
+ <ul class="contains-task-list">
<li class="task-list-item">
<input type="checkbox" class="task-list-item-checkbox" disabled> Task 1
</li>
@@ -296,7 +296,50 @@ class Redmine::WikiFormatting::CommonMark::FormatterTest < ActionView::TestCase
</ul>
EXPECTED
- assert_equal expected.gsub(%r{[\r\n\t]}, ''), format(text).gsub(%r{[\r\n\t]}, '').rstrip
+ assert_equal expected.gsub(%r{[\r\n\t]}, ''), to_html(text).gsub(%r{[\r\n\t]}, '').rstrip
+ end
+
+ def test_should_render_alert_blocks
+ text = <<~MD
+ > [!note]
+ > This is a note.
+
+ > [!tip]
+ > This is a tip.
+
+ > [!warning]
+ > This is a warning.
+
+ > [!caution]
+ > This is a caution.
+
+ > [!important]
+ > This is a important.
+ MD
+
+ html = to_html(text)
+ %w[note tip warning caution important].each do |alert|
+ icon = Redmine::WikiFormatting::CommonMark::ALERT_TYPE_TO_ICON_NAME[alert]
+ # rubocop:disable Layout/LineLength
+ expected = %r{<div class="markdown-alert markdown-alert-#{alert}">\n<p class="markdown-alert-title"><svg class="s18 icon-svg" aria-hidden="true"><use href="/assets/icons-\w+.svg\#icon--#{icon}"></use></svg><span class="icon-label">#{alert.capitalize}</span></p>\n<p>This is a #{alert}.</p>\n</div>}
+ # rubocop:enable Layout/LineLength
+ assert_match expected, html
+ end
+ end
+
+ def test_should_not_render_unknown_alert_type
+ text = <<~MD
+ > [!unknown]
+ > This should not become an alert.
+ MD
+
+ html = to_html(text)
+
+ assert_include "<blockquote>", html
+ assert_include "[!unknown]", html
+ assert_include "This should not become an alert.", html
+
+ assert_not_include 'markdown-alert', html
end
private
diff --git a/test/unit/lib/redmine/wiki_formatting/common_mark/sanitization_filter_test.rb b/test/unit/lib/redmine/wiki_formatting/common_mark/sanitization_filter_test.rb
index 4c0282f2d..b2d19eab9 100644
--- a/test/unit/lib/redmine/wiki_formatting/common_mark/sanitization_filter_test.rb
+++ b/test/unit/lib/redmine/wiki_formatting/common_mark/sanitization_filter_test.rb
@@ -47,10 +47,14 @@ if Object.const_defined?(:Commonmarker)
end
def test_should_support_footnotes
- input = %(<a href="#fn-1" id="fnref-1">foo</a>)
- assert_equal input, filter(input)
- input = %(<ol><li id="fn-1">footnote</li></ol>)
- assert_equal input, filter(input)
+ [
+ %(<a href="#fn-1" id="fnref-1">foo</a>),
+ %(<a href="#fn-1" id="fnref-1-2">foo</a>),
+ %(<ol><li id="fn-1">footnote</li></ol>),
+ ].each do |input|
+ assert_equal input, filter(input)
+ assert_equal input, filter(input)
+ end
end
def test_should_remove_invalid_ids
@@ -71,6 +75,32 @@ if Object.const_defined?(:Commonmarker)
assert_equal %(<code>foo</code>), filter(input)
end
+ def test_should_allow_valid_alert_div_and_p_classes
+ html = <<~HTML
+ <div class="markdown-alert markdown-alert-tip">
+ <p class="markdown-alert-title">Tip</p>
+ <p>Useful tip.</p>
+ </div>
+ HTML
+
+ sanitized = filter(html)
+
+ assert_include 'class="markdown-alert markdown-alert-tip"', sanitized
+ assert_include 'class="markdown-alert-title"', sanitized
+ end
+
+ def test_should_remove_invalid_div_class
+ html = '<div class="bad-class">Text</div>'
+ sanitized = filter(html)
+ assert_not_includes 'bad-class', sanitized
+ end
+
+ def test_should_remove_invalid_p_class
+ html = '<p class="bad-class">Text</p>'
+ sanitized = filter(html)
+ assert_not_include 'bad-class', sanitized
+ end
+
def test_should_allow_links_with_safe_url_schemes
%w(http https ftp ssh foo).each do |scheme|
input = %(<a href="#{scheme}://example.org/">foo</a>)
diff --git a/test/unit/lib/redmine/wiki_formatting/html_sanitizer_test.rb b/test/unit/lib/redmine/wiki_formatting/html_sanitizer_test.rb
index 11dddb5f8..f8793cf9f 100644
--- a/test/unit/lib/redmine/wiki_formatting/html_sanitizer_test.rb
+++ b/test/unit/lib/redmine/wiki_formatting/html_sanitizer_test.rb
@@ -35,4 +35,24 @@ class Redmine::WikiFormatting::HtmlSanitizerTest < ActiveSupport::TestCase
input = %(<a href="javascript:alert('hello');">foo</a>)
assert_equal "<a>foo</a>", @sanitizer.call(input)
end
+
+ def test_should_be_strict_with_task_list_items
+ to_test = {
+ %(<input type="checkbox" class="">) => "",
+ %(<input type="checkbox" class="task-list-item-checkbox other">) => "",
+ %(<input type="checkbox" class="task-list-item-checkbox" id="item1">) => %(<input type="checkbox" class="task-list-item-checkbox">),
+ %(<input type="text" class="">) => "",
+ %(<input />) => "",
+ %(<ul class="other"></ul) => "<ul></ul>",
+ %(<ul class="contains-task-list"></ul) => "<ul class=\"contains-task-list\"></ul>",
+ %(<ul class="contains-task-list" id="list1"></ul) => "<ul class=\"contains-task-list\"></ul>",
+ %(<li class="other"></li>) => "",
+ %(<li id="other"></li>) => "",
+ %(<li class="task-list-item"></li>) => "",
+ %(<li class="task-list-item">Item 1</li>) => "Item 1",
+ }
+ to_test.each do |input, result|
+ assert_equal result, @sanitizer.call(input)
+ end
+ end
end
diff --git a/test/unit/lib/redmine/wiki_formatting/macros_test.rb b/test/unit/lib/redmine/wiki_formatting/macros_test.rb
index f23c76fdd..a41428266 100644
--- a/test/unit/lib/redmine/wiki_formatting/macros_test.rb
+++ b/test/unit/lib/redmine/wiki_formatting/macros_test.rb
@@ -140,12 +140,12 @@ class Redmine::WikiFormatting::MacrosTest < Redmine::HelperTest
def test_macro_exception_should_be_displayed
Redmine::WikiFormatting::Macros.macro :exception do |obj, args|
- raise "My message"
+ raise "My exception's message"
end
text = "{{exception}}"
assert_include(
- '<div class="flash error">Error executing the <strong>exception</strong> macro (My message)</div>',
+ '<div class="flash error">Error executing the <strong>exception</strong> macro (My exception&#39;s message)</div>',
textilizable(text)
)
end
diff --git a/test/unit/lib/redmine/wiki_formatting/textile_formatter_test.rb b/test/unit/lib/redmine/wiki_formatting/textile_formatter_test.rb
index 32280cfdf..678d4c6b2 100644
--- a/test/unit/lib/redmine/wiki_formatting/textile_formatter_test.rb
+++ b/test/unit/lib/redmine/wiki_formatting/textile_formatter_test.rb
@@ -466,19 +466,19 @@ class Redmine::WikiFormatting::TextileFormatterTest < ActionView::TestCase
replacement = "New text"
assert_equal(
- [STR_WITHOUT_PRE[0], replacement, STR_WITHOUT_PRE[2..4]].flatten.join("\n\n"),
+ [STR_WITHOUT_PRE[0], replacement, STR_WITHOUT_PRE[2..4]].join("\n\n"),
@formatter.new(TEXT_WITHOUT_PRE).update_section(2, replacement)
)
assert_equal(
- [STR_WITHOUT_PRE[0..1], replacement, STR_WITHOUT_PRE[4]].flatten.join("\n\n"),
+ [STR_WITHOUT_PRE[0..1], replacement, STR_WITHOUT_PRE[4]].join("\n\n"),
@formatter.new(TEXT_WITHOUT_PRE).update_section(3, replacement)
)
assert_equal(
- [STR_WITHOUT_PRE[0..2], replacement, STR_WITHOUT_PRE[4]].flatten.join("\n\n"),
+ [STR_WITHOUT_PRE[0..2], replacement, STR_WITHOUT_PRE[4]].join("\n\n"),
@formatter.new(TEXT_WITHOUT_PRE).update_section(5, replacement)
)
assert_equal(
- [STR_WITHOUT_PRE[0..3], replacement].flatten.join("\n\n"),
+ [STR_WITHOUT_PRE[0..3], replacement].join("\n\n"),
@formatter.new(TEXT_WITHOUT_PRE).update_section(6, replacement)
)
assert_equal TEXT_WITHOUT_PRE, @formatter.new(TEXT_WITHOUT_PRE).update_section(0, replacement)
@@ -488,7 +488,7 @@ class Redmine::WikiFormatting::TextileFormatterTest < ActionView::TestCase
def test_update_section_with_hash_should_update_the_requested_section
replacement = "New text"
assert_equal(
- [STR_WITHOUT_PRE[0], replacement, STR_WITHOUT_PRE[2..4]].flatten.join("\n\n"),
+ [STR_WITHOUT_PRE[0], replacement, STR_WITHOUT_PRE[2..4]].join("\n\n"),
@formatter.new(TEXT_WITHOUT_PRE).
update_section(2, replacement, ActiveSupport::Digest.hexdigest(STR_WITHOUT_PRE[1]))
)
@@ -552,7 +552,7 @@ class Redmine::WikiFormatting::TextileFormatterTest < ActionView::TestCase
text = STR_WITH_PRE.join("\n\n")
replacement = "New text"
assert_equal(
- [STR_WITH_PRE[0..1], "New text"].flatten.join("\n\n"),
+ [STR_WITH_PRE[0..1], "New text"].join("\n\n"),
@formatter.new(text).update_section(3, replacement)
)
end
diff --git a/test/unit/member_test.rb b/test/unit/member_test.rb
index a7b1843dc..df9088027 100644
--- a/test/unit/member_test.rb
+++ b/test/unit/member_test.rb
@@ -58,6 +58,39 @@ class MemberTest < ActiveSupport::TestCase
assert_equal 2, @jsmith.reload.roles.size
end
+ def test_update_roles_with_inherited_roles
+ User.current = User.find(1)
+
+ project = Project.find(1)
+ group_a = Group.generate!
+ group_b = Group.generate!
+ test_user = User.generate!
+ group_a.users << test_user
+ group_b.users << test_user
+
+ # Verify that inherited roles are correctly assigned
+ group_a_member = Member.new(project: project, user_id: group_a.id)
+ group_a_member.set_editable_role_ids([1]) # Add Manager role to Group A
+ group_b_member = Member.new(project: project, user_id: group_b.id)
+ group_b_member.set_editable_role_ids([1, 2]) # Add Manager and Developer roles to Group B
+ project.members << [group_a_member, group_b_member]
+ test_user_member = test_user.members.find_by(project_id: project.id)
+ assert_equal [ # [role_id, inherited_from]
+ [1, group_a_member.member_roles.find_by(role_id: 1).id],
+ [1, group_b_member.member_roles.find_by(role_id: 1).id],
+ [2, group_b_member.member_roles.find_by(role_id: 2).id],
+ ].sort, test_user_member.member_roles.map{|r| [r.role_id, r.inherited_from]}.sort
+
+ # Verify that a new non-inherited role is added and inherited roles are maintained
+ test_user_member.set_editable_role_ids([3]) # Add Reporter role to test_user
+ assert_equal [ # [role_id, inherited_from]
+ [1, group_a_member.member_roles.find_by(role_id: 1).id],
+ [1, group_b_member.member_roles.find_by(role_id: 1).id],
+ [2, group_b_member.member_roles.find_by(role_id: 2).id],
+ [3, nil]
+ ].sort, test_user_member.member_roles.map{|r| [r.role_id, r.inherited_from]}.sort
+ end
+
def test_validate
member = Member.new(:project_id => 1, :user_id => 2, :role_ids => [2])
# same use cannot have more than one membership for a project
@@ -75,7 +108,7 @@ class MemberTest < ActiveSupport::TestCase
assert !member.save
assert_include I18n.translate('activerecord.errors.messages.empty'), member.errors[:role]
assert_equal 'Rôle doit être renseigné(e)',
- [member.errors.full_messages].flatten.join
+ [member.errors.full_messages].join
end
def test_validate_member_role
diff --git a/test/unit/project_admin_query_test.rb b/test/unit/project_admin_query_test.rb
new file mode 100644
index 000000000..8e58e2efb
--- /dev/null
+++ b/test/unit/project_admin_query_test.rb
@@ -0,0 +1,138 @@
+# frozen_string_literal: true
+
+# Redmine - project management software
+# Copyright (C) 2006- 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_relative '../test_helper'
+
+class ProjectAdminQueryTest < ActiveSupport::TestCase
+ include Redmine::I18n
+
+ def test_filter_values_be_arrays
+ q = ProjectAdminQuery.new
+ assert_nil q.project
+
+ q.available_filters.each do |name, filter|
+ values = filter.values
+ assert (values.nil? || values.is_a?(Array)),
+ "#values for #{name} filter returned a #{values.class.name}"
+ end
+ end
+
+ def test_project_statuses_filter_should_return_project_statuses
+ set_language_if_valid 'en'
+ query = ProjectAdminQuery.new(:name => '_')
+ query.filters = {'status' => {:operator => '=', :values => []}}
+ values = query.available_filters['status'][:values]
+ assert_equal ['active', 'closed', 'archived', 'scheduled for deletion'], values.map(&:first)
+ assert_equal ['1', '5', '9', '10'], values.map(&:second)
+ end
+
+ def test_default_columns
+ q = ProjectAdminQuery.new
+ assert q.columns.any?
+ assert q.inline_columns.any?
+ assert q.block_columns.empty?
+ end
+
+ def test_available_columns_should_include_project_custom_fields
+ query = ProjectAdminQuery.new
+ assert_include :cf_3, query.available_columns.map(&:name)
+ end
+
+ def test_available_display_types_should_always_returns_list
+ query = ProjectAdminQuery.new
+ assert_equal ['list'], query.available_display_types
+ end
+
+ def test_display_type_should_returns_list
+ ProjectAdminQuery.new.available_display_types.each do |t|
+ with_settings :project_list_display_type => t do
+ q = ProjectAdminQuery.new
+ assert_equal 'list', q.display_type
+ end
+ end
+ end
+
+ def test_no_default_project_admin_query
+ user = User.find(1)
+ query = ProjectQuery.find(11)
+ user_query = ProjectQuery.find(12)
+ user_query.update(visibility: Query::VISIBILITY_PUBLIC)
+
+ [nil, user, User.anonymous].each do |u|
+ assert_nil ProjectAdminQuery.default(user: u)
+ end
+
+ # ignore the default_project_query for admin queries
+ with_settings :default_project_query => query.id do
+ [nil, user, User.anonymous].each do |u|
+ assert_nil ProjectAdminQuery.default(user: u)
+ end
+ end
+
+ # user default, overrides global default
+ user.pref.default_project_query = user_query.id
+ user.pref.save
+
+ with_settings :default_project_query => query.id do
+ assert_nil ProjectAdminQuery.default(user: user)
+ end
+ end
+
+ def test_project_statuses_values_should_return_all_statuses
+ set_language_if_valid 'en'
+ q = ProjectAdminQuery.new
+ assert_equal [
+ ["active", "1"],
+ ["closed", "5"],
+ ["archived", "9"],
+ ["scheduled for deletion", "10"]
+ ], q.project_statuses_values
+ end
+
+ def test_base_scope_should_return_all_projects
+ q = ProjectAdminQuery.new
+ assert_equal Project.all, q.base_scope
+ end
+
+ def test_results_scope_has_last_activity_date
+ q = ProjectAdminQuery.generate!(column_names: [:last_activity_date])
+ result_projects = q.results_scope({})
+
+ assert_kind_of ActiveRecord::Relation, result_projects
+ assert_equal Project, result_projects.klass
+
+ last_activitiy_date = result_projects.find{|p| p.id == 1}.instance_variable_get(:@last_activity_date)
+ assert_not_nil last_activitiy_date
+ assert_equal Redmine::Activity::Fetcher.new(User.current).events(nil, nil, :project => Project.find(1)).first.updated_on, last_activitiy_date
+ end
+
+ def test_results_scope_with_offset_and_limit
+ q = ProjectAdminQuery.new
+
+ ((q.results_scope.count / 2) + 1).times do |i|
+ limit = 2
+ offset = i * 2
+
+ scope_without = q.results_scope.offset(offset).limit(limit).ids
+ scope_with = q.results_scope(:offset => offset, :limit => limit).ids
+
+ assert_equal scope_without, scope_with
+ end
+ end
+end
diff --git a/test/unit/project_query_test.rb b/test/unit/project_query_test.rb
index 3e232f820..2b8c0ea8d 100644
--- a/test/unit/project_query_test.rb
+++ b/test/unit/project_query_test.rb
@@ -56,16 +56,9 @@ class ProjectQueryTest < ActiveSupport::TestCase
def test_available_display_types_should_returns_bord_and_list
query = ProjectQuery.new
- query.admin_projects = nil
assert_equal ['board', 'list'], query.available_display_types
end
- def test_available_display_types_should_always_returns_list_when_admin_projects_is_set
- query = ProjectQuery.new
- query.admin_projects = 1
- assert_equal ['list'], query.available_display_types
- end
-
def test_display_type_default_should_equal_with_setting_project_list_display_type
ProjectQuery.new.available_display_types.each do |t|
with_settings :project_list_display_type => t do
@@ -81,8 +74,10 @@ class ProjectQueryTest < ActiveSupport::TestCase
user_query = ProjectQuery.find(12)
user_query.update(visibility: Query::VISIBILITY_PUBLIC)
- [nil, user, User.anonymous].each do |u|
- assert_nil IssueQuery.default(user: u)
+ with_settings :default_project_query => nil do
+ [nil, user, User.anonymous].each do |u|
+ assert_nil ProjectQuery.default(user: u)
+ end
end
# only global default is set
@@ -110,38 +105,17 @@ class ProjectQueryTest < ActiveSupport::TestCase
assert_nil ProjectQuery.default
end
- def test_display_type_should_returns_list_when_admin_projects_is_set
- q = ProjectQuery.new
- q.admin_projects = 1
- assert_equal 'list', q.display_type
- end
-
def test_project_statuses_values_should_equal_ancestors_return
ancestor = Query.new
q = ProjectQuery.new
assert_equal ancestor.project_statuses_values, q.project_statuses_values
end
- def test_project_statuses_values_should_includes_project_status_archeved_when_admin_projects_is_set
- q = ProjectQuery.new
- q.admin_projects = 1
- assert_includes q.project_statuses_values, [l(:project_status_archived), Project::STATUS_ARCHIVED.to_s]
- Query.new.project_statuses_values.each do |status|
- assert_includes q.project_statuses_values, status
- end
- end
-
def test_base_scope_should_return_visible_projects
q = ProjectQuery.new
assert_equal Project.visible, q.base_scope
end
- def test_base_scope_should_return_all_projects_when_admin_projects_is_set
- q = ProjectQuery.new
- q.admin_projects = 1
- assert_equal Project.all, q.base_scope
- end
-
def test_results_scope_has_last_activity_date
q = ProjectQuery.generate!(column_names: [:last_activity_date])
result_projects = q.results_scope({})
diff --git a/test/unit/query_test.rb b/test/unit/query_test.rb
index ff2cef903..155a74b64 100644
--- a/test/unit/query_test.rb
+++ b/test/unit/query_test.rb
@@ -623,7 +623,7 @@ class QueryTest < ActiveSupport::TestCase
query.add_filter('due_date', '><t+', ['15'])
issues = find_issues_with_query(query)
assert !issues.empty?
- issues.each {|issue| assert(issue.due_date >= Date.today && issue.due_date <= (Date.today + 15))}
+ issues.each {|issue| assert(issue.due_date.between?(Date.today, (Date.today + 15)))}
end
def test_operator_less_than_ago
@@ -641,7 +641,7 @@ class QueryTest < ActiveSupport::TestCase
query.add_filter('due_date', '><t-', ['3'])
issues = find_issues_with_query(query)
assert !issues.empty?
- issues.each {|issue| assert(issue.due_date >= (Date.today - 3) && issue.due_date <= Date.today)}
+ issues.each {|issue| assert(issue.due_date.between?((Date.today - 3), Date.today))}
end
def test_operator_more_than_ago
@@ -2326,7 +2326,7 @@ class QueryTest < ActiveSupport::TestCase
values =
issues.filter_map do |i|
begin
- Kernel.Float(i.custom_value_for(c.custom_field).to_s)
+ Kernel.Float(i.custom_value_for(c.custom_field).to_s, exception: false)
rescue
nil
end
diff --git a/test/unit/reaction_test.rb b/test/unit/reaction_test.rb
new file mode 100644
index 000000000..9b3da0738
--- /dev/null
+++ b/test/unit/reaction_test.rb
@@ -0,0 +1,118 @@
+# frozen_string_literal: true
+
+# Redmine - project management software
+# Copyright (C) 2006- 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_relative '../test_helper'
+
+class ReactionTest < ActiveSupport::TestCase
+ test 'validates :inclusion of reactable_type' do
+ %w(Issue Journal News Comment Message).each do |type|
+ reaction = Reaction.new(reactable_type: type, user: User.new)
+ assert reaction.valid?
+ end
+
+ assert_not Reaction.new(reactable_type: 'InvalidType', user: User.new).valid?
+ end
+
+ test 'scope: by' do
+ user2_reactions = issues(:issues_001).reactions.by(users(:users_002))
+
+ assert_equal [reactions(:reaction_002)], user2_reactions
+ end
+
+ test "should prevent duplicate reactions with unique constraint under concurrent creation" do
+ user = users(:users_001)
+ issue = issues(:issues_004)
+
+ threads = []
+ results = []
+
+ # Ensure both threads start at the same time
+ barrier = Concurrent::CyclicBarrier.new(2)
+
+ # Create two threads to simulate concurrent creation
+ 2.times do
+ threads << Thread.new do
+ barrier.wait # Wait for both threads to be ready
+ begin
+ reaction = Reaction.create(
+ reactable: issue,
+ user: user
+ )
+ results << reaction.persisted?
+ rescue ActiveRecord::RecordNotUnique
+ results << false
+ end
+ end
+ end
+
+ # Wait for both threads to finish
+ threads.each(&:join)
+
+ # Ensure only one reaction was created
+ assert_equal 1, Reaction.where(reactable: issue, user: user).count
+ assert_includes results, true
+ assert_equal 1, results.count(true)
+ end
+
+ test 'build_detail_map_for generates a detail map for reactable objects' do
+ result = Reaction.build_detail_map_for([issues(:issues_001), issues(:issues_006)], users(:users_003))
+
+ expected = {
+ 1 => Reaction::Detail.new(
+ visible_users: [users(:users_003), users(:users_002), users(:users_001)],
+ user_reaction: reactions(:reaction_003)
+ ),
+ 6 => Reaction::Detail.new(
+ visible_users: [users(:users_002)],
+ user_reaction: nil
+ )
+ }
+ assert_equal expected, result
+
+ # When an object have no reactions, the result should be empty.
+ result = Reaction.build_detail_map_for([journals(:journals_002)], users(:users_002))
+
+ assert_empty result
+ end
+
+ test 'build_detail_map_for filters users based on visibility' do
+ current_user = User.generate!
+ visible_user = users(:users_002)
+ non_visible_user = User.generate!
+
+ project = Project.generate!
+ role = Role.generate!(users_visibility: 'members_of_visible_projects')
+
+ User.add_to_project(current_user, project, role)
+ User.add_to_project(visible_user, project, roles(:roles_001))
+
+ issue = Issue.generate!(project: project)
+
+ [current_user, visible_user, non_visible_user].each do |user|
+ issue.reactions.create!(user: user)
+ end
+
+ result = Reaction.build_detail_map_for([issue], current_user)
+
+ assert_equal(
+ [current_user, visible_user].sort_by(&:id),
+ result[issue.id].visible_users.sort_by(&:id)
+ )
+ end
+end
diff --git a/test/unit/repository_bazaar_test.rb b/test/unit/repository_bazaar_test.rb
index 23f3ce48f..5fec37973 100644
--- a/test/unit/repository_bazaar_test.rb
+++ b/test/unit/repository_bazaar_test.rb
@@ -50,6 +50,7 @@ class RepositoryBazaarTest < ActiveSupport::TestCase
:log_encoding => 'UTF-8'
)
assert @repository
+ skip "SCM command is unavailable" unless @repository.class.scm_available
end
def test_blank_path_to_repository_error_message
diff --git a/test/unit/repository_cvs_test.rb b/test/unit/repository_cvs_test.rb
index af995eac0..84d0ed80b 100644
--- a/test/unit/repository_cvs_test.rb
+++ b/test/unit/repository_cvs_test.rb
@@ -36,6 +36,7 @@ class RepositoryCvsTest < ActiveSupport::TestCase
:url => MODULE_NAME,
:log_encoding => 'UTF-8')
assert @repository
+ skip "SCM command is unavailable" unless @repository.class.scm_available
end
def test_blank_module_error_message
diff --git a/test/unit/repository_git_test.rb b/test/unit/repository_git_test.rb
index ec1ca5157..857be9442 100644
--- a/test/unit/repository_git_test.rb
+++ b/test/unit/repository_git_test.rb
@@ -41,6 +41,7 @@ class RepositoryGitTest < ActiveSupport::TestCase
:path_encoding => 'ISO-8859-1'
)
assert @repository
+ skip "SCM command is unavailable" unless @repository.class.scm_available
end
def test_nondefault_repo_with_blank_identifier_destruction
diff --git a/test/unit/repository_mercurial_test.rb b/test/unit/repository_mercurial_test.rb
index 861729bac..991d19a6d 100644
--- a/test/unit/repository_mercurial_test.rb
+++ b/test/unit/repository_mercurial_test.rb
@@ -35,6 +35,7 @@ class RepositoryMercurialTest < ActiveSupport::TestCase
:path_encoding => 'ISO-8859-1'
)
assert @repository
+ skip "SCM command is unavailable" unless @repository.class.scm_available
end
def test_blank_path_to_repository_error_message
@@ -168,7 +169,7 @@ class RepositoryMercurialTest < ActiveSupport::TestCase
@repository.fetch_changesets
@project.reload
assert_equal NUM_REV, @repository.changesets.count
- assert_equal 53, @repository.filechanges.count
+ assert_equal 47, @repository.filechanges.count
rev0 = @repository.changesets.find_by_revision('0')
assert_equal "Initial import.\nThe repository contains 3 files.",
rev0.comments
@@ -261,13 +262,13 @@ class RepositoryMercurialTest < ActiveSupport::TestCase
@repository.latest_changesets(
'/sql_escape/percent%dir/percent%file1.txt', nil
)
- assert_equal %w|30 11 10 9|, changesets.collect(&:revision)
+ assert_equal %w|11 10 9|, changesets.collect(&:revision)
changesets =
@repository.latest_changesets(
'/sql_escape/underscore_dir/understrike_file.txt', nil
)
- assert_equal %w|30 12 9|, changesets.collect(&:revision)
+ assert_equal %w|12 9|, changesets.collect(&:revision)
changesets = @repository.latest_changesets('README', nil)
assert_equal %w|31 30 28 17 8 6 1 0|, changesets.collect(&:revision)
@@ -284,7 +285,7 @@ class RepositoryMercurialTest < ActiveSupport::TestCase
path = 'sql_escape/percent%dir'
changesets = @repository.latest_changesets(path, nil)
- assert_equal %w|30 13 11 10 9|, changesets.collect(&:revision)
+ assert_equal %w|13 11 10 9|, changesets.collect(&:revision)
changesets = @repository.latest_changesets(path, '11')
assert_equal %w|11 10 9|, changesets.collect(&:revision)
@@ -294,7 +295,7 @@ class RepositoryMercurialTest < ActiveSupport::TestCase
path = 'sql_escape/underscore_dir'
changesets = @repository.latest_changesets(path, nil)
- assert_equal %w|30 13 12 9|, changesets.collect(&:revision)
+ assert_equal %w|13 12 9|, changesets.collect(&:revision)
changesets = @repository.latest_changesets(path, '12')
assert_equal %w|12 9|, changesets.collect(&:revision)
diff --git a/test/unit/repository_subversion_test.rb b/test/unit/repository_subversion_test.rb
index b4590ce31..dfdf520e7 100644
--- a/test/unit/repository_subversion_test.rb
+++ b/test/unit/repository_subversion_test.rb
@@ -30,6 +30,7 @@ class RepositorySubversionTest < ActiveSupport::TestCase
@repository = Repository::Subversion.create(:project => @project,
:url => self.class.subversion_repository_url)
assert @repository
+ skip "SCM command is unavailable" unless @repository.class.scm_available
end
def test_invalid_url
diff --git a/test/unit/repository_test.rb b/test/unit/repository_test.rb
index 53b5e0ee7..84c22a73f 100644
--- a/test/unit/repository_test.rb
+++ b/test/unit/repository_test.rb
@@ -455,7 +455,7 @@ class RepositoryTest < ActiveSupport::TestCase
def test_stats_by_author_reflect_changesets_and_changes
repository = Repository.find(10)
- expected = {"Dave Lopper"=>{:commits_count=>10, :changes_count=>3}}
+ expected = {"Dave Lopper"=>{:commits_count=>11, :changes_count=>3}}
assert_equal expected, repository.stats_by_author
set = Changeset.create!(
@@ -467,7 +467,7 @@ class RepositoryTest < ActiveSupport::TestCase
)
Change.create!(:changeset => set, :action => 'A', :path => '/path/to/file1')
Change.create!(:changeset => set, :action => 'A', :path => '/path/to/file2')
- expected = {"Dave Lopper"=>{:commits_count=>11, :changes_count=>5}}
+ expected = {"Dave Lopper"=>{:commits_count=>12, :changes_count=>5}}
assert_equal expected, repository.stats_by_author
end
@@ -476,7 +476,7 @@ class RepositoryTest < ActiveSupport::TestCase
# to ensure things are dynamically linked to Users
User.find_by_login("dlopper").update_attribute(:firstname, "Dave's")
repository = Repository.find(10)
- expected = {"Dave's Lopper"=>{:commits_count=>10, :changes_count=>3}}
+ expected = {"Dave's Lopper"=>{:commits_count=>11, :changes_count=>3}}
assert_equal expected, repository.stats_by_author
end
@@ -502,7 +502,7 @@ class RepositoryTest < ActiveSupport::TestCase
# with committer="dlopper <dlopper@somefoo.net>"
repository = Repository.find(10)
- expected = {"Dave Lopper"=>{:commits_count=>10, :changes_count=>3}}
+ expected = {"Dave Lopper"=>{:commits_count=>11, :changes_count=>3}}
assert_equal expected, repository.stats_by_author
set = Changeset.create!(
@@ -513,7 +513,7 @@ class RepositoryTest < ActiveSupport::TestCase
:comments => 'Another commit by foo.'
)
- expected = {"Dave Lopper"=>{:commits_count=>11, :changes_count=>3}}
+ expected = {"Dave Lopper"=>{:commits_count=>12, :changes_count=>3}}
assert_equal expected, repository.stats_by_author
end
diff --git a/test/unit/role_test.rb b/test/unit/role_test.rb
index 21103919f..1d0d39d7e 100644
--- a/test/unit/role_test.rb
+++ b/test/unit/role_test.rb
@@ -175,6 +175,32 @@ class RoleTest < ActiveSupport::TestCase
assert_equal false, role.permissions_tracker_ids?(:view_issues, 1)
end
+ def test_allowed_to_with_symbol
+ role = Role.create!(:name => 'Test', :permissions => [:view_issues])
+ assert_equal true, role.allowed_to?(:view_issues)
+ assert_equal false, role.allowed_to?(:add_issues)
+ end
+
+ def test_allowed_to_with_symbol_and_scope
+ role = Role.create!(:name => 'Test', :permissions => [:view_issues, :delete_issues])
+ assert_equal true, role.allowed_to?(:view_issues, [:view_issues, :add_issues])
+ assert_equal false, role.allowed_to?(:add_issues, [:view_issues, :add_issues])
+ assert_equal false, role.allowed_to?(:delete_issues, [:view_issues, :add_issues])
+ end
+
+ def test_allowed_to_with_hash
+ role = Role.create!(:name => 'Test', :permissions => [:view_issues])
+ assert_equal true, role.allowed_to?(:controller => 'issues', :action => 'show')
+ assert_equal false, role.allowed_to?(:controller => 'issues', :action => 'create')
+ end
+
+ def test_allowed_to_with_hash_and_scope
+ role = Role.create!(:name => 'Test', :permissions => [:view_issues, :delete_issues])
+ assert_equal true, role.allowed_to?({:controller => 'issues', :action => 'show'}, [:view_issues, :add_issues])
+ assert_equal false, role.allowed_to?({:controller => 'issues', :action => 'create'}, [:view_issues, :add_issues])
+ assert_equal false, role.allowed_to?({:controller => 'issues', :action => 'destroy'}, [:view_issues, :add_issues])
+ end
+
def test_has_permission_without_permissions
role = Role.create!(:name => 'Test')
assert_equal false, role.has_permission?(:delete_issues)
diff --git a/test/unit/setting_test.rb b/test/unit/setting_test.rb
index 4ae07cebb..cbfabbb02 100644
--- a/test/unit/setting_test.rb
+++ b/test/unit/setting_test.rb
@@ -147,4 +147,8 @@ class SettingTest < ActiveSupport::TestCase
def test_default_text_formatting_for_new_installations_is_common_mark
assert_equal 'common_mark', Setting.text_formatting
end
+
+ def test_default_wiki_tablesort_enabled_for_new_installations_is_disabled
+ assert_equal "0", Setting.wiki_tablesort_enabled
+ end
end
diff --git a/test/unit/time_entry_test.rb b/test/unit/time_entry_test.rb
index 6d04619e8..19b1ba2a4 100644
--- a/test/unit/time_entry_test.rb
+++ b/test/unit/time_entry_test.rb
@@ -175,6 +175,18 @@ class TimeEntryTest < ActiveSupport::TestCase
end
end
+ def test_should_not_accept_closed_issue
+ with_settings :timelog_accept_closed_issues => '0' do
+ project = Project.find(1)
+ entry = TimeEntry.generate project: project
+ issue = project.issues.to_a.detect(&:closed?)
+ entry.issue = issue
+ assert !entry.save
+ assert entry.errors[:base].present?
+ assert_equal 'Cannot log time on a closed issue', entry.errors[:base].first
+ end
+ end
+
def test_should_require_spent_on
with_settings :timelog_accept_future_dates => '0' do
entry = TimeEntry.find(1)
diff --git a/test/unit/user_query_test.rb b/test/unit/user_query_test.rb
index 1f8ce3464..ef31ba2c2 100644
--- a/test/unit/user_query_test.rb
+++ b/test/unit/user_query_test.rb
@@ -209,6 +209,30 @@ class UserQueryTest < ActiveSupport::TestCase
assert_equal [2, 1], users.pluck(:id)
end
+ def test_user_query_is_only_visible_to_admins
+ q = UserQuery.new(name: '_')
+ assert q.save
+
+ admin = User.admin(true).first
+ user = User.admin(false).first
+
+ assert q.visible?(admin)
+ assert_include q, UserQuery.visible(admin).to_a
+
+ assert_not q.visible?(user)
+ assert_not_include q, UserQuery.visible(user)
+ end
+
+ def test_user_query_is_only_editable_by_admins
+ q = UserQuery.new(name: '_')
+
+ admin = User.admin(true).first
+ user = User.admin(false).first
+
+ assert q.editable_by?(admin)
+ assert_not q.editable_by?(user)
+ end
+
def find_users_with_query(query)
User.where(query.statement).to_a
end
diff --git a/test/unit/user_test.rb b/test/unit/user_test.rb
index ede12e1ce..967771c87 100644
--- a/test/unit/user_test.rb
+++ b/test/unit/user_test.rb
@@ -589,6 +589,27 @@ class UserTest < ActiveSupport::TestCase
end
end
+ def test_initials_format
+ assert_equal 'JS', @jsmith.initials(:firstname_lastinitial)
+ assert_equal 'SJ', @jsmith.initials(:lastname_comma_firstname)
+ assert_equal 'SJ', @jsmith.initials(:lastname_firstname)
+ assert_equal 'JS', @jsmith.initials(:firstinitial_lastname)
+ assert_equal 'JL', User.new(:firstname => 'Jean-Philippe', :lastname => 'Lang').initials(:firstinitial_lastname)
+ assert_equal 'JS', @jsmith.initials(:undefined_format)
+ end
+
+ def test_initials_should_use_setting_as_default_format
+ with_settings :user_format => :firstname_lastname do
+ assert_equal 'JS', @jsmith.reload.initials
+ end
+ with_settings :user_format => :username do
+ assert_equal 'JS', @jsmith.reload.initials
+ end
+ with_settings :user_format => :lastname do
+ assert_equal 'SM', @jsmith.reload.initials
+ end
+ end
+
def test_lastname_should_accept_255_characters
u = User.first
u.lastname = 'a' * 255
@@ -1376,4 +1397,77 @@ class UserTest < ActiveSupport::TestCase
User.prune(7)
end
end
+
+ def test_should_recognize_authorized_by_oauth
+ u = User.find 2
+ assert_not u.authorized_by_oauth?
+ u.oauth_scope = [:add_issues, :view_issues]
+ assert u.authorized_by_oauth?
+ end
+
+ def test_admin_should_be_limited_by_oauth_scope
+ u = User.find_by_admin(true)
+ assert u.admin?
+
+ u.oauth_scope = [:add_issues, :view_issues]
+ assert_not u.admin?
+
+ u.oauth_scope = [:add_issues, :view_issues, :admin]
+ assert u.admin?
+
+ u = User.find_by_admin(false)
+ assert_not u.admin?
+ u.oauth_scope = [:add_issues, :view_issues, :admin]
+ assert_not u.admin?
+ end
+
+ def test_oauth_scope_should_limit_global_user_permissions
+ admin = User.find 1
+ user = User.find 2
+ [admin, user].each do |u|
+ assert u.allowed_to?(:add_issues, nil, global: true)
+ assert u.allowed_to?(:view_issues, nil, global: true)
+ u.oauth_scope = [:view_issues]
+ assert_not u.allowed_to?(:add_issues, nil, global: true)
+ assert u.allowed_to?(:view_issues, nil, global: true)
+ end
+ end
+
+ def test_oauth_scope_should_limit_project_user_permissions
+ admin = User.find 1
+ project = Project.find 5
+ assert admin.allowed_to?(:add_issues, project)
+ assert admin.allowed_to?(:view_issues, project)
+ admin.oauth_scope = [:view_issues]
+ assert_not admin.allowed_to?(:add_issues, project)
+ assert admin.allowed_to?(:view_issues, project)
+
+ admin.oauth_scope = [:view_issues, :admin]
+ assert admin.allowed_to?(:add_issues, project)
+ assert admin.allowed_to?(:view_issues, project)
+
+ user = User.find 2
+ project = Project.find 1
+ assert user.allowed_to?(:add_issues, project)
+ assert user.allowed_to?(:view_issues, project)
+ user.oauth_scope = [:view_issues]
+ assert_not user.allowed_to?(:add_issues, project)
+ assert user.allowed_to?(:view_issues, project)
+
+ user.oauth_scope = [:view_issues, :admin]
+ assert_not user.allowed_to?(:add_issues, project)
+ assert user.allowed_to?(:view_issues, project)
+ end
+
+ def test_destroy_should_delete_associated_reactions
+ users(:users_004).reactions.create!(
+ [
+ {reactable: issues(:issues_001)},
+ {reactable: issues(:issues_002)}
+ ]
+ )
+ assert_difference 'Reaction.count', -2 do
+ users(:users_004).destroy
+ end
+ end
end