# frozen_string_literal: true

RSpec.describe Migration::ColumnDropper do
  def has_column?(table, column)
    DB.exec(<<~SQL, table: table, column: column) == 1
      SELECT 1
      FROM INFORMATION_SCHEMA.COLUMNS
      WHERE
        table_schema = 'public' AND
        table_name = :table AND
        column_name = :column
    SQL
  end

  describe ".execute_drop" do
    let(:columns) { %w[junk junk2] }

    before { columns.each { |column| DB.exec("ALTER TABLE topics ADD COLUMN #{column} int") } }

    after do
      columns.each { |column| DB.exec("ALTER TABLE topics DROP COLUMN IF EXISTS #{column}") }
    end

    it "drops the columns" do
      Migration::ColumnDropper.execute_drop("topics", columns)

      columns.each { |column| expect(has_column?("topics", column)).to eq(false) }
    end
  end

  describe ".mark_readonly" do
    let(:table_name) { "table_with_readonly_column" }

    before do
      DB.exec <<~SQL
      CREATE TABLE #{table_name} (topic_id INTEGER, email TEXT);

      INSERT INTO #{table_name} (topic_id, email)
      VALUES (1, 'something@email.com');
      SQL

      Migration::ColumnDropper.mark_readonly(table_name, "email")
    end

    after do
      ActiveRecord::Base.connection.reset!

      DB.exec <<~SQL
      DROP TABLE IF EXISTS #{table_name};
      DROP FUNCTION IF EXISTS #{Migration::BaseDropper.readonly_function_name(table_name, "email")} CASCADE;
      SQL
    end

    it "should be droppable" do
      Migration::ColumnDropper.execute_drop(table_name, ["email"])

      expect(has_trigger?(Migration::BaseDropper.readonly_trigger_name(table_name, "email"))).to eq(
        false,
      )

      expect(has_column?(table_name, "email")).to eq(false)
    end

    it "should prevent updates to the readonly column" do
      begin
        DB.exec <<~SQL
        UPDATE #{table_name}
        SET email = 'testing@email.com'
        WHERE topic_id = 1;
        SQL
      rescue PG::RaiseException => e
        [
          "Discourse: email in #{table_name} is readonly",
          "discourse_functions.raise_table_with_readonly_column_email_readonly()",
        ].each { |message| expect(e.message).to include(message) }
      end
    end

    it "should allow updates to the other columns" do
      DB.exec <<~SQL
      UPDATE #{table_name}
      SET topic_id = 2
      WHERE topic_id = 1
      SQL

      expect(DB.query("SELECT * FROM #{table_name};").first.values).to include 2,
              "something@email.com"
    end

    it "should prevent insertions to the readonly column" do
      expect do ActiveRecord::Base.connection.raw_connection.exec <<~SQL end.to raise_error(
        INSERT INTO #{table_name} (topic_id, email)
        VALUES (2, 'something@email.com');
        SQL
        PG::RaiseException,
        /Discourse: email in table_with_readonly_column is readonly/,
      )
    end

    it "should allow insertions to the other columns" do
      DB.exec <<~SQL
      INSERT INTO #{table_name} (topic_id)
      VALUES (2);
      SQL

      expect(DB.query_single("SELECT topic_id FROM #{table_name} WHERE topic_id = 2")).to eq([2])
    end
  end
end