2019-07-07 11:18:12 +05:30
|
|
|
# frozen_string_literal: true
|
|
|
|
|
2016-11-03 12:29:30 +05:30
|
|
|
require 'spec_helper'
|
|
|
|
|
2019-09-04 21:01:54 +05:30
|
|
|
describe CacheMarkdownField, :clean_gitlab_redis_cache do
|
|
|
|
let(:ar_class) do
|
|
|
|
Class.new(ActiveRecord::Base) do
|
|
|
|
self.table_name = 'issues'
|
|
|
|
include CacheMarkdownField
|
|
|
|
cache_markdown_field :title, pipeline: :single_line
|
|
|
|
cache_markdown_field :description
|
2016-11-03 12:29:30 +05:30
|
|
|
end
|
2019-09-04 21:01:54 +05:30
|
|
|
end
|
2016-11-03 12:29:30 +05:30
|
|
|
|
2019-09-04 21:01:54 +05:30
|
|
|
let(:other_class) do
|
|
|
|
Class.new do
|
|
|
|
include CacheMarkdownField
|
2016-11-03 12:29:30 +05:30
|
|
|
|
2019-09-04 21:01:54 +05:30
|
|
|
def initialize(args = {})
|
|
|
|
@title, @description, @cached_markdown_version = args[:title], args[:description], args[:cached_markdown_version]
|
|
|
|
@title_html, @description_html = args[:title_html], args[:description_html]
|
|
|
|
@author, @project = args[:author], args[:project]
|
2016-11-03 12:29:30 +05:30
|
|
|
end
|
|
|
|
|
2019-09-04 21:01:54 +05:30
|
|
|
attr_accessor :title, :description, :cached_markdown_version
|
2017-08-17 22:00:37 +05:30
|
|
|
|
2019-09-04 21:01:54 +05:30
|
|
|
cache_markdown_field :title, pipeline: :single_line
|
|
|
|
cache_markdown_field :description
|
2017-08-17 22:00:37 +05:30
|
|
|
|
2019-09-04 21:01:54 +05:30
|
|
|
def cache_key
|
|
|
|
"cache-key"
|
2016-11-03 12:29:30 +05:30
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2017-08-17 22:00:37 +05:30
|
|
|
let(:markdown) { '`Foo`' }
|
2019-09-04 21:01:54 +05:30
|
|
|
let(:html) { '<p data-sourcepos="1:1-1:5" dir="auto"><code>Foo</code></p>' }
|
2016-11-03 12:29:30 +05:30
|
|
|
|
2017-08-17 22:00:37 +05:30
|
|
|
let(:updated_markdown) { '`Bar`' }
|
2019-09-04 21:01:54 +05:30
|
|
|
let(:updated_html) { '<p data-sourcepos="1:1-1:5" dir="auto"><code>Bar</code></p>' }
|
2016-11-03 12:29:30 +05:30
|
|
|
|
2019-09-04 21:01:54 +05:30
|
|
|
let(:cache_version) { Gitlab::MarkdownCache::CACHE_COMMONMARK_VERSION << 16 }
|
2017-08-17 22:00:37 +05:30
|
|
|
|
2019-09-04 21:01:54 +05:30
|
|
|
def thing_subclass(klass, extra_attribute)
|
|
|
|
Class.new(klass) { attr_accessor(extra_attribute) }
|
2016-11-03 12:29:30 +05:30
|
|
|
end
|
|
|
|
|
2019-09-04 21:01:54 +05:30
|
|
|
shared_examples 'a class with cached markdown fields' do
|
|
|
|
describe '#cached_html_up_to_date?' do
|
|
|
|
let(:thing) { klass.new(title: markdown, title_html: html, cached_markdown_version: cache_version) }
|
2018-10-15 14:42:47 +05:30
|
|
|
|
2019-09-04 21:01:54 +05:30
|
|
|
subject { thing.cached_html_up_to_date?(:title) }
|
2016-11-03 12:29:30 +05:30
|
|
|
|
2019-09-04 21:01:54 +05:30
|
|
|
it 'returns false when the version is absent' do
|
|
|
|
thing.cached_markdown_version = nil
|
2016-11-03 12:29:30 +05:30
|
|
|
|
2019-09-04 21:01:54 +05:30
|
|
|
is_expected.to be_falsy
|
|
|
|
end
|
2017-08-17 22:00:37 +05:30
|
|
|
|
2019-09-04 21:01:54 +05:30
|
|
|
it 'returns false when the version is too early' do
|
|
|
|
thing.cached_markdown_version -= 1
|
2017-08-17 22:00:37 +05:30
|
|
|
|
2019-09-04 21:01:54 +05:30
|
|
|
is_expected.to be_falsy
|
|
|
|
end
|
2018-10-15 14:42:47 +05:30
|
|
|
|
2019-09-04 21:01:54 +05:30
|
|
|
it 'returns false when the version is too late' do
|
|
|
|
thing.cached_markdown_version += 1
|
2017-08-17 22:00:37 +05:30
|
|
|
|
2019-09-04 21:01:54 +05:30
|
|
|
is_expected.to be_falsy
|
|
|
|
end
|
2018-10-15 14:42:47 +05:30
|
|
|
|
2019-09-04 21:01:54 +05:30
|
|
|
it 'returns false when the local version was bumped' do
|
|
|
|
allow(Gitlab::CurrentSettings.current_application_settings).to receive(:local_markdown_version).and_return(2)
|
|
|
|
thing.cached_markdown_version = cache_version
|
2018-10-15 14:42:47 +05:30
|
|
|
|
2019-09-04 21:01:54 +05:30
|
|
|
is_expected.to be_falsy
|
|
|
|
end
|
2018-10-15 14:42:47 +05:30
|
|
|
|
2019-09-04 21:01:54 +05:30
|
|
|
it 'returns true when the local version is default' do
|
|
|
|
thing.cached_markdown_version = cache_version
|
2018-10-15 14:42:47 +05:30
|
|
|
|
2019-09-04 21:01:54 +05:30
|
|
|
is_expected.to be_truthy
|
|
|
|
end
|
2017-08-17 22:00:37 +05:30
|
|
|
|
2019-09-04 21:01:54 +05:30
|
|
|
it 'returns true when the cached version is just right' do
|
|
|
|
allow(Gitlab::CurrentSettings.current_application_settings).to receive(:local_markdown_version).and_return(2)
|
|
|
|
thing.cached_markdown_version = cache_version + 2
|
2017-08-17 22:00:37 +05:30
|
|
|
|
2019-09-04 21:01:54 +05:30
|
|
|
is_expected.to be_truthy
|
|
|
|
end
|
2017-08-17 22:00:37 +05:30
|
|
|
end
|
|
|
|
|
2019-09-04 21:01:54 +05:30
|
|
|
describe '#latest_cached_markdown_version' do
|
|
|
|
let(:thing) { klass.new }
|
2020-03-13 15:44:24 +05:30
|
|
|
|
2019-09-04 21:01:54 +05:30
|
|
|
subject { thing.latest_cached_markdown_version }
|
2019-03-02 22:35:43 +05:30
|
|
|
|
2019-09-04 21:01:54 +05:30
|
|
|
it 'returns default version' do
|
|
|
|
thing.cached_markdown_version = nil
|
|
|
|
is_expected.to eq(cache_version)
|
|
|
|
end
|
2018-10-15 14:42:47 +05:30
|
|
|
end
|
2019-03-02 22:35:43 +05:30
|
|
|
|
2019-09-04 21:01:54 +05:30
|
|
|
describe '#refresh_markdown_cache' do
|
|
|
|
let(:thing) { klass.new(description: markdown, description_html: html, cached_markdown_version: cache_version) }
|
2017-08-17 22:00:37 +05:30
|
|
|
|
2019-09-04 21:01:54 +05:30
|
|
|
before do
|
|
|
|
thing.description = updated_markdown
|
|
|
|
end
|
2017-08-17 22:00:37 +05:30
|
|
|
|
2019-09-04 21:01:54 +05:30
|
|
|
it 'fills all html fields' do
|
|
|
|
thing.refresh_markdown_cache
|
2017-08-17 22:00:37 +05:30
|
|
|
|
2019-09-04 21:01:54 +05:30
|
|
|
expect(thing.description_html).to eq(updated_html)
|
|
|
|
end
|
2017-08-17 22:00:37 +05:30
|
|
|
|
2019-09-04 21:01:54 +05:30
|
|
|
it 'does not save the result' do
|
|
|
|
expect(thing).not_to receive(:save_markdown)
|
2017-08-17 22:00:37 +05:30
|
|
|
|
2019-09-04 21:01:54 +05:30
|
|
|
thing.refresh_markdown_cache
|
|
|
|
end
|
2017-08-17 22:00:37 +05:30
|
|
|
|
2019-09-04 21:01:54 +05:30
|
|
|
it 'updates the markdown cache version' do
|
|
|
|
thing.cached_markdown_version = nil
|
|
|
|
thing.refresh_markdown_cache
|
2017-08-17 22:00:37 +05:30
|
|
|
|
2019-09-04 21:01:54 +05:30
|
|
|
expect(thing.cached_markdown_version).to eq(cache_version)
|
|
|
|
end
|
2018-03-17 18:26:18 +05:30
|
|
|
end
|
2017-08-17 22:00:37 +05:30
|
|
|
|
2019-09-04 21:01:54 +05:30
|
|
|
describe '#refresh_markdown_cache!' do
|
|
|
|
let(:thing) { klass.new(description: markdown, description_html: html, cached_markdown_version: cache_version) }
|
2017-08-17 22:00:37 +05:30
|
|
|
|
2019-09-04 21:01:54 +05:30
|
|
|
before do
|
|
|
|
thing.description = updated_markdown
|
|
|
|
end
|
2017-08-17 22:00:37 +05:30
|
|
|
|
2019-09-04 21:01:54 +05:30
|
|
|
it 'fills all html fields' do
|
|
|
|
thing.refresh_markdown_cache!
|
2017-08-17 22:00:37 +05:30
|
|
|
|
2019-09-04 21:01:54 +05:30
|
|
|
expect(thing.description_html).to eq(updated_html)
|
|
|
|
end
|
2017-08-17 22:00:37 +05:30
|
|
|
|
2019-09-04 21:01:54 +05:30
|
|
|
it 'saves the changes' do
|
|
|
|
expect(thing)
|
|
|
|
.to receive(:save_markdown)
|
|
|
|
.with("description_html" => updated_html, "title_html" => "", "cached_markdown_version" => cache_version)
|
2017-08-17 22:00:37 +05:30
|
|
|
|
2019-09-04 21:01:54 +05:30
|
|
|
thing.refresh_markdown_cache!
|
|
|
|
end
|
2017-08-17 22:00:37 +05:30
|
|
|
end
|
2016-11-03 12:29:30 +05:30
|
|
|
|
2019-09-04 21:01:54 +05:30
|
|
|
describe '#banzai_render_context' do
|
|
|
|
let(:thing) { klass.new(title: markdown, title_html: html, cached_markdown_version: cache_version) }
|
2020-03-13 15:44:24 +05:30
|
|
|
|
2019-09-04 21:01:54 +05:30
|
|
|
subject(:context) { thing.banzai_render_context(:title) }
|
2017-08-17 22:00:37 +05:30
|
|
|
|
2019-09-04 21:01:54 +05:30
|
|
|
it 'sets project to nil if the object lacks a project' do
|
|
|
|
is_expected.to have_key(:project)
|
|
|
|
expect(context[:project]).to be_nil
|
|
|
|
end
|
2016-11-03 12:29:30 +05:30
|
|
|
|
2019-09-04 21:01:54 +05:30
|
|
|
it 'excludes author if the object lacks an author' do
|
|
|
|
is_expected.not_to have_key(:author)
|
|
|
|
end
|
2016-11-03 12:29:30 +05:30
|
|
|
|
2019-09-04 21:01:54 +05:30
|
|
|
it 'raises if the context for an unrecognised field is requested' do
|
|
|
|
expect { thing.banzai_render_context(:not_found) }.to raise_error(ArgumentError)
|
|
|
|
end
|
2016-11-03 12:29:30 +05:30
|
|
|
|
2019-09-04 21:01:54 +05:30
|
|
|
it 'includes the pipeline' do
|
|
|
|
title_context = thing.banzai_render_context(:title)
|
2017-08-17 22:00:37 +05:30
|
|
|
|
2019-09-04 21:01:54 +05:30
|
|
|
expect(title_context[:pipeline]).to eq(:single_line)
|
|
|
|
end
|
2016-11-03 12:29:30 +05:30
|
|
|
|
2019-09-04 21:01:54 +05:30
|
|
|
it 'returns copies of the context template' do
|
|
|
|
template = thing.cached_markdown_fields[:description]
|
|
|
|
copy = thing.banzai_render_context(:description)
|
2017-08-17 22:00:37 +05:30
|
|
|
|
2019-09-04 21:01:54 +05:30
|
|
|
expect(copy).not_to be(template)
|
|
|
|
end
|
2016-11-03 12:29:30 +05:30
|
|
|
|
2019-09-04 21:01:54 +05:30
|
|
|
context 'with a project' do
|
|
|
|
let(:project) { build(:project, group: create(:group)) }
|
|
|
|
let(:thing) { thing_subclass(klass, :project).new(title: markdown, title_html: html, project: project) }
|
2016-11-03 12:29:30 +05:30
|
|
|
|
2019-09-04 21:01:54 +05:30
|
|
|
it 'sets the project in the context' do
|
|
|
|
is_expected.to have_key(:project)
|
|
|
|
expect(context[:project]).to eq(project)
|
|
|
|
end
|
2016-11-03 12:29:30 +05:30
|
|
|
end
|
|
|
|
|
2019-09-04 21:01:54 +05:30
|
|
|
context 'with an author' do
|
|
|
|
let(:thing) { thing_subclass(klass, :author).new(title: markdown, title_html: html, author: :author_value) }
|
2016-11-03 12:29:30 +05:30
|
|
|
|
2019-09-04 21:01:54 +05:30
|
|
|
it 'sets the author in the context' do
|
|
|
|
is_expected.to have_key(:author)
|
|
|
|
expect(context[:author]).to eq(:author_value)
|
|
|
|
end
|
2016-11-03 12:29:30 +05:30
|
|
|
end
|
|
|
|
end
|
2019-09-30 21:07:59 +05:30
|
|
|
|
|
|
|
describe '#updated_cached_html_for' do
|
|
|
|
let(:thing) { klass.new(description: markdown, description_html: html, cached_markdown_version: cache_version) }
|
|
|
|
|
|
|
|
context 'when the markdown cache is outdated' do
|
|
|
|
before do
|
|
|
|
thing.cached_markdown_version += 1
|
|
|
|
end
|
|
|
|
|
2020-05-30 21:06:31 +05:30
|
|
|
it 'calls #refresh_markdown_cache!' do
|
|
|
|
expect(thing).to receive(:refresh_markdown_cache!)
|
2019-09-30 21:07:59 +05:30
|
|
|
|
|
|
|
expect(thing.updated_cached_html_for(:description)).to eq(html)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when the markdown field does not exist' do
|
|
|
|
it 'returns nil' do
|
|
|
|
expect(thing.updated_cached_html_for(:something)).to eq(nil)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when the markdown cache is up to date' do
|
2020-05-24 23:13:21 +05:30
|
|
|
before do
|
|
|
|
thing.try(:save)
|
|
|
|
end
|
|
|
|
|
2020-05-30 21:06:31 +05:30
|
|
|
it 'does not call #refresh_markdown_cache!' do
|
|
|
|
expect(thing).not_to receive(:refresh_markdown_cache!)
|
2019-09-30 21:07:59 +05:30
|
|
|
|
|
|
|
expect(thing.updated_cached_html_for(:description)).to eq(html)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
2020-04-08 14:13:33 +05:30
|
|
|
|
|
|
|
describe '#rendered_field_content' do
|
|
|
|
let(:thing) { klass.new(description: markdown, description_html: nil, cached_markdown_version: cache_version) }
|
|
|
|
|
|
|
|
context 'when a field can be cached' do
|
|
|
|
it 'returns the html' do
|
|
|
|
thing.description = updated_markdown
|
|
|
|
|
|
|
|
expect(thing.rendered_field_content(:description)).to eq updated_html
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when a field cannot be cached' do
|
|
|
|
it 'returns nil' do
|
|
|
|
allow(thing).to receive(:can_cache_field?).with(:description).and_return false
|
|
|
|
|
|
|
|
expect(thing.rendered_field_content(:description)).to eq nil
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
2019-09-04 21:01:54 +05:30
|
|
|
end
|
2016-11-03 12:29:30 +05:30
|
|
|
|
2019-09-04 21:01:54 +05:30
|
|
|
context 'for Active record classes' do
|
|
|
|
let(:klass) { ar_class }
|
2016-11-03 12:29:30 +05:30
|
|
|
|
2019-09-04 21:01:54 +05:30
|
|
|
it_behaves_like 'a class with cached markdown fields'
|
2020-05-24 23:13:21 +05:30
|
|
|
|
|
|
|
describe '#attribute_invalidated?' do
|
|
|
|
let(:thing) { klass.create(description: markdown, description_html: html, cached_markdown_version: cache_version) }
|
|
|
|
|
|
|
|
it 'returns true when cached_markdown_version is different' do
|
|
|
|
thing.cached_markdown_version += 1
|
|
|
|
|
|
|
|
expect(thing.attribute_invalidated?(:description_html)).to eq(true)
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'returns true when markdown is changed' do
|
|
|
|
thing.description = updated_markdown
|
|
|
|
|
|
|
|
expect(thing.attribute_invalidated?(:description_html)).to eq(true)
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'returns true when both markdown and HTML are changed' do
|
|
|
|
thing.description = updated_markdown
|
|
|
|
thing.description_html = updated_html
|
|
|
|
|
|
|
|
expect(thing.attribute_invalidated?(:description_html)).to eq(true)
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'returns false when there are no changes' do
|
|
|
|
expect(thing.attribute_invalidated?(:description_html)).to eq(false)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when cache version is updated' do
|
|
|
|
let(:old_version) { cache_version - 1 }
|
|
|
|
let(:old_html) { '<p data-sourcepos="1:1-1:5" dir="auto" class="some-old-class"><code>Foo</code></p>' }
|
|
|
|
|
|
|
|
let(:thing) do
|
|
|
|
# This forces the record to have outdated HTML. We can't use `create` because the `before_create` hook
|
|
|
|
# would re-render the HTML to the latest version
|
|
|
|
klass.create.tap do |thing|
|
|
|
|
thing.update_columns(description: markdown, description_html: old_html, cached_markdown_version: old_version)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'correctly updates cached HTML even if refresh_markdown_cache is called before updating the attribute' do
|
|
|
|
thing.refresh_markdown_cache
|
|
|
|
|
|
|
|
thing.update(description: updated_markdown)
|
|
|
|
|
|
|
|
expect(thing.description_html).to eq(updated_html)
|
|
|
|
end
|
|
|
|
end
|
2019-09-04 21:01:54 +05:30
|
|
|
end
|
2016-11-03 12:29:30 +05:30
|
|
|
|
2019-09-04 21:01:54 +05:30
|
|
|
context 'for other classes' do
|
|
|
|
let(:klass) { other_class }
|
2016-11-03 12:29:30 +05:30
|
|
|
|
2019-09-04 21:01:54 +05:30
|
|
|
it_behaves_like 'a class with cached markdown fields'
|
2016-11-03 12:29:30 +05:30
|
|
|
end
|
|
|
|
end
|