diff --git a/README.md b/README.md index ba5d907..4bea839 100644 --- a/README.md +++ b/README.md @@ -67,6 +67,34 @@ Edit `lib/bodhi/open_router.ex` and modify the `@default_model` attribute: See all available models at: https://openrouter.ai/models +## Database Configuration + +By default, `dev` and `test` connect to the Postgres instance defined in +`config/dev.exs` / `config/test.exs`. To point either environment at a +different database, set `DATABASE_URL` before starting the app or running +tests — it overrides the hardcoded defaults: + +```bash +export DATABASE_URL="ecto://USER:PASS@HOST/DATABASE" +``` + +Leave `DATABASE_URL` unset to keep using the defaults. In `prod`, +`DATABASE_URL` is required, as before. + +> **Caution:** the root `.env` file (used by `compose.yml`) also defines a +> `DATABASE_URL`, pointing at the Docker-internal `postgres` hostname. That +> value is only reachable from inside the compose network. If you export +> `.env` into your shell (e.g. via `direnv` or `source .env`), a host-run +> `mix phx.server` / `mix test` will pick it up and try to connect to the +> unreachable `postgres` host instead of the exposed `localhost:5433` +> instance. Unset `DATABASE_URL` (or override it explicitly) before running +> Mix commands on the host. +> +> For `test`, setting `DATABASE_URL` replaces the whole `database` value, +> including the `MIX_TEST_PARTITION` suffix used for parallel CI test runs +> — don't combine a custom `DATABASE_URL` with partitioned test runs unless +> you account for that yourself. + ## Features ### Daily Dialog Summarization diff --git a/config/runtime.exs b/config/runtime.exs index 1f320e9..cd25887 100644 --- a/config/runtime.exs +++ b/config/runtime.exs @@ -31,6 +31,12 @@ config :telegex, config :posthog, api_key: "#{System.get_env("POSTHOG_KEY")}" +if config_env() in [:dev, :test] do + if database_url = System.get_env("DATABASE_URL") do + config :bodhi, Bodhi.Repo, url: database_url + end +end + if config_env() == :prod do database_url = System.get_env("DATABASE_URL") || diff --git a/test/config/runtime_test.exs b/test/config/runtime_test.exs new file mode 100644 index 0000000..0b83b04 --- /dev/null +++ b/test/config/runtime_test.exs @@ -0,0 +1,78 @@ +defmodule Config.RuntimeTest do + use ExUnit.Case, async: false + + @runtime_config_path "config/runtime.exs" + + setup do + original_database_url = System.get_env("DATABASE_URL") + + on_exit(fn -> + case original_database_url do + nil -> System.delete_env("DATABASE_URL") + value -> System.put_env("DATABASE_URL", value) + end + end) + + :ok + end + + for env <- [:dev, :test] do + test "preserves the default Bodhi.Repo config for #{env} " <> + "when DATABASE_URL is unset" do + System.delete_env("DATABASE_URL") + + config = read_runtime_config(unquote(env)) + + assert config + |> Keyword.get(:bodhi, []) + |> Keyword.get(Bodhi.Repo) == nil + end + + test "overrides the Bodhi.Repo url for #{env} " <> + "when DATABASE_URL is set" do + database_url = "ecto://user:pass@host/#{unquote(env)}_db" + System.put_env("DATABASE_URL", database_url) + + config = read_runtime_config(unquote(env)) + + assert config + |> get_in([:bodhi, Bodhi.Repo, :url]) == database_url + end + + test "keeps the compile-time Bodhi.Repo pool settings for #{env} " <> + "once merged with the DATABASE_URL override" do + database_url = "ecto://user:pass@host/#{unquote(env)}_db" + System.put_env("DATABASE_URL", database_url) + + compile_time_config = read_compile_time_config(unquote(env)) + + merged_repo_config = + compile_time_config + |> Config.Reader.merge(read_runtime_config(unquote(env))) + |> get_in([:bodhi, Bodhi.Repo]) + + compile_time_repo_config = + get_in(compile_time_config, [:bodhi, Bodhi.Repo]) + + assert merged_repo_config[:url] == database_url + + assert merged_repo_config[:pool_size] == + compile_time_repo_config[:pool_size] + + assert merged_repo_config[:pool] == compile_time_repo_config[:pool] + end + end + + defp read_runtime_config(env) do + Config.Reader.read!(@runtime_config_path, env: env, target: :host) + end + + defp read_compile_time_config(env) do + config = Config.Reader.read!("config/config.exs", env: env, target: :host) + + env_config = + Config.Reader.read!("config/#{env}.exs", env: env, target: :host) + + Config.Reader.merge(config, env_config) + end +end