1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2019 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] == [
49 webhook_url: "https://hooks.slack.com/services/KEY/some_val"
53 describe "update_or_create/1" do
55 config = insert(:config)
59 %{group: "pleroma", key: key2, value: "another_value"},
60 %{group: config.group, key: config.key, value: "new_value"}
63 assert Repo.all(ConfigDB) |> length() == 1
65 Enum.each(params, &ConfigDB.update_or_create(&1))
67 assert Repo.all(ConfigDB) |> length() == 2
69 config1 = ConfigDB.get_by_params(%{group: config.group, key: config.key})
70 config2 = ConfigDB.get_by_params(%{group: "pleroma", key: key2})
72 assert config1.value == ConfigDB.transform("new_value")
73 assert config2.value == ConfigDB.transform("another_value")
76 test "partial update" do
77 config = insert(:config, value: ConfigDB.to_binary(key1: "val1", key2: :val2))
80 ConfigDB.update_or_create(%{
83 value: [key1: :val1, key3: :val3]
86 updated = ConfigDB.get_by_params(%{group: config.group, key: config.key})
88 value = ConfigDB.from_binary(updated.value)
89 assert length(value) == 3
90 assert value[:key1] == :val1
91 assert value[:key2] == :val2
92 assert value[:key3] == :val3
96 config = insert(:config, value: ConfigDB.to_binary(key1: "val1", key2: [k1: :v1, k2: "v2"]))
99 ConfigDB.update_or_create(%{
102 value: [key1: :val1, key2: [k2: :v2, k3: :v3], key3: :val3]
105 updated = ConfigDB.get_by_params(%{group: config.group, key: config.key})
107 assert config.value == updated.value
109 value = ConfigDB.from_binary(updated.value)
110 assert value[:key1] == :val1
111 assert value[:key2] == [k1: :v1, k2: :v2, k3: :v3]
112 assert value[:key3] == :val3
115 test "only full update for some keys" do
116 config1 = insert(:config, key: ":ecto_repos", value: ConfigDB.to_binary(repo: Pleroma.Repo))
119 insert(:config, group: ":cors_plug", key: ":max_age", value: ConfigDB.to_binary(18))
122 ConfigDB.update_or_create(%{
123 group: config1.group,
125 value: [another_repo: [Pleroma.Repo]]
129 ConfigDB.update_or_create(%{
130 group: config2.group,
135 updated1 = ConfigDB.get_by_params(%{group: config1.group, key: config1.key})
136 updated2 = ConfigDB.get_by_params(%{group: config2.group, key: config2.key})
138 assert ConfigDB.from_binary(updated1.value) == [another_repo: [Pleroma.Repo]]
139 assert ConfigDB.from_binary(updated2.value) == 777
142 test "full update if value is not keyword" do
147 value: ConfigDB.to_binary(Tesla.Adapter.Hackney)
151 ConfigDB.update_or_create(%{
154 value: Tesla.Adapter.Httpc
157 updated = ConfigDB.get_by_params(%{group: config.group, key: config.key})
159 assert ConfigDB.from_binary(updated.value) == Tesla.Adapter.Httpc
162 test "only full update for some subkeys" do
166 value: ConfigDB.to_binary(groups: [a: 1, b: 2], key: [a: 1])
172 value: ConfigDB.to_binary(mascots: [a: 1, b: 2], key: [a: 1])
176 ConfigDB.update_or_create(%{
177 group: config1.group,
179 value: [groups: [c: 3, d: 4], key: [b: 2]]
183 ConfigDB.update_or_create(%{
184 group: config2.group,
186 value: [mascots: [c: 3, d: 4], key: [b: 2]]
189 updated1 = ConfigDB.get_by_params(%{group: config1.group, key: config1.key})
190 updated2 = ConfigDB.get_by_params(%{group: config2.group, key: config2.key})
192 assert ConfigDB.from_binary(updated1.value) == [groups: [c: 3, d: 4], key: [a: 1, b: 2]]
193 assert ConfigDB.from_binary(updated2.value) == [mascots: [c: 3, d: 4], key: [a: 1, b: 2]]
197 describe "delete/1" do
198 test "error on deleting non existing setting" do
199 {:error, error} = ConfigDB.delete(%{group: ":pleroma", key: ":key"})
200 assert error =~ "Config with params %{group: \":pleroma\", key: \":key\"} not found"
203 test "full delete" do
204 config = insert(:config)
205 {:ok, deleted} = ConfigDB.delete(%{group: config.group, key: config.key})
206 assert Ecto.get_meta(deleted, :state) == :deleted
207 refute ConfigDB.get_by_params(%{group: config.group, key: config.key})
210 test "partial subkeys delete" do
211 config = insert(:config, value: ConfigDB.to_binary(groups: [a: 1, b: 2], key: [a: 1]))
214 ConfigDB.delete(%{group: config.group, key: config.key, subkeys: [":groups"]})
216 assert Ecto.get_meta(deleted, :state) == :loaded
218 assert deleted.value == ConfigDB.to_binary(key: [a: 1])
220 updated = ConfigDB.get_by_params(%{group: config.group, key: config.key})
222 assert updated.value == deleted.value
225 test "full delete if remaining value after subkeys deletion is empty list" do
226 config = insert(:config, value: ConfigDB.to_binary(groups: [a: 1, b: 2]))
229 ConfigDB.delete(%{group: config.group, key: config.key, subkeys: [":groups"]})
231 assert Ecto.get_meta(deleted, :state) == :deleted
233 refute ConfigDB.get_by_params(%{group: config.group, key: config.key})
237 describe "transform/1" do
239 binary = ConfigDB.transform("value as string")
240 assert binary == :erlang.term_to_binary("value as string")
241 assert ConfigDB.from_binary(binary) == "value as string"
245 binary = ConfigDB.transform(false)
246 assert binary == :erlang.term_to_binary(false)
247 assert ConfigDB.from_binary(binary) == false
251 binary = ConfigDB.transform(nil)
252 assert binary == :erlang.term_to_binary(nil)
253 assert ConfigDB.from_binary(binary) == nil
257 binary = ConfigDB.transform(150)
258 assert binary == :erlang.term_to_binary(150)
259 assert ConfigDB.from_binary(binary) == 150
263 binary = ConfigDB.transform(":atom")
264 assert binary == :erlang.term_to_binary(:atom)
265 assert ConfigDB.from_binary(binary) == :atom
268 test "ssl options" do
269 binary = ConfigDB.transform([":tlsv1", ":tlsv1.1", ":tlsv1.2"])
270 assert binary == :erlang.term_to_binary([:tlsv1, :"tlsv1.1", :"tlsv1.2"])
271 assert ConfigDB.from_binary(binary) == [:tlsv1, :"tlsv1.1", :"tlsv1.2"]
274 test "pleroma module" do
275 binary = ConfigDB.transform("Pleroma.Bookmark")
276 assert binary == :erlang.term_to_binary(Pleroma.Bookmark)
277 assert ConfigDB.from_binary(binary) == Pleroma.Bookmark
280 test "pleroma string" do
281 binary = ConfigDB.transform("Pleroma")
282 assert binary == :erlang.term_to_binary("Pleroma")
283 assert ConfigDB.from_binary(binary) == "Pleroma"
286 test "phoenix module" do
287 binary = ConfigDB.transform("Phoenix.Socket.V1.JSONSerializer")
288 assert binary == :erlang.term_to_binary(Phoenix.Socket.V1.JSONSerializer)
289 assert ConfigDB.from_binary(binary) == Phoenix.Socket.V1.JSONSerializer
292 test "tesla module" do
293 binary = ConfigDB.transform("Tesla.Adapter.Hackney")
294 assert binary == :erlang.term_to_binary(Tesla.Adapter.Hackney)
295 assert ConfigDB.from_binary(binary) == Tesla.Adapter.Hackney
298 test "ExSyslogger module" do
299 binary = ConfigDB.transform("ExSyslogger")
300 assert binary == :erlang.term_to_binary(ExSyslogger)
301 assert ConfigDB.from_binary(binary) == ExSyslogger
304 test "Quack.Logger module" do
305 binary = ConfigDB.transform("Quack.Logger")
306 assert binary == :erlang.term_to_binary(Quack.Logger)
307 assert ConfigDB.from_binary(binary) == Quack.Logger
311 binary = ConfigDB.transform("~r[comp[lL][aA][iI][nN]er]")
312 assert binary == :erlang.term_to_binary(~r/comp[lL][aA][iI][nN]er/)
313 assert ConfigDB.from_binary(binary) == ~r/comp[lL][aA][iI][nN]er/
317 binary = ConfigDB.transform("~r/https:\/\/example.com/")
318 assert binary == :erlang.term_to_binary(~r/https:\/\/example.com/)
319 assert ConfigDB.from_binary(binary) == ~r/https:\/\/example.com/
322 test "link sigil with um modifiers" do
323 binary = ConfigDB.transform("~r/https:\/\/example.com/um")
324 assert binary == :erlang.term_to_binary(~r/https:\/\/example.com/um)
325 assert ConfigDB.from_binary(binary) == ~r/https:\/\/example.com/um
328 test "link sigil with i modifier" do
329 binary = ConfigDB.transform("~r/https:\/\/example.com/i")
330 assert binary == :erlang.term_to_binary(~r/https:\/\/example.com/i)
331 assert ConfigDB.from_binary(binary) == ~r/https:\/\/example.com/i
334 test "link sigil with s modifier" do
335 binary = ConfigDB.transform("~r/https:\/\/example.com/s")
336 assert binary == :erlang.term_to_binary(~r/https:\/\/example.com/s)
337 assert ConfigDB.from_binary(binary) == ~r/https:\/\/example.com/s
340 test "raise if valid delimiter not found" do
341 assert_raise ArgumentError, "valid delimiter for Regex expression not found", fn ->
342 ConfigDB.transform("~r/https://[]{}<>\"'()|example.com/s")
346 test "2 child tuple" do
347 binary = ConfigDB.transform(%{"tuple" => ["v1", ":v2"]})
348 assert binary == :erlang.term_to_binary({"v1", :v2})
349 assert ConfigDB.from_binary(binary) == {"v1", :v2}
352 test "proxy tuple with localhost" do
354 ConfigDB.transform(%{
355 "tuple" => [":proxy_url", %{"tuple" => [":socks5", "localhost", 1234]}]
358 assert binary == :erlang.term_to_binary({:proxy_url, {:socks5, :localhost, 1234}})
359 assert ConfigDB.from_binary(binary) == {:proxy_url, {:socks5, :localhost, 1234}}
362 test "proxy tuple with domain" do
364 ConfigDB.transform(%{
365 "tuple" => [":proxy_url", %{"tuple" => [":socks5", "domain.com", 1234]}]
368 assert binary == :erlang.term_to_binary({:proxy_url, {:socks5, 'domain.com', 1234}})
369 assert ConfigDB.from_binary(binary) == {:proxy_url, {:socks5, 'domain.com', 1234}}
372 test "proxy tuple with ip" do
374 ConfigDB.transform(%{
375 "tuple" => [":proxy_url", %{"tuple" => [":socks5", "127.0.0.1", 1234]}]
378 assert binary == :erlang.term_to_binary({:proxy_url, {:socks5, {127, 0, 0, 1}, 1234}})
379 assert ConfigDB.from_binary(binary) == {:proxy_url, {:socks5, {127, 0, 0, 1}, 1234}}
382 test "tuple with n childs" do
384 ConfigDB.transform(%{
391 "Phoenix.Socket.V1.JSONSerializer"
396 :erlang.term_to_binary(
397 {"v1", :v2, Pleroma.Bookmark, 150, false, Phoenix.Socket.V1.JSONSerializer}
400 assert ConfigDB.from_binary(binary) ==
401 {"v1", :v2, Pleroma.Bookmark, 150, false, Phoenix.Socket.V1.JSONSerializer}
404 test "map with string key" do
405 binary = ConfigDB.transform(%{"key" => "value"})
406 assert binary == :erlang.term_to_binary(%{"key" => "value"})
407 assert ConfigDB.from_binary(binary) == %{"key" => "value"}
410 test "map with atom key" do
411 binary = ConfigDB.transform(%{":key" => "value"})
412 assert binary == :erlang.term_to_binary(%{key: "value"})
413 assert ConfigDB.from_binary(binary) == %{key: "value"}
416 test "list of strings" do
417 binary = ConfigDB.transform(["v1", "v2", "v3"])
418 assert binary == :erlang.term_to_binary(["v1", "v2", "v3"])
419 assert ConfigDB.from_binary(binary) == ["v1", "v2", "v3"]
422 test "list of modules" do
423 binary = ConfigDB.transform(["Pleroma.Repo", "Pleroma.Activity"])
424 assert binary == :erlang.term_to_binary([Pleroma.Repo, Pleroma.Activity])
425 assert ConfigDB.from_binary(binary) == [Pleroma.Repo, Pleroma.Activity]
428 test "list of atoms" do
429 binary = ConfigDB.transform([":v1", ":v2", ":v3"])
430 assert binary == :erlang.term_to_binary([:v1, :v2, :v3])
431 assert ConfigDB.from_binary(binary) == [:v1, :v2, :v3]
434 test "list of mixed values" do
440 "Phoenix.Socket.V1.JSONSerializer",
446 :erlang.term_to_binary([
450 Phoenix.Socket.V1.JSONSerializer,
455 assert ConfigDB.from_binary(binary) == [
459 Phoenix.Socket.V1.JSONSerializer,
465 test "simple keyword" do
466 binary = ConfigDB.transform([%{"tuple" => [":key", "value"]}])
467 assert binary == :erlang.term_to_binary([{:key, "value"}])
468 assert ConfigDB.from_binary(binary) == [{:key, "value"}]
469 assert ConfigDB.from_binary(binary) == [key: "value"]
472 test "keyword with partial_chain key" do
474 ConfigDB.transform([%{"tuple" => [":partial_chain", "&:hackney_connect.partial_chain/1"]}])
476 assert binary == :erlang.term_to_binary(partial_chain: &:hackney_connect.partial_chain/1)
477 assert ConfigDB.from_binary(binary) == [partial_chain: &:hackney_connect.partial_chain/1]
483 %{"tuple" => [":types", "Pleroma.PostgresTypes"]},
484 %{"tuple" => [":telemetry_event", ["Pleroma.Repo.Instrumenter"]]},
485 %{"tuple" => [":migration_lock", nil]},
486 %{"tuple" => [":key1", 150]},
487 %{"tuple" => [":key2", "string"]}
491 :erlang.term_to_binary(
492 types: Pleroma.PostgresTypes,
493 telemetry_event: [Pleroma.Repo.Instrumenter],
499 assert ConfigDB.from_binary(binary) == [
500 types: Pleroma.PostgresTypes,
501 telemetry_event: [Pleroma.Repo.Instrumenter],
508 test "complex keyword with nested mixed childs" do
511 %{"tuple" => [":uploader", "Pleroma.Uploaders.Local"]},
512 %{"tuple" => [":filters", ["Pleroma.Upload.Filter.Dedupe"]]},
513 %{"tuple" => [":link_name", true]},
514 %{"tuple" => [":proxy_remote", false]},
515 %{"tuple" => [":common_map", %{":key" => "value"}]},
520 %{"tuple" => [":redirect_on_failure", false]},
521 %{"tuple" => [":max_body_length", 1_048_576]},
525 [%{"tuple" => [":follow_redirect", true]}, %{"tuple" => [":pool", ":upload"]}]
534 :erlang.term_to_binary(
535 uploader: Pleroma.Uploaders.Local,
536 filters: [Pleroma.Upload.Filter.Dedupe],
539 common_map: %{key: "value"},
541 redirect_on_failure: false,
542 max_body_length: 1_048_576,
544 follow_redirect: true,
550 assert ConfigDB.from_binary(binary) ==
552 uploader: Pleroma.Uploaders.Local,
553 filters: [Pleroma.Upload.Filter.Dedupe],
556 common_map: %{key: "value"},
558 redirect_on_failure: false,
559 max_body_length: 1_048_576,
561 follow_redirect: true,
568 test "common keyword" do
571 %{"tuple" => [":level", ":warn"]},
572 %{"tuple" => [":meta", [":all"]]},
573 %{"tuple" => [":path", ""]},
574 %{"tuple" => [":val", nil]},
575 %{"tuple" => [":webhook_url", "https://hooks.slack.com/services/YOUR-KEY-HERE"]}
579 :erlang.term_to_binary(
584 webhook_url: "https://hooks.slack.com/services/YOUR-KEY-HERE"
587 assert ConfigDB.from_binary(binary) == [
592 webhook_url: "https://hooks.slack.com/services/YOUR-KEY-HERE"
596 test "complex keyword with sigil" do
599 %{"tuple" => [":federated_timeline_removal", []]},
600 %{"tuple" => [":reject", ["~r/comp[lL][aA][iI][nN]er/"]]},
601 %{"tuple" => [":replace", []]}
605 :erlang.term_to_binary(
606 federated_timeline_removal: [],
607 reject: [~r/comp[lL][aA][iI][nN]er/],
611 assert ConfigDB.from_binary(binary) ==
612 [federated_timeline_removal: [], reject: [~r/comp[lL][aA][iI][nN]er/], replace: []]
615 test "complex keyword with tuples with more than 2 values" do
633 "Pleroma.Web.MastodonAPI.WebsocketHandler",
640 "Phoenix.Endpoint.CowboyWebSocket",
643 "Phoenix.Transports.WebSocket",
646 "Pleroma.Web.Endpoint",
647 "Pleroma.Web.UserSocket",
658 "Phoenix.Endpoint.Cowboy2Handler",
659 %{"tuple" => ["Pleroma.Web.Endpoint", []]}
674 :erlang.term_to_binary(
678 {"/api/v1/streaming", Pleroma.Web.MastodonAPI.WebsocketHandler, []},
679 {"/websocket", Phoenix.Endpoint.CowboyWebSocket,
680 {Phoenix.Transports.WebSocket,
681 {Pleroma.Web.Endpoint, Pleroma.Web.UserSocket, []}}},
682 {:_, Phoenix.Endpoint.Cowboy2Handler, {Pleroma.Web.Endpoint, []}}
688 assert ConfigDB.from_binary(binary) == [
693 {"/api/v1/streaming", Pleroma.Web.MastodonAPI.WebsocketHandler, []},
694 {"/websocket", Phoenix.Endpoint.CowboyWebSocket,
695 {Phoenix.Transports.WebSocket,
696 {Pleroma.Web.Endpoint, Pleroma.Web.UserSocket, []}}},
697 {:_, Phoenix.Endpoint.Cowboy2Handler, {Pleroma.Web.Endpoint, []}}