# frozen_string_literal: true require 'spec_helper' describe Gitlab::MarkdownCache::ActiveRecord::Extension do let(:klass) do Class.new(ActiveRecord::Base) do self.table_name = 'issues' include CacheMarkdownField cache_markdown_field :title, whitelisted: true cache_markdown_field :description, pipeline: :single_line attribute :author attribute :project end end let(:cache_version) { Gitlab::MarkdownCache::CACHE_COMMONMARK_VERSION << 16 } let(:thing) { klass.create(title: markdown, title_html: html, cached_markdown_version: cache_version) } let(:markdown) { '`Foo`' } let(:html) { '

Foo

' } let(:updated_markdown) { '`Bar`' } let(:updated_html) { '

Bar

' } context 'an unchanged markdown field' do let(:thing) { klass.new(title: markdown) } before do thing.title = thing.title thing.save end it { expect(thing.title).to eq(markdown) } it { expect(thing.title_html).to eq(html) } it { expect(thing.title_html_changed?).not_to be_truthy } it { expect(thing.cached_markdown_version).to eq(cache_version) } end context 'a changed markdown field' do let(:thing) { klass.create(title: markdown, title_html: html, cached_markdown_version: cache_version) } before do thing.title = updated_markdown thing.save end it { expect(thing.title_html).to eq(updated_html) } it { expect(thing.cached_markdown_version).to eq(cache_version) } end context 'when a markdown field is set repeatedly to an empty string' do it do expect(thing).to receive(:refresh_markdown_cache).once thing.title = '' thing.save thing.title = '' thing.save end end context 'when a markdown field is set repeatedly to a string which renders as empty html' do it do expect(thing).to receive(:refresh_markdown_cache).once thing.title = '[//]: # (This is also a comment.)' thing.save thing.title = '[//]: # (This is also a comment.)' thing.save end end context 'a non-markdown field changed' do let(:thing) { klass.new(title: markdown, title_html: html, cached_markdown_version: cache_version) } before do thing.state = 'closed' thing.save end it { expect(thing.state).to eq('closed') } it { expect(thing.title).to eq(markdown) } it { expect(thing.title_html).to eq(html) } it { expect(thing.cached_markdown_version).to eq(cache_version) } end context 'version is out of date' do let(:thing) { klass.new(title: updated_markdown, title_html: html, cached_markdown_version: nil) } before do thing.save end it { expect(thing.title_html).to eq(updated_html) } it { expect(thing.cached_markdown_version).to eq(cache_version) } end context 'when an invalidating field is changed' do it 'invalidates the cache when project changes' do thing.project = :new_project allow(Banzai::Renderer).to receive(:cacheless_render_field).and_return(updated_html) thing.save expect(thing.title_html).to eq(updated_html) expect(thing.description_html).to eq(updated_html) expect(thing.cached_markdown_version).to eq(cache_version) end it 'invalidates the cache when author changes' do thing.author = :new_author allow(Banzai::Renderer).to receive(:cacheless_render_field).and_return(updated_html) thing.save expect(thing.title_html).to eq(updated_html) expect(thing.description_html).to eq(updated_html) expect(thing.cached_markdown_version).to eq(cache_version) end end describe '.attributes' do it 'excludes cache attributes that is blacklisted by default' do expect(thing.attributes.keys.sort).not_to include(%w[description_html]) end end describe '#cached_html_up_to_date?' do let(:thing) { klass.create(title: updated_markdown, title_html: html, cached_markdown_version: nil) } subject { thing.cached_html_up_to_date?(:title) } it 'returns false if markdown has been changed but html has not' do thing.title = "changed!" is_expected.to be_falsy end it 'returns true if markdown has not been changed but html has' do thing.title_html = updated_html is_expected.to be_truthy end it 'returns true if markdown and html have both been changed' do thing.title = updated_markdown thing.title_html = updated_html is_expected.to be_truthy end it 'returns false if the markdown field is set but the html is not' do thing.title_html = nil is_expected.to be_falsy end end describe '#refresh_markdown_cache!' do before do thing.title = updated_markdown end it 'skips saving if not persisted' do expect(thing).to receive(:persisted?).and_return(false) expect(thing).not_to receive(:update_columns) thing.refresh_markdown_cache! end it 'saves the changes' do expect(thing).to receive(:persisted?).and_return(true) expect(thing).to receive(:update_columns) .with("title_html" => updated_html, "description_html" => "", "cached_markdown_version" => cache_version) thing.refresh_markdown_cache! end end end