Skip to content

Commit 5bf3a65

Browse files
authored
fork and onboard migrator.exs test suite (#107)
1 parent 10706d1 commit 5bf3a65

File tree

4 files changed

+254
-3
lines changed

4 files changed

+254
-3
lines changed

integration_test/exqlite/all_test.exs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,14 @@ Code.require_file "./ecto/type.exs", __DIR__
1818
ecto_sql = Mix.Project.deps_paths()[:ecto_sql]
1919
# Code.require_file "#{ecto_sql}/integration_test/sql/lock.exs", __DIR__
2020
Code.require_file "#{ecto_sql}/integration_test/sql/logging.exs", __DIR__
21-
# Code.require_file "#{ecto_sql}/integration_test/sql/migration.exs", __DIR__
22-
# Code.require_file "#{ecto_sql}/integration_test/sql/migrator.exs", __DIR__
2321
Code.require_file "#{ecto_sql}/integration_test/sql/sandbox.exs", __DIR__
2422
Code.require_file "#{ecto_sql}/integration_test/sql/sql.exs", __DIR__
2523
Code.require_file "#{ecto_sql}/integration_test/sql/stream.exs", __DIR__
2624
Code.require_file "#{ecto_sql}/integration_test/sql/subquery.exs", __DIR__
2725
# Code.require_file "#{ecto_sql}/integration_test/sql/transaction.exs", __DIR__
26+
27+
# added :modify_column and :alter_foreign_key
2828
Code.require_file "./ecto_sql/migration.exs", __DIR__
29+
30+
# added :prefix and :lock_for_migrations
31+
Code.require_file "./ecto_sql/migrator.exs", __DIR__

integration_test/exqlite/ecto_sql/migration.exs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -461,7 +461,6 @@ defmodule Ecto.Integration.MigrationTest do
461461
assert :ok == down(PoolRepo, num, OnDeleteMigration, log: false)
462462
end
463463

464-
@tag :composite_foreign_key
465464
test "composite foreign keys", %{migration_number: num} do
466465
assert :ok == up(PoolRepo, num, CompositeForeignKeyMigration, log: false)
467466

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

integration_test/exqlite/test_helper.exs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,12 @@ ExUnit.start(
106106
:with_conflict_target,
107107
:without_conflict_target,
108108

109+
# right now in lock_for_migrations() we do effectively nothing, this is because
110+
# SQLite is single-writer so there isn't really a need for us to do anything.
111+
# ecto assumes all implementing adapters need >=2 connections for migrations
112+
# which is not true for SQLite
113+
:lock_for_migrations,
114+
109115
# Migration we don't support
110116
:prefix,
111117
:add_column_if_not_exists,

0 commit comments

Comments
 (0)