diff --git a/.tool-versions b/.tool-versions index f6f0139..32431ba 100644 --- a/.tool-versions +++ b/.tool-versions @@ -1,3 +1,3 @@ -erlang 28.1.1 -elixir 1.19.1-otp-28 -rust 1.76.0 \ No newline at end of file +erlang 28.4.1 +elixir 1.19.5-otp-28 +rust 1.76.0 diff --git a/README.md b/README.md index c10b514..2ad8aa7 100644 --- a/README.md +++ b/README.md @@ -388,3 +388,12 @@ You'll need a local Postgres instance running MIX_ENV=test mix walex.setup MIX_ENV=test mix test ``` + +### Optional Custom DB Settings + +If you have a customized local postgresql server running, you can override the default user, pass etc by setting the following ENV vars. These will then take precedence in tests and `mix walex.setup`. + +* PGUSER +* PGPASSWORD +* PGHOST +* PGPORT diff --git a/lib/mix/tasks/helpers.ex b/lib/mix/tasks/helpers.ex index 114e8c3..f755b45 100644 --- a/lib/mix/tasks/helpers.ex +++ b/lib/mix/tasks/helpers.ex @@ -1,19 +1,19 @@ defmodule Mix.Tasks.Walex.Helpers do @config %{ - hostname: "localhost", - username: "postgres", - password: "postgres", - port: 5432 + hostname: System.get_env("PGHOST", "localhost"), + username: System.get_env("PGUSER", "postgres"), + password: System.get_env("PGPASSWORD", "postgres"), + port: String.to_integer(System.get_env("PGPORT", "5432")) } @moduledoc false def create_database(database_name) do - "-c \"CREATE DATABASE #{database_name};\"" + "-d postgres -c \"CREATE DATABASE #{database_name};\"" |> database_cmd() end def drop_database(database_name) do - "-c \"DROP DATABASE #{database_name};\"" + "-d postgres -c \"DROP DATABASE #{database_name};\"" |> database_cmd() end diff --git a/lib/mix/tasks/walex.setup.ex b/lib/mix/tasks/walex.setup.ex index 0e21b04..7cabc80 100644 --- a/lib/mix/tasks/walex.setup.ex +++ b/lib/mix/tasks/walex.setup.ex @@ -7,9 +7,9 @@ defmodule Mix.Tasks.Walex.Setup do @test_database "todos_test" @base_configs [ - hostname: "localhost", - username: "postgres", - password: "postgres", + hostname: System.get_env("PGHOST", "localhost"), + username: System.get_env("PGUSER", "postgres"), + password: System.get_env("PGPASSWORD", "postgres"), database: @test_database ] diff --git a/mix.exs b/mix.exs index f0ee892..6cfee31 100644 --- a/mix.exs +++ b/mix.exs @@ -36,7 +36,7 @@ defmodule WalEx.MixProject do {:timex, "~> 3.7.13"}, # Dev & Test - {:ex_doc, "~> 0.39.3", only: :dev, runtime: false}, + {:ex_doc, "~> 0.40", only: :dev, runtime: false}, {:sobelow, "~> 0.14.1", only: [:dev, :test], runtime: false}, {:credo, "~> 1.7.15", only: [:dev, :test], runtime: false}, {:excoveralls, "~> 0.18.5", only: [:dev, :test], runtime: false}, diff --git a/mix.lock b/mix.lock index ac55a53..d543d5b 100644 --- a/mix.lock +++ b/mix.lock @@ -2,30 +2,30 @@ "bunt": {:hex, :bunt, "1.0.0", "081c2c665f086849e6d57900292b3a161727ab40431219529f13c4ddcf3e7a44", [:mix], [], "hexpm", "dc5f86aa08a5f6fa6b8096f0735c4e76d54ae5c9fa2c143e5a1fc7c1cd9bb6b5"}, "certifi": {:hex, :certifi, "2.15.0", "0e6e882fcdaaa0a5a9f2b3db55b1394dba07e8d6d9bcad08318fb604c6839712", [:rebar3], [], "hexpm", "b147ed22ce71d72eafdad94f055165c1c182f61a2ff49df28bcc71d1d5b94a60"}, "combine": {:hex, :combine, "0.10.0", "eff8224eeb56498a2af13011d142c5e7997a80c8f5b97c499f84c841032e429f", [:mix], [], "hexpm", "1b1dbc1790073076580d0d1d64e42eae2366583e7aecd455d1215b0d16f2451b"}, - "credo": {:hex, :credo, "1.7.15", "283da72eeb2fd3ccf7248f4941a0527efb97afa224bcdef30b4b580bc8258e1c", [:mix], [{:bunt, "~> 0.2.1 or ~> 1.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "291e8645ea3fea7481829f1e1eb0881b8395db212821338e577a90bf225c5607"}, - "db_connection": {:hex, :db_connection, "2.7.0", "b99faa9291bb09892c7da373bb82cba59aefa9b36300f6145c5f201c7adf48ec", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "dcf08f31b2701f857dfc787fbad78223d61a32204f217f15e881dd93e4bdd3ff"}, + "credo": {:hex, :credo, "1.7.17", "f92b6aa5b26301eaa5a35e4d48ebf5aa1e7094ac00ae38f87086c562caf8a22f", [:mix], [{:bunt, "~> 0.2.1 or ~> 1.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "1eb5645c835f0b6c9b5410f94b5a185057bcf6d62a9c2b476da971cde8749645"}, + "db_connection": {:hex, :db_connection, "2.9.0", "a6a97c5c958a2d7091a58a9be40caf41ab496b0701d21e1d1abff3fa27a7f371", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "17d502eacaf61829db98facf6f20808ed33da6ccf495354a41e64fe42f9c509c"}, "decimal": {:hex, :decimal, "2.3.0", "3ad6255aa77b4a3c4f818171b12d237500e63525c2fd056699967a3e7ea20f62", [:mix], [], "hexpm", "a4d66355cb29cb47c3cf30e71329e58361cfcb37c34235ef3bf1d7bf3773aeac"}, "earmark_parser": {:hex, :earmark_parser, "1.4.44", "f20830dd6b5c77afe2b063777ddbbff09f9759396500cdbe7523efd58d7a339c", [:mix], [], "hexpm", "4778ac752b4701a5599215f7030989c989ffdc4f6df457c5f36938cc2d2a2750"}, - "ex_doc": {:hex, :ex_doc, "0.39.3", "519c6bc7e84a2918b737aec7ef48b96aa4698342927d080437f61395d361dcee", [:mix], [{:earmark_parser, "~> 1.4.44", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.0", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14 or ~> 1.0", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1 or ~> 1.0", [hex: :makeup_erlang, repo: "hexpm", optional: false]}, {:makeup_html, ">= 0.1.0", [hex: :makeup_html, repo: "hexpm", optional: true]}], "hexpm", "0590955cf7ad3b625780ee1c1ea627c28a78948c6c0a9b0322bd976a079996e1"}, + "ex_doc": {:hex, :ex_doc, "0.40.1", "67542e4b6dde74811cfd580e2c0149b78010fd13001fda7cfeb2b2c2ffb1344d", [:mix], [{:earmark_parser, "~> 1.4.44", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.0", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14 or ~> 1.0", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1 or ~> 1.0", [hex: :makeup_erlang, repo: "hexpm", optional: false]}, {:makeup_html, ">= 0.1.0", [hex: :makeup_html, repo: "hexpm", optional: true]}], "hexpm", "bcef0e2d360d93ac19f01a85d58f91752d930c0a30e2681145feea6bd3516e00"}, "excoveralls": {:hex, :excoveralls, "0.18.5", "e229d0a65982613332ec30f07940038fe451a2e5b29bce2a5022165f0c9b157e", [:mix], [{:castore, "~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "523fe8a15603f86d64852aab2abe8ddbd78e68579c8525ae765facc5eae01562"}, - "expo": {:hex, :expo, "1.1.0", "f7b9ed7fb5745ebe1eeedf3d6f29226c5dd52897ac67c0f8af62a07e661e5c75", [:mix], [], "hexpm", "fbadf93f4700fb44c331362177bdca9eeb8097e8b0ef525c9cc501cb9917c960"}, + "expo": {:hex, :expo, "1.1.1", "4202e1d2ca6e2b3b63e02f69cfe0a404f77702b041d02b58597c00992b601db5", [:mix], [], "hexpm", "5fb308b9cb359ae200b7e23d37c76978673aa1b06e2b3075d814ce12c5811640"}, "file_system": {:hex, :file_system, "1.1.1", "31864f4685b0148f25bd3fbef2b1228457c0c89024ad67f7a81a3ffbc0bbad3a", [:mix], [], "hexpm", "7a15ff97dfe526aeefb090a7a9d3d03aa907e100e262a0f8f7746b78f8f87a5d"}, "gettext": {:hex, :gettext, "0.26.2", "5978aa7b21fada6deabf1f6341ddba50bc69c999e812211903b169799208f2a8", [:mix], [{:expo, "~> 0.5.1 or ~> 1.0", [hex: :expo, repo: "hexpm", optional: false]}], "hexpm", "aa978504bcf76511efdc22d580ba08e2279caab1066b76bb9aa81c4a1e0a32a5"}, - "hackney": {:hex, :hackney, "1.24.1", "f5205a125bba6ed4587f9db3cc7c729d11316fa8f215d3e57ed1c067a9703fa9", [:rebar3], [{:certifi, "~> 2.15.0", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "~> 6.1.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "~> 1.0.0", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "~> 1.4", [hex: :mimerl, repo: "hexpm", optional: false]}, {:parse_trans, "3.4.1", [hex: :parse_trans, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "~> 1.1.0", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}, {:unicode_util_compat, "~> 0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "f4a7392a0b53d8bbc3eb855bdcc919cd677358e65b2afd3840b5b3690c4c8a39"}, + "hackney": {:hex, :hackney, "1.25.0", "390e9b83f31e5b325b9f43b76e1a785cbdb69b5b6cd4e079aa67835ded046867", [:rebar3], [{:certifi, "~> 2.15.0", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "~> 6.1.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "~> 1.0.0", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "~> 1.4", [hex: :mimerl, repo: "hexpm", optional: false]}, {:parse_trans, "3.4.1", [hex: :parse_trans, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "~> 1.1.0", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}, {:unicode_util_compat, "~> 0.7.1", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "7209bfd75fd1f42467211ff8f59ea74d6f2a9e81cbcee95a56711ee79fd6b1d4"}, "idna": {:hex, :idna, "6.1.1", "8a63070e9f7d0c62eb9d9fcb360a7de382448200fbbd1b106cc96d3d8099df8d", [:rebar3], [{:unicode_util_compat, "~> 0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "92376eb7894412ed19ac475e4a86f7b413c1b9fbb5bd16dccd57934157944cea"}, "jason": {:hex, :jason, "1.4.4", "b9226785a9aa77b6857ca22832cffa5d5011a667207eb2a0ad56adb5db443b8a", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "c5eb0cab91f094599f94d55bc63409236a8ec69a21a67814529e8d5f6cc90b3b"}, "makeup": {:hex, :makeup, "1.2.1", "e90ac1c65589ef354378def3ba19d401e739ee7ee06fb47f94c687016e3713d1", [:mix], [{:nimble_parsec, "~> 1.4", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "d36484867b0bae0fea568d10131197a4c2e47056a6fbe84922bf6ba71c8d17ce"}, "makeup_elixir": {:hex, :makeup_elixir, "1.0.1", "e928a4f984e795e41e3abd27bfc09f51db16ab8ba1aebdba2b3a575437efafc2", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "7284900d412a3e5cfd97fdaed4f5ed389b8f2b4cb49efc0eb3bd10e2febf9507"}, - "makeup_erlang": {:hex, :makeup_erlang, "1.0.2", "03e1804074b3aa64d5fad7aa64601ed0fb395337b982d9bcf04029d68d51b6a7", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "af33ff7ef368d5893e4a267933e7744e46ce3cf1f61e2dccf53a111ed3aa3727"}, + "makeup_erlang": {:hex, :makeup_erlang, "1.0.3", "4252d5d4098da7415c390e847c814bad3764c94a814a0b4245176215615e1035", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "953297c02582a33411ac6208f2c6e55f0e870df7f80da724ed613f10e6706afd"}, "metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], [], "hexpm", "69b09adddc4f74a40716ae54d140f93beb0fb8978d8636eaded0c31b6f099f16"}, "mimerl": {:hex, :mimerl, "1.4.0", "3882a5ca67fbbe7117ba8947f27643557adec38fa2307490c4c4207624cb213b", [:rebar3], [], "hexpm", "13af15f9f68c65884ecca3a3891d50a7b57d82152792f3e19d88650aa126b144"}, "nimble_parsec": {:hex, :nimble_parsec, "1.4.2", "8efba0122db06df95bfaa78f791344a89352ba04baedd3849593bfce4d0dc1c6", [:mix], [], "hexpm", "4b21398942dda052b403bbe1da991ccd03a053668d147d53fb8c4e0efe09c973"}, "parse_trans": {:hex, :parse_trans, "3.4.1", "6e6aa8167cb44cc8f39441d05193be6e6f4e7c2946cb2759f015f8c56b76e5ff", [:rebar3], [], "hexpm", "620a406ce75dada827b82e453c19cf06776be266f5a67cff34e1ef2cbb60e49a"}, - "postgrex": {:hex, :postgrex, "0.20.0", "363ed03ab4757f6bc47942eff7720640795eb557e1935951c1626f0d303a3aed", [:mix], [{:db_connection, "~> 2.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.5 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:table, "~> 0.1.0", [hex: :table, repo: "hexpm", optional: true]}], "hexpm", "d36ef8b36f323d29505314f704e21a1a038e2dc387c6409ee0cd24144e187c0f"}, + "postgrex": {:hex, :postgrex, "0.22.0", "fb027b58b6eab1f6de5396a2abcdaaeb168f9ed4eccbb594e6ac393b02078cbd", [:mix], [{:db_connection, "~> 2.9", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.5 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:table, "~> 0.1.0", [hex: :table, repo: "hexpm", optional: true]}], "hexpm", "a68c4261e299597909e03e6f8ff5a13876f5caadaddd0d23af0d0a61afcc5d84"}, "rambo": {:hex, :rambo, "0.3.4", "8962ac3bd1a633ee9d0e8b44373c7913e3ce3d875b4151dcd060886092d2dce7", [:mix], [], "hexpm", "0cc54ed089fbbc84b65f4b8a774224ebfe60e5c80186fafc7910b3e379ad58f1"}, "sobelow": {:hex, :sobelow, "0.14.1", "2f81e8632f15574cba2402bcddff5497b413c01e6f094bc0ab94e83c2f74db81", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "8fac9a2bd90fdc4b15d6fca6e1608efb7f7c600fa75800813b794ee9364c87f2"}, "ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.7", "354c321cf377240c7b8716899e182ce4890c5938111a1296add3ec74cf1715df", [:make, :mix, :rebar3], [], "hexpm", "fe4c190e8f37401d30167c8c405eda19469f34577987c76dde613e838bbc67f8"}, - "telemetry": {:hex, :telemetry, "1.3.0", "fedebbae410d715cf8e7062c96a1ef32ec22e764197f70cda73d82778d61e7a2", [:rebar3], [], "hexpm", "7015fc8919dbe63764f4b4b87a95b7c0996bd539e0d499be6ec9d7f3875b79e6"}, + "telemetry": {:hex, :telemetry, "1.4.1", "ab6de178e2b29b58e8256b92b382ea3f590a47152ca3651ea857a6cae05ac423", [:rebar3], [], "hexpm", "2172e05a27531d3d31dd9782841065c50dd5c3c7699d95266b2edd54c2dafa1c"}, "timex": {:hex, :timex, "3.7.13", "0688ce11950f5b65e154e42b47bf67b15d3bc0e0c3def62199991b8a8079a1e2", [:mix], [{:combine, "~> 0.10", [hex: :combine, repo: "hexpm", optional: false]}, {:gettext, "~> 0.26", [hex: :gettext, repo: "hexpm", optional: false]}, {:tzdata, "~> 1.1", [hex: :tzdata, repo: "hexpm", optional: false]}], "hexpm", "09588e0522669328e973b8b4fd8741246321b3f0d32735b589f78b136e6d4c54"}, "tzdata": {:hex, :tzdata, "1.1.3", "b1cef7bb6de1de90d4ddc25d33892b32830f907e7fc2fccd1e7e22778ab7dfbc", [:mix], [{:hackney, "~> 1.17", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm", "d4ca85575a064d29d4e94253ee95912edfb165938743dbf002acdf0dcecb0c28"}, "unicode_util_compat": {:hex, :unicode_util_compat, "0.7.1", "a48703a25c170eedadca83b11e88985af08d35f37c6f664d6dcfb106a97782fc", [:rebar3], [], "hexpm", "b3a917854ce3ae233619744ad1e0102e05673136776fb2fa76234f3e03b23642"}, diff --git a/test/walex/config/registry_test.exs b/test/walex/config/registry_test.exs index 115f4bf..b7e8460 100644 --- a/test/walex/config/registry_test.exs +++ b/test/walex/config/registry_test.exs @@ -1,5 +1,6 @@ defmodule WalEx.Config.RegistryTest do use ExUnit.Case, async: false + import ExUnit.CaptureLog require Logger @@ -27,7 +28,7 @@ defmodule WalEx.Config.RegistryTest do describe "set_name/3" do setup do - {:ok, _pid} = WalExRegistry.start_registry() + start_supervised!(WalExRegistry.child_spec()) :ok end @@ -47,49 +48,53 @@ defmodule WalEx.Config.RegistryTest do end test "should be able to find processes" do - assert {:ok, walex_supervisor_pid} = WalExSupervisor.start_link(@base_configs) - - assert walex_supervisor_pid == - GenServer.whereis( - WalExRegistry.set_name(:set_supervisor, WalExSupervisor, :test_name) - ) - - assert GenServer.whereis( - WalExRegistry.set_name( - :set_supervisor, - WalEx.Events.Supervisor, - :test_name + capture_log(fn -> + walex_supervisor_pid = start_supervised!({WalExSupervisor, @base_configs}) + + assert walex_supervisor_pid == + GenServer.whereis( + WalExRegistry.set_name(:set_supervisor, WalExSupervisor, :test_name) + ) + + assert GenServer.whereis( + WalExRegistry.set_name( + :set_supervisor, + WalEx.Events.Supervisor, + :test_name + ) ) - ) - |> is_pid() - - assert GenServer.whereis( - WalExRegistry.set_name( - :set_supervisor, - WalEx.Replication.Supervisor, - :test_name + |> is_pid() + + assert GenServer.whereis( + WalExRegistry.set_name( + :set_supervisor, + WalEx.Replication.Supervisor, + :test_name + ) ) - ) - |> is_pid() + |> is_pid() - assert GenServer.whereis(WalExRegistry.set_name(:set_gen_server, WalEx.Events, :test_name)) - |> is_pid() + assert GenServer.whereis( + WalExRegistry.set_name(:set_gen_server, WalEx.Events, :test_name) + ) + |> is_pid() - assert GenServer.whereis( - WalExRegistry.set_name(:set_gen_server, WalEx.Replication.Server, :test_name) - ) - |> is_pid() + assert GenServer.whereis( + WalExRegistry.set_name(:set_gen_server, WalEx.Replication.Server, :test_name) + ) + |> is_pid() - assert GenServer.whereis( - WalExRegistry.set_name(:set_gen_server, WalEx.Replication.Publisher, :test_name) - ) - |> is_pid() + assert GenServer.whereis( + WalExRegistry.set_name(:set_gen_server, WalEx.Replication.Publisher, :test_name) + ) + |> is_pid() + end) end end describe "get_state/3" do setup do - {:ok, _pid} = WalExRegistry.start_registry() + start_supervised!(WalExRegistry.child_spec()) :ok end @@ -105,12 +110,14 @@ defmodule WalEx.Config.RegistryTest do describe "find_pid" do test "should find Supervisor" do - assert {:ok, walex_supervisor_pid} = WalExSupervisor.start_link(@base_configs) - - assert walex_supervisor_pid == - GenServer.whereis( - WalEx.Config.Registry.set_name(:set_supervisor, WalEx.Supervisor, :test_name) - ) + capture_log(fn -> + walex_supervisor_pid = start_supervised!({WalExSupervisor, @base_configs}) + + assert walex_supervisor_pid == + GenServer.whereis( + WalEx.Config.Registry.set_name(:set_supervisor, WalEx.Supervisor, :test_name) + ) + end) end end end diff --git a/test/walex/database_test.exs b/test/walex/database_test.exs index 946e2e0..fb17f4e 100644 --- a/test/walex/database_test.exs +++ b/test/walex/database_test.exs @@ -1,14 +1,16 @@ defmodule WalEx.DatabaseTest do use ExUnit.Case, async: false import WalEx.Support.TestHelpers + import ExUnit.CaptureLog alias WalEx.Supervisor, as: WalExSupervisor require Logger - @hostname "localhost" - @username "postgres" - @password "postgres" + @hostname System.get_env("PGHOST", "localhost") + @username System.get_env("PGUSER", "postgres") + @password System.get_env("PGPASSWORD", "postgres") @database "todos_test" + @port String.to_integer(System.get_env("PGPORT", "5432")) @base_configs [ name: :todos, @@ -16,7 +18,7 @@ defmodule WalEx.DatabaseTest do username: @username, password: @password, database: @database, - port: 5432, + port: @port, subscriptions: ["user", "todo"], publication: "events", modules: [TestModule] @@ -31,7 +33,7 @@ defmodule WalEx.DatabaseTest do describe "logical replication" do setup do - {:ok, database_pid} = start_database() + {{:ok, database_pid}, _log} = with_log(fn -> start_database() end) pg_drop_slots(database_pid) %{database_pid: database_pid} @@ -46,16 +48,20 @@ defmodule WalEx.DatabaseTest do Process.flag(:trap_exit, true) config = Keyword.put(@base_configs, :publication, "non_existent_publication") - {_pid, ref} = - spawn_monitor(fn -> WalExSupervisor.start_link(config) end) + capture_log(fn -> + {_pid, ref} = + spawn_monitor(fn -> WalExSupervisor.start_link(config) end) - assert_receive {:DOWN, ^ref, _, _, {:shutdown, _}} + assert_receive {:DOWN, ^ref, _, _, {:shutdown, _}} + end) end test "should start replication slot", %{database_pid: database_pid} do - assert {:ok, replication_pid} = WalExSupervisor.start_link(@base_configs) - assert is_pid(replication_pid) - assert [@replication_slot] = pg_replication_slots(database_pid) + capture_log(fn -> + assert {:ok, replication_pid} = WalExSupervisor.start_link(@base_configs) + assert is_pid(replication_pid) + assert [@replication_slot] = pg_replication_slots(database_pid) + end) end test "user-defined slot_name", %{database_pid: database_pid} do @@ -63,150 +69,162 @@ defmodule WalEx.DatabaseTest do config = Keyword.put(@base_configs, :slot_name, slot_name) - assert {:ok, replication_pid} = WalExSupervisor.start_link(config) + capture_log(fn -> + assert {:ok, replication_pid} = WalExSupervisor.start_link(config) - assert is_pid(replication_pid) - assert [slot] = pg_replication_slots(database_pid) - assert Map.fetch!(slot, "slot_name") == slot_name + assert is_pid(replication_pid) + assert [slot] = pg_replication_slots(database_pid) + assert Map.fetch!(slot, "slot_name") == slot_name + end) end test "should re-initiate after forcing database process termination" do - assert {:ok, supervisor_pid} = TestSupervisor.start_link(@base_configs) - database_pid = get_database_pid(supervisor_pid) + capture_log(fn -> + assert {:ok, supervisor_pid} = TestSupervisor.start_link(@base_configs) + database_pid = get_database_pid(supervisor_pid) - assert is_pid(database_pid) - assert [@replication_slot] = pg_replication_slots(database_pid) + assert is_pid(database_pid) + assert [@replication_slot] = pg_replication_slots(database_pid) - assert Process.exit(database_pid, :kill) - |> tap_debug("Forcefully killed database connection: ") + assert Process.exit(database_pid, :kill) + |> tap_debug("Forcefully killed database connection: ") - refute Process.info(database_pid) + refute Process.info(database_pid) - new_database_pid = get_database_pid(supervisor_pid) + new_database_pid = get_database_pid(supervisor_pid) - assert is_pid(new_database_pid) - refute database_pid == new_database_pid - assert_update_user(new_database_pid) + assert is_pid(new_database_pid) + refute database_pid == new_database_pid + assert_update_user(new_database_pid) + end) end test "should re-initiate after database connection restarted by supervisor" do - assert {:ok, supervisor_pid} = TestSupervisor.start_link(@base_configs) - database_pid = get_database_pid(supervisor_pid) + capture_log(fn -> + assert {:ok, supervisor_pid} = TestSupervisor.start_link(@base_configs) + database_pid = get_database_pid(supervisor_pid) - Supervisor.terminate_child(supervisor_pid, DBConnection.ConnectionPool) - |> tap_debug("Supervisor terminated database connection: ") + Supervisor.terminate_child(supervisor_pid, DBConnection.ConnectionPool) + |> tap_debug("Supervisor terminated database connection: ") - assert :undefined == get_database_pid(supervisor_pid) + assert :undefined == get_database_pid(supervisor_pid) - refute Process.info(database_pid) + refute Process.info(database_pid) - Supervisor.restart_child(supervisor_pid, DBConnection.ConnectionPool) - |> tap_debug("Supervisor restarted database connection: ") + Supervisor.restart_child(supervisor_pid, DBConnection.ConnectionPool) + |> tap_debug("Supervisor restarted database connection: ") - wait_for_restart() + wait_for_restart() - restarted_database_pid = get_database_pid(supervisor_pid) + restarted_database_pid = get_database_pid(supervisor_pid) - assert is_pid(restarted_database_pid) - refute database_pid == restarted_database_pid - assert_update_user(restarted_database_pid) + assert is_pid(restarted_database_pid) + refute database_pid == restarted_database_pid + assert_update_user(restarted_database_pid) - assert [@replication_slot | _replication_slots] = - pg_replication_slots(restarted_database_pid) + assert [@replication_slot | _replication_slots] = + pg_replication_slots(restarted_database_pid) + end) end test "should re-initiate after database connection terminated" do - assert {:ok, supervisor_pid} = TestSupervisor.start_link(@base_configs) - database_pid = get_database_pid(supervisor_pid) + capture_log(fn -> + assert {:ok, supervisor_pid} = TestSupervisor.start_link(@base_configs) + database_pid = get_database_pid(supervisor_pid) - assert {:error, - %DBConnection.ConnectionError{ - message: "tcp recv: closed", - severity: :error, - reason: :error - }} == terminate_database_connection(database_pid, @username) + assert {:error, + %DBConnection.ConnectionError{ + message: "tcp recv: closed", + severity: :error, + reason: :closed + }} == terminate_database_connection(database_pid, @username) - assert_update_user(database_pid) + assert_update_user(database_pid) - assert [@replication_slot | _replication_slots] = pg_replication_slots(database_pid) + assert [@replication_slot | _replication_slots] = pg_replication_slots(database_pid) + end) end test "should re-initiate after database restart" do - assert {:ok, supervisor_pid} = TestSupervisor.start_link(@base_configs) - database_pid = get_database_pid(supervisor_pid) + capture_log(fn -> + assert {:ok, supervisor_pid} = TestSupervisor.start_link(@base_configs) + database_pid = get_database_pid(supervisor_pid) - assert is_pid(database_pid) - assert [@replication_slot | _replication_slots] = pg_replication_slots(database_pid) + assert is_pid(database_pid) + assert [@replication_slot | _replication_slots] = pg_replication_slots(database_pid) - assert Process.exit(database_pid, :kill) - |> tap_debug("Forcefully killed database connection: ") + assert Process.exit(database_pid, :kill) + |> tap_debug("Forcefully killed database connection: ") - assert :ok == pg_restart() + assert :ok == pg_restart() - new_database_pid = get_database_pid(supervisor_pid) + new_database_pid = get_database_pid(supervisor_pid) - assert is_pid(new_database_pid) - refute database_pid == new_database_pid - assert_update_user(new_database_pid) + assert is_pid(new_database_pid) + refute database_pid == new_database_pid + assert_update_user(new_database_pid) + end) end test "durable replication slot", %{database_pid: database_pid} do - assert [] = pg_replication_slots(database_pid) + capture_log(fn -> + assert [] = pg_replication_slots(database_pid) - slot_name = "durable_slot" - durable_opts = Keyword.merge(@base_configs, durable_slot: true, slot_name: slot_name) + slot_name = "durable_slot" + durable_opts = Keyword.merge(@base_configs, durable_slot: true, slot_name: slot_name) - durable_slot = %{ - "active" => true, - "slot_name" => slot_name, - "slot_type" => "logical", - "temporary" => false - } + durable_slot = %{ + "active" => true, + "slot_name" => slot_name, + "slot_type" => "logical", + "temporary" => false + } - inactive_slot = %{durable_slot | "active" => false} + inactive_slot = %{durable_slot | "active" => false} - start_supervised!({WalExSupervisor, durable_opts}, - restart: :temporary, - id: :ok_supervisor - ) + start_supervised!({WalExSupervisor, durable_opts}, + restart: :temporary, + id: :ok_supervisor + ) - assert [^durable_slot] = pg_replication_slots(database_pid) + assert [^durable_slot] = pg_replication_slots(database_pid) - other_app_opts = Keyword.replace!(durable_opts, :name, :other_app) + other_app_opts = Keyword.replace!(durable_opts, :name, :other_app) - # Start another supervisor with the same slot name - {:ok, pid} = - start_supervised({WalExSupervisor, other_app_opts}, - restart: :temporary, - id: :other_app_supervisor - ) + # Start another supervisor with the same slot name + {:ok, pid} = + start_supervised({WalExSupervisor, other_app_opts}, + restart: :temporary, + id: :other_app_supervisor + ) - # Wait for the retry mechanism to complete - Process.sleep(10_000) + # Wait for the retry mechanism to complete + Process.sleep(10_000) - # Check that the other app is still running and waiting - assert Process.alive?(pid) + # Check that the other app is still running and waiting + assert Process.alive?(pid) - # The original slot should still be active - assert [^durable_slot] = pg_replication_slots(database_pid) + # The original slot should still be active + assert [^durable_slot] = pg_replication_slots(database_pid) - # Stop the first supervisor - stop_supervised(:ok_supervisor) + # Stop the first supervisor + stop_supervised(:ok_supervisor) - # Sleep to make sure that Postgres detects that the connection is closed - # and the second supervisor has time to activate the slot - Process.sleep(10_000) + # Sleep to make sure that Postgres detects that the connection is closed + # and the second supervisor has time to activate the slot + Process.sleep(10_000) - # The slot should now be active under the second supervisor - assert [^durable_slot] = pg_replication_slots(database_pid) + # The slot should now be active under the second supervisor + assert [^durable_slot] = pg_replication_slots(database_pid) - # Clean up - stop_supervised(:other_app_supervisor) + # Clean up + stop_supervised(:other_app_supervisor) - # Wait for the slot to become inactive after stopping all supervisors - Process.sleep(5_000) - [slot] = pg_replication_slots(database_pid) - assert slot == inactive_slot + # Wait for the slot to become inactive after stopping all supervisors + Process.sleep(5_000) + [slot] = pg_replication_slots(database_pid) + assert slot == inactive_slot + end) end end @@ -295,7 +313,7 @@ defmodule WalEx.DatabaseTest do Logger.debug("PostgreSQL installed via Postgres.app.") @mac_app_path - true -> + _ -> raise "PostgreSQL not installed via Postgres.app or homebrew." end end diff --git a/test/walex/event/event_dsl_test.exs b/test/walex/event/event_dsl_test.exs index 0856c23..c4ad3c5 100644 --- a/test/walex/event/event_dsl_test.exs +++ b/test/walex/event/event_dsl_test.exs @@ -1,16 +1,18 @@ defmodule WalEx.EventDslTest do use ExUnit.Case, async: false import WalEx.Support.TestHelpers + import ExUnit.CaptureLog alias WalEx.Events.EventModules, as: EventsEventModules alias WalEx.Events.Supervisor, as: EventsSupervisor alias WalEx.Supervisor, as: WalExSupervisor @app_name :test_app - @hostname "localhost" - @username "postgres" - @password "postgres" + @hostname System.get_env("PGHOST", "localhost") + @username System.get_env("PGUSER", "postgres") + @password System.get_env("PGPASSWORD", "postgres") @database "todos_test" + @port String.to_integer(System.get_env("PGPORT", "5432")) @dsl_base_configs [ name: @app_name, @@ -18,7 +20,7 @@ defmodule WalEx.EventDslTest do username: @username, password: @password, database: @database, - port: 5432, + port: @port, subscriptions: ["user", "todo"], publication: ["events"], modules: [TestApp.DslTestModule] @@ -26,8 +28,10 @@ defmodule WalEx.EventDslTest do describe "on_event/2" do setup do - {:ok, database_pid} = start_database(@dsl_base_configs) - {:ok, supervisor_pid} = WalExSupervisor.start_link(@dsl_base_configs) + {{:ok, database_pid}, _log} = with_log(fn -> start_database(@dsl_base_configs) end) + + {{:ok, supervisor_pid}, _log} = + with_log(fn -> WalExSupervisor.start_link(@dsl_base_configs) end) %{database_pid: database_pid, supervisor_pid: supervisor_pid} end @@ -59,8 +63,10 @@ defmodule WalEx.EventDslTest do describe "on_update/4" do setup do - {:ok, database_pid} = start_database(@dsl_base_configs) - {:ok, supervisor_pid} = WalExSupervisor.start_link(@dsl_base_configs) + {{:ok, database_pid}, _log} = with_log(fn -> start_database(@dsl_base_configs) end) + + {{:ok, supervisor_pid}, _log} = + with_log(fn -> WalExSupervisor.start_link(@dsl_base_configs) end) %{database_pid: database_pid, supervisor_pid: supervisor_pid} end diff --git a/test/walex/event/event_test.exs b/test/walex/event/event_test.exs index 7acb2ae..d1fecca 100644 --- a/test/walex/event/event_test.exs +++ b/test/walex/event/event_test.exs @@ -1,6 +1,7 @@ defmodule WalEx.EventTest do use ExUnit.Case, async: false import WalEx.Support.TestHelpers + import ExUnit.CaptureLog alias WalEx.Events.EventModules, as: EventsEventModules alias WalEx.Events.Supervisor, as: EventsSupervisor @@ -9,10 +10,11 @@ defmodule WalEx.EventTest do alias WalEx.Replication.Publisher, as: ReplicationPublisher @app_name :test_app - @hostname "localhost" - @username "postgres" - @password "postgres" + @hostname System.get_env("PGHOST", "localhost") + @username System.get_env("PGUSER", "postgres") + @password System.get_env("PGPASSWORD", "postgres") @database "todos_test" + @port String.to_integer(System.get_env("PGPORT", "5432")) @base_configs [ name: @app_name, @@ -20,7 +22,7 @@ defmodule WalEx.EventTest do username: @username, password: @password, database: @database, - port: 5432, + port: @port, subscriptions: ["user", "todo"], publication: ["events"], modules: [TestApp.TestModule] @@ -28,8 +30,10 @@ defmodule WalEx.EventTest do describe "process_all/1" do setup do - {:ok, database_pid} = start_database(@base_configs) - {:ok, supervisor_pid} = WalExSupervisor.start_link(@base_configs) + {{:ok, database_pid}, _log} = with_log(fn -> start_database(@base_configs) end) + + {{:ok, supervisor_pid}, _log} = + with_log(fn -> WalExSupervisor.start_link(@base_configs) end) %{database_pid: database_pid, supervisor_pid: supervisor_pid} end @@ -38,107 +42,111 @@ defmodule WalEx.EventTest do database_pid: database_pid, supervisor_pid: supervisor_pid } do - destinations_supervisor_pid = find_child_pid(supervisor_pid, EventsSupervisor) - - assert is_pid(destinations_supervisor_pid) - - events_pid = - find_child_pid(destinations_supervisor_pid, EventsEventModules) - - assert is_pid(events_pid) - - update_user(database_pid) - - # https://www.thegreatcodeadventure.com/testing-genservers-with-erlang-trace/ - :erlang.trace(events_pid, true, [:receive]) - - assert_receive { - :trace, - ^events_pid, - :receive, - {:"$gen_call", _pid_and_ref, - { - :process, - %WalEx.Changes.Transaction{ - changes: [ - %WalEx.Changes.UpdatedRecord{ - type: "UPDATE", - old_record: _old_record, - record: %{ - id: 1, - name: "John Doe", - age: 30, - created_at: _created_at, - email: "john.doe@example.com", - books: ["book1, 2 and 3", "book4"], - favorite_numbers: [1, 2, 3], - meta: %{ - "key" => %{"foo" => "bar"}, - "list" => [1, 2, 3] + capture_log(fn -> + destinations_supervisor_pid = find_child_pid(supervisor_pid, EventsSupervisor) + + assert is_pid(destinations_supervisor_pid) + + events_pid = + find_child_pid(destinations_supervisor_pid, EventsEventModules) + + assert is_pid(events_pid) + + update_user(database_pid) + + # https://www.thegreatcodeadventure.com/testing-genservers-with-erlang-trace/ + :erlang.trace(events_pid, true, [:receive]) + + assert_receive { + :trace, + ^events_pid, + :receive, + {:"$gen_call", _pid_and_ref, + { + :process, + %WalEx.Changes.Transaction{ + changes: [ + %WalEx.Changes.UpdatedRecord{ + type: "UPDATE", + old_record: _old_record, + record: %{ + id: 1, + name: "John Doe", + age: 30, + created_at: _created_at, + email: "john.doe@example.com", + books: ["book1, 2 and 3", "book4"], + favorite_numbers: [1, 2, 3], + meta: %{ + "key" => %{"foo" => "bar"}, + "list" => [1, 2, 3] + }, + updated_at: _updated_at }, - updated_at: _updated_at - }, - schema: "public", - table: "user", - columns: _columns, - commit_timestamp: _updated_record_commit_timestamp - } - ], - commit_timestamp: _transaction_commit_timestamp - }, - :test_app - }} - } + schema: "public", + table: "user", + columns: _columns, + commit_timestamp: _updated_record_commit_timestamp + } + ], + commit_timestamp: _transaction_commit_timestamp + }, + :test_app + }} + } + end) end test "should restart the Publisher & Events processes when error", %{ database_pid: database_pid, supervisor_pid: supervisor_pid } do - destinations_supervisor_pid = find_child_pid(supervisor_pid, EventsSupervisor) + capture_log(fn -> + destinations_supervisor_pid = find_child_pid(supervisor_pid, EventsSupervisor) - assert is_pid(destinations_supervisor_pid) + assert is_pid(destinations_supervisor_pid) - events_pid = find_child_pid(destinations_supervisor_pid, EventsEventModules) + events_pid = find_child_pid(destinations_supervisor_pid, EventsEventModules) - assert is_pid(events_pid) + assert is_pid(events_pid) - replication_supervisor_pid = find_child_pid(supervisor_pid, ReplicationSupervisor) + replication_supervisor_pid = find_child_pid(supervisor_pid, ReplicationSupervisor) - assert is_pid(replication_supervisor_pid) + assert is_pid(replication_supervisor_pid) - replication_publisher_pid = - find_child_pid(replication_supervisor_pid, ReplicationPublisher) + replication_publisher_pid = + find_child_pid(replication_supervisor_pid, ReplicationPublisher) - assert is_pid(replication_publisher_pid) + assert is_pid(replication_publisher_pid) - update_user(database_pid) + update_user(database_pid) - # https://smartlogic.io/blog/test-process-monitoring/ - _process_ref = Process.monitor(events_pid) + # https://smartlogic.io/blog/test-process-monitoring/ + _process_ref = Process.monitor(events_pid) - assert_receive { - :DOWN, - _process_ref, - :process, - ^events_pid, - {%RuntimeError{message: "Process error"}, _stacktrace} - } + assert_receive { + :DOWN, + _process_ref, + :process, + ^events_pid, + {%RuntimeError{message: "Process error"}, _stacktrace} + } - # Wait for supervisor to restart Events GenServer and Publisher - :timer.sleep(1000) + # Wait for supervisor to restart Events GenServer and Publisher + :timer.sleep(1000) - new_events_pid = - find_child_pid(destinations_supervisor_pid, EventsEventModules) + new_events_pid = + find_child_pid(destinations_supervisor_pid, EventsEventModules) - assert is_pid(new_events_pid) - refute events_pid == new_events_pid + assert is_pid(new_events_pid) + refute events_pid == new_events_pid - new_replication_publisher_pid = - find_child_pid(replication_supervisor_pid, ReplicationPublisher) + new_replication_publisher_pid = + find_child_pid(replication_supervisor_pid, ReplicationPublisher) - assert is_pid(new_replication_publisher_pid) - refute replication_publisher_pid == new_replication_publisher_pid + assert is_pid(new_replication_publisher_pid) + refute replication_publisher_pid == new_replication_publisher_pid + end) end end end diff --git a/test/walex/supervisor_test.exs b/test/walex/supervisor_test.exs index 762c425..f04f4a5 100644 --- a/test/walex/supervisor_test.exs +++ b/test/walex/supervisor_test.exs @@ -1,6 +1,7 @@ defmodule WalEx.SupervisorTest do use ExUnit.Case, async: false import WalEx.Support.TestHelpers + import ExUnit.CaptureLog alias WalEx.Supervisor, as: WalExSupervisor alias WalEx.Replication @@ -18,26 +19,28 @@ defmodule WalEx.SupervisorTest do describe "start_link/2" do test "should start Supervisor and child processes" do - assert {:ok, walex_supervisor_pid} = WalExSupervisor.start_link(@base_configs) - assert is_pid(walex_supervisor_pid) + capture_log(fn -> + assert {:ok, walex_supervisor_pid} = WalExSupervisor.start_link(@base_configs) + assert is_pid(walex_supervisor_pid) - assert %{active: 3, specs: 3, supervisors: 2, workers: 1} = - Supervisor.count_children(walex_supervisor_pid) + assert %{active: 3, specs: 3, supervisors: 2, workers: 1} = + Supervisor.count_children(walex_supervisor_pid) - replication_supervisor_pid = - find_child_pid(walex_supervisor_pid, Replication.Supervisor) + replication_supervisor_pid = + find_child_pid(walex_supervisor_pid, Replication.Supervisor) - assert is_pid(replication_supervisor_pid) + assert is_pid(replication_supervisor_pid) - replication_publisher_pid = - find_child_pid(replication_supervisor_pid, Replication.Publisher) + replication_publisher_pid = + find_child_pid(replication_supervisor_pid, Replication.Publisher) - assert is_pid(replication_publisher_pid) + assert is_pid(replication_publisher_pid) - replication_server_pid = - find_child_pid(replication_supervisor_pid, Replication.Server) + replication_server_pid = + find_child_pid(replication_supervisor_pid, Replication.Server) - assert is_pid(replication_server_pid) + assert is_pid(replication_server_pid) + end) end test "should raise if any required config is missing" do @@ -47,22 +50,26 @@ defmodule WalEx.SupervisorTest do end test "should start multiple supervision trees" do - configs_1 = @base_configs - configs_2 = Keyword.put(configs_1, :name, :other_name) - configs_3 = Keyword.put(configs_1, :name, :another_name) + capture_log(fn -> + configs_1 = @base_configs + configs_2 = Keyword.put(configs_1, :name, :other_name) + configs_3 = Keyword.put(configs_1, :name, :another_name) - assert {:ok, pid_1} = WalExSupervisor.start_link(configs_1) - assert {:ok, pid_2} = WalExSupervisor.start_link(configs_2) - assert {:ok, pid_3} = WalExSupervisor.start_link(configs_3) + assert {:ok, pid_1} = WalExSupervisor.start_link(configs_1) + assert {:ok, pid_2} = WalExSupervisor.start_link(configs_2) + assert {:ok, pid_3} = WalExSupervisor.start_link(configs_3) - assert is_pid(pid_1) - assert is_pid(pid_2) - assert is_pid(pid_3) + assert is_pid(pid_1) + assert is_pid(pid_2) + assert is_pid(pid_3) + end) end test "should start WalEx.Registry" do - assert {:ok, _pid} = WalExSupervisor.start_link(@base_configs) - assert is_pid(Process.whereis(:walex_registry)) + capture_log(fn -> + assert {:ok, _pid} = WalExSupervisor.start_link(@base_configs) + assert is_pid(Process.whereis(:walex_registry)) + end) end end end