From 88bcdabd94bb951416e9ce77cc1b35aa7b41f15b Mon Sep 17 00:00:00 2001 From: Alex Banna Date: Thu, 26 Mar 2026 01:03:26 -0500 Subject: [PATCH 1/2] test: add auto-migration integration tests - Verify items table exists after application startup - Verify all migrations are in :up state - Test re-running migrations is idempotent (no error, no new migrations) - Verify migration path resolves correctly with migration files present - End-to-end CRUD test against auto-migrated table Implements: HAR-573 --- test/items_api/auto_migrate_test.exs | 70 ++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 test/items_api/auto_migrate_test.exs diff --git a/test/items_api/auto_migrate_test.exs b/test/items_api/auto_migrate_test.exs new file mode 100644 index 0000000..39a0079 --- /dev/null +++ b/test/items_api/auto_migrate_test.exs @@ -0,0 +1,70 @@ +defmodule ItemsApi.AutoMigrateTest do + use ExUnit.Case, async: false + + setup do + :ok = Ecto.Adapters.SQL.Sandbox.checkout(ItemsApi.Repo) + :ok + end + + describe "auto-migration on startup" do + test "items table exists after application startup" do + # The application has already started (via mix test which starts the app). + # Auto-migration should have run, creating the items table. + result = + Ecto.Adapters.SQL.query!( + ItemsApi.Repo, + "SELECT name FROM sqlite_master WHERE type='table' AND name='items'" + ) + + assert result.rows == [["items"]] + end + + test "all migrations are in :up state" do + migrations_path = Application.app_dir(:items_api, "priv/repo/migrations") + status = Ecto.Migrator.migrations(ItemsApi.Repo, migrations_path) + + assert length(status) >= 1 + + for {state, _version, _name} <- status do + assert state == :up, "all migrations must be applied" + end + end + + test "re-running migrations is idempotent (no error)" do + # Calling run_migrations again should not raise — already-applied migrations are skipped. + migrations_path = Application.app_dir(:items_api, "priv/repo/migrations") + + # This should return an empty list (nothing to migrate) and not crash. + result = Ecto.Migrator.run(ItemsApi.Repo, migrations_path, :up, all: true) + + assert result == [], "no new migrations should be applied" + end + + test "migration path resolves correctly" do + migrations_path = Application.app_dir(:items_api, "priv/repo/migrations") + + assert File.dir?(migrations_path), "migrations directory must exist" + assert File.ls!(migrations_path) != [], "migrations directory must not be empty" + + migration_files = File.ls!(migrations_path) + + assert Enum.any?(migration_files, &String.contains?(&1, "create_items_table")), + "items table migration file must exist" + end + + test "items table is fully functional after auto-migration" do + # Verify we can actually CRUD against the auto-migrated table + changeset = ItemsApi.Item.changeset(%ItemsApi.Item{}, %{"name" => "AutoMigTest"}) + {:ok, item} = ItemsApi.Repo.insert(changeset) + + assert item.id != nil + assert item.name == "AutoMigTest" + + fetched = ItemsApi.Repo.get(ItemsApi.Item, item.id) + assert fetched.name == "AutoMigTest" + + {:ok, _} = ItemsApi.Repo.delete(item) + assert ItemsApi.Repo.get(ItemsApi.Item, item.id) == nil + end + end +end From d62f20915e64518f0086e10e45fb7e4fd3cd5036 Mon Sep 17 00:00:00 2001 From: Alex Banna Date: Sat, 28 Mar 2026 04:17:54 -0500 Subject: [PATCH 2/2] refactor(test): consolidate auto-migration tests into migration_test.exs Merge main into branch and consolidate test files: - Remove auto_migrate_test.exs (overlapped with migration_test.exs from main) - Add 3 unique tests to migration_test.exs: migration state verification, path resolution, and CRUD functional test against auto-migrated table All 38 tests pass. Implements: HAR-573 --- test/items_api/auto_migrate_test.exs | 70 ---------------------------- test/items_api/migration_test.exs | 43 ++++++++++++++++- 2 files changed, 42 insertions(+), 71 deletions(-) delete mode 100644 test/items_api/auto_migrate_test.exs diff --git a/test/items_api/auto_migrate_test.exs b/test/items_api/auto_migrate_test.exs deleted file mode 100644 index 39a0079..0000000 --- a/test/items_api/auto_migrate_test.exs +++ /dev/null @@ -1,70 +0,0 @@ -defmodule ItemsApi.AutoMigrateTest do - use ExUnit.Case, async: false - - setup do - :ok = Ecto.Adapters.SQL.Sandbox.checkout(ItemsApi.Repo) - :ok - end - - describe "auto-migration on startup" do - test "items table exists after application startup" do - # The application has already started (via mix test which starts the app). - # Auto-migration should have run, creating the items table. - result = - Ecto.Adapters.SQL.query!( - ItemsApi.Repo, - "SELECT name FROM sqlite_master WHERE type='table' AND name='items'" - ) - - assert result.rows == [["items"]] - end - - test "all migrations are in :up state" do - migrations_path = Application.app_dir(:items_api, "priv/repo/migrations") - status = Ecto.Migrator.migrations(ItemsApi.Repo, migrations_path) - - assert length(status) >= 1 - - for {state, _version, _name} <- status do - assert state == :up, "all migrations must be applied" - end - end - - test "re-running migrations is idempotent (no error)" do - # Calling run_migrations again should not raise — already-applied migrations are skipped. - migrations_path = Application.app_dir(:items_api, "priv/repo/migrations") - - # This should return an empty list (nothing to migrate) and not crash. - result = Ecto.Migrator.run(ItemsApi.Repo, migrations_path, :up, all: true) - - assert result == [], "no new migrations should be applied" - end - - test "migration path resolves correctly" do - migrations_path = Application.app_dir(:items_api, "priv/repo/migrations") - - assert File.dir?(migrations_path), "migrations directory must exist" - assert File.ls!(migrations_path) != [], "migrations directory must not be empty" - - migration_files = File.ls!(migrations_path) - - assert Enum.any?(migration_files, &String.contains?(&1, "create_items_table")), - "items table migration file must exist" - end - - test "items table is fully functional after auto-migration" do - # Verify we can actually CRUD against the auto-migrated table - changeset = ItemsApi.Item.changeset(%ItemsApi.Item{}, %{"name" => "AutoMigTest"}) - {:ok, item} = ItemsApi.Repo.insert(changeset) - - assert item.id != nil - assert item.name == "AutoMigTest" - - fetched = ItemsApi.Repo.get(ItemsApi.Item, item.id) - assert fetched.name == "AutoMigTest" - - {:ok, _} = ItemsApi.Repo.delete(item) - assert ItemsApi.Repo.get(ItemsApi.Item, item.id) == nil - end - end -end diff --git a/test/items_api/migration_test.exs b/test/items_api/migration_test.exs index 6ea8721..041a07e 100644 --- a/test/items_api/migration_test.exs +++ b/test/items_api/migration_test.exs @@ -11,7 +11,10 @@ defmodule ItemsApi.MigrationTest do # The application has already started (mix test starts it via mod:), # so migrations should have run. Verify the items table exists. {:ok, %{rows: tables}} = - Ecto.Adapters.SQL.query(ItemsApi.Repo, "SELECT name FROM sqlite_master WHERE type='table' AND name='items'") + Ecto.Adapters.SQL.query( + ItemsApi.Repo, + "SELECT name FROM sqlite_master WHERE type='table' AND name='items'" + ) assert tables == [["items"]] end @@ -27,6 +30,17 @@ defmodule ItemsApi.MigrationTest do assert "inserted_at" in column_names end + test "all migrations are in :up state" do + migrations_path = Application.app_dir(:items_api, "priv/repo/migrations") + status = Ecto.Migrator.migrations(ItemsApi.Repo, migrations_path) + + assert length(status) >= 1 + + for {state, _version, _name} <- status do + assert state == :up, "all migrations must be applied" + end + end + test "re-running migrations is idempotent" do migrations_path = Application.app_dir(:items_api, "priv/repo/migrations") # Running migrations again should not crash @@ -34,5 +48,32 @@ defmodule ItemsApi.MigrationTest do # Returns empty list when all migrations already applied assert result == [] end + + test "migration path resolves correctly" do + migrations_path = Application.app_dir(:items_api, "priv/repo/migrations") + + assert File.dir?(migrations_path), "migrations directory must exist" + assert File.ls!(migrations_path) != [], "migrations directory must not be empty" + + migration_files = File.ls!(migrations_path) + + assert Enum.any?(migration_files, &String.contains?(&1, "create_items_table")), + "items table migration file must exist" + end + + test "items table is fully functional after auto-migration" do + # Verify we can actually CRUD against the auto-migrated table + changeset = ItemsApi.Item.changeset(%ItemsApi.Item{}, %{"name" => "AutoMigTest"}) + {:ok, item} = ItemsApi.Repo.insert(changeset) + + assert item.id != nil + assert item.name == "AutoMigTest" + + fetched = ItemsApi.Repo.get(ItemsApi.Item, item.id) + assert fetched.name == "AutoMigTest" + + {:ok, _} = ItemsApi.Repo.delete(item) + assert ItemsApi.Repo.get(ItemsApi.Item, item.id) == nil + end end end