analytics/test/plausible/config_test.exs

750 lines
25 KiB
Elixir

defmodule Plausible.ConfigTest do
use ExUnit.Case
import Plausible.ConfigHelpers
describe "get_bool_from_path_or_env/3" do
test "parses truthy vars" do
truthy = ["1", "t", "true", "y", "yes", "on"]
for var <- truthy do
env = [{"ENABLE_EMAIL_VERIFICATION", var}]
config = runtime_config(env)
assert get_in(config, [:plausible, :selfhost, :enable_email_verification]) == true
end
end
test "parses false vars" do
falsy = ["0", "f", "false", "n", "no", "off"]
for var <- falsy do
env = [{"ENABLE_EMAIL_VERIFICATION", var}]
config = runtime_config(env)
assert get_in(config, [:plausible, :selfhost, :enable_email_verification]) == false
end
end
test "supports defaults" do
put_system_env_undo([{"ENABLE_EMAIL_VERIFICATION", nil}])
config_dir = "/run/secrets"
assert get_bool_from_path_or_env(config_dir, "ENABLE_EMAIL_VERIFICATION") == nil
assert get_bool_from_path_or_env(config_dir, "ENABLE_EMAIL_VERIFICATION", true) == true
assert get_bool_from_path_or_env(config_dir, "ENABLE_EMAIL_VERIFICATION", false) == false
end
test "raises on invalid var" do
env = [{"ENABLE_EMAIL_VERIFICATION", "YOLO"}]
assert_raise ArgumentError,
"Invalid boolean value: \"YOLO\". Expected one of: 1, 0, t, f, true, false, y, n, yes, no, on, off",
fn -> runtime_config(env) end
end
end
describe "mailer" do
test "mailer email default" do
env = [{"MAILER_EMAIL", nil}]
assert get_in(runtime_config(env), [:plausible, :mailer_email]) == "plausible@localhost"
end
test "mailer email from base url" do
env = [{"MAILER_EMAIL", nil}, {"BASE_URL", "https://plausible.example.com:8443"}]
assert get_in(runtime_config(env), [:plausible, :mailer_email]) ==
"plausible@plausible.example.com"
end
test "mailer email custom" do
env = [{"MAILER_EMAIL", "custom@mailer.email"}]
assert get_in(runtime_config(env), [:plausible, :mailer_email]) == "custom@mailer.email"
end
test "mailer name" do
env = [{"MAILER_EMAIL", nil}, {"MAILER_NAME", "John"}]
assert get_in(runtime_config(env), [:plausible, :mailer_email]) ==
{"John", "plausible@localhost"}
env = [{"MAILER_EMAIL", "custom@mailer.email"}, {"MAILER_NAME", "John"}]
assert get_in(runtime_config(env), [:plausible, :mailer_email]) ==
{"John", "custom@mailer.email"}
end
test "defaults to Bamboo.Mua" do
env = {"MAILER_ADAPTER", nil}
assert get_in(runtime_config(env), [:plausible, Plausible.Mailer]) == [
adapter: Bamboo.Mua,
ssl: [middlebox_comp_mode: false]
]
end
test "Bamboo.PostmarkAdapter" do
env = [
{"MAILER_ADAPTER", "Bamboo.PostmarkAdapter"},
{"POSTMARK_API_KEY", "some-postmark-key"}
]
assert get_in(runtime_config(env), [:plausible, Plausible.Mailer]) == [
adapter: Bamboo.PostmarkAdapter,
request_options: [recv_timeout: 10_000],
api_key: "some-postmark-key"
]
end
test "Bamboo.MailgunAdapter" do
env = [
{"MAILER_ADAPTER", "Bamboo.MailgunAdapter"},
{"MAILGUN_API_KEY", "some-mailgun-key"},
{"MAILGUN_DOMAIN", "example.com"}
]
assert get_in(runtime_config(env), [:plausible, Plausible.Mailer]) == [
adapter: Bamboo.MailgunAdapter,
hackney_opts: [recv_timeout: 10_000],
api_key: "some-mailgun-key",
domain: "example.com"
]
end
test "Bamboo.MailgunAdapter with custom MAILGUN_BASE_URI" do
env = [
{"MAILER_ADAPTER", "Bamboo.MailgunAdapter"},
{"MAILGUN_API_KEY", "some-mailgun-key"},
{"MAILGUN_DOMAIN", "example.com"},
{"MAILGUN_BASE_URI", "https://api.eu.mailgun.net/v3"}
]
assert get_in(runtime_config(env), [:plausible, Plausible.Mailer]) == [
adapter: Bamboo.MailgunAdapter,
hackney_opts: [recv_timeout: 10_000],
api_key: "some-mailgun-key",
domain: "example.com",
base_uri: "https://api.eu.mailgun.net/v3"
]
end
test "Bamboo.MandrillAdapter" do
env = [
{"MAILER_ADAPTER", "Bamboo.MandrillAdapter"},
{"MANDRILL_API_KEY", "some-mandrill-key"}
]
assert get_in(runtime_config(env), [:plausible, Plausible.Mailer]) == [
adapter: Bamboo.MandrillAdapter,
hackney_opts: [recv_timeout: 10_000],
api_key: "some-mandrill-key"
]
end
test "Bamboo.SendGridAdapter" do
env = [
{"MAILER_ADAPTER", "Bamboo.SendGridAdapter"},
{"SENDGRID_API_KEY", "some-sendgrid-key"}
]
assert get_in(runtime_config(env), [:plausible, Plausible.Mailer]) == [
adapter: Bamboo.SendGridAdapter,
hackney_opts: [recv_timeout: 10_000],
api_key: "some-sendgrid-key"
]
end
test "Bamboo.SMTPAdapter" do
env = [
{"MAILER_ADAPTER", "Bamboo.SMTPAdapter"},
{"SMTP_HOST_ADDR", "localhost"},
{"SMTP_HOST_PORT", "2525"},
{"SMTP_USER_NAME", "neo"},
{"SMTP_USER_PWD", "one"},
{"SMTP_HOST_SSL_ENABLED", "true"},
{"SMTP_RETRIES", "3"},
{"SMTP_MX_LOOKUPS_ENABLED", "true"}
]
assert get_in(runtime_config(env), [:plausible, Plausible.Mailer]) == [
{:adapter, Bamboo.SMTPAdapter},
{:server, "localhost"},
{:hostname, "localhost"},
{:port, "2525"},
{:username, "neo"},
{:password, "one"},
{:tls, :if_available},
{:allowed_tls_versions, [:tlsv1, :"tlsv1.1", :"tlsv1.2"]},
{:ssl, true},
{:retries, "3"},
{:no_mx_lookups, true}
]
end
test "Bamboo.Mua (no config)" do
env = [{"MAILER_ADAPTER", "Bamboo.Mua"}]
assert get_in(runtime_config(env), [:plausible, Plausible.Mailer]) == [
{:adapter, Bamboo.Mua},
{:ssl, [middlebox_comp_mode: false]}
]
end
test "Bamboo.Mua (middlebox_comp_mode enabled)" do
env = [{"MAILER_ADAPTER", "Bamboo.Mua"}, {"SMTP_MIDDLEBOX_COMP_MODE", "true"}]
assert get_in(runtime_config(env), [:plausible, Plausible.Mailer]) == [
{:adapter, Bamboo.Mua},
{:ssl, [middlebox_comp_mode: true]}
]
end
test "Bamboo.Mua (relay config)" do
env = [
{"MAILER_ADAPTER", "Bamboo.Mua"},
{"SMTP_HOST_ADDR", "localhost"},
{"SMTP_HOST_PORT", "2525"},
{"SMTP_USER_NAME", "neo"},
{"SMTP_USER_PWD", "one"}
]
assert get_in(runtime_config(env), [:plausible, Plausible.Mailer]) == [
{:adapter, Bamboo.Mua},
{:ssl, [middlebox_comp_mode: false]},
{:protocol, :tcp},
{:relay, "localhost"},
{:port, 2525},
{:auth, [username: "neo", password: "one"]}
]
end
test "Bamboo.Mua (ssl relay config)" do
env = [
{"MAILER_ADAPTER", "Bamboo.Mua"},
{"SMTP_HOST_ADDR", "localhost"},
{"SMTP_HOST_PORT", "2525"},
{"SMTP_HOST_SSL_ENABLED", "true"},
{"SMTP_USER_NAME", "neo"},
{"SMTP_USER_PWD", "one"}
]
assert get_in(runtime_config(env), [:plausible, Plausible.Mailer]) == [
{:adapter, Bamboo.Mua},
{:ssl, [middlebox_comp_mode: false]},
{:protocol, :ssl},
{:relay, "localhost"},
{:port, 2525},
{:auth, [username: "neo", password: "one"]}
]
end
test "Bamboo.Mua (port=465 relay config)" do
env = [
{"MAILER_ADAPTER", "Bamboo.Mua"},
{"SMTP_HOST_ADDR", "localhost"},
{"SMTP_HOST_PORT", "465"},
{"SMTP_USER_NAME", "neo"},
{"SMTP_USER_PWD", "one"}
]
assert get_in(runtime_config(env), [:plausible, Plausible.Mailer]) == [
{:adapter, Bamboo.Mua},
{:ssl, [middlebox_comp_mode: false]},
{:protocol, :ssl},
{:relay, "localhost"},
{:port, 465},
{:auth, [username: "neo", password: "one"]}
]
end
test "Bamboo.Mua (no auth relay config)" do
env = [
{"MAILER_ADAPTER", "Bamboo.Mua"},
{"SMTP_HOST_ADDR", "localhost"},
{"SMTP_HOST_PORT", "2525"},
{"SMTP_USER_NAME", nil},
{"SMTP_USER_PWD", nil}
]
assert get_in(runtime_config(env), [:plausible, Plausible.Mailer]) == [
adapter: Bamboo.Mua,
ssl: [middlebox_comp_mode: false],
protocol: :tcp,
relay: "localhost",
port: 2525
]
end
test "unknown adapter raises" do
env = {"MAILER_ADAPTER", "Bamboo.FakeAdapter"}
assert_raise ArgumentError,
~r/Unknown mailer_adapter: "Bamboo.FakeAdapter"/,
fn -> runtime_config(env) end
end
end
describe "log_failed_login_attempts" do
test "can be true" do
env = {"LOG_FAILED_LOGIN_ATTEMPTS", "true"}
assert get_in(runtime_config(env), [:plausible, :log_failed_login_attempts]) == true
end
test "can be false" do
env = {"LOG_FAILED_LOGIN_ATTEMPTS", "false"}
assert get_in(runtime_config(env), [:plausible, :log_failed_login_attempts]) == false
end
test "is false by default" do
env = {"LOG_FAILED_LOGIN_ATTEMPTS", nil}
assert get_in(runtime_config(env), [:plausible, :log_failed_login_attempts]) == false
end
end
describe "s3" do
test "has required env vars" do
env = [
{"S3_ACCESS_KEY_ID", nil},
{"S3_SECRET_ACCESS_KEY", nil},
{"S3_REGION", nil},
{"S3_ENDPOINT", nil},
{"S3_EXPORTS_BUCKET", nil},
{"S3_IMPORTS_BUCKET", nil}
]
result =
try do
runtime_config(env)
rescue
e -> e
end
assert %ArgumentError{} = result
assert Exception.message(result) == """
Missing S3 configuration. Please set S3_ACCESS_KEY_ID, S3_SECRET_ACCESS_KEY, S3_REGION, S3_ENDPOINT, S3_EXPORTS_BUCKET, S3_IMPORTS_BUCKET environment variable(s):
\tS3_ACCESS_KEY_ID=AKIAIOSFODNN7EXAMPLE
\tS3_SECRET_ACCESS_KEY=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
\tS3_REGION=us-east-1
\tS3_ENDPOINT=https://<ACCOUNT_ID>.r2.cloudflarestorage.com
\tS3_EXPORTS_BUCKET=my-csv-exports-bucket
\tS3_IMPORTS_BUCKET=my-csv-imports-bucket
"""
end
test "renders only missing env vars" do
env = [
{"S3_ACCESS_KEY_ID", "AKIAIOSFODNN7EXAMPLE"},
{"S3_SECRET_ACCESS_KEY", nil},
{"S3_REGION", "eu-north-1"},
{"S3_ENDPOINT", nil},
{"S3_EXPORTS_BUCKET", "my-exports"},
{"S3_IMPORTS_BUCKET", nil}
]
result =
try do
runtime_config(env)
rescue
e -> e
end
assert %ArgumentError{} = result
assert Exception.message(result) == """
Missing S3 configuration. Please set S3_SECRET_ACCESS_KEY, S3_ENDPOINT, S3_IMPORTS_BUCKET environment variable(s):
\tS3_SECRET_ACCESS_KEY=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
\tS3_ENDPOINT=https://<ACCOUNT_ID>.r2.cloudflarestorage.com
\tS3_IMPORTS_BUCKET=my-csv-imports-bucket
"""
end
test "works when everything is set" do
env = [
{"S3_ACCESS_KEY_ID", "minioadmin"},
{"S3_SECRET_ACCESS_KEY", "minioadmin"},
{"S3_REGION", "us-east-1"},
{"S3_ENDPOINT", "http://localhost:6000"},
{"S3_EXPORTS_BUCKET", "my-exports"},
{"S3_IMPORTS_BUCKET", "my-imports"}
]
config = runtime_config(env)
assert config[:ex_aws] == [
http_client: Plausible.S3.Client,
access_key_id: "minioadmin",
secret_access_key: "minioadmin",
region: "us-east-1",
s3: [scheme: "http://", host: "localhost", port: 6000]
]
assert get_in(config, [:plausible, Plausible.S3]) == [
exports_bucket: "my-exports",
imports_bucket: "my-imports"
]
end
end
describe "storage" do
setup do
defaults = [
# comes from our Dockerfile
{"DEFAULT_DATA_DIR", "/var/lib/plausible"},
# needed to exercise Plausible.Geo :cache_dir
{"MAXMIND_LICENSE_KEY", "abc"}
]
env = fn env -> env ++ defaults end
{:ok, env: env}
end
test "defaults", %{env: env} do
config = runtime_config(env.([{"PERSISTENT_CACHE_DIR", nil}, {"DATA_DIR", nil}]))
# exports/imports
assert get_in(config, [:plausible, :data_dir]) == "/var/lib/plausible"
# locus (mmdb cache)
assert get_in(config, [:plausible, Plausible.Geo, :cache_dir]) == "/var/lib/plausible"
# tzdata (timezones cache)
assert get_in(config, [:tzdata, :data_dir]) == "/var/lib/plausible/tzdata_data"
# session transfer
assert get_in(config, [:plausible, :session_transfer_dir]) == "/var/lib/plausible/sessions"
end
test "with only DATA_DIR set", %{env: env} do
config = runtime_config(env.([{"PERSISTENT_CACHE_DIR", nil}, {"DATA_DIR", "/data"}]))
# exports/imports
assert get_in(config, [:plausible, :data_dir]) == "/data"
# locus (mmdb cache)
assert get_in(config, [:plausible, Plausible.Geo, :cache_dir]) == "/data"
# tzdata (timezones cache)
assert get_in(config, [:tzdata, :data_dir]) == "/data/tzdata_data"
# session transfer
assert get_in(config, [:plausible, :session_transfer_dir]) == "/data/sessions"
end
test "with only PERSISTENT_CACHE_DIR set", %{env: env} do
config = runtime_config(env.([{"PERSISTENT_CACHE_DIR", "/cache"}, {"DATA_DIR", nil}]))
# exports/imports
assert get_in(config, [:plausible, :data_dir]) == "/cache"
# locus (mmdb cache)
assert get_in(config, [:plausible, Plausible.Geo, :cache_dir]) == "/cache"
# tzdata (timezones cache)
assert get_in(config, [:tzdata, :data_dir]) == "/cache/tzdata_data"
# session transfer
assert get_in(config, [:plausible, :session_transfer_dir]) == "/cache/sessions"
end
test "with both DATA_DIR and PERSISTENT_CACHE_DIR set", %{env: env} do
config = runtime_config(env.([{"PERSISTENT_CACHE_DIR", "/cache"}, {"DATA_DIR", "/data"}]))
# exports/imports
assert get_in(config, [:plausible, :data_dir]) == "/data"
# locus (mmdb cache)
assert get_in(config, [:plausible, Plausible.Geo, :cache_dir]) == "/cache"
# tzdata (timezones cache)
assert get_in(config, [:tzdata, :data_dir]) == "/cache/tzdata_data"
# session transfer
assert get_in(config, [:plausible, :session_transfer_dir]) == "/cache/sessions"
end
end
describe "postgres" do
test "default" do
env = [{"DATABASE_URL", nil}]
config = runtime_config(env)
assert get_in(config, [:plausible, Plausible.Repo]) == [
url: "postgres://postgres:postgres@plausible_db:5432/plausible_db",
ssl: false
]
end
test "socket_dir in hostname" do
env = [{"DATABASE_URL", "postgresql://postgres:postgres@%2Frun%2Fpostgresql/plausible_db"}]
config = runtime_config(env)
assert get_in(config, [:plausible, Plausible.Repo]) == [
socket_dir: "/run/postgresql",
database: "plausible_db",
username: "postgres",
password: "postgres"
]
end
test "socket_dir in query" do
env = [{"DATABASE_URL", "postgresql:///plausible_db?host=/run/postgresql"}]
config = runtime_config(env)
assert get_in(config, [:plausible, Plausible.Repo]) == [
socket_dir: "/run/postgresql",
database: "plausible_db"
]
end
test "socket_dir missing" do
env = [{"DATABASE_URL", "postgresql:///plausible_db"}]
assert_raise ArgumentError, ~r/doesn't include host info/, fn -> runtime_config(env) end
end
test "custom URL" do
env = [
{"DATABASE_URL",
"postgresql://your_username:your_password@cluster-do-user-1234567-0.db.ondigitalocean.com:25060/defaultdb"}
]
config = runtime_config(env)
assert get_in(config, [:plausible, Plausible.Repo]) == [
url:
"postgresql://your_username:your_password@cluster-do-user-1234567-0.db.ondigitalocean.com:25060/defaultdb",
ssl: false
]
end
test "DATABASE_CACERTFILE enables SSL" do
env = [
{"DATABASE_URL",
"postgresql://your_username:your_password@cluster-do-user-1234567-0.db.ondigitalocean.com:25060/defaultdb"},
{"DATABASE_CACERTFILE", "/path/to/cacert.pem"}
]
config = runtime_config(env)
assert get_in(config, [:plausible, Plausible.Repo]) == [
url:
"postgresql://your_username:your_password@cluster-do-user-1234567-0.db.ondigitalocean.com:25060/defaultdb",
ssl: [cacertfile: "/path/to/cacert.pem", verify: :verify_peer]
]
end
test "sslmode=require disables peer verification" do
env = [
{"DATABASE_URL",
"postgresql://username:password@company.postgres.database.azure.com:5432/prod_plausible?sslmode=require"}
]
config = runtime_config(env)
assert get_in(config, [:plausible, Plausible.Repo]) == [
url:
"postgresql://username:password@company.postgres.database.azure.com:5432/prod_plausible?sslmode=require",
ssl: [verify: :verify_none]
]
end
test "sslmode=disable explicitly disables SSL" do
env = [
{"DATABASE_URL",
"postgresql://username:password@company.postgres.database.azure.com:5432/prod_plausible?sslmode=disable"}
]
config = runtime_config(env)
assert get_in(config, [:plausible, Plausible.Repo]) == [
url:
"postgresql://username:password@company.postgres.database.azure.com:5432/prod_plausible?sslmode=disable",
ssl: false
]
end
test "sslmode=verify-ca uses system certificates" do
env = [
{"DATABASE_URL",
"postgresql://username:password@company.postgres.database.azure.com:5432/prod_plausible?sslmode=verify-ca"}
]
config = runtime_config(env)
assert get_in(config, [:plausible, Plausible.Repo]) == [
url:
"postgresql://username:password@company.postgres.database.azure.com:5432/prod_plausible?sslmode=verify-ca",
ssl: [
cacerts: :public_key.cacerts_get(),
verify: :verify_peer
]
]
end
test "sslmode=verify-full raises error without certificate" do
env = [
{"DATABASE_URL",
"postgresql://username:password@company.postgres.database.azure.com:5432/prod_plausible?sslmode=verify-full"}
]
assert_raise ArgumentError,
~r/PostgreSQL SSL mode `sslmode=verify-full` requires a certificate/,
fn -> runtime_config(env) end
end
test "unsupported sslmode raises error" do
env = [
{"DATABASE_URL",
"postgresql://username:password@company.postgres.database.azure.com:5432/prod_plausible?sslmode=prefer"}
]
assert_raise ArgumentError, ~r/PostgreSQL SSL mode `sslmode=prefer` is not supported/, fn ->
runtime_config(env)
end
end
test "verify-full with sslrootcert enables peer verification" do
env = [
{"DATABASE_URL",
"postgresql://username:password@company.postgres.database.azure.com:5432/prod_plausible?sslmode=verify-full&sslrootcert=/path/to/cert.pem"}
]
config = runtime_config(env)
assert get_in(config, [:plausible, Plausible.Repo]) == [
url:
"postgresql://username:password@company.postgres.database.azure.com:5432/prod_plausible?sslmode=verify-full&sslrootcert=/path/to/cert.pem",
ssl: [cacertfile: "/path/to/cert.pem", verify: :verify_peer]
]
end
test "sslrootcert alone is not enough for peer verification" do
env = [
{"DATABASE_URL",
"postgresql://username:password@company.postgres.database.azure.com:5432/prod_plausible?sslrootcert=/path/to/cert.pem"}
]
config = runtime_config(env)
assert get_in(config, [:plausible, Plausible.Repo]) == [
url:
"postgresql://username:password@company.postgres.database.azure.com:5432/prod_plausible?sslrootcert=/path/to/cert.pem",
ssl: false
]
end
test "DATABASE_CACERTFILE takes precedence over sslrootcert" do
env = [
{"DATABASE_URL",
"postgresql://username:password@company.postgres.database.azure.com:5432/prod_plausible?sslrootcert=/url/cert.pem"},
{"DATABASE_CACERTFILE", "/env/cacert.pem"}
]
config = runtime_config(env)
assert get_in(config, [:plausible, Plausible.Repo]) == [
url:
"postgresql://username:password@company.postgres.database.azure.com:5432/prod_plausible?sslrootcert=/url/cert.pem",
ssl: [cacertfile: "/env/cacert.pem", verify: :verify_peer]
]
end
end
describe "extra config" do
test "no-op when no extra path is set" do
put_system_env_undo({"EXTRA_CONFIG_PATH", nil})
assert Config.Reader.read!("rel/overlays/import_extra_config.exs") == []
end
test "raises if path is invalid" do
put_system_env_undo({"EXTRA_CONFIG_PATH", "no-such-file"})
assert_raise File.Error, ~r/could not read file/, fn ->
Config.Reader.read!("rel/overlays/import_extra_config.exs")
end
end
@tag :tmp_dir
test "reads extra config", %{tmp_dir: tmp_dir} do
extra_config_path = Path.join(tmp_dir, "config.exs")
File.write!(extra_config_path, """
import Config
config :plausible, Plausible.Repo,
after_connect: {Postgrex, :query!, ["SET search_path TO global_prefix", []]}
""")
put_system_env_undo({"EXTRA_CONFIG_PATH", extra_config_path})
assert Config.Reader.read!("rel/overlays/import_extra_config.exs") == [
{:plausible,
[
{Plausible.Repo,
[after_connect: {Postgrex, :query!, ["SET search_path TO global_prefix", []]}]}
]}
]
end
end
describe "totp" do
test "pbkdf2 if not set" do
env = [
{"TOTP_VAULT_KEY", nil}
]
config = runtime_config(env)
assert [vault_key: vault_key] = get_in(config, [:plausible, Plausible.Auth.TOTP])
assert byte_size(vault_key) == 32
# make sure it doesn't change between releases
assert vault_key ==
"\x95\x9C\x05\x9A\xCD\xE4\xEF\xDDH\xFB\xCA\xD5o\xD1z\xCC\xBC\"J\xF8:\xFAs\xCA\x0Fo\x10\x9B\x84"
end
test "can be Base64-encoded 32 bytes (with padding)" do
# $ openssl rand -base64 32
# dx2W6PNd/QIC6IyYVWMEaG2fI8/5WVylryM3mRaOpAo=
env = [
{"TOTP_VAULT_KEY", "dx2W6PNd/QIC6IyYVWMEaG2fI8/5WVylryM3mRaOpAo="}
]
config = runtime_config(env)
assert [vault_key: vault_key] = get_in(config, [:plausible, Plausible.Auth.TOTP])
assert byte_size(vault_key) == 32
assert vault_key == Base.decode64!("dx2W6PNd/QIC6IyYVWMEaG2fI8/5WVylryM3mRaOpAo=")
end
test "fails on invalid key length" do
assert_raise ArgumentError, ~r/Got Base64 encoded 31 bytes/, fn ->
runtime_config(_env = [{"TOTP_VAULT_KEY", Base.encode64(:crypto.strong_rand_bytes(31))}])
end
assert_raise ArgumentError, ~r/Got Base64 encoded 33 bytes/, fn ->
runtime_config(_env = [{"TOTP_VAULT_KEY", Base.encode64(:crypto.strong_rand_bytes(33))}])
end
end
test "fails on invalid encoding" do
assert_raise ArgumentError,
~r/TOTP_VAULT_KEY must be Base64 encoded/,
fn ->
runtime_config(
_env = [
{"TOTP_VAULT_KEY",
"openssl" <> Base.encode64(:crypto.strong_rand_bytes(32))}
]
)
end
end
end
defp runtime_config(env) do
put_system_env_undo(env)
Config.Reader.read!("config/runtime.exs", env: :prod)
end
defp put_system_env_undo(env) do
before = System.get_env()
{to_delete, to_put} = Enum.split_with(List.wrap(env), fn {_, v} -> is_nil(v) end)
Enum.each(to_delete, fn {k, _} -> System.delete_env(k) end)
System.put_env(to_put)
on_exit(fn ->
Enum.each(System.get_env(), fn {k, _} -> System.delete_env(k) end)
System.put_env(before)
end)
end
end