annotate test/unit/query_test.rb @ 1082:997f6d7738f7 bug_531

In repo controller entry action, show the page for the file even if it's binary (so user still has access to history etc links). This makes it possible to use the entry action as the default when a file is clicked on
author Chris Cannam <chris.cannam@soundsoftware.ac.uk>
date Thu, 22 Nov 2012 18:04:17 +0000
parents cbb26bc654de
children 433d4f72a19b
rev   line source
Chris@441 1 # Redmine - project management software
Chris@441 2 # Copyright (C) 2006-2011 Jean-Philippe Lang
Chris@0 3 #
Chris@0 4 # This program is free software; you can redistribute it and/or
Chris@0 5 # modify it under the terms of the GNU General Public License
Chris@0 6 # as published by the Free Software Foundation; either version 2
Chris@0 7 # of the License, or (at your option) any later version.
Chris@909 8 #
Chris@0 9 # This program is distributed in the hope that it will be useful,
Chris@0 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
Chris@0 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
Chris@0 12 # GNU General Public License for more details.
Chris@909 13 #
Chris@0 14 # You should have received a copy of the GNU General Public License
Chris@0 15 # along with this program; if not, write to the Free Software
Chris@0 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
Chris@0 17
Chris@119 18 require File.expand_path('../../test_helper', __FILE__)
Chris@0 19
Chris@0 20 class QueryTest < ActiveSupport::TestCase
Chris@909 21 fixtures :projects, :enabled_modules, :users, :members,
Chris@909 22 :member_roles, :roles, :trackers, :issue_statuses,
Chris@909 23 :issue_categories, :enumerations, :issues,
Chris@909 24 :watchers, :custom_fields, :custom_values, :versions,
Chris@909 25 :queries,
Chris@909 26 :projects_trackers
Chris@0 27
Chris@0 28 def test_custom_fields_for_all_projects_should_be_available_in_global_queries
Chris@0 29 query = Query.new(:project => nil, :name => '_')
Chris@0 30 assert query.available_filters.has_key?('cf_1')
Chris@0 31 assert !query.available_filters.has_key?('cf_3')
Chris@0 32 end
Chris@909 33
Chris@0 34 def test_system_shared_versions_should_be_available_in_global_queries
Chris@0 35 Version.find(2).update_attribute :sharing, 'system'
Chris@0 36 query = Query.new(:project => nil, :name => '_')
Chris@0 37 assert query.available_filters.has_key?('fixed_version_id')
Chris@0 38 assert query.available_filters['fixed_version_id'][:values].detect {|v| v.last == '2'}
Chris@0 39 end
Chris@909 40
Chris@14 41 def test_project_filter_in_global_queries
Chris@14 42 query = Query.new(:project => nil, :name => '_')
Chris@14 43 project_filter = query.available_filters["project_id"]
Chris@14 44 assert_not_nil project_filter
Chris@14 45 project_ids = project_filter[:values].map{|p| p[1]}
Chris@14 46 assert project_ids.include?("1") #public project
Chris@14 47 assert !project_ids.include?("2") #private project user cannot see
Chris@14 48 end
Chris@909 49
Chris@0 50 def find_issues_with_query(query)
Chris@0 51 Issue.find :all,
Chris@909 52 :include => [ :assigned_to, :status, :tracker, :project, :priority ],
Chris@0 53 :conditions => query.statement
Chris@0 54 end
Chris@0 55
chris@22 56 def assert_find_issues_with_query_is_successful(query)
chris@22 57 assert_nothing_raised do
chris@22 58 find_issues_with_query(query)
chris@22 59 end
chris@22 60 end
chris@22 61
chris@22 62 def assert_query_statement_includes(query, condition)
chris@22 63 assert query.statement.include?(condition), "Query statement condition not found in: #{query.statement}"
chris@22 64 end
Chris@909 65
Chris@909 66 def assert_query_result(expected, query)
Chris@909 67 assert_nothing_raised do
Chris@909 68 assert_equal expected.map(&:id).sort, query.issues.map(&:id).sort
Chris@909 69 assert_equal expected.size, query.issue_count
Chris@909 70 end
Chris@909 71 end
chris@22 72
Chris@0 73 def test_query_should_allow_shared_versions_for_a_project_query
Chris@0 74 subproject_version = Version.find(4)
Chris@0 75 query = Query.new(:project => Project.find(1), :name => '_')
Chris@0 76 query.add_filter('fixed_version_id', '=', [subproject_version.id.to_s])
Chris@0 77
Chris@0 78 assert query.statement.include?("#{Issue.table_name}.fixed_version_id IN ('4')")
Chris@0 79 end
Chris@909 80
Chris@0 81 def test_query_with_multiple_custom_fields
Chris@0 82 query = Query.find(1)
Chris@0 83 assert query.valid?
Chris@0 84 assert query.statement.include?("#{CustomValue.table_name}.value IN ('MySQL')")
Chris@0 85 issues = find_issues_with_query(query)
Chris@0 86 assert_equal 1, issues.length
Chris@0 87 assert_equal Issue.find(3), issues.first
Chris@0 88 end
Chris@909 89
Chris@0 90 def test_operator_none
Chris@0 91 query = Query.new(:project => Project.find(1), :name => '_')
Chris@0 92 query.add_filter('fixed_version_id', '!*', [''])
Chris@0 93 query.add_filter('cf_1', '!*', [''])
Chris@0 94 assert query.statement.include?("#{Issue.table_name}.fixed_version_id IS NULL")
Chris@0 95 assert query.statement.include?("#{CustomValue.table_name}.value IS NULL OR #{CustomValue.table_name}.value = ''")
Chris@0 96 find_issues_with_query(query)
Chris@0 97 end
Chris@909 98
Chris@0 99 def test_operator_none_for_integer
Chris@0 100 query = Query.new(:project => Project.find(1), :name => '_')
Chris@0 101 query.add_filter('estimated_hours', '!*', [''])
Chris@0 102 issues = find_issues_with_query(query)
Chris@0 103 assert !issues.empty?
Chris@0 104 assert issues.all? {|i| !i.estimated_hours}
Chris@0 105 end
Chris@0 106
Chris@909 107 def test_operator_none_for_date
Chris@909 108 query = Query.new(:project => Project.find(1), :name => '_')
Chris@909 109 query.add_filter('start_date', '!*', [''])
Chris@909 110 issues = find_issues_with_query(query)
Chris@909 111 assert !issues.empty?
Chris@909 112 assert issues.all? {|i| i.start_date.nil?}
Chris@909 113 end
Chris@909 114
Chris@0 115 def test_operator_all
Chris@0 116 query = Query.new(:project => Project.find(1), :name => '_')
Chris@0 117 query.add_filter('fixed_version_id', '*', [''])
Chris@0 118 query.add_filter('cf_1', '*', [''])
Chris@0 119 assert query.statement.include?("#{Issue.table_name}.fixed_version_id IS NOT NULL")
Chris@0 120 assert query.statement.include?("#{CustomValue.table_name}.value IS NOT NULL AND #{CustomValue.table_name}.value <> ''")
Chris@0 121 find_issues_with_query(query)
Chris@0 122 end
Chris@909 123
Chris@909 124 def test_operator_all_for_date
Chris@909 125 query = Query.new(:project => Project.find(1), :name => '_')
Chris@909 126 query.add_filter('start_date', '*', [''])
Chris@909 127 issues = find_issues_with_query(query)
Chris@909 128 assert !issues.empty?
Chris@909 129 assert issues.all? {|i| i.start_date.present?}
Chris@909 130 end
Chris@909 131
Chris@909 132 def test_numeric_filter_should_not_accept_non_numeric_values
Chris@909 133 query = Query.new(:name => '_')
Chris@909 134 query.add_filter('estimated_hours', '=', ['a'])
Chris@909 135
Chris@909 136 assert query.has_filter?('estimated_hours')
Chris@909 137 assert !query.valid?
Chris@909 138 end
Chris@909 139
Chris@909 140 def test_operator_is_on_float
Chris@909 141 Issue.update_all("estimated_hours = 171.2", "id=2")
Chris@909 142
Chris@909 143 query = Query.new(:name => '_')
Chris@909 144 query.add_filter('estimated_hours', '=', ['171.20'])
Chris@909 145 issues = find_issues_with_query(query)
Chris@909 146 assert_equal 1, issues.size
Chris@909 147 assert_equal 2, issues.first.id
Chris@909 148 end
Chris@909 149
Chris@0 150 def test_operator_greater_than
Chris@0 151 query = Query.new(:project => Project.find(1), :name => '_')
Chris@0 152 query.add_filter('done_ratio', '>=', ['40'])
Chris@909 153 assert query.statement.include?("#{Issue.table_name}.done_ratio >= 40.0")
Chris@909 154 find_issues_with_query(query)
Chris@909 155 end
Chris@909 156
Chris@909 157 def test_operator_greater_than_a_float
Chris@909 158 query = Query.new(:project => Project.find(1), :name => '_')
Chris@909 159 query.add_filter('estimated_hours', '>=', ['40.5'])
Chris@909 160 assert query.statement.include?("#{Issue.table_name}.estimated_hours >= 40.5")
Chris@909 161 find_issues_with_query(query)
Chris@909 162 end
Chris@909 163
Chris@909 164 def test_operator_greater_than_on_custom_field
Chris@909 165 f = IssueCustomField.create!(:name => 'filter', :field_format => 'int', :is_filter => true, :is_for_all => true)
Chris@909 166 query = Query.new(:project => Project.find(1), :name => '_')
Chris@909 167 query.add_filter("cf_#{f.id}", '>=', ['40'])
Chris@909 168 assert query.statement.include?("CAST(custom_values.value AS decimal(60,3)) >= 40.0")
Chris@909 169 find_issues_with_query(query)
Chris@909 170 end
Chris@909 171
Chris@909 172 def test_operator_lesser_than
Chris@909 173 query = Query.new(:project => Project.find(1), :name => '_')
Chris@909 174 query.add_filter('done_ratio', '<=', ['30'])
Chris@909 175 assert query.statement.include?("#{Issue.table_name}.done_ratio <= 30.0")
Chris@909 176 find_issues_with_query(query)
Chris@909 177 end
Chris@909 178
Chris@909 179 def test_operator_lesser_than_on_custom_field
Chris@909 180 f = IssueCustomField.create!(:name => 'filter', :field_format => 'int', :is_filter => true, :is_for_all => true)
Chris@909 181 query = Query.new(:project => Project.find(1), :name => '_')
Chris@909 182 query.add_filter("cf_#{f.id}", '<=', ['30'])
Chris@909 183 assert query.statement.include?("CAST(custom_values.value AS decimal(60,3)) <= 30.0")
Chris@909 184 find_issues_with_query(query)
Chris@909 185 end
Chris@909 186
Chris@909 187 def test_operator_between
Chris@909 188 query = Query.new(:project => Project.find(1), :name => '_')
Chris@909 189 query.add_filter('done_ratio', '><', ['30', '40'])
Chris@909 190 assert_include "#{Issue.table_name}.done_ratio BETWEEN 30.0 AND 40.0", query.statement
Chris@909 191 find_issues_with_query(query)
Chris@909 192 end
Chris@909 193
Chris@909 194 def test_operator_between_on_custom_field
Chris@909 195 f = IssueCustomField.create!(:name => 'filter', :field_format => 'int', :is_filter => true, :is_for_all => true)
Chris@909 196 query = Query.new(:project => Project.find(1), :name => '_')
Chris@909 197 query.add_filter("cf_#{f.id}", '><', ['30', '40'])
Chris@909 198 assert_include "CAST(custom_values.value AS decimal(60,3)) BETWEEN 30.0 AND 40.0", query.statement
Chris@909 199 find_issues_with_query(query)
Chris@909 200 end
Chris@909 201
Chris@909 202 def test_date_filter_should_not_accept_non_date_values
Chris@909 203 query = Query.new(:name => '_')
Chris@909 204 query.add_filter('created_on', '=', ['a'])
Chris@909 205
Chris@909 206 assert query.has_filter?('created_on')
Chris@909 207 assert !query.valid?
Chris@909 208 end
Chris@909 209
Chris@909 210 def test_date_filter_should_not_accept_invalid_date_values
Chris@909 211 query = Query.new(:name => '_')
Chris@909 212 query.add_filter('created_on', '=', ['2011-01-34'])
Chris@909 213
Chris@909 214 assert query.has_filter?('created_on')
Chris@909 215 assert !query.valid?
Chris@909 216 end
Chris@909 217
Chris@909 218 def test_relative_date_filter_should_not_accept_non_integer_values
Chris@909 219 query = Query.new(:name => '_')
Chris@909 220 query.add_filter('created_on', '>t-', ['a'])
Chris@909 221
Chris@909 222 assert query.has_filter?('created_on')
Chris@909 223 assert !query.valid?
Chris@909 224 end
Chris@909 225
Chris@909 226 def test_operator_date_equals
Chris@909 227 query = Query.new(:name => '_')
Chris@909 228 query.add_filter('due_date', '=', ['2011-07-10'])
Chris@909 229 assert_match /issues\.due_date > '2011-07-09 23:59:59(\.9+)?' AND issues\.due_date <= '2011-07-10 23:59:59(\.9+)?/, query.statement
Chris@909 230 find_issues_with_query(query)
Chris@909 231 end
Chris@909 232
Chris@909 233 def test_operator_date_lesser_than
Chris@909 234 query = Query.new(:name => '_')
Chris@909 235 query.add_filter('due_date', '<=', ['2011-07-10'])
Chris@909 236 assert_match /issues\.due_date <= '2011-07-10 23:59:59(\.9+)?/, query.statement
Chris@909 237 find_issues_with_query(query)
Chris@909 238 end
Chris@909 239
Chris@909 240 def test_operator_date_greater_than
Chris@909 241 query = Query.new(:name => '_')
Chris@909 242 query.add_filter('due_date', '>=', ['2011-07-10'])
Chris@909 243 assert_match /issues\.due_date > '2011-07-09 23:59:59(\.9+)?'/, query.statement
Chris@909 244 find_issues_with_query(query)
Chris@909 245 end
Chris@909 246
Chris@909 247 def test_operator_date_between
Chris@909 248 query = Query.new(:name => '_')
Chris@909 249 query.add_filter('due_date', '><', ['2011-06-23', '2011-07-10'])
Chris@909 250 assert_match /issues\.due_date > '2011-06-22 23:59:59(\.9+)?' AND issues\.due_date <= '2011-07-10 23:59:59(\.9+)?/, query.statement
Chris@0 251 find_issues_with_query(query)
Chris@0 252 end
Chris@0 253
Chris@0 254 def test_operator_in_more_than
Chris@0 255 Issue.find(7).update_attribute(:due_date, (Date.today + 15))
Chris@0 256 query = Query.new(:project => Project.find(1), :name => '_')
Chris@0 257 query.add_filter('due_date', '>t+', ['15'])
Chris@0 258 issues = find_issues_with_query(query)
Chris@0 259 assert !issues.empty?
Chris@0 260 issues.each {|issue| assert(issue.due_date >= (Date.today + 15))}
Chris@0 261 end
Chris@0 262
Chris@0 263 def test_operator_in_less_than
Chris@0 264 query = Query.new(:project => Project.find(1), :name => '_')
Chris@0 265 query.add_filter('due_date', '<t+', ['15'])
Chris@0 266 issues = find_issues_with_query(query)
Chris@0 267 assert !issues.empty?
Chris@0 268 issues.each {|issue| assert(issue.due_date >= Date.today && issue.due_date <= (Date.today + 15))}
Chris@0 269 end
Chris@909 270
Chris@0 271 def test_operator_less_than_ago
Chris@0 272 Issue.find(7).update_attribute(:due_date, (Date.today - 3))
Chris@0 273 query = Query.new(:project => Project.find(1), :name => '_')
Chris@0 274 query.add_filter('due_date', '>t-', ['3'])
Chris@0 275 issues = find_issues_with_query(query)
Chris@0 276 assert !issues.empty?
Chris@0 277 issues.each {|issue| assert(issue.due_date >= (Date.today - 3) && issue.due_date <= Date.today)}
Chris@0 278 end
Chris@909 279
Chris@0 280 def test_operator_more_than_ago
Chris@0 281 Issue.find(7).update_attribute(:due_date, (Date.today - 10))
Chris@0 282 query = Query.new(:project => Project.find(1), :name => '_')
Chris@0 283 query.add_filter('due_date', '<t-', ['10'])
Chris@0 284 assert query.statement.include?("#{Issue.table_name}.due_date <=")
Chris@0 285 issues = find_issues_with_query(query)
Chris@0 286 assert !issues.empty?
Chris@0 287 issues.each {|issue| assert(issue.due_date <= (Date.today - 10))}
Chris@0 288 end
Chris@0 289
Chris@0 290 def test_operator_in
Chris@0 291 Issue.find(7).update_attribute(:due_date, (Date.today + 2))
Chris@0 292 query = Query.new(:project => Project.find(1), :name => '_')
Chris@0 293 query.add_filter('due_date', 't+', ['2'])
Chris@0 294 issues = find_issues_with_query(query)
Chris@0 295 assert !issues.empty?
Chris@0 296 issues.each {|issue| assert_equal((Date.today + 2), issue.due_date)}
Chris@0 297 end
Chris@0 298
Chris@0 299 def test_operator_ago
Chris@0 300 Issue.find(7).update_attribute(:due_date, (Date.today - 3))
Chris@0 301 query = Query.new(:project => Project.find(1), :name => '_')
Chris@0 302 query.add_filter('due_date', 't-', ['3'])
Chris@0 303 issues = find_issues_with_query(query)
Chris@0 304 assert !issues.empty?
Chris@0 305 issues.each {|issue| assert_equal((Date.today - 3), issue.due_date)}
Chris@0 306 end
Chris@0 307
Chris@0 308 def test_operator_today
Chris@0 309 query = Query.new(:project => Project.find(1), :name => '_')
Chris@0 310 query.add_filter('due_date', 't', [''])
Chris@0 311 issues = find_issues_with_query(query)
Chris@0 312 assert !issues.empty?
Chris@0 313 issues.each {|issue| assert_equal Date.today, issue.due_date}
Chris@0 314 end
Chris@0 315
Chris@0 316 def test_operator_this_week_on_date
Chris@0 317 query = Query.new(:project => Project.find(1), :name => '_')
Chris@0 318 query.add_filter('due_date', 'w', [''])
Chris@0 319 find_issues_with_query(query)
Chris@0 320 end
Chris@0 321
Chris@0 322 def test_operator_this_week_on_datetime
Chris@0 323 query = Query.new(:project => Project.find(1), :name => '_')
Chris@0 324 query.add_filter('created_on', 'w', [''])
Chris@0 325 find_issues_with_query(query)
Chris@0 326 end
Chris@0 327
Chris@0 328 def test_operator_contains
Chris@0 329 query = Query.new(:project => Project.find(1), :name => '_')
Chris@0 330 query.add_filter('subject', '~', ['uNable'])
Chris@0 331 assert query.statement.include?("LOWER(#{Issue.table_name}.subject) LIKE '%unable%'")
Chris@0 332 result = find_issues_with_query(query)
Chris@0 333 assert result.empty?
Chris@0 334 result.each {|issue| assert issue.subject.downcase.include?('unable') }
Chris@0 335 end
Chris@909 336
Chris@441 337 def test_range_for_this_week_with_week_starting_on_monday
Chris@441 338 I18n.locale = :fr
Chris@441 339 assert_equal '1', I18n.t(:general_first_day_of_week)
Chris@909 340
Chris@441 341 Date.stubs(:today).returns(Date.parse('2011-04-29'))
Chris@909 342
Chris@441 343 query = Query.new(:project => Project.find(1), :name => '_')
Chris@441 344 query.add_filter('due_date', 'w', [''])
Chris@441 345 assert query.statement.match(/issues\.due_date > '2011-04-24 23:59:59(\.9+)?' AND issues\.due_date <= '2011-05-01 23:59:59(\.9+)?/), "range not found in #{query.statement}"
Chris@441 346 I18n.locale = :en
Chris@441 347 end
Chris@909 348
Chris@441 349 def test_range_for_this_week_with_week_starting_on_sunday
Chris@441 350 I18n.locale = :en
Chris@441 351 assert_equal '7', I18n.t(:general_first_day_of_week)
Chris@909 352
Chris@441 353 Date.stubs(:today).returns(Date.parse('2011-04-29'))
Chris@909 354
Chris@441 355 query = Query.new(:project => Project.find(1), :name => '_')
Chris@441 356 query.add_filter('due_date', 'w', [''])
Chris@441 357 assert query.statement.match(/issues\.due_date > '2011-04-23 23:59:59(\.9+)?' AND issues\.due_date <= '2011-04-30 23:59:59(\.9+)?/), "range not found in #{query.statement}"
Chris@441 358 end
Chris@909 359
Chris@0 360 def test_operator_does_not_contains
Chris@0 361 query = Query.new(:project => Project.find(1), :name => '_')
Chris@0 362 query.add_filter('subject', '!~', ['uNable'])
Chris@0 363 assert query.statement.include?("LOWER(#{Issue.table_name}.subject) NOT LIKE '%unable%'")
Chris@0 364 find_issues_with_query(query)
Chris@0 365 end
Chris@909 366
Chris@909 367 def test_filter_assigned_to_me
Chris@909 368 user = User.find(2)
Chris@909 369 group = Group.find(10)
Chris@909 370 User.current = user
Chris@909 371 i1 = Issue.generate!(:project_id => 1, :tracker_id => 1, :assigned_to => user)
Chris@909 372 i2 = Issue.generate!(:project_id => 1, :tracker_id => 1, :assigned_to => group)
Chris@909 373 i3 = Issue.generate!(:project_id => 1, :tracker_id => 1, :assigned_to => Group.find(11))
Chris@909 374 group.users << user
Chris@909 375
Chris@909 376 query = Query.new(:name => '_', :filters => { 'assigned_to_id' => {:operator => '=', :values => ['me']}})
Chris@909 377 result = query.issues
Chris@909 378 assert_equal Issue.visible.all(:conditions => {:assigned_to_id => ([2] + user.reload.group_ids)}).sort_by(&:id), result.sort_by(&:id)
Chris@909 379
Chris@909 380 assert result.include?(i1)
Chris@909 381 assert result.include?(i2)
Chris@909 382 assert !result.include?(i3)
Chris@909 383 end
Chris@909 384
Chris@0 385 def test_filter_watched_issues
Chris@0 386 User.current = User.find(1)
Chris@0 387 query = Query.new(:name => '_', :filters => { 'watcher_id' => {:operator => '=', :values => ['me']}})
Chris@0 388 result = find_issues_with_query(query)
Chris@0 389 assert_not_nil result
Chris@0 390 assert !result.empty?
Chris@0 391 assert_equal Issue.visible.watched_by(User.current).sort_by(&:id), result.sort_by(&:id)
Chris@0 392 User.current = nil
Chris@0 393 end
Chris@909 394
Chris@0 395 def test_filter_unwatched_issues
Chris@0 396 User.current = User.find(1)
Chris@0 397 query = Query.new(:name => '_', :filters => { 'watcher_id' => {:operator => '!', :values => ['me']}})
Chris@0 398 result = find_issues_with_query(query)
Chris@0 399 assert_not_nil result
Chris@0 400 assert !result.empty?
Chris@0 401 assert_equal((Issue.visible - Issue.watched_by(User.current)).sort_by(&:id).size, result.sort_by(&:id).size)
Chris@0 402 User.current = nil
Chris@0 403 end
Chris@909 404
Chris@441 405 def test_statement_should_be_nil_with_no_filters
Chris@441 406 q = Query.new(:name => '_')
Chris@441 407 q.filters = {}
Chris@909 408
Chris@441 409 assert q.valid?
Chris@441 410 assert_nil q.statement
Chris@441 411 end
Chris@909 412
Chris@0 413 def test_default_columns
Chris@0 414 q = Query.new
Chris@909 415 assert !q.columns.empty?
Chris@0 416 end
Chris@909 417
Chris@0 418 def test_set_column_names
Chris@0 419 q = Query.new
Chris@0 420 q.column_names = ['tracker', :subject, '', 'unknonw_column']
Chris@0 421 assert_equal [:tracker, :subject], q.columns.collect {|c| c.name}
Chris@0 422 c = q.columns.first
Chris@0 423 assert q.has_column?(c)
Chris@0 424 end
Chris@909 425
Chris@0 426 def test_groupable_columns_should_include_custom_fields
Chris@0 427 q = Query.new
Chris@0 428 assert q.groupable_columns.detect {|c| c.is_a? QueryCustomFieldColumn}
Chris@0 429 end
Chris@119 430
Chris@119 431 def test_grouped_with_valid_column
Chris@119 432 q = Query.new(:group_by => 'status')
Chris@119 433 assert q.grouped?
Chris@119 434 assert_not_nil q.group_by_column
Chris@119 435 assert_equal :status, q.group_by_column.name
Chris@119 436 assert_not_nil q.group_by_statement
Chris@119 437 assert_equal 'status', q.group_by_statement
Chris@119 438 end
Chris@909 439
Chris@119 440 def test_grouped_with_invalid_column
Chris@119 441 q = Query.new(:group_by => 'foo')
Chris@119 442 assert !q.grouped?
Chris@119 443 assert_nil q.group_by_column
Chris@119 444 assert_nil q.group_by_statement
Chris@119 445 end
Chris@0 446
Chris@909 447 def test_sortable_columns_should_sort_assignees_according_to_user_format_setting
Chris@909 448 with_settings :user_format => 'lastname_coma_firstname' do
Chris@909 449 q = Query.new
Chris@909 450 assert q.sortable_columns.has_key?('assigned_to')
Chris@909 451 assert_equal %w(users.lastname users.firstname users.id), q.sortable_columns['assigned_to']
Chris@909 452 end
Chris@909 453 end
Chris@909 454
Chris@909 455 def test_sortable_columns_should_sort_authors_according_to_user_format_setting
Chris@909 456 with_settings :user_format => 'lastname_coma_firstname' do
Chris@909 457 q = Query.new
Chris@909 458 assert q.sortable_columns.has_key?('author')
Chris@909 459 assert_equal %w(authors.lastname authors.firstname authors.id), q.sortable_columns['author']
Chris@909 460 end
Chris@909 461 end
Chris@909 462
Chris@0 463 def test_default_sort
Chris@0 464 q = Query.new
Chris@0 465 assert_equal [], q.sort_criteria
Chris@0 466 end
Chris@909 467
Chris@0 468 def test_set_sort_criteria_with_hash
Chris@0 469 q = Query.new
Chris@0 470 q.sort_criteria = {'0' => ['priority', 'desc'], '2' => ['tracker']}
Chris@0 471 assert_equal [['priority', 'desc'], ['tracker', 'asc']], q.sort_criteria
Chris@0 472 end
Chris@909 473
Chris@0 474 def test_set_sort_criteria_with_array
Chris@0 475 q = Query.new
Chris@0 476 q.sort_criteria = [['priority', 'desc'], 'tracker']
Chris@0 477 assert_equal [['priority', 'desc'], ['tracker', 'asc']], q.sort_criteria
Chris@0 478 end
Chris@909 479
Chris@0 480 def test_create_query_with_sort
Chris@0 481 q = Query.new(:name => 'Sorted')
Chris@0 482 q.sort_criteria = [['priority', 'desc'], 'tracker']
Chris@0 483 assert q.save
Chris@0 484 q.reload
Chris@0 485 assert_equal [['priority', 'desc'], ['tracker', 'asc']], q.sort_criteria
Chris@0 486 end
Chris@909 487
Chris@0 488 def test_sort_by_string_custom_field_asc
Chris@0 489 q = Query.new
Chris@0 490 c = q.available_columns.find {|col| col.is_a?(QueryCustomFieldColumn) && col.custom_field.field_format == 'string' }
Chris@0 491 assert c
Chris@0 492 assert c.sortable
Chris@0 493 issues = Issue.find :all,
Chris@909 494 :include => [ :assigned_to, :status, :tracker, :project, :priority ],
Chris@0 495 :conditions => q.statement,
Chris@0 496 :order => "#{c.sortable} ASC"
Chris@0 497 values = issues.collect {|i| i.custom_value_for(c.custom_field).to_s}
Chris@0 498 assert !values.empty?
Chris@0 499 assert_equal values.sort, values
Chris@0 500 end
Chris@909 501
Chris@0 502 def test_sort_by_string_custom_field_desc
Chris@0 503 q = Query.new
Chris@0 504 c = q.available_columns.find {|col| col.is_a?(QueryCustomFieldColumn) && col.custom_field.field_format == 'string' }
Chris@0 505 assert c
Chris@0 506 assert c.sortable
Chris@0 507 issues = Issue.find :all,
Chris@909 508 :include => [ :assigned_to, :status, :tracker, :project, :priority ],
Chris@0 509 :conditions => q.statement,
Chris@0 510 :order => "#{c.sortable} DESC"
Chris@0 511 values = issues.collect {|i| i.custom_value_for(c.custom_field).to_s}
Chris@0 512 assert !values.empty?
Chris@0 513 assert_equal values.sort.reverse, values
Chris@0 514 end
Chris@909 515
Chris@0 516 def test_sort_by_float_custom_field_asc
Chris@0 517 q = Query.new
Chris@0 518 c = q.available_columns.find {|col| col.is_a?(QueryCustomFieldColumn) && col.custom_field.field_format == 'float' }
Chris@0 519 assert c
Chris@0 520 assert c.sortable
Chris@0 521 issues = Issue.find :all,
Chris@909 522 :include => [ :assigned_to, :status, :tracker, :project, :priority ],
Chris@0 523 :conditions => q.statement,
Chris@0 524 :order => "#{c.sortable} ASC"
Chris@0 525 values = issues.collect {|i| begin; Kernel.Float(i.custom_value_for(c.custom_field).to_s); rescue; nil; end}.compact
Chris@0 526 assert !values.empty?
Chris@0 527 assert_equal values.sort, values
Chris@0 528 end
Chris@909 529
Chris@0 530 def test_invalid_query_should_raise_query_statement_invalid_error
Chris@0 531 q = Query.new
Chris@0 532 assert_raise Query::StatementInvalid do
Chris@0 533 q.issues(:conditions => "foo = 1")
Chris@0 534 end
Chris@0 535 end
Chris@909 536
Chris@909 537 def test_issue_count
Chris@909 538 q = Query.new(:name => '_')
Chris@909 539 issue_count = q.issue_count
Chris@909 540 assert_equal q.issues.size, issue_count
Chris@909 541 end
Chris@909 542
Chris@909 543 def test_issue_count_with_archived_issues
Chris@909 544 p = Project.generate!( :status => Project::STATUS_ARCHIVED )
Chris@909 545 i = Issue.generate!( :project => p, :tracker => p.trackers.first )
Chris@909 546 assert !i.visible?
Chris@909 547
Chris@909 548 test_issue_count
Chris@909 549 end
Chris@909 550
Chris@0 551 def test_issue_count_by_association_group
Chris@0 552 q = Query.new(:name => '_', :group_by => 'assigned_to')
Chris@0 553 count_by_group = q.issue_count_by_group
Chris@0 554 assert_kind_of Hash, count_by_group
Chris@0 555 assert_equal %w(NilClass User), count_by_group.keys.collect {|k| k.class.name}.uniq.sort
Chris@0 556 assert_equal %w(Fixnum), count_by_group.values.collect {|k| k.class.name}.uniq
Chris@0 557 assert count_by_group.has_key?(User.find(3))
Chris@0 558 end
Chris@0 559
Chris@0 560 def test_issue_count_by_list_custom_field_group
Chris@0 561 q = Query.new(:name => '_', :group_by => 'cf_1')
Chris@0 562 count_by_group = q.issue_count_by_group
Chris@0 563 assert_kind_of Hash, count_by_group
Chris@0 564 assert_equal %w(NilClass String), count_by_group.keys.collect {|k| k.class.name}.uniq.sort
Chris@0 565 assert_equal %w(Fixnum), count_by_group.values.collect {|k| k.class.name}.uniq
Chris@0 566 assert count_by_group.has_key?('MySQL')
Chris@0 567 end
Chris@909 568
Chris@0 569 def test_issue_count_by_date_custom_field_group
Chris@0 570 q = Query.new(:name => '_', :group_by => 'cf_8')
Chris@0 571 count_by_group = q.issue_count_by_group
Chris@0 572 assert_kind_of Hash, count_by_group
Chris@0 573 assert_equal %w(Date NilClass), count_by_group.keys.collect {|k| k.class.name}.uniq.sort
Chris@0 574 assert_equal %w(Fixnum), count_by_group.values.collect {|k| k.class.name}.uniq
Chris@0 575 end
Chris@909 576
Chris@0 577 def test_label_for
Chris@0 578 q = Query.new
Chris@0 579 assert_equal 'assigned_to', q.label_for('assigned_to_id')
Chris@0 580 end
Chris@909 581
Chris@0 582 def test_editable_by
Chris@0 583 admin = User.find(1)
Chris@0 584 manager = User.find(2)
Chris@0 585 developer = User.find(3)
Chris@909 586
Chris@0 587 # Public query on project 1
Chris@0 588 q = Query.find(1)
Chris@0 589 assert q.editable_by?(admin)
Chris@0 590 assert q.editable_by?(manager)
Chris@0 591 assert !q.editable_by?(developer)
Chris@0 592
Chris@0 593 # Private query on project 1
Chris@0 594 q = Query.find(2)
Chris@0 595 assert q.editable_by?(admin)
Chris@0 596 assert !q.editable_by?(manager)
Chris@0 597 assert q.editable_by?(developer)
Chris@0 598
Chris@0 599 # Private query for all projects
Chris@0 600 q = Query.find(3)
Chris@0 601 assert q.editable_by?(admin)
Chris@0 602 assert !q.editable_by?(manager)
Chris@0 603 assert q.editable_by?(developer)
Chris@0 604
Chris@0 605 # Public query for all projects
Chris@0 606 q = Query.find(4)
Chris@0 607 assert q.editable_by?(admin)
Chris@0 608 assert !q.editable_by?(manager)
Chris@0 609 assert !q.editable_by?(developer)
Chris@0 610 end
Chris@14 611
Chris@909 612 def test_visible_scope
Chris@909 613 query_ids = Query.visible(User.anonymous).map(&:id)
Chris@909 614
Chris@909 615 assert query_ids.include?(1), 'public query on public project was not visible'
Chris@909 616 assert query_ids.include?(4), 'public query for all projects was not visible'
Chris@909 617 assert !query_ids.include?(2), 'private query on public project was visible'
Chris@909 618 assert !query_ids.include?(3), 'private query for all projects was visible'
Chris@909 619 assert !query_ids.include?(7), 'public query on private project was visible'
Chris@909 620 end
Chris@909 621
Chris@14 622 context "#available_filters" do
chris@22 623 setup do
chris@22 624 @query = Query.new(:name => "_")
chris@22 625 end
Chris@909 626
Chris@14 627 should "include users of visible projects in cross-project view" do
chris@22 628 users = @query.available_filters["assigned_to_id"]
Chris@14 629 assert_not_nil users
Chris@14 630 assert users[:values].map{|u|u[1]}.include?("3")
Chris@14 631 end
chris@22 632
Chris@119 633 should "include visible projects in cross-project view" do
Chris@119 634 projects = @query.available_filters["project_id"]
Chris@119 635 assert_not_nil projects
Chris@119 636 assert projects[:values].map{|u|u[1]}.include?("1")
Chris@119 637 end
Chris@119 638
chris@22 639 context "'member_of_group' filter" do
chris@22 640 should "be present" do
chris@22 641 assert @query.available_filters.keys.include?("member_of_group")
chris@22 642 end
Chris@909 643
chris@22 644 should "be an optional list" do
chris@22 645 assert_equal :list_optional, @query.available_filters["member_of_group"][:type]
chris@22 646 end
Chris@909 647
chris@22 648 should "have a list of the groups as values" do
chris@22 649 Group.destroy_all # No fixtures
chris@22 650 group1 = Group.generate!.reload
chris@22 651 group2 = Group.generate!.reload
chris@22 652
chris@22 653 expected_group_list = [
chris@37 654 [group1.name, group1.id.to_s],
chris@37 655 [group2.name, group2.id.to_s]
chris@22 656 ]
chris@22 657 assert_equal expected_group_list.sort, @query.available_filters["member_of_group"][:values].sort
chris@22 658 end
chris@22 659
chris@22 660 end
chris@22 661
chris@22 662 context "'assigned_to_role' filter" do
chris@22 663 should "be present" do
chris@22 664 assert @query.available_filters.keys.include?("assigned_to_role")
chris@22 665 end
Chris@909 666
chris@22 667 should "be an optional list" do
chris@22 668 assert_equal :list_optional, @query.available_filters["assigned_to_role"][:type]
chris@22 669 end
Chris@909 670
chris@22 671 should "have a list of the Roles as values" do
chris@37 672 assert @query.available_filters["assigned_to_role"][:values].include?(['Manager','1'])
chris@37 673 assert @query.available_filters["assigned_to_role"][:values].include?(['Developer','2'])
chris@37 674 assert @query.available_filters["assigned_to_role"][:values].include?(['Reporter','3'])
chris@22 675 end
chris@22 676
chris@22 677 should "not include the built in Roles as values" do
chris@37 678 assert ! @query.available_filters["assigned_to_role"][:values].include?(['Non member','4'])
chris@37 679 assert ! @query.available_filters["assigned_to_role"][:values].include?(['Anonymous','5'])
chris@22 680 end
chris@22 681
chris@22 682 end
chris@22 683
Chris@14 684 end
chris@22 685
chris@22 686 context "#statement" do
chris@22 687 context "with 'member_of_group' filter" do
chris@22 688 setup do
chris@22 689 Group.destroy_all # No fixtures
chris@22 690 @user_in_group = User.generate!
chris@22 691 @second_user_in_group = User.generate!
chris@22 692 @user_in_group2 = User.generate!
chris@22 693 @user_not_in_group = User.generate!
Chris@909 694
chris@22 695 @group = Group.generate!.reload
chris@22 696 @group.users << @user_in_group
chris@22 697 @group.users << @second_user_in_group
Chris@909 698
chris@22 699 @group2 = Group.generate!.reload
chris@22 700 @group2.users << @user_in_group2
Chris@909 701
chris@22 702 end
Chris@909 703
chris@22 704 should "search assigned to for users in the group" do
chris@22 705 @query = Query.new(:name => '_')
chris@22 706 @query.add_filter('member_of_group', '=', [@group.id.to_s])
chris@22 707
chris@22 708 assert_query_statement_includes @query, "#{Issue.table_name}.assigned_to_id IN ('#{@user_in_group.id}','#{@second_user_in_group.id}')"
chris@22 709 assert_find_issues_with_query_is_successful @query
chris@22 710 end
chris@22 711
chris@22 712 should "search not assigned to any group member (none)" do
chris@22 713 @query = Query.new(:name => '_')
chris@22 714 @query.add_filter('member_of_group', '!*', [''])
chris@22 715
chris@22 716 # Users not in a group
chris@22 717 assert_query_statement_includes @query, "#{Issue.table_name}.assigned_to_id IS NULL OR #{Issue.table_name}.assigned_to_id NOT IN ('#{@user_in_group.id}','#{@second_user_in_group.id}','#{@user_in_group2.id}')"
chris@22 718 assert_find_issues_with_query_is_successful @query
chris@22 719 end
chris@22 720
chris@22 721 should "search assigned to any group member (all)" do
chris@22 722 @query = Query.new(:name => '_')
chris@22 723 @query.add_filter('member_of_group', '*', [''])
chris@22 724
chris@22 725 # Only users in a group
chris@22 726 assert_query_statement_includes @query, "#{Issue.table_name}.assigned_to_id IN ('#{@user_in_group.id}','#{@second_user_in_group.id}','#{@user_in_group2.id}')"
chris@22 727 assert_find_issues_with_query_is_successful @query
Chris@245 728 end
Chris@909 729
Chris@245 730 should "return an empty set with = empty group" do
Chris@245 731 @empty_group = Group.generate!
Chris@245 732 @query = Query.new(:name => '_')
Chris@245 733 @query.add_filter('member_of_group', '=', [@empty_group.id.to_s])
Chris@909 734
Chris@245 735 assert_equal [], find_issues_with_query(@query)
Chris@245 736 end
Chris@909 737
Chris@245 738 should "return issues with ! empty group" do
Chris@245 739 @empty_group = Group.generate!
Chris@245 740 @query = Query.new(:name => '_')
Chris@245 741 @query.add_filter('member_of_group', '!', [@empty_group.id.to_s])
Chris@909 742
Chris@245 743 assert_find_issues_with_query_is_successful @query
chris@22 744 end
chris@22 745 end
chris@22 746
chris@22 747 context "with 'assigned_to_role' filter" do
chris@22 748 setup do
Chris@909 749 @manager_role = Role.find_by_name('Manager')
Chris@909 750 @developer_role = Role.find_by_name('Developer')
chris@22 751
chris@22 752 @project = Project.generate!
chris@22 753 @manager = User.generate!
chris@22 754 @developer = User.generate!
chris@22 755 @boss = User.generate!
Chris@909 756 @guest = User.generate!
chris@22 757 User.add_to_project(@manager, @project, @manager_role)
chris@22 758 User.add_to_project(@developer, @project, @developer_role)
chris@22 759 User.add_to_project(@boss, @project, [@manager_role, @developer_role])
Chris@909 760
Chris@909 761 @issue1 = Issue.generate_for_project!(@project, :assigned_to_id => @manager.id)
Chris@909 762 @issue2 = Issue.generate_for_project!(@project, :assigned_to_id => @developer.id)
Chris@909 763 @issue3 = Issue.generate_for_project!(@project, :assigned_to_id => @boss.id)
Chris@909 764 @issue4 = Issue.generate_for_project!(@project, :assigned_to_id => @guest.id)
Chris@909 765 @issue5 = Issue.generate_for_project!(@project)
chris@22 766 end
Chris@909 767
chris@22 768 should "search assigned to for users with the Role" do
Chris@909 769 @query = Query.new(:name => '_', :project => @project)
chris@22 770 @query.add_filter('assigned_to_role', '=', [@manager_role.id.to_s])
chris@22 771
Chris@909 772 assert_query_result [@issue1, @issue3], @query
Chris@909 773 end
Chris@909 774
Chris@909 775 should "search assigned to for users with the Role on the issue project" do
Chris@909 776 other_project = Project.generate!
Chris@909 777 User.add_to_project(@developer, other_project, @manager_role)
Chris@909 778
Chris@909 779 @query = Query.new(:name => '_', :project => @project)
Chris@909 780 @query.add_filter('assigned_to_role', '=', [@manager_role.id.to_s])
Chris@909 781
Chris@909 782 assert_query_result [@issue1, @issue3], @query
Chris@909 783 end
Chris@909 784
Chris@909 785 should "return an empty set with empty role" do
Chris@909 786 @empty_role = Role.generate!
Chris@909 787 @query = Query.new(:name => '_', :project => @project)
Chris@909 788 @query.add_filter('assigned_to_role', '=', [@empty_role.id.to_s])
Chris@909 789
Chris@909 790 assert_query_result [], @query
Chris@909 791 end
Chris@909 792
Chris@909 793 should "search assigned to for users without the Role" do
Chris@909 794 @query = Query.new(:name => '_', :project => @project)
Chris@909 795 @query.add_filter('assigned_to_role', '!', [@manager_role.id.to_s])
Chris@909 796
Chris@909 797 assert_query_result [@issue2, @issue4, @issue5], @query
chris@22 798 end
chris@22 799
chris@22 800 should "search assigned to for users not assigned to any Role (none)" do
Chris@909 801 @query = Query.new(:name => '_', :project => @project)
chris@22 802 @query.add_filter('assigned_to_role', '!*', [''])
chris@22 803
Chris@909 804 assert_query_result [@issue4, @issue5], @query
chris@22 805 end
chris@22 806
chris@22 807 should "search assigned to for users assigned to any Role (all)" do
Chris@909 808 @query = Query.new(:name => '_', :project => @project)
chris@22 809 @query.add_filter('assigned_to_role', '*', [''])
chris@22 810
Chris@909 811 assert_query_result [@issue1, @issue2, @issue3], @query
chris@22 812 end
Chris@909 813
Chris@245 814 should "return issues with ! empty role" do
Chris@245 815 @empty_role = Role.generate!
Chris@909 816 @query = Query.new(:name => '_', :project => @project)
Chris@909 817 @query.add_filter('assigned_to_role', '!', [@empty_role.id.to_s])
Chris@909 818
Chris@909 819 assert_query_result [@issue1, @issue2, @issue3, @issue4, @issue5], @query
Chris@245 820 end
chris@22 821 end
chris@22 822 end
Chris@909 823
Chris@0 824 end