1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
3 # SPDX-License-Identifier: AGPL-3.0-only
5 defmodule Pleroma.ConfigDBTest do
6 use Pleroma.DataCase, async: true
10 test "get_by_params/1" do
11 config = insert(:config)
14 assert config == ConfigDB.get_by_params(%{group: config.group, key: config.key})
17 test "get_all_as_keyword/0" do
18 saved = insert(:config)
19 insert(:config, group: ":goose", key: ":level", value: :info)
20 insert(:config, group: ":goose", key: ":meta", value: [:none])
25 value: "https://gander.com/"
28 config = ConfigDB.get_all_as_keyword()
30 assert config[:pleroma] == [
31 {saved.key, saved.value}
34 assert config[:goose][:level] == :info
35 assert config[:goose][:meta] == [:none]
36 assert config[:goose][:webhook_url] == "https://gander.com/"
39 describe "update_or_create/1" do
41 config = insert(:config)
45 %{group: :pleroma, key: key2, value: "another_value"},
46 %{group: :pleroma, key: config.key, value: [a: 1, b: 2, c: "new_value"]}
49 assert Repo.all(ConfigDB) |> length() == 1
51 Enum.each(params, &ConfigDB.update_or_create(&1))
53 assert Repo.all(ConfigDB) |> length() == 2
55 config1 = ConfigDB.get_by_params(%{group: config.group, key: config.key})
56 config2 = ConfigDB.get_by_params(%{group: :pleroma, key: key2})
58 assert config1.value == [a: 1, b: 2, c: "new_value"]
59 assert config2.value == "another_value"
62 test "partial update" do
63 config = insert(:config, value: [key1: "val1", key2: :val2])
66 ConfigDB.update_or_create(%{
69 value: [key1: :val1, key3: :val3]
72 updated = ConfigDB.get_by_params(%{group: config.group, key: config.key})
74 assert config.value == updated.value
75 assert updated.value[:key1] == :val1
76 assert updated.value[:key2] == :val2
77 assert updated.value[:key3] == :val3
81 config = insert(:config, value: [key1: "val1", key2: [k1: :v1, k2: "v2"]])
84 ConfigDB.update_or_create(%{
87 value: [key1: :val1, key2: [k2: :v2, k3: :v3], key3: :val3]
90 updated = ConfigDB.get_by_params(%{group: config.group, key: config.key})
92 assert config.value == updated.value
93 assert updated.value[:key1] == :val1
94 assert updated.value[:key2] == [k1: :v1, k2: :v2, k3: :v3]
95 assert updated.value[:key3] == :val3
98 test "only full update for some keys" do
99 config1 = insert(:config, key: :ecto_repos, value: [repo: Pleroma.Repo])
101 config2 = insert(:config, group: :cors_plug, key: :max_age, value: 18)
104 ConfigDB.update_or_create(%{
105 group: config1.group,
107 value: [another_repo: [Pleroma.Repo]]
111 ConfigDB.update_or_create(%{
112 group: config2.group,
117 updated1 = ConfigDB.get_by_params(%{group: config1.group, key: config1.key})
118 updated2 = ConfigDB.get_by_params(%{group: config2.group, key: config2.key})
120 assert updated1.value == [another_repo: [Pleroma.Repo]]
121 assert updated2.value == 777
124 test "full update if value is not keyword" do
129 value: Tesla.Adapter.Hackney
133 ConfigDB.update_or_create(%{
136 value: Tesla.Adapter.Httpc
139 updated = ConfigDB.get_by_params(%{group: config.group, key: config.key})
141 assert updated.value == Tesla.Adapter.Httpc
144 test "only full update for some subkeys" do
148 value: [groups: [a: 1, b: 2], key: [a: 1]]
154 value: [mascots: [a: 1, b: 2], key: [a: 1]]
158 ConfigDB.update_or_create(%{
159 group: config1.group,
161 value: [groups: [c: 3, d: 4], key: [b: 2]]
165 ConfigDB.update_or_create(%{
166 group: config2.group,
168 value: [mascots: [c: 3, d: 4], key: [b: 2]]
171 updated1 = ConfigDB.get_by_params(%{group: config1.group, key: config1.key})
172 updated2 = ConfigDB.get_by_params(%{group: config2.group, key: config2.key})
174 assert updated1.value == [groups: [c: 3, d: 4], key: [a: 1, b: 2]]
175 assert updated2.value == [mascots: [c: 3, d: 4], key: [a: 1, b: 2]]
179 describe "delete/1" do
180 test "error on deleting non existing setting" do
181 {:error, error} = ConfigDB.delete(%{group: ":pleroma", key: ":key"})
182 assert error =~ "Config with params %{group: \":pleroma\", key: \":key\"} not found"
185 test "full delete" do
186 config = insert(:config)
187 {:ok, deleted} = ConfigDB.delete(%{group: config.group, key: config.key})
188 assert Ecto.get_meta(deleted, :state) == :deleted
189 refute ConfigDB.get_by_params(%{group: config.group, key: config.key})
192 test "partial subkeys delete" do
193 config = insert(:config, value: [groups: [a: 1, b: 2], key: [a: 1]])
196 ConfigDB.delete(%{group: config.group, key: config.key, subkeys: [":groups"]})
198 assert Ecto.get_meta(deleted, :state) == :loaded
200 assert deleted.value == [key: [a: 1]]
202 updated = ConfigDB.get_by_params(%{group: config.group, key: config.key})
204 assert updated.value == deleted.value
207 test "full delete if remaining value after subkeys deletion is empty list" do
208 config = insert(:config, value: [groups: [a: 1, b: 2]])
211 ConfigDB.delete(%{group: config.group, key: config.key, subkeys: [":groups"]})
213 assert Ecto.get_meta(deleted, :state) == :deleted
215 refute ConfigDB.get_by_params(%{group: config.group, key: config.key})
219 describe "to_elixir_types/1" do
221 assert ConfigDB.to_elixir_types("value as string") == "value as string"
225 assert ConfigDB.to_elixir_types(false) == false
229 assert ConfigDB.to_elixir_types(nil) == nil
233 assert ConfigDB.to_elixir_types(150) == 150
237 assert ConfigDB.to_elixir_types(":atom") == :atom
240 test "ssl options" do
241 assert ConfigDB.to_elixir_types([":tlsv1", ":tlsv1.1", ":tlsv1.2", ":tlsv1.3"]) == [
249 test "pleroma module" do
250 assert ConfigDB.to_elixir_types("Pleroma.Bookmark") == Pleroma.Bookmark
253 test "pleroma string" do
254 assert ConfigDB.to_elixir_types("Pleroma") == "Pleroma"
257 test "phoenix module" do
258 assert ConfigDB.to_elixir_types("Phoenix.Socket.V1.JSONSerializer") ==
259 Phoenix.Socket.V1.JSONSerializer
262 test "tesla module" do
263 assert ConfigDB.to_elixir_types("Tesla.Adapter.Hackney") == Tesla.Adapter.Hackney
266 test "ExSyslogger module" do
267 assert ConfigDB.to_elixir_types("ExSyslogger") == ExSyslogger
270 test "Swoosh.Adapters modules" do
271 assert ConfigDB.to_elixir_types("Swoosh.Adapters.SMTP") == Swoosh.Adapters.SMTP
272 assert ConfigDB.to_elixir_types("Swoosh.Adapters.AmazonSES") == Swoosh.Adapters.AmazonSES
276 assert ConfigDB.to_elixir_types("~r[comp[lL][aA][iI][nN]er]") == ~r/comp[lL][aA][iI][nN]er/
280 assert ConfigDB.to_elixir_types("~r/https:\/\/example.com/") == ~r/https:\/\/example.com/
283 test "link sigil with um modifiers" do
284 assert ConfigDB.to_elixir_types("~r/https:\/\/example.com/um") ==
285 ~r/https:\/\/example.com/um
288 test "link sigil with i modifier" do
289 assert ConfigDB.to_elixir_types("~r/https:\/\/example.com/i") == ~r/https:\/\/example.com/i
292 test "link sigil with s modifier" do
293 assert ConfigDB.to_elixir_types("~r/https:\/\/example.com/s") == ~r/https:\/\/example.com/s
296 test "raise if valid delimiter not found" do
297 assert_raise ArgumentError, "valid delimiter for Regex expression not found", fn ->
298 ConfigDB.to_elixir_types("~r/https://[]{}<>\"'()|example.com/s")
302 test "2 child tuple" do
303 assert ConfigDB.to_elixir_types(%{"tuple" => ["v1", ":v2"]}) == {"v1", :v2}
306 test "proxy tuple with localhost" do
307 assert ConfigDB.to_elixir_types(%{
308 "tuple" => [":proxy_url", %{"tuple" => [":socks5", "localhost", 1234]}]
309 }) == {:proxy_url, {:socks5, :localhost, 1234}}
312 test "proxy tuple with domain" do
313 assert ConfigDB.to_elixir_types(%{
314 "tuple" => [":proxy_url", %{"tuple" => [":socks5", "domain.com", 1234]}]
315 }) == {:proxy_url, {:socks5, 'domain.com', 1234}}
318 test "proxy tuple with ip" do
319 assert ConfigDB.to_elixir_types(%{
320 "tuple" => [":proxy_url", %{"tuple" => [":socks5", "127.0.0.1", 1234]}]
321 }) == {:proxy_url, {:socks5, {127, 0, 0, 1}, 1234}}
324 test "tuple with n children" do
325 assert ConfigDB.to_elixir_types(%{
332 "Phoenix.Socket.V1.JSONSerializer"
334 }) == {"v1", :v2, Pleroma.Bookmark, 150, false, Phoenix.Socket.V1.JSONSerializer}
337 test "map with string key" do
338 assert ConfigDB.to_elixir_types(%{"key" => "value"}) == %{"key" => "value"}
341 test "map with atom key" do
342 assert ConfigDB.to_elixir_types(%{":key" => "value"}) == %{key: "value"}
345 test "list of strings" do
346 assert ConfigDB.to_elixir_types(["v1", "v2", "v3"]) == ["v1", "v2", "v3"]
349 test "list of modules" do
350 assert ConfigDB.to_elixir_types(["Pleroma.Repo", "Pleroma.Activity"]) == [
356 test "list of atoms" do
357 assert ConfigDB.to_elixir_types([":v1", ":v2", ":v3"]) == [:v1, :v2, :v3]
360 test "list of mixed values" do
361 assert ConfigDB.to_elixir_types([
365 "Phoenix.Socket.V1.JSONSerializer",
372 Phoenix.Socket.V1.JSONSerializer,
378 test "simple keyword" do
379 assert ConfigDB.to_elixir_types([%{"tuple" => [":key", "value"]}]) == [key: "value"]
383 assert ConfigDB.to_elixir_types([
384 %{"tuple" => [":types", "Pleroma.PostgresTypes"]},
385 %{"tuple" => [":telemetry_event", ["Pleroma.Repo.Instrumenter"]]},
386 %{"tuple" => [":migration_lock", nil]},
387 %{"tuple" => [":key1", 150]},
388 %{"tuple" => [":key2", "string"]}
390 types: Pleroma.PostgresTypes,
391 telemetry_event: [Pleroma.Repo.Instrumenter],
398 test "trandformed keyword" do
399 assert ConfigDB.to_elixir_types(a: 1, b: 2, c: "string") == [a: 1, b: 2, c: "string"]
402 test "complex keyword with nested mixed children" do
403 assert ConfigDB.to_elixir_types([
404 %{"tuple" => [":uploader", "Pleroma.Uploaders.Local"]},
405 %{"tuple" => [":filters", ["Pleroma.Upload.Filter.Dedupe"]]},
406 %{"tuple" => [":link_name", true]},
407 %{"tuple" => [":proxy_remote", false]},
408 %{"tuple" => [":common_map", %{":key" => "value"}]},
413 %{"tuple" => [":redirect_on_failure", false]},
414 %{"tuple" => [":max_body_length", 1_048_576]},
419 %{"tuple" => [":follow_redirect", true]},
420 %{"tuple" => [":pool", ":upload"]}
428 uploader: Pleroma.Uploaders.Local,
429 filters: [Pleroma.Upload.Filter.Dedupe],
432 common_map: %{key: "value"},
434 redirect_on_failure: false,
435 max_body_length: 1_048_576,
437 follow_redirect: true,
444 test "common keyword" do
445 assert ConfigDB.to_elixir_types([
446 %{"tuple" => [":level", ":warning"]},
447 %{"tuple" => [":meta", [":all"]]},
448 %{"tuple" => [":path", ""]},
449 %{"tuple" => [":val", nil]},
450 %{"tuple" => [":webhook_url", "https://hooks.slack.com/services/YOUR-KEY-HERE"]}
456 webhook_url: "https://hooks.slack.com/services/YOUR-KEY-HERE"
460 test "complex keyword with sigil" do
461 assert ConfigDB.to_elixir_types([
462 %{"tuple" => [":federated_timeline_removal", []]},
463 %{"tuple" => [":reject", ["~r/comp[lL][aA][iI][nN]er/"]]},
464 %{"tuple" => [":replace", []]}
466 federated_timeline_removal: [],
467 reject: [~r/comp[lL][aA][iI][nN]er/],
472 test "complex keyword with tuples with more than 2 values" do
473 assert ConfigDB.to_elixir_types([
489 "Pleroma.Web.MastodonAPI.WebsocketHandler",
496 "Phoenix.Endpoint.CowboyWebSocket",
499 "Phoenix.Transports.WebSocket",
502 "Pleroma.Web.Endpoint",
503 "Pleroma.Web.UserSocket",
514 "Phoenix.Endpoint.Cowboy2Handler",
515 %{"tuple" => ["Pleroma.Web.Endpoint", []]}
532 {"/api/v1/streaming", Pleroma.Web.MastodonAPI.WebsocketHandler, []},
533 {"/websocket", Phoenix.Endpoint.CowboyWebSocket,
534 {Phoenix.Transports.WebSocket,
535 {Pleroma.Web.Endpoint, Pleroma.Web.UserSocket, []}}},
536 {:_, Phoenix.Endpoint.Cowboy2Handler, {Pleroma.Web.Endpoint, []}}