# frozen_string_literal: true

RSpec.describe SemverDialects::SemanticVersion do
  context 'validation' do
    it 'raises no error for valid version strings' do
      %w[1 1- 1. 1.2.3 1.2a-20240221_ddae6 v1 v1.2 1.* 2024.03.14 2024-03-14 1.2.3_RC1
         1.2.3+build456].each do |version_string|
        expect { described_class.new(version_string) }.not_to raise_error
      end
    end

    it 'raises an error for invalid version strings' do
      %w[whatever dev-master -1 vx 1?].each do |version_string|
        expect { described_class.new(version_string) }
          .to raise_error(SemverDialects::InvalidVersionError, "invalid version '#{version_string}'")
      end
    end
  end

  describe '#pre_release?' do
    tests = [
      ['1.10-beta', true],
      ['1.10.0alpha+test', true],
      ['1.10.M1', true],
      ['2.0.1.rc1', true],
      ['2.3.1a', true],
      ['2.0.1.SP200', false],
      ['2.0.1', false]
    ]

    tests.each do |input, want|
      it_behaves_like 'table-driven test', input, want do
        subject { described_class.new(input).pre_release? }
      end
    end
  end

  describe '#post_release?' do
    tests = [
      ['2.0.1.rc1', false],
      ['2.0.1.SP1', true],
      ['2.0.1', false]
    ]

    tests.each do |input, want|
      it_behaves_like 'table-driven test', input, want do
        subject { described_class.new(input).post_release? }
      end
    end
  end

  describe '#to_normalized_s' do
    tests = [
      ['1.2-beta-2', '1:2:-12:2'],
      ['1.2rc0', '1:2:-11:0'],
      ['1.2-alpha-6', '1:2:-13:6']
    ]

    tests.each do |input, want|
      context "given input #{input}" do
        it "returns #{want}" do
          got = described_class.new(input).to_normalized_s
          expect(got).to eq(want)
        end
      end
    end
  end

  describe '#major, #minor, #patch' do
    using RSpec::Parameterized::TableSyntax
    subject(:version) { described_class.new(input) }

    # rubocop:disable Lint/BinaryOperatorWithIdenticalOperands
    where(:input, :major, :minor, :patch) do
      '1.2.3' | '1' | '2' | '3'
      'v1.2.3' | '1' | '2' | '3'
      '1.2' | '1' | '2' | '0'
      '1' | '1' | '0' | '0'
      '10.20.30.40' | '10' | '20' | '30'
      '1.2.3-rc1' | '1' | '2' | '3'
    end
    # rubocop:enable Lint/BinaryOperatorWithIdenticalOperands

    with_them do
      it { expect(version.major).to eq(major) }
      it { expect(version.minor).to eq(minor) }
      it { expect(version.patch).to eq(patch) }
    end
  end

  describe '#<=>' do
    using RSpec::Parameterized::TableSyntax
    subject { version.<=>(other_version) }
    let(:version) { described_class.new(a) }
    let(:other_version) { described_class.new(b) }

    # rubocop:disable Lint/BinaryOperatorWithIdenticalOperands
    where(:a, :b, :result) do
      '2.3.1' | '2.3.1' | 0
      '2.3.1' | 'v2.3.1' | 0
      '2.3.1' | '2.3.1+20240319' | 0
      '2.3.x' | '2.3.1' | 0
      '2.0.1.rc1' | '2.0.x' | 0
      '2.3.1' | '2.3.2' | -1
      '1.10.0alpha+test' | '1.10.0alpha+test' | 0
      '2.3.1' | '2.3' | 1
      '2' | '3' | -1
      '2.3.1a' | '2.3.1rc1' | -1
      '1.10.0alpha' | '1.10rc1' | -1
    end
    # rubocop:enable Lint/BinaryOperatorWithIdenticalOperands

    with_them do
      it { is_expected.to eq(result) }

      it 'gives opposite result when arguments are swapped' do
        expect(other_version.<=>(version)).to eq(-result)
      end
    end
  end
end
