require 'flipper/adapters/active_record'

# Turn off migration logging for specs
ActiveRecord::Migration.verbose = false

RSpec.describe Flipper::Adapters::ActiveRecord do
  subject { described_class.new }

  before(:all) do
    ActiveRecord::Base.establish_connection(adapter: 'sqlite3',
                                            database: ':memory:')
  end

  before(:each) do
    ActiveRecord::Base.connection.execute <<-SQL
      CREATE TABLE flipper_features (
        id integer PRIMARY KEY,
        key text NOT NULL UNIQUE,
        created_at datetime NOT NULL,
        updated_at datetime NOT NULL
      )
    SQL

    ActiveRecord::Base.connection.execute <<-SQL
      CREATE TABLE flipper_gates (
        id integer PRIMARY KEY,
        feature_key text NOT NULL,
        key text NOT NULL,
        value text DEFAULT NULL,
        created_at datetime NOT NULL,
        updated_at datetime NOT NULL
      )
    SQL

    ActiveRecord::Base.connection.execute <<-SQL
      CREATE UNIQUE INDEX index_gates_on_keys_and_value on flipper_gates (feature_key, key, value)
    SQL
  end

  after(:each) do
    ActiveRecord::Base.connection.execute("DROP table IF EXISTS `flipper_features`")
    ActiveRecord::Base.connection.execute("DROP table IF EXISTS `flipper_gates`")
  end

  it_should_behave_like 'a flipper adapter'

  context 'requiring "flipper-active_record"' do
    before do
      Flipper.configuration = nil
      Flipper.instance = nil

      load 'flipper/adapters/active_record.rb'
      ActiveSupport.run_load_hooks(:active_record, ActiveRecord::Base)
    end

    it 'configures itself' do
      expect(Flipper.adapter.adapter).to be_a(Flipper::Adapters::ActiveRecord)
    end
  end

  context "ActiveRecord connection_pool" do
    before do
      ActiveRecord::Base.clear_active_connections!
    end

    context "#features" do
      it "does not hold onto connections" do
        expect(ActiveRecord::Base.connection_handler.active_connections?).to be(false)
        subject.features
        expect(ActiveRecord::Base.connection_handler.active_connections?).to be(false)
      end

      it "does not release previously held connection" do
        ActiveRecord::Base.connection # establish a new connection
        expect(ActiveRecord::Base.connection_handler.active_connections?).to be(true)
        subject.features
        expect(ActiveRecord::Base.connection_handler.active_connections?).to be(true)
      end
    end

    context "#get_all" do
      it "does not hold onto connections" do
        expect(ActiveRecord::Base.connection_handler.active_connections?).to be(false)
        subject.get_all
        expect(ActiveRecord::Base.connection_handler.active_connections?).to be(false)
      end

      it "does not release previously held connection" do
        ActiveRecord::Base.connection # establish a new connection
        expect(ActiveRecord::Base.connection_handler.active_connections?).to be(true)
        subject.get_all
        expect(ActiveRecord::Base.connection_handler.active_connections?).to be(true)
      end
    end

    context "#add / #remove / #clear" do
      let(:feature) { Flipper::Feature.new(:search, subject) }

      it "does not hold onto connections" do
        expect(ActiveRecord::Base.connection_handler.active_connections?).to be(false)
        subject.add(feature)
        expect(ActiveRecord::Base.connection_handler.active_connections?).to be(false)
        subject.remove(feature)
        expect(ActiveRecord::Base.connection_handler.active_connections?).to be(false)
        subject.clear(feature)
        expect(ActiveRecord::Base.connection_handler.active_connections?).to be(false)
      end

      it "does not release previously held connection" do
        ActiveRecord::Base.connection # establish a new connection
        expect(ActiveRecord::Base.connection_handler.active_connections?).to be(true)
        subject.add(feature)
        expect(ActiveRecord::Base.connection_handler.active_connections?).to be(true)
        subject.remove(feature)
        expect(ActiveRecord::Base.connection_handler.active_connections?).to be(true)
        subject.clear(feature)
        expect(ActiveRecord::Base.connection_handler.active_connections?).to be(true)
      end
    end

    context "#get_multi" do
      let(:feature) { Flipper::Feature.new(:search, subject) }

      it "does not hold onto connections" do
        expect(ActiveRecord::Base.connection_handler.active_connections?).to be(false)
        subject.get_multi([feature])
        expect(ActiveRecord::Base.connection_handler.active_connections?).to be(false)
      end

      it "does not release previously held connection" do
        ActiveRecord::Base.connection # establish a new connection
        expect(ActiveRecord::Base.connection_handler.active_connections?).to be(true)
        subject.get_multi([feature])
        expect(ActiveRecord::Base.connection_handler.active_connections?).to be(true)
      end
    end

    context "#enable/#disable boolean" do
      let(:feature) { Flipper::Feature.new(:search, subject) }
      let(:gate) { feature.gate(:boolean)}

      it "does not hold onto connections" do
        expect(ActiveRecord::Base.connection_handler.active_connections?).to be(false)
        subject.enable(feature, gate, gate.wrap(true))
        expect(ActiveRecord::Base.connection_handler.active_connections?).to be(false)
        subject.disable(feature, gate, gate.wrap(false))
        expect(ActiveRecord::Base.connection_handler.active_connections?).to be(false)
      end

      it "does not release previously held connection" do
        ActiveRecord::Base.connection # establish a new connection
        expect(ActiveRecord::Base.connection_handler.active_connections?).to be(true)
        subject.enable(feature, gate, gate.wrap(true))
        expect(ActiveRecord::Base.connection_handler.active_connections?).to be(true)
        subject.disable(feature, gate, gate.wrap(false))
        expect(ActiveRecord::Base.connection_handler.active_connections?).to be(true)
      end
    end

    context "#enable/#disable set" do
      let(:feature) { Flipper::Feature.new(:search, subject) }
      let(:gate) { feature.gate(:group) }

      it "does not hold onto connections" do
        expect(ActiveRecord::Base.connection_handler.active_connections?).to be(false)
        subject.enable(feature, gate, gate.wrap(:admin))
        expect(ActiveRecord::Base.connection_handler.active_connections?).to be(false)
        subject.disable(feature, gate, gate.wrap(:admin))
        expect(ActiveRecord::Base.connection_handler.active_connections?).to be(false)
      end

      it "does not release previously held connection" do
        ActiveRecord::Base.connection # establish a new connection
        expect(ActiveRecord::Base.connection_handler.active_connections?).to be(true)
        subject.enable(feature, gate, gate.wrap(:admin))
        expect(ActiveRecord::Base.connection_handler.active_connections?).to be(true)
        subject.disable(feature, gate, gate.wrap(:admin))
        expect(ActiveRecord::Base.connection_handler.active_connections?).to be(true)
      end
    end
  end
end
