gitlab-org--gitlab-foss/spec/models/note_spec.rb
Alexandru Croitor 5af535d919 Limit the size of issuable description and comments
Limiting the size of issuable description and comments to 1_000_000,
which is close to ~1MB of ASCII characters, which represents 99.9% of
all descriptions and comments we have in DB at the moment. This should
help prevent DoS attacks when comments contain refference strings.

Also this change updates regexp matching the namespaces paths by
limiting the namespaces paths to Namespace::NUMBER_OF_ANCESTORS_ALLOWED,
as we allow 20 levels deep groups.

see https://gitlab.com/gitlab-org/gitlab-ce/issues/61974#note_191274234
2019-08-22 10:43:13 +03:00

947 lines
30 KiB
Ruby

# frozen_string_literal: true
require 'spec_helper'
describe Note do
include RepoHelpers
describe 'associations' do
it { is_expected.to belong_to(:project) }
it { is_expected.to belong_to(:noteable).touch(false) }
it { is_expected.to belong_to(:author).class_name('User') }
it { is_expected.to have_many(:todos) }
end
describe 'modules' do
subject { described_class }
it { is_expected.to include_module(Participable) }
it { is_expected.to include_module(Mentionable) }
it { is_expected.to include_module(Awardable) }
end
describe 'validation' do
it { is_expected.to validate_length_of(:note).is_at_most(1_000_000) }
it { is_expected.to validate_presence_of(:note) }
it { is_expected.to validate_presence_of(:project) }
context 'when note is on commit' do
before do
allow(subject).to receive(:for_commit?).and_return(true)
end
it { is_expected.to validate_presence_of(:commit_id) }
it { is_expected.not_to validate_presence_of(:noteable_id) }
end
context 'when note is not on commit' do
before do
allow(subject).to receive(:for_commit?).and_return(false)
end
it { is_expected.not_to validate_presence_of(:commit_id) }
it { is_expected.to validate_presence_of(:noteable_id) }
end
context 'when noteable and note project differ' do
subject do
build(:note, noteable: build_stubbed(:issue),
project: build_stubbed(:project))
end
it { is_expected.to be_invalid }
end
context 'when noteable and note project are the same' do
subject { create(:note) }
it { is_expected.to be_valid }
end
context 'when project is missing for a project related note' do
subject { build(:note, project: nil, noteable: build_stubbed(:issue)) }
it { is_expected.to be_invalid }
end
context 'when noteable is a personal snippet' do
subject { build(:note_on_personal_snippet) }
it 'is valid without project' do
is_expected.to be_valid
end
end
end
describe "Commit notes" do
let!(:note) { create(:note_on_commit, note: "+1 from me") }
let!(:commit) { note.noteable }
it "is accessible through #noteable" do
expect(note.commit_id).to eq(commit.id)
expect(note.noteable).to be_a(Commit)
expect(note.noteable).to eq(commit)
end
it "saves a valid note" do
expect(note.commit_id).to eq(commit.id)
note.noteable == commit
end
it "is recognized by #for_commit?" do
expect(note).to be_for_commit
end
it "keeps the commit around" do
expect(note.project.repository.kept_around?(commit.id)).to be_truthy
end
it 'does not generate N+1 queries for participants', :request_store do
def retrieve_participants
commit.notes_with_associations.map(&:participants).to_a
end
# Project authorization checks are cached, establish a baseline
retrieve_participants
control_count = ActiveRecord::QueryRecorder.new do
retrieve_participants
end
create(:note_on_commit, project: note.project, note: 'another note', noteable_id: commit.id)
expect { retrieve_participants }.not_to exceed_query_limit(control_count)
end
end
describe 'authorization' do
before do
@p1 = create(:project)
@p2 = create(:project)
@u1 = create(:user)
@u2 = create(:user)
@u3 = create(:user)
end
describe 'read' do
before do
@p1.project_members.create(user: @u2, access_level: ProjectMember::GUEST)
@p2.project_members.create(user: @u3, access_level: ProjectMember::GUEST)
end
it { expect(Ability.allowed?(@u1, :read_note, @p1)).to be_falsey }
it { expect(Ability.allowed?(@u2, :read_note, @p1)).to be_truthy }
it { expect(Ability.allowed?(@u3, :read_note, @p1)).to be_falsey }
end
describe 'write' do
before do
@p1.project_members.create(user: @u2, access_level: ProjectMember::DEVELOPER)
@p2.project_members.create(user: @u3, access_level: ProjectMember::DEVELOPER)
end
it { expect(Ability.allowed?(@u1, :create_note, @p1)).to be_falsey }
it { expect(Ability.allowed?(@u2, :create_note, @p1)).to be_truthy }
it { expect(Ability.allowed?(@u3, :create_note, @p1)).to be_falsey }
end
describe 'admin' do
before do
@p1.project_members.create(user: @u1, access_level: ProjectMember::REPORTER)
@p1.project_members.create(user: @u2, access_level: ProjectMember::MAINTAINER)
@p2.project_members.create(user: @u3, access_level: ProjectMember::MAINTAINER)
end
it { expect(Ability.allowed?(@u1, :admin_note, @p1)).to be_falsey }
it { expect(Ability.allowed?(@u2, :admin_note, @p1)).to be_truthy }
it { expect(Ability.allowed?(@u3, :admin_note, @p1)).to be_falsey }
end
end
it_behaves_like 'an editable mentionable' do
subject { create :note, noteable: issue, project: issue.project }
let(:issue) { create(:issue, project: create(:project, :repository)) }
let(:backref_text) { issue.gfm_reference }
let(:set_mentionable_text) { ->(txt) { subject.note = txt } }
end
describe "#all_references" do
let!(:note1) { create(:note_on_issue) }
let!(:note2) { create(:note_on_issue) }
it "reads the rendered note body from the cache" do
expect(Banzai::Renderer).to receive(:cache_collection_render)
.with([{
text: note1.note,
context: {
skip_project_check: false,
pipeline: :note,
cache_key: [note1, "note"],
project: note1.project,
rendered: note1.note_html,
author: note1.author
}
}]).and_call_original
expect(Banzai::Renderer).to receive(:cache_collection_render)
.with([{
text: note2.note,
context: {
skip_project_check: false,
pipeline: :note,
cache_key: [note2, "note"],
project: note2.project,
rendered: note2.note_html,
author: note2.author
}
}]).and_call_original
note1.all_references.users
note2.all_references.users
end
end
describe "editable?" do
it "returns true" do
note = build(:note)
expect(note.editable?).to be_truthy
end
it "returns false" do
note = build(:note, system: true)
expect(note.editable?).to be_falsy
end
end
describe "edited?" do
let(:note) { build(:note, updated_by_id: nil, created_at: Time.now, updated_at: Time.now + 5.hours) }
context "with updated_by" do
it "returns true" do
note.updated_by = build(:user)
expect(note.edited?).to be_truthy
end
end
context "without updated_by" do
it "returns false" do
expect(note.edited?).to be_falsy
end
end
end
describe "confidential?" do
it "delegates to noteable" do
issue_note = build(:note, :on_issue)
confidential_note = build(:note, noteable: create(:issue, confidential: true))
expect(issue_note.confidential?).to be_falsy
expect(confidential_note.confidential?).to be_truthy
end
it "is falsey when noteable can't be confidential" do
commit_note = build(:note_on_commit)
expect(commit_note.confidential?).to be_falsy
end
end
describe "cross_reference_not_visible_for?" do
let(:private_user) { create(:user) }
let(:private_project) { create(:project, namespace: private_user.namespace) { |p| p.add_maintainer(private_user) } }
let(:private_issue) { create(:issue, project: private_project) }
let(:ext_proj) { create(:project, :public) }
let(:ext_issue) { create(:issue, project: ext_proj) }
shared_examples "checks references" do
it "returns true" do
expect(note.cross_reference_not_visible_for?(ext_issue.author)).to be_truthy
end
it "returns false" do
expect(note.cross_reference_not_visible_for?(private_user)).to be_falsy
end
it "returns false if user visible reference count set" do
note.user_visible_reference_count = 1
note.total_reference_count = 1
expect(note).not_to receive(:reference_mentionables)
expect(note.cross_reference_not_visible_for?(ext_issue.author)).to be_falsy
end
it "returns true if ref count is 0" do
note.user_visible_reference_count = 0
expect(note).not_to receive(:reference_mentionables)
expect(note.cross_reference_not_visible_for?(ext_issue.author)).to be_truthy
end
end
context "when there is one reference in note" do
let(:note) do
create :note,
noteable: ext_issue, project: ext_proj,
note: "mentioned in issue #{private_issue.to_reference(ext_proj)}",
system: true
end
it_behaves_like "checks references"
end
context "when there are two references in note" do
let(:note) do
create :note,
noteable: ext_issue, project: ext_proj,
note: "mentioned in issue #{private_issue.to_reference(ext_proj)} and " \
"public issue #{ext_issue.to_reference(ext_proj)}",
system: true
end
it_behaves_like "checks references"
it "returns true if user visible reference count set and there is a private reference" do
note.user_visible_reference_count = 1
note.total_reference_count = 2
expect(note).not_to receive(:reference_mentionables)
expect(note.cross_reference_not_visible_for?(ext_issue.author)).to be_truthy
end
end
end
describe '#cross_reference?' do
it 'falsey for user-generated notes' do
note = create(:note, system: false)
expect(note.cross_reference?).to be_falsy
end
context 'when the note might contain cross references' do
SystemNoteMetadata.new.cross_reference_types.each do |type|
let(:note) { create(:note, :system) }
let!(:metadata) { create(:system_note_metadata, note: note, action: type) }
it 'delegates to the cross-reference regex' do
expect(note).to receive(:matches_cross_reference_regex?).and_return(false)
note.cross_reference?
end
end
end
context 'when the note cannot contain cross references' do
let(:commit_note) { build(:note, note: 'mentioned in 1312312313 something else.', system: true) }
let(:label_note) { build(:note, note: 'added ~2323232323', system: true) }
it 'scan for a `mentioned in` prefix' do
expect(commit_note.cross_reference?).to be_truthy
expect(label_note.cross_reference?).to be_falsy
end
end
end
describe 'clear_blank_line_code!' do
it 'clears a blank line code before validation' do
note = build(:note, line_code: ' ')
expect { note.valid? }.to change(note, :line_code).to(nil)
end
end
describe '#participants' do
it 'includes the note author' do
project = create(:project, :public)
issue = create(:issue, project: project)
note = create(:note_on_issue, noteable: issue, project: project)
expect(note.participants).to include(note.author)
end
end
describe '.find_discussion' do
let!(:note) { create(:discussion_note_on_merge_request) }
let!(:note2) { create(:discussion_note_on_merge_request, in_reply_to: note) }
let(:merge_request) { note.noteable }
it 'returns a discussion with multiple notes' do
discussion = merge_request.notes.find_discussion(note.discussion_id)
expect(discussion).not_to be_nil
expect(discussion.notes).to match_array([note, note2])
expect(discussion.first_note.discussion_id).to eq(note.discussion_id)
end
end
describe ".grouped_diff_discussions" do
let!(:merge_request) { create(:merge_request) }
let(:project) { merge_request.project }
let!(:active_diff_note1) { create(:diff_note_on_merge_request, project: project, noteable: merge_request) }
let!(:active_diff_note2) { create(:diff_note_on_merge_request, project: project, noteable: merge_request, in_reply_to: active_diff_note1) }
let!(:active_diff_note3) { create(:diff_note_on_merge_request, project: project, noteable: merge_request, position: active_position2) }
let!(:outdated_diff_note1) { create(:diff_note_on_merge_request, project: project, noteable: merge_request, position: outdated_position) }
let!(:outdated_diff_note2) { create(:diff_note_on_merge_request, project: project, noteable: merge_request, in_reply_to: outdated_diff_note1) }
let(:active_position2) do
Gitlab::Diff::Position.new(
old_path: "files/ruby/popen.rb",
new_path: "files/ruby/popen.rb",
old_line: nil,
new_line: 13,
diff_refs: project.commit(sample_commit.id).diff_refs
)
end
let(:outdated_position) do
Gitlab::Diff::Position.new(
old_path: "files/ruby/popen.rb",
new_path: "files/ruby/popen.rb",
old_line: nil,
new_line: 9,
diff_refs: project.commit("874797c3a73b60d2187ed6e2fcabd289ff75171e").diff_refs
)
end
context 'active diff discussions' do
subject { merge_request.notes.grouped_diff_discussions }
it "includes active discussions" do
discussions = subject.values.flatten
expect(discussions.count).to eq(2)
expect(discussions.map(&:id)).to eq([active_diff_note1.discussion_id, active_diff_note3.discussion_id])
expect(discussions.all?(&:active?)).to be true
expect(discussions.first.notes).to eq([active_diff_note1, active_diff_note2])
expect(discussions.last.notes).to eq([active_diff_note3])
end
it "doesn't include outdated discussions" do
expect(subject.values.flatten.map(&:id)).not_to include(outdated_diff_note1.discussion_id)
end
it "groups the discussions by line code" do
expect(subject[active_diff_note1.line_code].first.id).to eq(active_diff_note1.discussion_id)
expect(subject[active_diff_note3.line_code].first.id).to eq(active_diff_note3.discussion_id)
end
context 'with image discussions' do
let(:merge_request2) { create(:merge_request_with_diffs, :with_image_diffs, source_project: project, title: "Added images and changes") }
let(:image_path) { "files/images/ee_repo_logo.png" }
let(:text_path) { "bar/branch-test.txt" }
let!(:image_note) { create(:diff_note_on_merge_request, project: project, noteable: merge_request2, position: image_position) }
let!(:text_note) { create(:diff_note_on_merge_request, project: project, noteable: merge_request2, position: text_position) }
let(:image_position) do
Gitlab::Diff::Position.new(
old_path: image_path,
new_path: image_path,
width: 100,
height: 100,
x: 1,
y: 1,
position_type: "image",
diff_refs: merge_request2.diff_refs
)
end
let(:text_position) do
Gitlab::Diff::Position.new(
old_path: text_path,
new_path: text_path,
old_line: nil,
new_line: 2,
position_type: "text",
diff_refs: merge_request2.diff_refs
)
end
it "groups image discussions by file identifier" do
diff_discussion = DiffDiscussion.new([image_note])
discussions = merge_request2.notes.grouped_diff_discussions
expect(discussions.size).to eq(2)
expect(discussions[image_note.diff_file.new_path]).to include(diff_discussion)
end
it "groups text discussions by line code" do
diff_discussion = DiffDiscussion.new([text_note])
discussions = merge_request2.notes.grouped_diff_discussions
expect(discussions.size).to eq(2)
expect(discussions[text_note.line_code]).to include(diff_discussion)
end
end
end
context 'diff discussions for older diff refs' do
subject { merge_request.notes.grouped_diff_discussions(diff_refs) }
context 'for diff refs a discussion was created at' do
let(:diff_refs) { active_position2.diff_refs }
it "includes discussions that were created then" do
discussions = subject.values.flatten
expect(discussions.count).to eq(1)
discussion = discussions.first
expect(discussion.id).to eq(active_diff_note3.discussion_id)
expect(discussion.active?).to be true
expect(discussion.active?(diff_refs)).to be false
expect(discussion.created_at_diff?(diff_refs)).to be true
expect(discussion.notes).to eq([active_diff_note3])
end
it "groups the discussions by original line code" do
expect(subject[active_diff_note3.original_line_code].first.id).to eq(active_diff_note3.discussion_id)
end
end
context 'for diff refs a discussion was last active at' do
let(:diff_refs) { outdated_position.diff_refs }
it "includes discussions that were last active" do
discussions = subject.values.flatten
expect(discussions.count).to eq(1)
discussion = discussions.first
expect(discussion.id).to eq(outdated_diff_note1.discussion_id)
expect(discussion.active?).to be false
expect(discussion.active?(diff_refs)).to be true
expect(discussion.created_at_diff?(diff_refs)).to be true
expect(discussion.notes).to eq([outdated_diff_note1, outdated_diff_note2])
end
it "groups the discussions by line code" do
expect(subject[outdated_diff_note1.line_code].first.id).to eq(outdated_diff_note1.discussion_id)
end
end
end
end
describe '#for_personal_snippet?' do
it 'returns false for a project snippet note' do
expect(build(:note_on_project_snippet).for_personal_snippet?).to be_falsy
end
it 'returns true for a personal snippet note' do
expect(build(:note_on_personal_snippet).for_personal_snippet?).to be_truthy
end
end
describe '#to_ability_name' do
it 'returns snippet for a project snippet note' do
expect(build(:note_on_project_snippet).to_ability_name).to eq('project_snippet')
end
it 'returns personal_snippet for a personal snippet note' do
expect(build(:note_on_personal_snippet).to_ability_name).to eq('personal_snippet')
end
it 'returns merge_request for an MR note' do
expect(build(:note_on_merge_request).to_ability_name).to eq('merge_request')
end
it 'returns issue for an issue note' do
expect(build(:note_on_issue).to_ability_name).to eq('issue')
end
it 'returns issue for a commit note' do
expect(build(:note_on_commit).to_ability_name).to eq('commit')
end
end
describe '#cache_markdown_field' do
let(:html) { '<p>some html</p>'}
context 'note for a project snippet' do
let(:note) { build(:note_on_project_snippet) }
before do
expect(Banzai::Renderer).to receive(:cacheless_render_field)
.with(note, :note, { skip_project_check: false }).and_return(html)
note.save
end
it 'creates a note' do
expect(note.note_html).to eq(html)
end
end
context 'note for a personal snippet' do
let(:note) { build(:note_on_personal_snippet) }
before do
expect(Banzai::Renderer).to receive(:cacheless_render_field)
.with(note, :note, { skip_project_check: true }).and_return(html)
note.save
end
it 'creates a note' do
expect(note.note_html).to eq(html)
end
end
end
describe '#can_be_discussion_note?' do
context 'for a note on a merge request' do
it 'returns true' do
note = build(:note_on_merge_request)
expect(note.can_be_discussion_note?).to be_truthy
end
end
context 'for a note on an issue' do
it 'returns true' do
note = build(:note_on_issue)
expect(note.can_be_discussion_note?).to be_truthy
end
end
context 'for a note on a commit' do
it 'returns true' do
note = build(:note_on_commit)
expect(note.can_be_discussion_note?).to be_truthy
end
end
context 'for a note on a snippet' do
it 'returns true' do
note = build(:note_on_project_snippet)
expect(note.can_be_discussion_note?).to be_truthy
end
end
context 'for a diff note on merge request' do
it 'returns false' do
note = build(:diff_note_on_merge_request)
expect(note.can_be_discussion_note?).to be_falsey
end
end
context 'for a diff note on commit' do
it 'returns false' do
note = build(:diff_note_on_commit)
expect(note.can_be_discussion_note?).to be_falsey
end
end
context 'for a discussion note' do
it 'returns false' do
note = build(:discussion_note_on_merge_request)
expect(note.can_be_discussion_note?).to be_falsey
end
end
end
describe '#discussion_class' do
let(:note) { build(:note_on_commit) }
let(:merge_request) { create(:merge_request) }
context 'when the note is displayed out of context' do
it 'returns OutOfContextDiscussion' do
expect(note.discussion_class(merge_request)).to be(OutOfContextDiscussion)
end
end
context 'when the note is displayed in the original context' do
it 'returns IndividualNoteDiscussion' do
expect(note.discussion_class(note.noteable)).to be(IndividualNoteDiscussion)
end
end
end
describe "#discussion_id" do
let(:note) { create(:note_on_commit) }
context "when it is newly created" do
it "has a discussion id" do
expect(note.discussion_id).not_to be_nil
expect(note.discussion_id).to match(/\A\h{40}\z/)
end
end
context "when it didn't store a discussion id before" do
before do
note.update_column(:discussion_id, nil)
end
it "has a discussion id" do
# The discussion_id is set in `after_initialize`, so `reload` won't work
reloaded_note = described_class.find(note.id)
expect(reloaded_note.discussion_id).not_to be_nil
expect(reloaded_note.discussion_id).to match(/\A\h{40}\z/)
end
end
context 'when the note is displayed out of context' do
let(:merge_request) { create(:merge_request) }
it 'overrides the discussion id' do
expect(note.discussion_id(merge_request)).not_to eq(note.discussion_id)
end
end
end
describe '#to_discussion' do
subject { create(:discussion_note_on_merge_request) }
let!(:note2) { create(:discussion_note_on_merge_request, project: subject.project, noteable: subject.noteable, in_reply_to: subject) }
it "returns a discussion with just this note" do
discussion = subject.to_discussion
expect(discussion.id).to eq(subject.discussion_id)
expect(discussion.notes).to eq([subject])
end
end
describe "#discussion" do
let!(:note1) { create(:discussion_note_on_merge_request) }
let!(:note2) { create(:diff_note_on_merge_request, project: note1.project, noteable: note1.noteable) }
context 'when the note is part of a discussion' do
subject { create(:discussion_note_on_merge_request, project: note1.project, noteable: note1.noteable, in_reply_to: note1) }
it "returns the discussion this note is in" do
discussion = subject.discussion
expect(discussion.id).to eq(subject.discussion_id)
expect(discussion.notes).to eq([note1, subject])
end
end
context 'when the note is not part of a discussion' do
subject { create(:note) }
it "returns a discussion with just this note" do
discussion = subject.discussion
expect(discussion.id).to eq(subject.discussion_id)
expect(discussion.notes).to eq([subject])
end
end
end
describe "#part_of_discussion?" do
context 'for a regular note' do
let(:note) { build(:note) }
it 'returns false' do
expect(note.part_of_discussion?).to be_falsey
end
end
context 'for a diff note' do
let(:note) { build(:diff_note_on_commit) }
it 'returns true' do
expect(note.part_of_discussion?).to be_truthy
end
end
context 'for a discussion note' do
let(:note) { build(:discussion_note_on_merge_request) }
it 'returns true' do
expect(note.part_of_discussion?).to be_truthy
end
end
end
describe '#in_reply_to?' do
context 'for a note' do
context 'when part of a discussion' do
subject { create(:discussion_note_on_issue) }
let(:note) { create(:discussion_note_on_issue, in_reply_to: subject) }
it 'checks if the note is in reply to the other discussion' do
expect(subject).to receive(:in_reply_to?).with(note).and_call_original
expect(subject).to receive(:in_reply_to?).with(note.noteable).and_call_original
expect(subject).to receive(:in_reply_to?).with(note.to_discussion).and_call_original
subject.in_reply_to?(note)
end
end
context 'when not part of a discussion' do
subject { create(:note) }
let(:note) { create(:note, in_reply_to: subject) }
it 'checks if the note is in reply to the other noteable' do
expect(subject).to receive(:in_reply_to?).with(note).and_call_original
expect(subject).to receive(:in_reply_to?).with(note.noteable).and_call_original
subject.in_reply_to?(note)
end
end
end
context 'for a discussion' do
context 'when part of the same discussion' do
subject { create(:diff_note_on_merge_request) }
let(:note) { create(:diff_note_on_merge_request, in_reply_to: subject) }
it 'returns true' do
expect(subject.in_reply_to?(note.to_discussion)).to be_truthy
end
end
context 'when not part of the same discussion' do
subject { create(:diff_note_on_merge_request) }
let(:note) { create(:diff_note_on_merge_request) }
it 'returns false' do
expect(subject.in_reply_to?(note.to_discussion)).to be_falsey
end
end
end
context 'for a noteable' do
context 'when a comment on the same noteable' do
subject { create(:note) }
let(:note) { create(:note, in_reply_to: subject) }
it 'returns true' do
expect(subject.in_reply_to?(note.noteable)).to be_truthy
end
end
context 'when not a comment on the same noteable' do
subject { create(:note) }
let(:note) { create(:note) }
it 'returns false' do
expect(subject.in_reply_to?(note.noteable)).to be_falsey
end
end
end
end
describe '#references' do
context 'when part of a discussion' do
it 'references all earlier notes in the discussion' do
first_note = create(:discussion_note_on_issue)
second_note = create(:discussion_note_on_issue, in_reply_to: first_note)
third_note = create(:discussion_note_on_issue, in_reply_to: second_note)
create(:discussion_note_on_issue, in_reply_to: third_note)
expect(third_note.references).to eq([first_note.noteable, first_note, second_note])
end
end
context 'when not part of a discussion' do
subject { create(:note) }
let(:note) { create(:note, in_reply_to: subject) }
it 'returns the noteable' do
expect(note.references).to eq([note.noteable])
end
end
end
describe 'expiring ETag cache' do
let(:note) { build(:note_on_issue) }
def expect_expiration(note)
expect_any_instance_of(Gitlab::EtagCaching::Store)
.to receive(:touch)
.with("/#{note.project.namespace.to_param}/#{note.project.to_param}/noteable/issue/#{note.noteable.id}/notes")
end
it "expires cache for note's issue when note is saved" do
expect_expiration(note)
note.save!
end
it "expires cache for note's issue when note is destroyed" do
expect_expiration(note)
note.destroy!
end
context 'when issuable etag caching is disabled' do
it 'does not store cache key' do
allow(note.noteable).to receive(:etag_caching_enabled?).and_return(false)
expect_any_instance_of(Gitlab::EtagCaching::Store).not_to receive(:touch)
note.save!
end
end
describe '#with_notes_filter' do
let!(:comment) { create(:note) }
let!(:system_note) { create(:note, system: true) }
context 'when notes filter is nil' do
subject { described_class.with_notes_filter(nil) }
it { is_expected.to include(comment, system_note) }
end
context 'when notes filter is set to all notes' do
subject { described_class.with_notes_filter(UserPreference::NOTES_FILTERS[:all_notes]) }
it { is_expected.to include(comment, system_note) }
end
context 'when notes filter is set to only comments' do
subject { described_class.with_notes_filter(UserPreference::NOTES_FILTERS[:only_comments]) }
it { is_expected.to include(comment) }
it { is_expected.not_to include(system_note) }
end
end
end
describe '#special_role=' do
let(:role) { Note::SpecialRole::FIRST_TIME_CONTRIBUTOR }
it 'assigns role' do
subject.special_role = role
expect(subject.special_role).to eq(role)
end
it 'does not assign unknown role' do
expect { subject.special_role = :bogus }.to raise_error(/Role is undefined/)
expect(subject.special_role).to be_nil
end
end
describe '#parent' do
it 'returns project for project notes' do
project = create(:project)
note = create(:note_on_issue, project: project)
expect(note.parent).to eq(project)
end
it 'returns nil for personal snippet note' do
note = create(:note_on_personal_snippet)
expect(note.parent).to be_nil
end
end
end