summaryrefslogtreecommitdiffstats
path: root/test/unit
diff options
context:
space:
mode:
Diffstat (limited to 'test/unit')
-rw-r--r--test/unit/lib/redmine/field_format/progressbar_format_test.rb12
-rw-r--r--test/unit/lib/redmine/quote_reply_helper_test.rb2
-rw-r--r--test/unit/lib/redmine/reaction_test.rb189
-rw-r--r--test/unit/lib/redmine/wiki_formatting/common_mark/formatter_test.rb8
-rw-r--r--test/unit/lib/redmine/wiki_formatting/textile_formatter_test.rb12
-rw-r--r--test/unit/member_test.rb2
-rw-r--r--test/unit/reaction_test.rb118
-rw-r--r--test/unit/role_test.rb26
-rw-r--r--test/unit/setting_test.rb4
-rw-r--r--test/unit/user_test.rb94
10 files changed, 457 insertions, 10 deletions
diff --git a/test/unit/lib/redmine/field_format/progressbar_format_test.rb b/test/unit/lib/redmine/field_format/progressbar_format_test.rb
index 51bd8bc5f..6e0df724d 100644
--- a/test/unit/lib/redmine/field_format/progressbar_format_test.rb
+++ b/test/unit/lib/redmine/field_format/progressbar_format_test.rb
@@ -116,5 +116,17 @@ module Redmine::FieldFormat
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..cbac1f6d0 100644
--- a/test/unit/lib/redmine/quote_reply_helper_test.rb
+++ b/test/unit/lib/redmine/quote_reply_helper_test.rb
@@ -29,7 +29,7 @@ class QuoteReplyHelperTest < ActionView::TestCase
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_includes a_tag, %|class="icon icon-quote"|
assert_not_includes a_tag, 'title='
# When icon_only is true
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/wiki_formatting/common_mark/formatter_test.rb b/test/unit/lib/redmine/wiki_formatting/common_mark/formatter_test.rb
index d9a84a2c2..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
@@ -226,7 +226,7 @@ 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
@@ -319,7 +319,11 @@ class Redmine::WikiFormatting::CommonMark::FormatterTest < ActionView::TestCase
html = to_html(text)
%w[note tip warning caution important].each do |alert|
- assert_include "<div class=\"markdown-alert markdown-alert-note\">\n<p class=\"markdown-alert-title\">Note</p>\n<p>This is a note.</p>\n</div>", html
+ 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
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 42fba4783..df9088027 100644
--- a/test/unit/member_test.rb
+++ b/test/unit/member_test.rb
@@ -108,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/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/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/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