1# frozen_string_literal: true 2 3require 'spec_helper' 4 5RSpec.describe TodosFinder do 6 describe '#execute' do 7 let_it_be(:user) { create(:user) } 8 let_it_be(:group) { create(:group) } 9 let_it_be(:project) { create(:project, :repository, namespace: group) } 10 let_it_be(:issue) { create(:issue, project: project) } 11 let_it_be(:merge_request) { create(:merge_request, source_project: project) } 12 13 let(:finder) { described_class } 14 15 before_all do 16 group.add_developer(user) 17 end 18 19 describe '#execute' do 20 it 'returns no todos if user is nil' do 21 expect(described_class.new(nil, {}).execute).to be_empty 22 end 23 24 context 'filtering' do 25 let!(:todo1) { create(:todo, user: user, project: project, target: issue) } 26 let!(:todo2) { create(:todo, user: user, group: group, target: merge_request) } 27 28 it 'returns correct todos when filtered by a project' do 29 todos = finder.new(user, { project_id: project.id }).execute 30 31 expect(todos).to match_array([todo1]) 32 end 33 34 it 'returns correct todos when filtered by a group' do 35 todos = finder.new(user, { group_id: group.id }).execute 36 37 expect(todos).to match_array([todo1, todo2]) 38 end 39 40 context 'when filtering by type' do 41 it 'returns todos by type when filtered by a single type' do 42 todos = finder.new(user, { type: 'Issue' }).execute 43 44 expect(todos).to match_array([todo1]) 45 end 46 47 it 'returns todos by type when filtered by multiple types' do 48 design_todo = create(:todo, user: user, group: group, target: create(:design)) 49 50 todos = finder.new(user, { type: %w[Issue MergeRequest] }).execute 51 52 expect(todos).to contain_exactly(todo1, todo2) 53 expect(todos).not_to include(design_todo) 54 end 55 56 it 'returns all todos when type is nil' do 57 todos = finder.new(user, { type: nil }).execute 58 59 expect(todos).to contain_exactly(todo1, todo2) 60 end 61 62 it 'returns all todos when type is an empty collection' do 63 todos = finder.new(user, { type: [] }).execute 64 65 expect(todos).to contain_exactly(todo1, todo2) 66 end 67 68 it 'returns all todos when type is blank' do 69 todos = finder.new(user, { type: '' }).execute 70 71 expect(todos).to contain_exactly(todo1, todo2) 72 end 73 74 it 'returns todos by type when blank type is in type collection' do 75 todos = finder.new(user, { type: ['', 'MergeRequest'] }).execute 76 77 expect(todos).to contain_exactly(todo2) 78 end 79 80 it 'returns todos of all types when only blanks are in a collection' do 81 todos = finder.new(user, { type: ['', ''] }).execute 82 83 expect(todos).to contain_exactly(todo1, todo2) 84 end 85 86 it 'returns all todos when no type param' do 87 todos = finder.new(user).execute 88 89 expect(todos).to contain_exactly(todo1, todo2) 90 end 91 92 it 'raises an argument error when invalid type is passed' do 93 todos_finder = finder.new(user, { type: %w[Issue MergeRequest NotAValidType] }) 94 95 expect { todos_finder.execute }.to raise_error(ArgumentError) 96 end 97 end 98 99 context 'when filtering for actions' do 100 let!(:todo1) { create(:todo, user: user, project: project, target: issue, action: Todo::ASSIGNED) } 101 let!(:todo2) { create(:todo, user: user, group: group, target: merge_request, action: Todo::DIRECTLY_ADDRESSED) } 102 103 context 'by action ids' do 104 it 'returns the expected todos' do 105 todos = finder.new(user, { action_id: Todo::DIRECTLY_ADDRESSED }).execute 106 107 expect(todos).to match_array([todo2]) 108 end 109 110 it 'returns the expected todos when filtering for multiple action ids' do 111 todos = finder.new(user, { action_id: [Todo::DIRECTLY_ADDRESSED, Todo::ASSIGNED] }).execute 112 113 expect(todos).to match_array([todo2, todo1]) 114 end 115 end 116 117 context 'by action names' do 118 it 'returns the expected todos' do 119 todos = finder.new(user, { action: :directly_addressed }).execute 120 121 expect(todos).to match_array([todo2]) 122 end 123 124 it 'returns the expected todos when filtering for multiple action names' do 125 todos = finder.new(user, { action: [:directly_addressed, :assigned] }).execute 126 127 expect(todos).to match_array([todo2, todo1]) 128 end 129 end 130 end 131 132 context 'when filtering by author' do 133 let_it_be(:author1) { create(:user) } 134 let_it_be(:author2) { create(:user) } 135 136 let!(:todo1) { create(:todo, user: user, author: author1) } 137 let!(:todo2) { create(:todo, user: user, author: author2) } 138 139 it 'returns correct todos when filtering by an author' do 140 todos = finder.new(user, { author_id: author1.id }).execute 141 142 expect(todos).to match_array([todo1]) 143 end 144 145 context 'querying for multiple authors' do 146 it 'returns the correct todo items' do 147 todos = finder.new(user, { author_id: [author2.id, author1.id] }).execute 148 149 expect(todos).to match_array([todo2, todo1]) 150 end 151 end 152 end 153 154 context 'by groups' do 155 context 'with subgroups' do 156 let_it_be(:subgroup) { create(:group, parent: group) } 157 158 let!(:todo3) { create(:todo, user: user, group: subgroup, target: issue) } 159 160 it 'returns todos from subgroups when filtered by a group' do 161 todos = finder.new(user, { group_id: group.id }).execute 162 163 expect(todos).to match_array([todo1, todo2, todo3]) 164 end 165 end 166 167 context 'filtering for multiple groups' do 168 let_it_be(:group2) { create(:group) } 169 let_it_be(:group3) { create(:group) } 170 let_it_be(:subgroup1) { create(:group, parent: group) } 171 let_it_be(:subgroup2) { create(:group, parent: group2) } 172 173 let!(:todo1) { create(:todo, user: user, project: project, target: issue) } 174 let!(:todo2) { create(:todo, user: user, group: group, target: merge_request) } 175 let!(:todo3) { create(:todo, user: user, group: group2, target: merge_request) } 176 let!(:todo4) { create(:todo, user: user, group: subgroup1, target: issue) } 177 let!(:todo5) { create(:todo, user: user, group: subgroup2, target: issue) } 178 let!(:todo6) { create(:todo, user: user, group: group3, target: issue) } 179 180 it 'returns the expected groups' do 181 todos = finder.new(user, { group_id: [group.id, group2.id] }).execute 182 183 expect(todos).to match_array([todo1, todo2, todo3, todo4, todo5]) 184 end 185 end 186 end 187 188 context 'by state' do 189 let!(:todo1) { create(:todo, user: user, group: group, target: issue, state: :done) } 190 let!(:todo2) { create(:todo, user: user, group: group, target: issue, state: :pending) } 191 192 it 'returns the expected items when no state is provided' do 193 todos = finder.new(user, {}).execute 194 195 expect(todos).to match_array([todo2]) 196 end 197 198 it 'returns the expected items when a state is provided' do 199 todos = finder.new(user, { state: :done }).execute 200 201 expect(todos).to match_array([todo1]) 202 end 203 204 it 'returns the expected items when multiple states are provided' do 205 todos = finder.new(user, { state: [:pending, :done] }).execute 206 207 expect(todos).to match_array([todo1, todo2]) 208 end 209 end 210 211 context 'by project' do 212 let_it_be(:project1) { create(:project) } 213 let_it_be(:project2) { create(:project) } 214 let_it_be(:project3) { create(:project) } 215 216 let!(:todo1) { create(:todo, user: user, project: project1, state: :pending) } 217 let!(:todo2) { create(:todo, user: user, project: project2, state: :pending) } 218 let!(:todo3) { create(:todo, user: user, project: project3, state: :pending) } 219 220 it 'returns the expected todos for one project' do 221 todos = finder.new(user, { project_id: project2.id }).execute 222 223 expect(todos).to match_array([todo2]) 224 end 225 226 it 'returns the expected todos for many projects' do 227 todos = finder.new(user, { project_id: [project2.id, project1.id] }).execute 228 229 expect(todos).to match_array([todo2, todo1]) 230 end 231 end 232 233 context 'when filtering by target id' do 234 it 'returns the expected todos for the target' do 235 todos = finder.new(user, { type: 'Issue', target_id: issue.id }).execute 236 237 expect(todos).to match_array([todo1]) 238 end 239 240 it 'returns the expected todos for multiple target ids' do 241 another_issue = create(:issue, project: project) 242 todo3 = create(:todo, user: user, project: project, target: another_issue) 243 244 todos = finder.new(user, { type: 'Issue', target_id: [issue.id, another_issue.id] }).execute 245 246 expect(todos).to match_array([todo1, todo3]) 247 end 248 249 it 'returns the expected todos for empty target id collection' do 250 todos = finder.new(user, { target_id: [] }).execute 251 252 expect(todos).to match_array([todo1, todo2]) 253 end 254 end 255 end 256 257 context 'external authorization' do 258 it_behaves_like 'a finder with external authorization service' do 259 let!(:subject) { create(:todo, project: project, user: user) } 260 let(:project_params) { { project_id: project.id } } 261 end 262 end 263 end 264 265 describe '#sort' do 266 context 'by date' do 267 let!(:todo1) { create(:todo, user: user, project: project) } 268 let!(:todo2) { create(:todo, user: user, project: project) } 269 let!(:todo3) { create(:todo, user: user, project: project) } 270 271 it 'sorts with oldest created first' do 272 todos = finder.new(user, { sort: 'id_asc' }).execute 273 274 expect(todos.first).to eq(todo1) 275 expect(todos.second).to eq(todo2) 276 expect(todos.third).to eq(todo3) 277 end 278 279 it 'sorts with newest created first' do 280 todos = finder.new(user, { sort: 'id_desc' }).execute 281 282 expect(todos.first).to eq(todo3) 283 expect(todos.second).to eq(todo2) 284 expect(todos.third).to eq(todo1) 285 end 286 end 287 288 it "sorts by priority" do 289 project_2 = create(:project) 290 291 label_1 = create(:label, title: 'label_1', project: project, priority: 1) 292 label_2 = create(:label, title: 'label_2', project: project, priority: 2) 293 label_3 = create(:label, title: 'label_3', project: project, priority: 3) 294 label_1_2 = create(:label, title: 'label_1', project: project_2, priority: 1) 295 296 issue_1 = create(:issue, title: 'issue_1', project: project) 297 issue_2 = create(:issue, title: 'issue_2', project: project) 298 issue_3 = create(:issue, title: 'issue_3', project: project) 299 issue_4 = create(:issue, title: 'issue_4', project: project) 300 merge_request_1 = create(:merge_request, source_project: project_2) 301 302 merge_request_1.labels << label_1_2 303 304 # Covers the case where Todo has more than one label 305 issue_3.labels << label_1 306 issue_3.labels << label_3 307 308 issue_2.labels << label_3 309 issue_1.labels << label_2 310 311 todo_1 = create(:todo, user: user, project: project, target: issue_4) 312 todo_2 = create(:todo, user: user, project: project, target: issue_2) 313 todo_3 = create(:todo, user: user, project: project, target: issue_3, created_at: 2.hours.ago) 314 todo_4 = create(:todo, user: user, project: project, target: issue_1) 315 todo_5 = create(:todo, user: user, project: project_2, target: merge_request_1, created_at: 1.hour.ago) 316 317 project_2.add_developer(user) 318 319 todos = finder.new(user, { sort: 'priority' }).execute 320 321 expect(todos).to eq([todo_3, todo_5, todo_4, todo_2, todo_1]) 322 end 323 end 324 end 325 326 describe '.todo_types' do 327 it 'returns the expected types' do 328 expected_result = 329 if Gitlab.ee? 330 %w[Epic Issue MergeRequest DesignManagement::Design AlertManagement::Alert] 331 else 332 %w[Issue MergeRequest DesignManagement::Design AlertManagement::Alert] 333 end 334 335 expect(described_class.todo_types).to contain_exactly(*expected_result) 336 end 337 end 338 339 describe '#any_for_target?' do 340 it 'returns true if there are any todos for the given target' do 341 todo = create(:todo, :pending) 342 finder = described_class.new(todo.user) 343 344 expect(finder.any_for_target?(todo.target)).to eq(true) 345 end 346 347 it 'returns false if there are no todos for the given target' do 348 issue = create(:issue) 349 finder = described_class.new(issue.author) 350 351 expect(finder.any_for_target?(issue)).to eq(false) 352 end 353 end 354end 355