# frozen_string_literal: true

require 'spec_helper'

RSpec.describe Gitlab::Ci::Status::Factory do
  let(:user) { create(:user) }
  let(:fabricated_status) { factory.fabricate! }
  let(:factory) { described_class.new(resource, user) }

  context 'when object has a core status' do
    Ci::HasStatus::AVAILABLE_STATUSES.each do |simple_status|
      context "when simple core status is #{simple_status}" do
        let(:resource) { double('resource', status: simple_status) }

        let(:expected_status) do
          Gitlab::Ci::Status.const_get(simple_status.to_s.camelize, false)
        end

        it "fabricates a core status #{simple_status}" do
          expect(fabricated_status).to be_a expected_status
        end

        it "matches a valid core status for #{simple_status}" do
          expect(factory.core_status).to be_a expected_status
        end

        it "does not match any extended statuses for #{simple_status}" do
          expect(factory.extended_statuses).to be_empty
        end
      end
    end
  end

  context 'when resource supports multiple extended statuses' do
    let(:resource) { double('resource', status: :success) }

    let(:first_extended_status) do
      Class.new(SimpleDelegator) do
        def first_method
          'first return value'
        end

        def second_method
          'second return value'
        end

        def self.matches?(*)
          true
        end
      end
    end

    let(:second_extended_status) do
      Class.new(SimpleDelegator) do
        def first_method
          'decorated return value'
        end

        def third_method
          'third return value'
        end

        def self.matches?(*)
          true
        end
      end
    end

    shared_examples 'compound decorator factory' do
      it 'fabricates compound decorator' do
        expect(fabricated_status.first_method).to eq 'decorated return value'
        expect(fabricated_status.second_method).to eq 'second return value'
        expect(fabricated_status.third_method).to eq 'third return value'
      end

      it 'delegates to core status' do
        expect(fabricated_status.text).to eq 'Passed'
      end

      it 'latest matches status becomes a status name' do
        expect(fabricated_status.class).to eq second_extended_status
      end

      it 'matches correct core status' do
        expect(factory.core_status).to be_a Gitlab::Ci::Status::Success
      end

      it 'matches correct extended statuses' do
        expect(factory.extended_statuses)
          .to eq [first_extended_status, second_extended_status]
      end
    end

    context 'when exclusive statuses are matches' do
      before do
        allow(described_class).to receive(:extended_statuses)
          .and_return([[first_extended_status, second_extended_status]])
      end

      it 'does not fabricate compound decorator' do
        expect(fabricated_status.first_method).to eq 'first return value'
        expect(fabricated_status.second_method).to eq 'second return value'
        expect(fabricated_status).not_to respond_to(:third_method)
      end

      it 'delegates to core status' do
        expect(fabricated_status.text).to eq 'Passed'
      end

      it 'matches correct core status' do
        expect(factory.core_status).to be_a Gitlab::Ci::Status::Success
      end

      it 'matches correct extended statuses' do
        expect(factory.extended_statuses).to eq [first_extended_status]
      end
    end

    context 'when exclusive statuses are not matched' do
      before do
        allow(described_class).to receive(:extended_statuses)
          .and_return([[first_extended_status], [second_extended_status]])
      end

      it_behaves_like 'compound decorator factory'
    end

    context 'when using simplified status grouping' do
      before do
        allow(described_class).to receive(:extended_statuses)
          .and_return([first_extended_status, second_extended_status])
      end

      it_behaves_like 'compound decorator factory'
    end
  end

  context 'behaviour of FactoryBot traits that create associations' do
    context 'creating a namespace with an associated aggregation_schedule record' do
      it 'creates only one Namespace record and one Namespace::AggregationSchedule record' do
        expect { create(:namespace, :with_aggregation_schedule) }
          .to change { Namespace.count }.by(1)
          .and change { Namespace::AggregationSchedule.count }.by(1)
      end
    end
  end
end
