# frozen_string_literal: true

require 'spec_helper'
require 'puppet-strings/yard'

describe PuppetStrings::Yard::Handlers::Ruby::ProviderHandler do
  subject {
    YARD::Parser::SourceParser.parse_string(source, :ruby)
    YARD::Registry.all(:puppet_provider)
  }

  describe 'parsing source without a provider definition' do
    let(:source) { 'puts "hi"' }

    it 'no providers should be in the registry' do
      expect(subject.empty?).to eq(true)
    end
  end

  describe 'parsing a provider with a missing description' do
    let(:source) { <<-SOURCE
Puppet::Type.type(:custom).provide :linux do
end
    SOURCE
    }

    it 'should log a warning' do
      expect{ subject }.to output(/\[warn\]: Missing a description for Puppet provider 'linux' \(resource type 'custom'\) at \(stdin\):1\./).to_stdout_from_any_process
    end
  end

  describe 'parsing a provider with an invalid docstring assignment' do
    let(:source) { <<-SOURCE
Puppet::Type.type(:custom).provide :linux do
  @doc = 123
end
    SOURCE
    }

    it 'should log an error' do
      expect { subject }.to output(/Failed to parse docstring/).to_stdout_from_any_process
    end
  end

  describe 'parsing a provider with a valid docstring assignment' do
    let(:source) { <<-SOURCE
Puppet::Type.type(:custom).provide :linux do
  @doc = 'An example provider on Linux.'
end
    SOURCE
    }

    it 'should correctly detect the docstring' do
      expect(subject.size).to eq(1)
      object = subject.first
      expect(object.docstring).to eq('An example provider on Linux.')
    end
  end

  describe 'parsing a provider with a docstring which uses ruby `%Q` notation' do
    let(:source) { <<-'SOURCE'
Puppet::Type.type(:custom).provide :linux do
  test = 'hello world!'
  desc %Q{This is a multi-line
  doc in %Q with #{test}}
end
    SOURCE
    }

    it 'should strip the `%Q{}` and render the interpolation expression literally' do
      expect(subject.size).to eq(1)
      object = subject.first
      expect(object.docstring).to eq("This is a multi-line\ndoc in %Q with \#{test}")
    end
  end

  describe 'parsing a provider definition' do
    let(:source) { <<-SOURCE
Puppet::Type.type(:custom).provide :linux do
  desc 'An example provider on Linux.'
  confine kernel: 'Linux'
  confine osfamily: 'RedHat'
  defaultfor :kernel => 'Linux'
  defaultfor :osfamily => 'RedHat', :operatingsystemmajrelease => '7'
  has_feature :implements_some_feature
  has_feature :some_other_feature
  commands foo: '/usr/bin/foo'
end
    SOURCE
    }

    it 'should register a provider object' do
      expect(subject.size).to eq(1)
      object = subject.first
      expect(object).to be_a(PuppetStrings::Yard::CodeObjects::Provider)
      expect(object.namespace).to eq(PuppetStrings::Yard::CodeObjects::Providers.instance('custom'))
      expect(object.name).to eq(:linux)
      expect(object.type_name).to eq('custom')
      expect(object.docstring).to eq('An example provider on Linux.')
      expect(object.docstring.tags.size).to eq(1)
      tags = object.docstring.tags(:api)
      expect(tags.size).to eq(1)
      expect(tags[0].text).to eq('public')
      expect(object.confines).to eq({ 'kernel' => 'Linux', 'osfamily' => 'RedHat'})
      expect(object.defaults).to eq([[["kernel", "Linux"]], [["osfamily", "RedHat"], ["operatingsystemmajrelease", "7"]]])
      expect(object.features).to eq(['implements_some_feature', 'some_other_feature'])
      expect(object.commands).to eq({'foo' => '/usr/bin/foo'})
    end
  end

  describe 'parsing a provider definition with a string based name' do
    let(:source) { <<-SOURCE
Puppet::Type.type(:'custom').provide :'linux' do
  desc 'An example provider on Linux.'
end
    SOURCE
    }

    it 'should register a provider object' do
      expect(subject.size).to eq(1)
      object = subject.first
      expect(object).to be_a(PuppetStrings::Yard::CodeObjects::Provider)
      expect(object.namespace).to eq(PuppetStrings::Yard::CodeObjects::Providers.instance('custom'))
      expect(object.name).to eq(:linux)
      expect(object.type_name).to eq('custom')
      expect(object.docstring).to eq('An example provider on Linux.')
    end
  end

  describe 'parsing a provider with a summary' do
    context 'when the summary has fewer than 140 characters' do
      let(:source) { <<-SOURCE
Puppet::Type.type(:custom).provide :linux do
  @doc = '@summary A short summary.'
end
      SOURCE
      }

      it 'should parse the summary' do
        expect{ subject }.to output('').to_stdout_from_any_process
        expect(subject.size).to eq(1)
        summary = subject.first.tags(:summary)
        expect(summary.first.text).to eq('A short summary.')
      end
    end

    context 'when the summary has more than 140 characters' do
      let(:source) { <<-SOURCE
Puppet::Type.type(:custom).provide :linux do
  @doc = '@summary A short summary that is WAY TOO LONG. AHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH this is not what a summary is for! It should be fewer than 140 characters!!'
end
      SOURCE
      }

      it 'should log a warning' do
        expect{ subject }.to output(/\[warn\]: The length of the summary for puppet_provider 'linux' exceeds the recommended limit of 140 characters./).to_stdout_from_any_process
      end
    end
  end
end
