notes_finder_spec.rb 10.9 KB
Newer Older
Jacob Vosmaer's avatar
Jacob Vosmaer committed
1 2 3 4
require 'spec_helper'

describe NotesFinder do
  let(:user) { create :user }
5
  let(:project) { create(:project) }
Jacob Vosmaer's avatar
Jacob Vosmaer committed
6 7

  before do
8
    project.add_maintainer(user)
Jacob Vosmaer's avatar
Jacob Vosmaer committed
9 10
  end

11
  describe '#execute' do
12 13 14 15
    context 'when notes filter is present' do
      let!(:comment) { create(:note_on_issue, project: project) }
      let!(:system_note) { create(:note_on_issue, project: project, system: true) }

16
      it 'returns only user notes when using only_comments filter' do
17 18 19 20 21 22 23
        finder = described_class.new(project, user, notes_filter: UserPreference::NOTES_FILTERS[:only_comments])

        notes = finder.execute

        expect(notes).to match_array(comment)
      end

24 25 26 27 28 29 30 31
      it 'returns only system notes when using only_activity filters' do
        finder = described_class.new(project, user, notes_filter: UserPreference::NOTES_FILTERS[:only_activity])

        notes = finder.execute

        expect(notes).to match_array(system_note)
      end

32 33 34 35 36 37 38 39 40
      it 'gets all notes' do
        finder = described_class.new(project, user, notes_filter: UserPreference::NOTES_FILTERS[:all_activity])

        notes = finder.execute

        expect(notes).to match_array([comment, system_note])
      end
    end

41 42 43 44 45 46
    it 'finds notes on merge requests' do
      create(:note_on_merge_request, project: project)

      notes = described_class.new(project, user).execute

      expect(notes.count).to eq(1)
Jacob Vosmaer's avatar
Jacob Vosmaer committed
47 48
    end

49 50 51 52 53 54
    it 'finds notes on snippets' do
      create(:note_on_project_snippet, project: project)

      notes = described_class.new(project, user).execute

      expect(notes.count).to eq(1)
Jacob Vosmaer's avatar
Jacob Vosmaer committed
55
    end
56

57
    it "excludes notes on commits the author can't download" do
58
      project = create(:project, :private, :repository)
59 60 61 62 63 64
      note = create(:note_on_commit, project: project)
      params = { target_type: 'commit', target_id: note.noteable.id }

      notes = described_class.new(project, create(:user), params).execute

      expect(notes.count).to eq(0)
65
    end
66

67 68 69 70
    it 'succeeds when no notes found' do
      notes = described_class.new(project, create(:user)).execute

      expect(notes.count).to eq(0)
71
    end
72

73 74
    context 'on restricted projects' do
      let(:project) do
75
        create(:project,
76 77 78 79
               :public,
               :issues_private,
               :snippets_private,
               :merge_requests_private)
80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106
      end

      it 'publicly excludes notes on merge requests' do
        create(:note_on_merge_request, project: project)

        notes = described_class.new(project, create(:user)).execute

        expect(notes.count).to eq(0)
      end

      it 'publicly excludes notes on issues' do
        create(:note_on_issue, project: project)

        notes = described_class.new(project, create(:user)).execute

        expect(notes.count).to eq(0)
      end

      it 'publicly excludes notes on snippets' do
        create(:note_on_project_snippet, project: project)

        notes = described_class.new(project, create(:user)).execute

        expect(notes.count).to eq(0)
      end
    end

107 108 109 110 111 112 113 114 115 116 117 118
    context 'for target type' do
      let(:project) { create(:project, :repository) }
      let!(:note1) { create :note_on_issue, project: project }
      let!(:note2) { create :note_on_commit, project: project }

      it 'finds only notes for the selected type' do
        notes = described_class.new(project, user, target_type: 'issue').execute

        expect(notes).to eq([note1])
      end
    end

119
    context 'for target' do
120
      let(:project) { create(:project, :repository) }
121 122 123
      let(:note1) { create :note_on_commit, project: project }
      let(:note2) { create :note_on_commit, project: project }
      let(:commit) { note1.noteable }
124
      let(:params) { { target_id: commit.id, target_type: 'commit', last_fetched_at: 1.hour.ago.to_i } }
125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147

      before do
        note1
        note2
      end

      it 'finds all notes' do
        notes = described_class.new(project, user, params).execute
        expect(notes.size).to eq(2)
      end

      it 'finds notes on merge requests' do
        note = create(:note_on_merge_request, project: project)
        params = { target_type: 'merge_request', target_id: note.noteable.id }

        notes = described_class.new(project, user, params).execute

        expect(notes).to include(note)
      end

      it 'finds notes on snippets' do
        note = create(:note_on_project_snippet, project: project)
        params = { target_type: 'snippet', target_id: note.noteable.id }
148

149
        notes = described_class.new(project, user, params).execute
150

151
        expect(notes.count).to eq(1)
152 153
      end

154 155 156 157 158 159 160 161 162
      it 'finds notes on personal snippets' do
        note = create(:note_on_personal_snippet)
        params = { target_type: 'personal_snippet', target_id: note.noteable_id }

        notes = described_class.new(project, user, params).execute

        expect(notes.count).to eq(1)
      end

163
      it 'raises an exception for an invalid target_type' do
164
        params[:target_type] = 'invalid'
Felipe Artur's avatar
Felipe Artur committed
165
        expect { described_class.new(project, user, params).execute }.to raise_error("invalid target_type '#{params[:target_type]}'")
166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190
      end

      it 'filters out old notes' do
        note2.update_attribute(:updated_at, 2.hours.ago)
        notes = described_class.new(project, user, params).execute
        expect(notes).to eq([note1])
      end

      context 'confidential issue notes' do
        let(:confidential_issue) { create(:issue, :confidential, project: project, author: user) }
        let!(:confidential_note) { create(:note, noteable: confidential_issue, project: confidential_issue.project) }

        let(:params) { { target_id: confidential_issue.id, target_type: 'issue', last_fetched_at: 1.hour.ago.to_i } }

        it 'returns notes if user can see the issue' do
          expect(described_class.new(project, user, params).execute).to eq([confidential_note])
        end

        it 'raises an error if user can not see the issue' do
          user = create(:user)
          expect { described_class.new(project, user, params).execute }.to raise_error(ActiveRecord::RecordNotFound)
        end

        it 'raises an error for project members with guest role' do
          user = create(:user)
191
          project.add_guest(user)
192 193 194 195 196 197 198 199

          expect { described_class.new(project, user, params).execute }.to raise_error(ActiveRecord::RecordNotFound)
        end
      end
    end
  end

  describe '.search' do
200
    let(:project) { create(:project, :public) }
201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226
    let(:note) { create(:note_on_issue, note: 'WoW', project: project) }

    it 'returns notes with matching content' do
      expect(described_class.new(note.project, nil, search: note.note).execute).to eq([note])
    end

    it 'returns notes with matching content regardless of the casing' do
      expect(described_class.new(note.project, nil, search: 'WOW').execute).to eq([note])
    end

    it 'returns commit notes user can access' do
      note = create(:note_on_commit, project: project)

      expect(described_class.new(note.project, create(:user), search: note.note).execute).to eq([note])
    end

    context "confidential issues" do
      let(:user) { create(:user) }
      let(:confidential_issue) { create(:issue, :confidential, project: project, author: user) }
      let(:confidential_note) { create(:note, note: "Random", noteable: confidential_issue, project: confidential_issue.project) }

      it "returns notes with matching content if user can see the issue" do
        expect(described_class.new(confidential_note.project, user, search: confidential_note.note).execute).to eq([confidential_note])
      end

      it "does not return notes with matching content if user can not see the issue" do
227
        user = create(:user)
228
        expect(described_class.new(confidential_note.project, user, search: confidential_note.note).execute).to be_empty
229
      end
230

231
      it "does not return notes with matching content for project members with guest role" do
232
        user = create(:user)
233
        project.add_guest(user)
234 235 236 237 238 239 240 241 242 243 244 245 246 247 248
        expect(described_class.new(confidential_note.project, user, search: confidential_note.note).execute).to be_empty
      end

      it "does not return notes with matching content for unauthenticated users" do
        expect(described_class.new(confidential_note.project, nil, search: confidential_note.note).execute).to be_empty
      end
    end

    context 'inlines SQL filters on subqueries for performance' do
      let(:sql) { described_class.new(note.project, nil, search: note.note).execute.to_sql }
      let(:number_of_noteable_types) { 4 }

      specify 'project_id check' do
        expect(sql.scan(/project_id/).count).to be >= (number_of_noteable_types + 2)
      end
249

250 251
      specify 'search filter' do
        expect(sql.scan(/LIKE/).count).to be >= number_of_noteable_types
252
      end
253
    end
Jacob Vosmaer's avatar
Jacob Vosmaer committed
254
  end
255 256

  describe '#target' do
Douwe Maan's avatar
Douwe Maan committed
257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294
    subject { described_class.new(project, user, params) }

    context 'for a issue target' do
      let(:issue) { create(:issue, project: project) }
      let(:params) { { target_type: 'issue', target_id: issue.id } }

      it 'returns the issue' do
        expect(subject.target).to eq(issue)
      end
    end

    context 'for a merge request target' do
      let(:merge_request) { create(:merge_request, source_project: project) }
      let(:params) { { target_type: 'merge_request', target_id: merge_request.id } }

      it 'returns the merge_request' do
        expect(subject.target).to eq(merge_request)
      end
    end

    context 'for a snippet target' do
      let(:snippet) { create(:project_snippet, project: project) }
      let(:params) { { target_type: 'snippet', target_id: snippet.id } }

      it 'returns the snippet' do
        expect(subject.target).to eq(snippet)
      end
    end

    context 'for a commit target' do
      let(:project) { create(:project, :repository) }
      let(:commit) { project.commit }
      let(:params) { { target_type: 'commit', target_id: commit.id } }

      it 'returns the commit' do
        expect(subject.target).to eq(commit)
      end
    end
295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320

    context 'target_iid' do
      let(:issue) { create(:issue, project: project) }
      let(:merge_request) { create(:merge_request, source_project: project, target_project: project) }

      it 'finds issues by iid' do
        iid_params = { target_type: 'issue', target_iid: issue.iid }
        expect(described_class.new(project, user, iid_params).target).to eq(issue)
      end

      it 'finds merge requests by iid' do
        iid_params = { target_type: 'merge_request', target_iid: merge_request.iid }
        expect(described_class.new(project, user, iid_params).target).to eq(merge_request)
      end

      it 'returns nil if both target_id and target_iid are not given' do
        params_without_any_id = { target_type: 'issue' }
        expect(described_class.new(project, user, params_without_any_id).target).to be_nil
      end

      it 'prioritizes target_id over target_iid' do
        issue2 = create(:issue, project: project)
        iid_params = { target_type: 'issue', target_id: issue2.id, target_iid: issue.iid }
        expect(described_class.new(project, user, iid_params).target).to eq(issue2)
      end
    end
321
  end
Jacob Vosmaer's avatar
Jacob Vosmaer committed
322
end