# frozen_string_literal: true

require 'spec_helper'

describe Gitlab::WikiPages::FrontMatterParser do
  subject(:parser) { described_class.new(raw_content, gate) }

  let(:content) { 'This is the content' }
  let(:end_divider) { '---' }
  let(:gate) { stub_feature_flag_gate('Gate') }

  let(:with_front_matter) do
    <<~MD
    ---
    a: 1
    b: 2
    c:
     - foo
     - bar
    date: I am safe. Not actually a date
    #{end_divider}
    #{content}
    MD
  end

  def have_correct_front_matter
    include(a: 1, b: 2, c: %w(foo bar))
  end

  describe '#parse' do
    subject { parser.parse }

    context 'there is front matter' do
      let(:raw_content) { with_front_matter }

      it do
        is_expected.to have_attributes(
          front_matter: have_correct_front_matter,
          content: content + "\n",
          error: be_nil
        )
      end
    end

    context 'there is no content' do
      let(:raw_content) { '' }

      it do
        is_expected.to have_attributes(
          front_matter: {},
          content: raw_content,
          error: be_nil
        )
      end
    end

    context 'there is no front_matter' do
      let(:raw_content) { content }

      it { is_expected.to have_attributes(front_matter: be_empty, content: raw_content) }

      it { is_expected.to have_attributes(reason: :no_match) }
    end

    context 'the feature flag is disabled' do
      let(:raw_content) { with_front_matter }

      before do
        stub_feature_flags(Gitlab::WikiPages::FrontMatterParser::FEATURE_FLAG => false)
      end

      it { is_expected.to have_attributes(front_matter: be_empty, content: raw_content) }
    end

    context 'the feature flag is enabled for the gated object' do
      let(:raw_content) { with_front_matter }

      before do
        stub_feature_flags(Gitlab::WikiPages::FrontMatterParser::FEATURE_FLAG => gate)
      end

      it do
        is_expected.to have_attributes(
          front_matter: have_correct_front_matter,
          content: content + "\n",
          reason: be_nil
        )
      end
    end

    context 'the end divider is ...' do
      let(:end_divider) { '...' }
      let(:raw_content) { with_front_matter }

      it { is_expected.to have_attributes(front_matter: have_correct_front_matter) }
    end

    context 'the front-matter is not a mapping' do
      let(:raw_content) do
        <<~MD
        ---
        - thing one
        - thing two
        ---
        #{content}
        MD
      end

      it { is_expected.to have_attributes(reason: :not_mapping) }
    end

    context 'there is nothing in the front-matter block' do
      let(:raw_content) do
        <<~MD
        ---
        ---
        My content here
        MD
      end

      it { is_expected.to have_attributes(reason: :not_mapping) }
    end

    context 'there is a string in the YAML block' do
      let(:raw_content) do
        <<~MD
        ---
        This is a string
        ---
        #{content}
        MD
      end

      it { is_expected.to have_attributes(reason: :not_mapping) }
    end

    context 'there is dangerous YAML in the block' do
      let(:raw_content) do
        <<~MD
        ---
        date: 2010-02-11 11:02:57
        ---
        #{content}
        MD
      end

      it { is_expected.to have_attributes(reason: :parse_error, error: be_present) }
    end

    context 'there is acceptably long YAML in the front-matter block' do
      let(:raw_content) do
        key = 'title: '
        length = described_class::MAX_FRONT_MATTER_LENGTH - key.size

        <<~MD
        ---
        title: #{FFaker::Lorem.characters(length)}
        ---
        #{content}
        MD
      end

      it { is_expected.to have_attributes(front_matter: include(title: be_present)) }
    end

    context 'there is suspiciously long YAML in the front-matter block' do
      let(:raw_content) do
        <<~MD
        ---
        title: #{FFaker::Lorem.characters(described_class::MAX_FRONT_MATTER_LENGTH)}
        ---
        #{content}
        MD
      end

      it { is_expected.to have_attributes(reason: :too_long) }
    end

    context 'TOML front matter' do
      let(:raw_content) do
        <<~MD
        +++
        title = "My title"
        +++
        #{content}
        MD
      end

      it { is_expected.to have_attributes(reason: :not_yaml) }
    end

    context 'TOML style fences, advertised as YAML' do
      let(:raw_content) do
        <<~MD
        +++ yaml
        title: "My title"
        +++
        #{content}
        MD
      end

      it { is_expected.to have_attributes(front_matter: include(title: 'My title')) }
    end

    context 'YAML, advertised as something else' do
      let(:raw_content) do
        <<~MD
        --- toml
        title: My title
        ---
        #{content}
        MD
      end

      it { is_expected.to have_attributes(reason: :not_yaml) }
    end

    context 'there is text content in the YAML block, in comments' do
      let(:raw_content) do
        <<~MD
        ---
        # This is YAML
        #
        # It has comments though. Explaining things
        foo: 1

        ## It has headings

        headings:

         - heading one
         - heading two

        # And lists

        lists:

         - and lists
         - with things in them
        ---
        #{content}
        MD
      end

      it { is_expected.to have_attributes(front_matter: include(foo: 1)) }
    end

    context 'there is text content in the YAML block' do
      let(:raw_content) do
        <<~MD
        ---
        # This is not YAML

        In fact is looks like markdown

        ## It has headings

        Paragraphs

        - and lists
        - with things in them
        ---
        #{content}
        MD
      end

      it { is_expected.to have_attributes(reason: :not_mapping) }
    end
  end
end
