Current section

72 Versions

Jump to

Compare versions

6 files changed
+106 additions
-29 deletions
  @@ -63,5 +63,10 @@
63 63 {<<"name">>,<<"ecto">>},
64 64 {<<"optional">>,true},
65 65 {<<"repository">>,<<"hexpm">>},
66 - {<<"requirement">>,<<"~> 3.3">>}]]}.
67 - {<<"version">>,<<"1.10.3">>}.
66 + {<<"requirement">>,<<"~> 3.3">>}],
67 + [{<<"app">>,<<"fs">>},
68 + {<<"name">>,<<"fs">>},
69 + {<<"optional">>,false},
70 + {<<"repository">>,<<"hexpm">>},
71 + {<<"requirement">>,<<"~> 8.6">>}]]}.
72 + {<<"version">>,<<"1.10.4">>}.
  @@ -7,23 +7,22 @@ defmodule Testcontainers.ContainerBuilderHelper do
7 7 def build(builder, state) when is_map(state) and is_struct(builder) do
8 8 config =
9 9 ContainerBuilder.build(builder)
10 - |> Container.with_label(container_version_label(), library_version())
11 10 |> Container.with_label(container_lang_label(), container_lang_value())
12 11 |> Container.with_label(container_label(), "#{true}")
13 12
14 - reuse = config.reuse && true == Map.get(state.properties, "testcontainers.reuse.enable", false)
15 -
16 - if reuse do
13 + if config.reuse && "true" == Map.get(state.properties, "testcontainers.reuse.enable", "false") do
17 14 hash = Hash.struct_to_hash(config)
18 15 config
19 16 |> Container.with_label(container_reuse(), "true")
20 17 |> Container.with_label(container_reuse_hash_label(), hash)
21 18 |> Container.with_label(container_sessionId_label(), state.session_id)
19 + |> Container.with_label(container_version_label(), library_version())
22 20 |> Kernel.then(&{:reuse, &1, hash})
23 21 else
24 22 config
25 23 |> Container.with_label(container_reuse(), "false")
26 24 |> Container.with_label(container_sessionId_label(), state.session_id)
25 + |> Container.with_label(container_version_label(), library_version())
27 26 |> Kernel.then(&{:noreuse, &1, nil})
28 27 end
29 28 end
  @@ -5,17 +5,61 @@ defmodule Mix.Tasks.Testcontainers.Test do
5 5
6 6 @shortdoc "Runs tests with a Postgres container"
7 7 def run(args) do
8 - Application.ensure_all_started(:tesla)
9 - Application.ensure_all_started(:hackney)
8 + Enum.each([:tesla, :hackney, :fs, :logger], fn app ->
9 + {:ok, _} = Application.ensure_all_started(app)
10 + end)
11 +
10 12 {:ok, _} = Testcontainers.start_link()
11 13
12 - {opts, _, _} = OptionParser.parse(args, switches: [
13 - database: :string
14 - ])
14 + {opts, _, _} = OptionParser.parse(args,
15 + switches: [
16 + database: :string,
17 + watch: [:string, :keep]
18 + ]
19 + )
15 20
16 21 database = opts[:database] || "postgres"
22 + folder_to_watch = Keyword.get_values(opts, :watch)
17 23
18 - {container, port} = case database do
24 + if Enum.empty?(folder_to_watch) do
25 + IO.puts("No folders specified. Only running tests.")
26 + run_tests_and_exit(database)
27 + else
28 + check_folders_exist(folder_to_watch)
29 + run_tests_and_watch(database, folder_to_watch)
30 + end
31 + end
32 +
33 + defp check_folders_exist(folders) do
34 + Enum.each(folders, fn folder ->
35 + unless File.dir?(folder) do
36 + Mix.raise("Folder does not exist: #{folder}")
37 + end
38 + end)
39 + end
40 +
41 + defp run_tests_and_exit(database) do
42 + {container, env} = setup_container(database)
43 + run_tests(env)
44 + Testcontainers.stop_container(container.container_id)
45 + end
46 +
47 + defp run_tests_and_watch(database, folders) do
48 + {container, env} = setup_container(database)
49 +
50 + Enum.each(folders, fn folder ->
51 + :fs.start_link(String.to_atom("watcher_" <> folder), Path.absname(folder))
52 + :fs.subscribe(String.to_atom("watcher_" <> folder))
53 + end)
54 +
55 + Process.flag(:trap_exit, true)
56 +
57 + run_tests(env)
58 + loop(env, container)
59 + end
60 +
61 + defp setup_container(database) do
62 + case database do
19 63 "postgres" ->
20 64 {:ok, container} = Testcontainers.start_container(
21 65 PostgresContainer.new()
  @@ -24,7 +68,7 @@ defmodule Mix.Tasks.Testcontainers.Test do
24 68 |> PostgresContainer.with_reuse(true)
25 69 )
26 70 port = PostgresContainer.port(container)
27 - {container, port}
71 + {container, create_env(port)}
28 72 "mysql" ->
29 73 {:ok, container} = Testcontainers.start_container(
30 74 MySqlContainer.new()
  @@ -33,26 +77,54 @@ defmodule Mix.Tasks.Testcontainers.Test do
33 77 |> MySqlContainer.with_reuse(true)
34 78 )
35 79 port = MySqlContainer.port(container)
36 - {container, port}
80 + {container, create_env(port)}
37 81 _ -> Mix.raise("Unsupported database: #{database}")
38 82 end
83 + end
39 84
40 - env = [
85 + defp create_env(port) do
86 + [
41 87 {"DB_USER", "test"},
42 88 {"DB_PASSWORD", "test"},
43 89 {"DB_HOST", Testcontainers.get_host()},
44 - {"DB_PORT", port |> Integer.to_string()}
90 + {"DB_PORT", Integer.to_string(port)}
45 91 ]
92 + end
46 93
47 - try do
48 - {output, exit_code} = System.cmd("mix", ["test"], env: env)
49 - if exit_code != 0 do
50 - IO.puts(output)
51 - raise "\u274c Tests failed"
94 + defp run_tests(env) do
95 + test_pid = spawn(fn ->
96 + System.cmd("mix", ["test"], env: env, into: IO.stream(:stdio, :line))
97 + end)
98 +
99 + Process.monitor(test_pid)
100 +
101 + receive do
102 + {:DOWN, _ref, :process, ^test_pid, _reason} ->
103 + IO.puts("Test process completed.")
52 104 end
53 - IO.puts("\u2705 Tests passed")
54 - after
55 - Testcontainers.stop_container(container.container_id)
105 + end
106 +
107 + defp loop(env, container) do
108 + receive do
109 + {_watcher_process, {:fs, :file_event}, {changed_file, _type}} ->
110 + IO.puts("#{changed_file} was updated, waiting for more changes...")
111 + wait_for_changes(env, container)
112 +
113 + after 5000 ->
114 + loop(env, container)
115 + end
116 + end
117 +
118 + defp wait_for_changes(env, container) do
119 + receive do
120 + {_watcher_process, {:fs, :file_event}, {changed_file, _type}} ->
121 + IO.puts("#{changed_file} was updated, waiting for more changes...")
122 + wait_for_changes(env, container)
123 +
124 + after 1000 ->
125 + IO.ANSI.clear()
126 + run_tests(env)
127 + loop(env, container)
56 128 end
57 129 end
58 130 end
  @@ -13,7 +13,6 @@ defmodule Testcontainers do
13 13 alias Testcontainers.Connection
14 14 alias Testcontainers.Container
15 15 alias Testcontainers.ContainerBuilder
16 - alias Testcontainers.Util.Hash
17 16 alias Testcontainers.Util.PropertiesParser
18 17
19 18 import Testcontainers.Constants
  @@ -2,7 +2,7 @@ defmodule Testcontainers.Constants do
2 2 @moduledoc false
3 3
4 4 def library_name, do: :testcontainers
5 - def library_version, do: "1.10.3"
5 + def library_version, do: "1.10.4"
6 6 def container_label, do: "org.testcontainers"
7 7 def container_lang_label, do: "org.testcontainers.lang"
8 8 def container_reuse_hash_label, do: "org.testcontainers.reuse-hash"
Loading more files…