1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2020 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_key/1" do
11 config = insert(:config)
14 assert config == ConfigDB.get_by_params(%{group: config.group, key: config.key})
18 {:ok, config} = ConfigDB.create(%{group: ":pleroma", key: ":some_key", value: "some_value"})
19 assert config == ConfigDB.get_by_params(%{group: ":pleroma", key: ":some_key"})
23 config = insert(:config)
24 {:ok, updated} = ConfigDB.update(config, %{value: "some_value"})
25 loaded = ConfigDB.get_by_params(%{group: config.group, key: config.key})
26 assert loaded == updated
29 test "get_all_as_keyword/0" do
30 saved = insert(:config)
31 insert(:config, group: ":quack", key: ":level", value: ConfigDB.to_binary(:info))
32 insert(:config, group: ":quack", key: ":meta", value: ConfigDB.to_binary([:none]))
37 value: ConfigDB.to_binary("https://hooks.slack.com/services/KEY/some_val")
40 config = ConfigDB.get_all_as_keyword()
42 assert config[:pleroma] == [
43 {ConfigDB.from_string(saved.key), ConfigDB.from_binary(saved.value)}
46 assert config[:quack][:level] == :info
47 assert config[:quack][:meta] == [:none]
48 assert config[:quack][:webhook_url] == "https://hooks.slack.com/services/KEY/some_val"
51 describe "update_or_create/1" do
53 config = insert(:config)
57 %{group: "pleroma", key: key2, value: "another_value"},
58 %{group: config.group, key: config.key, value: "new_value"}
61 assert Repo.all(ConfigDB) |> length() == 1
63 Enum.each(params, &ConfigDB.update_or_create(&1))
65 assert Repo.all(ConfigDB) |> length() == 2
67 config1 = ConfigDB.get_by_params(%{group: config.group, key: config.key})
68 config2 = ConfigDB.get_by_params(%{group: "pleroma", key: key2})
70 assert config1.value == ConfigDB.transform("new_value")
71 assert config2.value == ConfigDB.transform("another_value")
74 test "partial update" do
75 config = insert(:config, value: ConfigDB.to_binary(key1: "val1", key2: :val2))
78 ConfigDB.update_or_create(%{
81 value: [key1: :val1, key3: :val3]
84 updated = ConfigDB.get_by_params(%{group: config.group, key: config.key})
86 value = ConfigDB.from_binary(updated.value)
87 assert length(value) == 3
88 assert value[:key1] == :val1
89 assert value[:key2] == :val2
90 assert value[:key3] == :val3
94 config = insert(:config, value: ConfigDB.to_binary(key1: "val1", key2: [k1: :v1, k2: "v2"]))
97 ConfigDB.update_or_create(%{
100 value: [key1: :val1, key2: [k2: :v2, k3: :v3], key3: :val3]
103 updated = ConfigDB.get_by_params(%{group: config.group, key: config.key})
105 assert config.value == updated.value
107 value = ConfigDB.from_binary(updated.value)
108 assert value[:key1] == :val1
109 assert value[:key2] == [k1: :v1, k2: :v2, k3: :v3]
110 assert value[:key3] == :val3
113 test "only full update for some keys" do
114 config1 = insert(:config, key: ":ecto_repos", value: ConfigDB.to_binary(repo: Pleroma.Repo))
117 insert(:config, group: ":cors_plug", key: ":max_age", value: ConfigDB.to_binary(18))
120 ConfigDB.update_or_create(%{
121 group: config1.group,
123 value: [another_repo: [Pleroma.Repo]]
127 ConfigDB.update_or_create(%{
128 group: config2.group,
133 updated1 = ConfigDB.get_by_params(%{group: config1.group, key: config1.key})
134 updated2 = ConfigDB.get_by_params(%{group: config2.group, key: config2.key})
136 assert ConfigDB.from_binary(updated1.value) == [another_repo: [Pleroma.Repo]]
137 assert ConfigDB.from_binary(updated2.value) == 777
140 test "full update if value is not keyword" do
145 value: ConfigDB.to_binary(Tesla.Adapter.Hackney)
149 ConfigDB.update_or_create(%{
152 value: Tesla.Adapter.Httpc
155 updated = ConfigDB.get_by_params(%{group: config.group, key: config.key})
157 assert ConfigDB.from_binary(updated.value) == Tesla.Adapter.Httpc
160 test "only full update for some subkeys" do
164 value: ConfigDB.to_binary(groups: [a: 1, b: 2], key: [a: 1])
170 value: ConfigDB.to_binary(mascots: [a: 1, b: 2], key: [a: 1])
174 ConfigDB.update_or_create(%{
175 group: config1.group,
177 value: [groups: [c: 3, d: 4], key: [b: 2]]
181 ConfigDB.update_or_create(%{
182 group: config2.group,
184 value: [mascots: [c: 3, d: 4], key: [b: 2]]
187 updated1 = ConfigDB.get_by_params(%{group: config1.group, key: config1.key})
188 updated2 = ConfigDB.get_by_params(%{group: config2.group, key: config2.key})
190 assert ConfigDB.from_binary(updated1.value) == [groups: [c: 3, d: 4], key: [a: 1, b: 2]]
191 assert ConfigDB.from_binary(updated2.value) == [mascots: [c: 3, d: 4], key: [a: 1, b: 2]]
195 describe "delete/1" do
196 test "error on deleting non existing setting" do
197 {:error, error} = ConfigDB.delete(%{group: ":pleroma", key: ":key"})
198 assert error =~ "Config with params %{group: \":pleroma\", key: \":key\"} not found"
201 test "full delete" do
202 config = insert(:config)
203 {:ok, deleted} = ConfigDB.delete(%{group: config.group, key: config.key})
204 assert Ecto.get_meta(deleted, :state) == :deleted
205 refute ConfigDB.get_by_params(%{group: config.group, key: config.key})
208 test "partial subkeys delete" do
209 config = insert(:config, value: ConfigDB.to_binary(groups: [a: 1, b: 2], key: [a: 1]))
212 ConfigDB.delete(%{group: config.group, key: config.key, subkeys: [":groups"]})
214 assert Ecto.get_meta(deleted, :state) == :loaded
216 assert deleted.value == ConfigDB.to_binary(key: [a: 1])
218 updated = ConfigDB.get_by_params(%{group: config.group, key: config.key})
220 assert updated.value == deleted.value
223 test "full delete if remaining value after subkeys deletion is empty list" do
224 config = insert(:config, value: ConfigDB.to_binary(groups: [a: 1, b: 2]))
227 ConfigDB.delete(%{group: config.group, key: config.key, subkeys: [":groups"]})
229 assert Ecto.get_meta(deleted, :state) == :deleted
231 refute ConfigDB.get_by_params(%{group: config.group, key: config.key})
235 describe "transform/1" do
237 binary = ConfigDB.transform("value as string")
238 assert binary == :erlang.term_to_binary("value as string")
239 assert ConfigDB.from_binary(binary) == "value as string"
243 binary = ConfigDB.transform(false)
244 assert binary == :erlang.term_to_binary(false)
245 assert ConfigDB.from_binary(binary) == false
249 binary = ConfigDB.transform(nil)
250 assert binary == :erlang.term_to_binary(nil)
251 assert ConfigDB.from_binary(binary) == nil
255 binary = ConfigDB.transform(150)
256 assert binary == :erlang.term_to_binary(150)
257 assert ConfigDB.from_binary(binary) == 150
261 binary = ConfigDB.transform(":atom")
262 assert binary == :erlang.term_to_binary(:atom)
263 assert ConfigDB.from_binary(binary) == :atom
266 test "ssl options" do
267 binary = ConfigDB.transform([":tlsv1", ":tlsv1.1", ":tlsv1.2"])
268 assert binary == :erlang.term_to_binary([:tlsv1, :"tlsv1.1", :"tlsv1.2"])
269 assert ConfigDB.from_binary(binary) == [:tlsv1, :"tlsv1.1", :"tlsv1.2"]
272 test "pleroma module" do
273 binary = ConfigDB.transform("Pleroma.Bookmark")
274 assert binary == :erlang.term_to_binary(Pleroma.Bookmark)
275 assert ConfigDB.from_binary(binary) == Pleroma.Bookmark
278 test "pleroma string" do
279 binary = ConfigDB.transform("Pleroma")
280 assert binary == :erlang.term_to_binary("Pleroma")
281 assert ConfigDB.from_binary(binary) == "Pleroma"
284 test "phoenix module" do
285 binary = ConfigDB.transform("Phoenix.Socket.V1.JSONSerializer")
286 assert binary == :erlang.term_to_binary(Phoenix.Socket.V1.JSONSerializer)
287 assert ConfigDB.from_binary(binary) == Phoenix.Socket.V1.JSONSerializer
290 test "tesla module" do
291 binary = ConfigDB.transform("Tesla.Adapter.Hackney")
292 assert binary == :erlang.term_to_binary(Tesla.Adapter.Hackney)
293 assert ConfigDB.from_binary(binary) == Tesla.Adapter.Hackney
296 test "ExSyslogger module" do
297 binary = ConfigDB.transform("ExSyslogger")
298 assert binary == :erlang.term_to_binary(ExSyslogger)
299 assert ConfigDB.from_binary(binary) == ExSyslogger
302 test "Quack.Logger module" do
303 binary = ConfigDB.transform("Quack.Logger")
304 assert binary == :erlang.term_to_binary(Quack.Logger)
305 assert ConfigDB.from_binary(binary) == Quack.Logger
308 test "Swoosh.Adapters modules" do
309 binary = ConfigDB.transform("Swoosh.Adapters.SMTP")
310 assert binary == :erlang.term_to_binary(Swoosh.Adapters.SMTP)
311 assert ConfigDB.from_binary(binary) == Swoosh.Adapters.SMTP
312 binary = ConfigDB.transform("Swoosh.Adapters.AmazonSES")
313 assert binary == :erlang.term_to_binary(Swoosh.Adapters.AmazonSES)
314 assert ConfigDB.from_binary(binary) == Swoosh.Adapters.AmazonSES
318 binary = ConfigDB.transform("~r[comp[lL][aA][iI][nN]er]")
319 assert binary == :erlang.term_to_binary(~r/comp[lL][aA][iI][nN]er/)
320 assert ConfigDB.from_binary(binary) == ~r/comp[lL][aA][iI][nN]er/
324 binary = ConfigDB.transform("~r/https:\/\/example.com/")
325 assert binary == :erlang.term_to_binary(~r/https:\/\/example.com/)
326 assert ConfigDB.from_binary(binary) == ~r/https:\/\/example.com/
329 test "link sigil with um modifiers" do
330 binary = ConfigDB.transform("~r/https:\/\/example.com/um")
331 assert binary == :erlang.term_to_binary(~r/https:\/\/example.com/um)
332 assert ConfigDB.from_binary(binary) == ~r/https:\/\/example.com/um
335 test "link sigil with i modifier" do
336 binary = ConfigDB.transform("~r/https:\/\/example.com/i")
337 assert binary == :erlang.term_to_binary(~r/https:\/\/example.com/i)
338 assert ConfigDB.from_binary(binary) == ~r/https:\/\/example.com/i
341 test "link sigil with s modifier" do
342 binary = ConfigDB.transform("~r/https:\/\/example.com/s")
343 assert binary == :erlang.term_to_binary(~r/https:\/\/example.com/s)
344 assert ConfigDB.from_binary(binary) == ~r/https:\/\/example.com/s
347 test "raise if valid delimiter not found" do
348 assert_raise ArgumentError, "valid delimiter for Regex expression not found", fn ->
349 ConfigDB.transform("~r/https://[]{}<>\"'()|example.com/s")
353 test "2 child tuple" do
354 binary = ConfigDB.transform(%{"tuple" => ["v1", ":v2"]})
355 assert binary == :erlang.term_to_binary({"v1", :v2})
356 assert ConfigDB.from_binary(binary) == {"v1", :v2}
359 test "proxy tuple with localhost" do
361 ConfigDB.transform(%{
362 "tuple" => [":proxy_url", %{"tuple" => [":socks5", "localhost", 1234]}]
365 assert binary == :erlang.term_to_binary({:proxy_url, {:socks5, :localhost, 1234}})
366 assert ConfigDB.from_binary(binary) == {:proxy_url, {:socks5, :localhost, 1234}}
369 test "proxy tuple with domain" do
371 ConfigDB.transform(%{
372 "tuple" => [":proxy_url", %{"tuple" => [":socks5", "domain.com", 1234]}]
375 assert binary == :erlang.term_to_binary({:proxy_url, {:socks5, 'domain.com', 1234}})
376 assert ConfigDB.from_binary(binary) == {:proxy_url, {:socks5, 'domain.com', 1234}}
379 test "proxy tuple with ip" do
381 ConfigDB.transform(%{
382 "tuple" => [":proxy_url", %{"tuple" => [":socks5", "127.0.0.1", 1234]}]
385 assert binary == :erlang.term_to_binary({:proxy_url, {:socks5, {127, 0, 0, 1}, 1234}})
386 assert ConfigDB.from_binary(binary) == {:proxy_url, {:socks5, {127, 0, 0, 1}, 1234}}
389 test "tuple with n childs" do
391 ConfigDB.transform(%{
398 "Phoenix.Socket.V1.JSONSerializer"
403 :erlang.term_to_binary(
404 {"v1", :v2, Pleroma.Bookmark, 150, false, Phoenix.Socket.V1.JSONSerializer}
407 assert ConfigDB.from_binary(binary) ==
408 {"v1", :v2, Pleroma.Bookmark, 150, false, Phoenix.Socket.V1.JSONSerializer}
411 test "map with string key" do
412 binary = ConfigDB.transform(%{"key" => "value"})
413 assert binary == :erlang.term_to_binary(%{"key" => "value"})
414 assert ConfigDB.from_binary(binary) == %{"key" => "value"}
417 test "map with atom key" do
418 binary = ConfigDB.transform(%{":key" => "value"})
419 assert binary == :erlang.term_to_binary(%{key: "value"})
420 assert ConfigDB.from_binary(binary) == %{key: "value"}
423 test "list of strings" do
424 binary = ConfigDB.transform(["v1", "v2", "v3"])
425 assert binary == :erlang.term_to_binary(["v1", "v2", "v3"])
426 assert ConfigDB.from_binary(binary) == ["v1", "v2", "v3"]
429 test "list of modules" do
430 binary = ConfigDB.transform(["Pleroma.Repo", "Pleroma.Activity"])
431 assert binary == :erlang.term_to_binary([Pleroma.Repo, Pleroma.Activity])
432 assert ConfigDB.from_binary(binary) == [Pleroma.Repo, Pleroma.Activity]
435 test "list of atoms" do
436 binary = ConfigDB.transform([":v1", ":v2", ":v3"])
437 assert binary == :erlang.term_to_binary([:v1, :v2, :v3])
438 assert ConfigDB.from_binary(binary) == [:v1, :v2, :v3]
441 test "list of mixed values" do
447 "Phoenix.Socket.V1.JSONSerializer",
453 :erlang.term_to_binary([
457 Phoenix.Socket.V1.JSONSerializer,
462 assert ConfigDB.from_binary(binary) == [
466 Phoenix.Socket.V1.JSONSerializer,
472 test "simple keyword" do
473 binary = ConfigDB.transform([%{"tuple" => [":key", "value"]}])
474 assert binary == :erlang.term_to_binary([{:key, "value"}])
475 assert ConfigDB.from_binary(binary) == [{:key, "value"}]
476 assert ConfigDB.from_binary(binary) == [key: "value"]
482 %{"tuple" => [":types", "Pleroma.PostgresTypes"]},
483 %{"tuple" => [":telemetry_event", ["Pleroma.Repo.Instrumenter"]]},
484 %{"tuple" => [":migration_lock", nil]},
485 %{"tuple" => [":key1", 150]},
486 %{"tuple" => [":key2", "string"]}
490 :erlang.term_to_binary(
491 types: Pleroma.PostgresTypes,
492 telemetry_event: [Pleroma.Repo.Instrumenter],
498 assert ConfigDB.from_binary(binary) == [
499 types: Pleroma.PostgresTypes,
500 telemetry_event: [Pleroma.Repo.Instrumenter],
507 test "complex keyword with nested mixed childs" do
510 %{"tuple" => [":uploader", "Pleroma.Uploaders.Local"]},
511 %{"tuple" => [":filters", ["Pleroma.Upload.Filter.Dedupe"]]},
512 %{"tuple" => [":link_name", true]},
513 %{"tuple" => [":proxy_remote", false]},
514 %{"tuple" => [":common_map", %{":key" => "value"}]},
519 %{"tuple" => [":redirect_on_failure", false]},
520 %{"tuple" => [":max_body_length", 1_048_576]},
524 [%{"tuple" => [":follow_redirect", true]}, %{"tuple" => [":pool", ":upload"]}]
533 :erlang.term_to_binary(
534 uploader: Pleroma.Uploaders.Local,
535 filters: [Pleroma.Upload.Filter.Dedupe],
538 common_map: %{key: "value"},
540 redirect_on_failure: false,
541 max_body_length: 1_048_576,
543 follow_redirect: true,
549 assert ConfigDB.from_binary(binary) ==
551 uploader: Pleroma.Uploaders.Local,
552 filters: [Pleroma.Upload.Filter.Dedupe],
555 common_map: %{key: "value"},
557 redirect_on_failure: false,
558 max_body_length: 1_048_576,
560 follow_redirect: true,
567 test "common keyword" do
570 %{"tuple" => [":level", ":warn"]},
571 %{"tuple" => [":meta", [":all"]]},
572 %{"tuple" => [":path", ""]},
573 %{"tuple" => [":val", nil]},
574 %{"tuple" => [":webhook_url", "https://hooks.slack.com/services/YOUR-KEY-HERE"]}
578 :erlang.term_to_binary(
583 webhook_url: "https://hooks.slack.com/services/YOUR-KEY-HERE"
586 assert ConfigDB.from_binary(binary) == [
591 webhook_url: "https://hooks.slack.com/services/YOUR-KEY-HERE"
595 test "complex keyword with sigil" do
598 %{"tuple" => [":federated_timeline_removal", []]},
599 %{"tuple" => [":reject", ["~r/comp[lL][aA][iI][nN]er/"]]},
600 %{"tuple" => [":replace", []]}
604 :erlang.term_to_binary(
605 federated_timeline_removal: [],
606 reject: [~r/comp[lL][aA][iI][nN]er/],
610 assert ConfigDB.from_binary(binary) ==
611 [federated_timeline_removal: [], reject: [~r/comp[lL][aA][iI][nN]er/], replace: []]
614 test "complex keyword with tuples with more than 2 values" do
632 "Pleroma.Web.MastodonAPI.WebsocketHandler",
639 "Phoenix.Endpoint.CowboyWebSocket",
642 "Phoenix.Transports.WebSocket",
645 "Pleroma.Web.Endpoint",
646 "Pleroma.Web.UserSocket",
657 "Phoenix.Endpoint.Cowboy2Handler",
658 %{"tuple" => ["Pleroma.Web.Endpoint", []]}
673 :erlang.term_to_binary(
677 {"/api/v1/streaming", Pleroma.Web.MastodonAPI.WebsocketHandler, []},
678 {"/websocket", Phoenix.Endpoint.CowboyWebSocket,
679 {Phoenix.Transports.WebSocket,
680 {Pleroma.Web.Endpoint, Pleroma.Web.UserSocket, []}}},
681 {:_, Phoenix.Endpoint.Cowboy2Handler, {Pleroma.Web.Endpoint, []}}
687 assert ConfigDB.from_binary(binary) == [
692 {"/api/v1/streaming", Pleroma.Web.MastodonAPI.WebsocketHandler, []},
693 {"/websocket", Phoenix.Endpoint.CowboyWebSocket,
694 {Phoenix.Transports.WebSocket,
695 {Pleroma.Web.Endpoint, Pleroma.Web.UserSocket, []}}},
696 {:_, Phoenix.Endpoint.Cowboy2Handler, {Pleroma.Web.Endpoint, []}}