Skip to content

Commit

Permalink
feat(test): sqlite schema testing
Browse files Browse the repository at this point in the history
We add a migration generator module which provides a macro to generate the migrations needed to test any Ecto Schema.
This allows you to swap in another Ecto adapter for local testing without connecting to Snowflake.

To use this you can bring the Snowflex.SQLiteTestRepo into your project and use it as you normally would for testing.
You will need to drop and create the repo before starting the tests each time so the migrations can work from a blank slate.

You will also need to run the generate_migrations macro in your test_helper files with a list of all the Ecto Schemas you wish to test.
  • Loading branch information
PM-Pepsico committed Nov 2, 2022
1 parent 48c8e4d commit 2b83809
Show file tree
Hide file tree
Showing 7 changed files with 104 additions and 7 deletions.
12 changes: 12 additions & 0 deletions config/test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,15 @@ config :snowflex, Snowflex.DBConnectionTest.SnowflakeDBConnection,
role: "DEV",
warehouse: "CUSTOMER_DEV_WH"
]

config :logger, level: :warn

config :snowflex, ecto_repos: [Snowflex.SQLiteTestRepo]

config :snowflex, repo: Snowflex.SQLiteTestRepo

config :snowflex, Snowflex.SQLiteTestRepo,
database: "test/snowflex_test_repo.sql",
journal_mode: :delete,
temp_store: :memory,
pool: Ecto.Adapters.SQL.Sandbox
44 changes: 44 additions & 0 deletions lib/snowflex/migration_generator.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
defmodule Snowflex.MigrationGenerator do
@moduledoc """
This module provides macros to autmoatically generate and start migrations based off Ecto Schema.
This is helpful for building tests of Snowflake schema using another database as the backend.
"""
defmacro generate_migrations(repo, modules) do
quote bind_quoted: [repo: repo, modules: modules] do
for {module, index} <- Enum.with_index(modules) do
defmodule Module.concat(module, Migration) do
use Ecto.Migration

@module module

def change do
primary_keys = @module.__schema__(:primary_key)

create table(@module.__schema__(:source), primary_key: false) do
for field <- @module.__schema__(:fields) do
type =
:type
|> @module.__schema__(field)
|> ecto_type_to_db_type()

add(field, type, primary_key: field in primary_keys)
end
end
end

defp ecto_type_to_db_type({:parameterized, Ecto.Enum, _}), do: :string
defp ecto_type_to_db_type(any), do: any
end

Ecto.Migrator.up(
repo,
index,
Module.concat(module, Migration),
log: :info,
skip_table_creation: false
)
end
end
end
end
5 changes: 5 additions & 0 deletions lib/snowflex/sqlite_test_repo.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
defmodule Snowflex.SQLiteTestRepo do
use Ecto.Repo,
otp_app: :snowflex,
adapter: Ecto.Adapters.SQLite3
end
7 changes: 4 additions & 3 deletions mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -47,13 +47,14 @@ defmodule Snowflex.MixProject do
[
{:poolboy, "~> 1.5.1"},
{:backoff, "~> 1.1.6"},
{:ecto, "~> 3.0"},
{:ecto_sql, "~> 3.0"},
{:ecto, "~> 3.9"},
{:ecto_sql, "~> 3.9"},
{:db_connection, "~> 2.4"},
{:telemetry, "~> 0.4 or ~> 1.0"},
{:dialyxir, "~> 1.0", only: :dev, runtime: false},
{:ex_doc, ">= 0.0.0", only: :dev, runtime: false},
{:meck, "~> 0.9", only: :test}
{:meck, "~> 0.9", only: :test},
{:ecto_sqlite3, "~> 0.8.2"}
]
end

Expand Down
11 changes: 7 additions & 4 deletions mix.lock
Original file line number Diff line number Diff line change
@@ -1,20 +1,23 @@
%{
"backoff": {:hex, :backoff, "1.1.6", "83b72ed2108ba1ee8f7d1c22e0b4a00cfe3593a67dbc792799e8cce9f42f796b", [:rebar3], [], "hexpm", "cf0cfff8995fb20562f822e5cc47d8ccf664c5ecdc26a684cbe85c225f9d7c39"},
"connection": {:hex, :connection, "1.1.0", "ff2a49c4b75b6fb3e674bfc5536451607270aac754ffd1bdfe175abe4a6d7a68", [:mix], [], "hexpm", "722c1eb0a418fbe91ba7bd59a47e28008a189d47e37e0e7bb85585a016b2869c"},
"db_connection": {:hex, :db_connection, "2.4.1", "6411f6e23f1a8b68a82fa3a36366d4881f21f47fc79a9efb8c615e62050219da", [:mix], [{:connection, "~> 1.0", [hex: :connection, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "ea36d226ec5999781a9a8ad64e5d8c4454ecedc7a4d643e4832bf08efca01f00"},
"db_connection": {:hex, :db_connection, "2.4.2", "f92e79aff2375299a16bcb069a14ee8615c3414863a6fef93156aee8e86c2ff3", [:mix], [{:connection, "~> 1.0", [hex: :connection, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "4fe53ca91b99f55ea249693a0229356a08f4d1a7931d8ffa79289b145fe83668"},
"decimal": {:hex, :decimal, "2.0.0", "a78296e617b0f5dd4c6caf57c714431347912ffb1d0842e998e9792b5642d697", [:mix], [], "hexpm", "34666e9c55dea81013e77d9d87370fe6cb6291d1ef32f46a1600230b1d44f577"},
"dialyxir": {:hex, :dialyxir, "1.1.0", "c5aab0d6e71e5522e77beff7ba9e08f8e02bad90dfbeffae60eaf0cb47e29488", [:mix], [{:erlex, ">= 0.2.6", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "07ea8e49c45f15264ebe6d5b93799d4dd56a44036cf42d0ad9c960bc266c0b9a"},
"earmark": {:hex, :earmark, "1.4.3", "364ca2e9710f6bff494117dbbd53880d84bebb692dafc3a78eb50aa3183f2bfd", [:mix], [], "hexpm", "8cf8a291ebf1c7b9539e3cddb19e9cef066c2441b1640f13c34c1d3cfc825fec"},
"earmark_parser": {:hex, :earmark_parser, "1.4.19", "de0d033d5ff9fc396a24eadc2fcf2afa3d120841eb3f1004d138cbf9273210e8", [:mix], [], "hexpm", "527ab6630b5c75c3a3960b75844c314ec305c76d9899bb30f71cb85952a9dc45"},
"ecto": {:hex, :ecto, "3.8.4", "e06b8b87e62b27fea17fd2ff6041572ddd10339fd16cdf58446e402c6c90a74b", [:mix], [{:decimal, "~> 1.6 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "f9244288b8d42db40515463a008cf3f4e0e564bb9c249fe87bf28a6d79fe82d4"},
"ecto_sql": {:hex, :ecto_sql, "3.8.3", "a7d22c624202546a39d615ed7a6b784580391e65723f2d24f65941b4dd73d471", [:mix], [{:db_connection, "~> 2.5 or ~> 2.4.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.8.4", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.6.0", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.15.0 or ~> 0.16.0 or ~> 1.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:tds, "~> 2.1.1 or ~> 2.2", [hex: :tds, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "348cb17fb9e6daf6f251a87049eafcb57805e2892e5e6a0f5dea0985d367329b"},
"ecto": {:hex, :ecto, "3.9.1", "67173b1687afeb68ce805ee7420b4261649d5e2deed8fe5550df23bab0bc4396", [:mix], [{:decimal, "~> 1.6 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "c80bb3d736648df790f7f92f81b36c922d9dd3203ca65be4ff01d067f54eb304"},
"ecto_sql": {:hex, :ecto_sql, "3.9.0", "2bb21210a2a13317e098a420a8c1cc58b0c3421ab8e3acfa96417dab7817918c", [:mix], [{:db_connection, "~> 2.5 or ~> 2.4.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.9.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.6.0", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.16.0 or ~> 1.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:tds, "~> 2.1.1 or ~> 2.2", [hex: :tds, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "a8f3f720073b8b1ac4c978be25fa7960ed7fd44997420c304a4a2e200b596453"},
"ecto_sqlite3": {:hex, :ecto_sqlite3, "0.8.2", "6851fd15e052cc31a7e1c61593afa991f7647bd029e30e8a1bc5a8b4ea10ae50", [:mix], [{:decimal, "~> 1.6 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:ecto, "~> 3.7", [hex: :ecto, repo: "hexpm", optional: false]}, {:ecto_sql, "~> 3.7", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:exqlite, "~> 0.9", [hex: :exqlite, repo: "hexpm", optional: false]}], "hexpm", "3f1466926132846d0433ccf7e9032a81f785dd573fabc0206d0d738997aefdb8"},
"elixir_make": {:hex, :elixir_make, "0.6.3", "bc07d53221216838d79e03a8019d0839786703129599e9619f4ab74c8c096eac", [:mix], [], "hexpm", "f5cbd651c5678bcaabdbb7857658ee106b12509cd976c2c2fca99688e1daf716"},
"erlex": {:hex, :erlex, "0.2.6", "c7987d15e899c7a2f34f5420d2a2ea0d659682c06ac607572df55a43753aa12e", [:mix], [], "hexpm", "2ed2e25711feb44d52b17d2780eabf998452f6efda104877a3881c2f8c0c0c75"},
"ex_doc": {:hex, :ex_doc, "0.27.3", "d09ed7ab590b71123959d9017f6715b54a448d76b43cf909eb0b2e5a78a977b2", [:mix], [{:earmark_parser, "~> 1.4.19", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "ee60b329d08195039bfeb25231a208749be4f2274eae42ce38f9be0538a2f2e6"},
"exqlite": {:hex, :exqlite, "0.11.6", "a8210b2adfa49c7155a54014bd0c2a51b8bb7bbabb8fd5fa70a5497531176991", [:make, :mix], [{:db_connection, "~> 2.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:elixir_make, "~> 0.6", [hex: :elixir_make, repo: "hexpm", optional: false]}, {:table, "~> 0.1.0", [hex: :table, repo: "hexpm", optional: true]}], "hexpm", "9262f7570ac326b34ec0e03e13322f54690eff06bbbe34fae5180c28bc5916dc"},
"makeup": {:hex, :makeup, "1.0.5", "d5a830bc42c9800ce07dd97fa94669dfb93d3bf5fcf6ea7a0c67b2e0e4a7f26c", [:mix], [{:nimble_parsec, "~> 0.5 or ~> 1.0", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "cfa158c02d3f5c0c665d0af11512fed3fba0144cf1aadee0f2ce17747fba2ca9"},
"makeup_elixir": {:hex, :makeup_elixir, "0.15.2", "dc72dfe17eb240552857465cc00cce390960d9a0c055c4ccd38b70629227e97c", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.1", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "fd23ae48d09b32eff49d4ced2b43c9f086d402ee4fd4fcb2d7fad97fa8823e75"},
"makeup_erlang": {:hex, :makeup_erlang, "0.1.1", "3fcb7f09eb9d98dc4d208f49cc955a34218fc41ff6b84df7c75b3e6e533cc65f", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "174d0809e98a4ef0b3309256cbf97101c6ec01c4ab0b23e926a9e17df2077cbb"},
"meck": {:hex, :meck, "0.9.2", "85ccbab053f1db86c7ca240e9fc718170ee5bda03810a6292b5306bf31bae5f5", [:rebar3], [], "hexpm", "81344f561357dc40a8344afa53767c32669153355b626ea9fcbc8da6b3045826"},
"nimble_parsec": {:hex, :nimble_parsec, "1.2.0", "b44d75e2a6542dcb6acf5d71c32c74ca88960421b6874777f79153bbbbd7dccc", [:mix], [], "hexpm", "52b2871a7515a5ac49b00f214e4165a40724cf99798d8e4a65e4fd64ebd002c1"},
"poolboy": {:hex, :poolboy, "1.5.2", "392b007a1693a64540cead79830443abf5762f5d30cf50bc95cb2c1aaafa006b", [:rebar3], [], "hexpm", "dad79704ce5440f3d5a3681c8590b9dc25d1a561e8f5a9c995281012860901e3"},
"telemetry": {:hex, :telemetry, "1.0.0", "0f453a102cdf13d506b7c0ab158324c337c41f1cc7548f0bc0e130bbf0ae9452", [:rebar3], [], "hexpm", "73bc09fa59b4a0284efb4624335583c528e07ec9ae76aca96ea0673850aec57a"},
"telemetry": {:hex, :telemetry, "1.1.0", "a589817034a27eab11144ad24d5c0f9fab1f58173274b1e9bae7074af9cbee51", [:rebar3], [], "hexpm", "b727b2a1f75614774cff2d7565b64d0dfa5bd52ba517f16543e6fc7efcc0df48"},
}
15 changes: 15 additions & 0 deletions test/snowflex_sqlite_test.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
defmodule SnowflexSqliteTest do
use ExUnit.Case

alias Snowflex.SQLiteTestRepo, as: Repo

setup do
:ok = Ecto.Adapters.SQL.Sandbox.checkout(Repo)
end

test "can crete schema" do
Repo.insert!(%TestSchema{x: 1, y: 2, z: 3})

assert [%TestSchema{x: 1, y: 2, z: 3}] = Repo.all(TestSchema)
end
end
17 changes: 17 additions & 0 deletions test/test_helper.exs
Original file line number Diff line number Diff line change
@@ -1 +1,18 @@
require Snowflex.MigrationGenerator

opts = [strategy: :one_for_one, name: Snowflex.Supervisor]
Supervisor.start_link([Snowflex.SQLiteTestRepo], opts)

Snowflex.SQLiteTestRepo.__adapter__().storage_up(Snowflex.SQLiteTestRepo.config())

Snowflex.MigrationGenerator.generate_migrations(Snowflex.SQLiteTestRepo, [
TestSchema,
TestSchema2,
TestSchema3
])

ExUnit.start()

ExUnit.after_suite(fn _ ->
Snowflex.SQLiteTestRepo.__adapter__().storage_down(Snowflex.SQLiteTestRepo.config())
end)

0 comments on commit 2b83809

Please sign in to comment.