zf

zenflows testing
git clone https://s.sonu.ch/~srfsh/zf.git
Log | Files | Refs | Submodules | README | LICENSE

migrator.exs (7446B)


      1 Code.require_file "../support/file_helpers.exs", __DIR__
      2 
      3 defmodule Ecto.Integration.MigratorTest do
      4   use Ecto.Integration.Case
      5 
      6   import Support.FileHelpers
      7   import ExUnit.CaptureLog
      8   import Ecto.Migrator
      9 
     10   alias Ecto.Integration.{TestRepo, PoolRepo}
     11   alias Ecto.Migration.SchemaMigration
     12 
     13   setup config do
     14     Process.register(self(), config.test)
     15     PoolRepo.delete_all(SchemaMigration)
     16     :ok
     17   end
     18 
     19   defmodule AnotherSchemaMigration do
     20     use Ecto.Migration
     21 
     22     def change do
     23       execute TestRepo.create_prefix("bad_schema_migrations"),
     24               TestRepo.drop_prefix("bad_schema_migrations")
     25 
     26       create table(:schema_migrations, prefix: "bad_schema_migrations") do
     27         add :version, :string
     28         add :inserted_at, :integer
     29       end
     30     end
     31   end
     32 
     33   defmodule BrokenLinkMigration do
     34     use Ecto.Migration
     35 
     36     def change do
     37       Task.start_link(fn -> raise "oops" end)
     38       Process.sleep(:infinity)
     39     end
     40   end
     41 
     42   defmodule GoodMigration do
     43     use Ecto.Migration
     44 
     45     def up do
     46       create table(:good_migration)
     47     end
     48 
     49     def down do
     50       drop table(:good_migration)
     51     end
     52   end
     53 
     54   defmodule BadMigration do
     55     use Ecto.Migration
     56 
     57     def change do
     58       execute "CREATE WHAT"
     59     end
     60   end
     61 
     62   test "migrations up and down" do
     63     assert migrated_versions(PoolRepo) == []
     64     assert up(PoolRepo, 31, GoodMigration, log: false) == :ok
     65 
     66     [migration] = PoolRepo.all(SchemaMigration)
     67     assert migration.version == 31
     68     assert migration.inserted_at
     69 
     70     assert migrated_versions(PoolRepo) == [31]
     71     assert up(PoolRepo, 31, GoodMigration, log: false) == :already_up
     72     assert migrated_versions(PoolRepo) == [31]
     73     assert down(PoolRepo, 32, GoodMigration, log: false) == :already_down
     74     assert migrated_versions(PoolRepo) == [31]
     75     assert down(PoolRepo, 31, GoodMigration, log: false) == :ok
     76     assert migrated_versions(PoolRepo) == []
     77   end
     78 
     79   @tag :prefix
     80   test "does not commit migration if insert into schema migration fails" do
     81     # First we create a new schema migration table in another prefix
     82     assert up(PoolRepo, 33, AnotherSchemaMigration, log: false) == :ok
     83     assert migrated_versions(PoolRepo) == [33]
     84 
     85     catch_error(up(PoolRepo, 34, GoodMigration, log: false, prefix: "bad_schema_migrations"))
     86     catch_error(PoolRepo.all("good_migration"))
     87     catch_error(PoolRepo.all("good_migration", prefix: "bad_schema_migrations"))
     88 
     89     assert down(PoolRepo, 33, AnotherSchemaMigration, log: false) == :ok
     90   end
     91 
     92   test "ecto-generated migration queries pass schema_migration in telemetry options" do
     93     handler = fn _event_name, _measurements, metadata ->
     94       send(self(), metadata)
     95     end
     96 
     97     # migration table creation
     98     Process.put(:telemetry, handler)
     99     migrated_versions(PoolRepo, log: false)
    100     assert_received %{options: [schema_migration: true]}
    101 
    102     # transaction begin statement
    103     Process.put(:telemetry, handler)
    104     migrated_versions(PoolRepo, skip_table_creation: true, log: false)
    105     assert_received %{options: [schema_migration: true]}
    106 
    107     # retrieving the migration versions
    108     Process.put(:telemetry, handler)
    109     migrated_versions(PoolRepo, migration_lock: false, skip_table_creation: true, log: false)
    110     assert_received %{options: [schema_migration: true]}
    111   end
    112 
    113   test "bad execute migration" do
    114     assert catch_error(up(PoolRepo, 31, BadMigration, log: false))
    115   end
    116 
    117   test "broken link migration" do
    118     Process.flag(:trap_exit, true)
    119 
    120     assert capture_log(fn ->
    121       {:ok, pid} = Task.start_link(fn -> up(PoolRepo, 31, BrokenLinkMigration, log: false) end)
    122       assert_receive {:EXIT, ^pid, _}
    123     end) =~ "oops"
    124 
    125     assert capture_log(fn ->
    126       catch_exit(up(PoolRepo, 31, BrokenLinkMigration, log: false))
    127     end) =~ "oops"
    128   end
    129 
    130   test "run up to/step migration", config do
    131     in_tmp fn path ->
    132       create_migration(47, config)
    133       create_migration(48, config)
    134 
    135       assert [47] = run(PoolRepo, path, :up, step: 1, log: false)
    136       assert count_entries() == 1
    137 
    138       assert [48] = run(PoolRepo, path, :up, to: 48, log: false)
    139     end
    140   end
    141 
    142   test "run down to/step migration", config do
    143     in_tmp fn path ->
    144       migrations = [
    145         create_migration(49, config),
    146         create_migration(50, config),
    147       ]
    148 
    149       assert [49, 50] = run(PoolRepo, path, :up, all: true, log: false)
    150       purge migrations
    151 
    152       assert [50] = run(PoolRepo, path, :down, step: 1, log: false)
    153       purge migrations
    154 
    155       assert count_entries() == 1
    156       assert [50] = run(PoolRepo, path, :up, to: 50, log: false)
    157     end
    158   end
    159 
    160   test "runs all migrations", config do
    161     in_tmp fn path ->
    162       migrations = [
    163         create_migration(53, config),
    164         create_migration(54, config),
    165       ]
    166 
    167       assert [53, 54] = run(PoolRepo, path, :up, all: true, log: false)
    168       assert [] = run(PoolRepo, path, :up, all: true, log: false)
    169       purge migrations
    170 
    171       assert [54, 53] = run(PoolRepo, path, :down, all: true, log: false)
    172       purge migrations
    173 
    174       assert count_entries() == 0
    175       assert [53, 54] = run(PoolRepo, path, :up, all: true, log: false)
    176     end
    177   end
    178 
    179   test "does not commit half transactions on bad syntax", config do
    180     in_tmp fn path ->
    181       migrations = [
    182         create_migration(64, config),
    183         create_migration("65_+", config)
    184       ]
    185 
    186       assert_raise SyntaxError, fn ->
    187         run(PoolRepo, path, :up, all: true, log: false)
    188       end
    189 
    190       refute_received {:up, _}
    191       assert count_entries() == 0
    192       purge migrations
    193     end
    194   end
    195 
    196   @tag :lock_for_migrations
    197   test "raises when connection pool is too small" do
    198     config = Application.fetch_env!(:ecto_sql, PoolRepo)
    199     config = Keyword.merge(config, pool_size: 1)
    200     Application.put_env(:ecto_sql, __MODULE__.SingleConnectionRepo, config)
    201 
    202     defmodule SingleConnectionRepo do
    203       use Ecto.Repo, otp_app: :ecto_sql, adapter: PoolRepo.__adapter__()
    204     end
    205 
    206     {:ok, _pid} = SingleConnectionRepo.start_link()
    207 
    208     in_tmp fn path ->
    209       exception_message = ~r/Migrations failed to run because the connection pool size is less than 2/
    210 
    211       assert_raise Ecto.MigrationError, exception_message, fn ->
    212         run(SingleConnectionRepo, path, :up, all: true, log: false)
    213       end
    214     end
    215   end
    216 
    217   test "does not raise when connection pool is too small but there is no lock" do
    218     config = Application.fetch_env!(:ecto_sql, PoolRepo)
    219     config = Keyword.merge(config, pool_size: 1, migration_lock: nil)
    220     Application.put_env(:ecto_sql, __MODULE__.SingleConnectionNoLockRepo, config)
    221 
    222     defmodule SingleConnectionNoLockRepo do
    223       use Ecto.Repo, otp_app: :ecto_sql, adapter: PoolRepo.__adapter__()
    224     end
    225 
    226     {:ok, _pid} = SingleConnectionNoLockRepo.start_link()
    227 
    228     in_tmp fn path ->
    229       run(SingleConnectionNoLockRepo, path, :up, all: true, log: false)
    230     end
    231   end
    232 
    233   defp count_entries() do
    234     PoolRepo.aggregate(SchemaMigration, :count, :version)
    235   end
    236 
    237   defp create_migration(num, config) do
    238     module = Module.concat(__MODULE__, "Migration#{num}")
    239 
    240     File.write! "#{num}_migration_#{num}.exs", """
    241     defmodule #{module} do
    242       use Ecto.Migration
    243 
    244       def up do
    245         send #{inspect config.test}, {:up, #{inspect num}}
    246       end
    247 
    248       def down do
    249         send #{inspect config.test}, {:down, #{inspect num}}
    250       end
    251     end
    252     """
    253 
    254     module
    255   end
    256 
    257   defp purge(modules) do
    258     Enum.each(List.wrap(modules), fn m ->
    259       :code.delete m
    260       :code.purge m
    261     end)
    262   end
    263 end