defmodule Ecto.Integration.MigrationsTest do
  use ExUnit.Case, async: true

  alias Ecto.Integration.PoolRepo
  import ExUnit.CaptureLog

  @moduletag :capture_log
  @base_migration 3_000_000

  defmodule NormalMigration do
    use Ecto.Migration

    def change do
      create_if_not_exists table(:log_mode_table)

      alter table(:log_mode_table) do
        add :name, :text
      end
    end
  end

  defmodule RollbackMigration do
    use Ecto.Migration

    def change do
      create_if_not_exists table(:log_mode_table)

      alter table(:log_mode_table) do
        remove :not_exists, :text
      end
    end
  end

  describe "Migrator" do
    @create_table_sql ~s(CREATE TABLE IF NOT EXISTS "log_mode_table")
    @create_table_log "create table if not exists log_mode_table"
    @add_column_sql ~S(ALTER TABLE "log_mode_table" ADD COLUMN "name" TEXT)
    @alter_table_log "alter table log_mode_table"
    @drop_column_sql ~S(ALTER TABLE "log_mode_table" DROP COLUMN "name")
    @drop_table_sql ~s(DROP TABLE IF EXISTS "log_mode_table")
    @drop_table_log "drop table if exists log_mode_table"
    @version_insert ~s(INSERT INTO "schema_migrations")
    @version_delete ~s(DELETE FROM "schema_migrations")

    test "logs transaction commands" do
      num = @base_migration + System.unique_integer([:positive])
      up_log =
        capture_log(fn ->
          Ecto.Migrator.up(PoolRepo, num, NormalMigration, log_migrator_sql: :info, log_migrations_sql: :info, log: :info)
        end)

      assert up_log =~ "begin []"
      assert up_log =~ @create_table_sql
      assert up_log =~ @create_table_log
      assert up_log =~ @add_column_sql
      assert up_log =~ @alter_table_log
      assert up_log =~ @version_insert
      assert up_log =~ "commit []"

      # two columns in the table
      assert %{num_rows: 2} = PoolRepo.query!("PRAGMA table_info(log_mode_table)", [])

      down_log =
        capture_log(fn ->
          Ecto.Migrator.down(PoolRepo, num, NormalMigration, log_migrator_sql: :info, log_migrations_sql: :info, log: :info)
        end)

      assert down_log =~ "begin []"
      assert down_log =~ @drop_column_sql
      assert down_log =~ @drop_table_sql
      assert down_log =~ @drop_table_log
      assert down_log =~ @version_delete
      assert down_log =~ "commit []"
    end

    test "does not log sql when log is default" do
      num = @base_migration + System.unique_integer([:positive])
      up_log =
        capture_log(fn ->
          Ecto.Migrator.up(PoolRepo, num, NormalMigration, log: :info)
        end)

      refute up_log =~ "begin []"
      refute up_log =~ @create_table_sql
      assert up_log =~ @create_table_log
      refute up_log =~ @add_column_sql
      assert up_log =~ @alter_table_log
      refute up_log =~ @version_insert
      refute up_log =~ "commit []"

      down_log =
        capture_log(fn ->
          Ecto.Migrator.down(PoolRepo, num, NormalMigration, log: :info)
        end)

      refute down_log =~ "begin []"
      refute down_log =~ @drop_column_sql
      refute down_log =~ @drop_table_sql
      assert down_log =~ @drop_table_log
      refute down_log =~ @version_delete
      refute down_log =~ "commit []"
    end

    test "rolling back undoes previous migrations" do
      num = @base_migration + System.unique_integer([:positive])
      up_log =
        capture_log(fn ->
          try do
            Ecto.Migrator.up(PoolRepo, num, RollbackMigration, log_migrator_sql: :info, log_migrations_sql: :info, log: :info)
          rescue
            _ -> :ok
          end
        end)

      assert up_log =~ "begin []"
      assert up_log =~ @create_table_sql
      assert up_log =~ @create_table_log
      assert up_log =~ "rollback []"
      refute up_log =~ @version_insert
      refute up_log =~ "commit []"

      # table was not created due to rollback
      assert %{num_rows: 0} = PoolRepo.query!("PRAGMA table_info(log_mode_table)", [])
    end
  end
end
