Remove quack, ensure adapter is finch
[akkoma] / test / pleroma / config_db_test.exs
1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
3 # SPDX-License-Identifier: AGPL-3.0-only
4
5 defmodule Pleroma.ConfigDBTest do
6 use Pleroma.DataCase, async: true
7 import Pleroma.Factory
8 alias Pleroma.ConfigDB
9
10 test "get_by_params/1" do
11 config = insert(:config)
12 insert(:config)
13
14 assert config == ConfigDB.get_by_params(%{group: config.group, key: config.key})
15 end
16
17 describe "update_or_create/1" do
18 test "common" do
19 config = insert(:config)
20 key2 = :another_key
21
22 params = [
23 %{group: :pleroma, key: key2, value: "another_value"},
24 %{group: :pleroma, key: config.key, value: [a: 1, b: 2, c: "new_value"]}
25 ]
26
27 assert Repo.all(ConfigDB) |> length() == 1
28
29 Enum.each(params, &ConfigDB.update_or_create(&1))
30
31 assert Repo.all(ConfigDB) |> length() == 2
32
33 config1 = ConfigDB.get_by_params(%{group: config.group, key: config.key})
34 config2 = ConfigDB.get_by_params(%{group: :pleroma, key: key2})
35
36 assert config1.value == [a: 1, b: 2, c: "new_value"]
37 assert config2.value == "another_value"
38 end
39
40 test "partial update" do
41 config = insert(:config, value: [key1: "val1", key2: :val2])
42
43 {:ok, config} =
44 ConfigDB.update_or_create(%{
45 group: config.group,
46 key: config.key,
47 value: [key1: :val1, key3: :val3]
48 })
49
50 updated = ConfigDB.get_by_params(%{group: config.group, key: config.key})
51
52 assert config.value == updated.value
53 assert updated.value[:key1] == :val1
54 assert updated.value[:key2] == :val2
55 assert updated.value[:key3] == :val3
56 end
57
58 test "deep merge" do
59 config = insert(:config, value: [key1: "val1", key2: [k1: :v1, k2: "v2"]])
60
61 {:ok, config} =
62 ConfigDB.update_or_create(%{
63 group: config.group,
64 key: config.key,
65 value: [key1: :val1, key2: [k2: :v2, k3: :v3], key3: :val3]
66 })
67
68 updated = ConfigDB.get_by_params(%{group: config.group, key: config.key})
69
70 assert config.value == updated.value
71 assert updated.value[:key1] == :val1
72 assert updated.value[:key2] == [k1: :v1, k2: :v2, k3: :v3]
73 assert updated.value[:key3] == :val3
74 end
75
76 test "only full update for some keys" do
77 config1 = insert(:config, key: :ecto_repos, value: [repo: Pleroma.Repo])
78
79 config2 = insert(:config, group: :cors_plug, key: :max_age, value: 18)
80
81 {:ok, _config} =
82 ConfigDB.update_or_create(%{
83 group: config1.group,
84 key: config1.key,
85 value: [another_repo: [Pleroma.Repo]]
86 })
87
88 {:ok, _config} =
89 ConfigDB.update_or_create(%{
90 group: config2.group,
91 key: config2.key,
92 value: 777
93 })
94
95 updated1 = ConfigDB.get_by_params(%{group: config1.group, key: config1.key})
96 updated2 = ConfigDB.get_by_params(%{group: config2.group, key: config2.key})
97
98 assert updated1.value == [another_repo: [Pleroma.Repo]]
99 assert updated2.value == 777
100 end
101
102 test "full update if value is not keyword" do
103 config =
104 insert(:config,
105 group: ":tesla",
106 key: ":adapter",
107 value: Tesla.Adapter.Hackney
108 )
109
110 {:ok, _config} =
111 ConfigDB.update_or_create(%{
112 group: config.group,
113 key: config.key,
114 value: Tesla.Adapter.Httpc
115 })
116
117 updated = ConfigDB.get_by_params(%{group: config.group, key: config.key})
118
119 assert updated.value == Tesla.Adapter.Httpc
120 end
121
122 test "only full update for some subkeys" do
123 config1 =
124 insert(:config,
125 key: ":emoji",
126 value: [groups: [a: 1, b: 2], key: [a: 1]]
127 )
128
129 config2 =
130 insert(:config,
131 key: ":assets",
132 value: [mascots: [a: 1, b: 2], key: [a: 1]]
133 )
134
135 {:ok, _config} =
136 ConfigDB.update_or_create(%{
137 group: config1.group,
138 key: config1.key,
139 value: [groups: [c: 3, d: 4], key: [b: 2]]
140 })
141
142 {:ok, _config} =
143 ConfigDB.update_or_create(%{
144 group: config2.group,
145 key: config2.key,
146 value: [mascots: [c: 3, d: 4], key: [b: 2]]
147 })
148
149 updated1 = ConfigDB.get_by_params(%{group: config1.group, key: config1.key})
150 updated2 = ConfigDB.get_by_params(%{group: config2.group, key: config2.key})
151
152 assert updated1.value == [groups: [c: 3, d: 4], key: [a: 1, b: 2]]
153 assert updated2.value == [mascots: [c: 3, d: 4], key: [a: 1, b: 2]]
154 end
155 end
156
157 describe "delete/1" do
158 test "error on deleting non existing setting" do
159 {:error, error} = ConfigDB.delete(%{group: ":pleroma", key: ":key"})
160 assert error =~ "Config with params %{group: \":pleroma\", key: \":key\"} not found"
161 end
162
163 test "full delete" do
164 config = insert(:config)
165 {:ok, deleted} = ConfigDB.delete(%{group: config.group, key: config.key})
166 assert Ecto.get_meta(deleted, :state) == :deleted
167 refute ConfigDB.get_by_params(%{group: config.group, key: config.key})
168 end
169
170 test "partial subkeys delete" do
171 config = insert(:config, value: [groups: [a: 1, b: 2], key: [a: 1]])
172
173 {:ok, deleted} =
174 ConfigDB.delete(%{group: config.group, key: config.key, subkeys: [":groups"]})
175
176 assert Ecto.get_meta(deleted, :state) == :loaded
177
178 assert deleted.value == [key: [a: 1]]
179
180 updated = ConfigDB.get_by_params(%{group: config.group, key: config.key})
181
182 assert updated.value == deleted.value
183 end
184
185 test "full delete if remaining value after subkeys deletion is empty list" do
186 config = insert(:config, value: [groups: [a: 1, b: 2]])
187
188 {:ok, deleted} =
189 ConfigDB.delete(%{group: config.group, key: config.key, subkeys: [":groups"]})
190
191 assert Ecto.get_meta(deleted, :state) == :deleted
192
193 refute ConfigDB.get_by_params(%{group: config.group, key: config.key})
194 end
195 end
196
197 describe "to_elixir_types/1" do
198 test "string" do
199 assert ConfigDB.to_elixir_types("value as string") == "value as string"
200 end
201
202 test "boolean" do
203 assert ConfigDB.to_elixir_types(false) == false
204 end
205
206 test "nil" do
207 assert ConfigDB.to_elixir_types(nil) == nil
208 end
209
210 test "integer" do
211 assert ConfigDB.to_elixir_types(150) == 150
212 end
213
214 test "atom" do
215 assert ConfigDB.to_elixir_types(":atom") == :atom
216 end
217
218 test "ssl options" do
219 assert ConfigDB.to_elixir_types([":tlsv1", ":tlsv1.1", ":tlsv1.2"]) == [
220 :tlsv1,
221 :"tlsv1.1",
222 :"tlsv1.2"
223 ]
224 end
225
226 test "pleroma module" do
227 assert ConfigDB.to_elixir_types("Pleroma.Bookmark") == Pleroma.Bookmark
228 end
229
230 test "pleroma string" do
231 assert ConfigDB.to_elixir_types("Pleroma") == "Pleroma"
232 end
233
234 test "phoenix module" do
235 assert ConfigDB.to_elixir_types("Phoenix.Socket.V1.JSONSerializer") ==
236 Phoenix.Socket.V1.JSONSerializer
237 end
238
239 test "tesla module" do
240 assert ConfigDB.to_elixir_types("Tesla.Adapter.Hackney") == Tesla.Adapter.Hackney
241 end
242
243 test "ExSyslogger module" do
244 assert ConfigDB.to_elixir_types("ExSyslogger") == ExSyslogger
245 end
246
247 test "Quack.Logger module" do
248 assert ConfigDB.to_elixir_types("Quack.Logger") == Quack.Logger
249 end
250
251 test "Swoosh.Adapters modules" do
252 assert ConfigDB.to_elixir_types("Swoosh.Adapters.SMTP") == Swoosh.Adapters.SMTP
253 assert ConfigDB.to_elixir_types("Swoosh.Adapters.AmazonSES") == Swoosh.Adapters.AmazonSES
254 end
255
256 test "sigil" do
257 assert ConfigDB.to_elixir_types("~r[comp[lL][aA][iI][nN]er]") == ~r/comp[lL][aA][iI][nN]er/
258 end
259
260 test "link sigil" do
261 assert ConfigDB.to_elixir_types("~r/https:\/\/example.com/") == ~r/https:\/\/example.com/
262 end
263
264 test "link sigil with um modifiers" do
265 assert ConfigDB.to_elixir_types("~r/https:\/\/example.com/um") ==
266 ~r/https:\/\/example.com/um
267 end
268
269 test "link sigil with i modifier" do
270 assert ConfigDB.to_elixir_types("~r/https:\/\/example.com/i") == ~r/https:\/\/example.com/i
271 end
272
273 test "link sigil with s modifier" do
274 assert ConfigDB.to_elixir_types("~r/https:\/\/example.com/s") == ~r/https:\/\/example.com/s
275 end
276
277 test "raise if valid delimiter not found" do
278 assert_raise ArgumentError, "valid delimiter for Regex expression not found", fn ->
279 ConfigDB.to_elixir_types("~r/https://[]{}<>\"'()|example.com/s")
280 end
281 end
282
283 test "2 child tuple" do
284 assert ConfigDB.to_elixir_types(%{"tuple" => ["v1", ":v2"]}) == {"v1", :v2}
285 end
286
287 test "proxy tuple with localhost" do
288 assert ConfigDB.to_elixir_types(%{
289 "tuple" => [":proxy_url", %{"tuple" => [":socks5", "localhost", 1234]}]
290 }) == {:proxy_url, {:socks5, :localhost, 1234}}
291 end
292
293 test "proxy tuple with domain" do
294 assert ConfigDB.to_elixir_types(%{
295 "tuple" => [":proxy_url", %{"tuple" => [":socks5", "domain.com", 1234]}]
296 }) == {:proxy_url, {:socks5, 'domain.com', 1234}}
297 end
298
299 test "proxy tuple with ip" do
300 assert ConfigDB.to_elixir_types(%{
301 "tuple" => [":proxy_url", %{"tuple" => [":socks5", "127.0.0.1", 1234]}]
302 }) == {:proxy_url, {:socks5, {127, 0, 0, 1}, 1234}}
303 end
304
305 test "tuple with n childs" do
306 assert ConfigDB.to_elixir_types(%{
307 "tuple" => [
308 "v1",
309 ":v2",
310 "Pleroma.Bookmark",
311 150,
312 false,
313 "Phoenix.Socket.V1.JSONSerializer"
314 ]
315 }) == {"v1", :v2, Pleroma.Bookmark, 150, false, Phoenix.Socket.V1.JSONSerializer}
316 end
317
318 test "map with string key" do
319 assert ConfigDB.to_elixir_types(%{"key" => "value"}) == %{"key" => "value"}
320 end
321
322 test "map with atom key" do
323 assert ConfigDB.to_elixir_types(%{":key" => "value"}) == %{key: "value"}
324 end
325
326 test "list of strings" do
327 assert ConfigDB.to_elixir_types(["v1", "v2", "v3"]) == ["v1", "v2", "v3"]
328 end
329
330 test "list of modules" do
331 assert ConfigDB.to_elixir_types(["Pleroma.Repo", "Pleroma.Activity"]) == [
332 Pleroma.Repo,
333 Pleroma.Activity
334 ]
335 end
336
337 test "list of atoms" do
338 assert ConfigDB.to_elixir_types([":v1", ":v2", ":v3"]) == [:v1, :v2, :v3]
339 end
340
341 test "list of mixed values" do
342 assert ConfigDB.to_elixir_types([
343 "v1",
344 ":v2",
345 "Pleroma.Repo",
346 "Phoenix.Socket.V1.JSONSerializer",
347 15,
348 false
349 ]) == [
350 "v1",
351 :v2,
352 Pleroma.Repo,
353 Phoenix.Socket.V1.JSONSerializer,
354 15,
355 false
356 ]
357 end
358
359 test "simple keyword" do
360 assert ConfigDB.to_elixir_types([%{"tuple" => [":key", "value"]}]) == [key: "value"]
361 end
362
363 test "keyword" do
364 assert ConfigDB.to_elixir_types([
365 %{"tuple" => [":types", "Pleroma.PostgresTypes"]},
366 %{"tuple" => [":telemetry_event", ["Pleroma.Repo.Instrumenter"]]},
367 %{"tuple" => [":migration_lock", nil]},
368 %{"tuple" => [":key1", 150]},
369 %{"tuple" => [":key2", "string"]}
370 ]) == [
371 types: Pleroma.PostgresTypes,
372 telemetry_event: [Pleroma.Repo.Instrumenter],
373 migration_lock: nil,
374 key1: 150,
375 key2: "string"
376 ]
377 end
378
379 test "trandformed keyword" do
380 assert ConfigDB.to_elixir_types(a: 1, b: 2, c: "string") == [a: 1, b: 2, c: "string"]
381 end
382
383 test "complex keyword with nested mixed childs" do
384 assert ConfigDB.to_elixir_types([
385 %{"tuple" => [":uploader", "Pleroma.Uploaders.Local"]},
386 %{"tuple" => [":filters", ["Pleroma.Upload.Filter.Dedupe"]]},
387 %{"tuple" => [":link_name", true]},
388 %{"tuple" => [":proxy_remote", false]},
389 %{"tuple" => [":common_map", %{":key" => "value"}]},
390 %{
391 "tuple" => [
392 ":proxy_opts",
393 [
394 %{"tuple" => [":redirect_on_failure", false]},
395 %{"tuple" => [":max_body_length", 1_048_576]},
396 %{
397 "tuple" => [
398 ":http",
399 [
400 %{"tuple" => [":follow_redirect", true]},
401 %{"tuple" => [":pool", ":upload"]}
402 ]
403 ]
404 }
405 ]
406 ]
407 }
408 ]) == [
409 uploader: Pleroma.Uploaders.Local,
410 filters: [Pleroma.Upload.Filter.Dedupe],
411 link_name: true,
412 proxy_remote: false,
413 common_map: %{key: "value"},
414 proxy_opts: [
415 redirect_on_failure: false,
416 max_body_length: 1_048_576,
417 http: [
418 follow_redirect: true,
419 pool: :upload
420 ]
421 ]
422 ]
423 end
424
425 test "common keyword" do
426 assert ConfigDB.to_elixir_types([
427 %{"tuple" => [":level", ":warn"]},
428 %{"tuple" => [":meta", [":all"]]},
429 %{"tuple" => [":path", ""]},
430 %{"tuple" => [":val", nil]},
431 %{"tuple" => [":webhook_url", "https://hooks.slack.com/services/YOUR-KEY-HERE"]}
432 ]) == [
433 level: :warn,
434 meta: [:all],
435 path: "",
436 val: nil,
437 webhook_url: "https://hooks.slack.com/services/YOUR-KEY-HERE"
438 ]
439 end
440
441 test "complex keyword with sigil" do
442 assert ConfigDB.to_elixir_types([
443 %{"tuple" => [":federated_timeline_removal", []]},
444 %{"tuple" => [":reject", ["~r/comp[lL][aA][iI][nN]er/"]]},
445 %{"tuple" => [":replace", []]}
446 ]) == [
447 federated_timeline_removal: [],
448 reject: [~r/comp[lL][aA][iI][nN]er/],
449 replace: []
450 ]
451 end
452
453 test "complex keyword with tuples with more than 2 values" do
454 assert ConfigDB.to_elixir_types([
455 %{
456 "tuple" => [
457 ":http",
458 [
459 %{
460 "tuple" => [
461 ":key1",
462 [
463 %{
464 "tuple" => [
465 ":_",
466 [
467 %{
468 "tuple" => [
469 "/api/v1/streaming",
470 "Pleroma.Web.MastodonAPI.WebsocketHandler",
471 []
472 ]
473 },
474 %{
475 "tuple" => [
476 "/websocket",
477 "Phoenix.Endpoint.CowboyWebSocket",
478 %{
479 "tuple" => [
480 "Phoenix.Transports.WebSocket",
481 %{
482 "tuple" => [
483 "Pleroma.Web.Endpoint",
484 "Pleroma.Web.UserSocket",
485 []
486 ]
487 }
488 ]
489 }
490 ]
491 },
492 %{
493 "tuple" => [
494 ":_",
495 "Phoenix.Endpoint.Cowboy2Handler",
496 %{"tuple" => ["Pleroma.Web.Endpoint", []]}
497 ]
498 }
499 ]
500 ]
501 }
502 ]
503 ]
504 }
505 ]
506 ]
507 }
508 ]) == [
509 http: [
510 key1: [
511 {:_,
512 [
513 {"/api/v1/streaming", Pleroma.Web.MastodonAPI.WebsocketHandler, []},
514 {"/websocket", Phoenix.Endpoint.CowboyWebSocket,
515 {Phoenix.Transports.WebSocket,
516 {Pleroma.Web.Endpoint, Pleroma.Web.UserSocket, []}}},
517 {:_, Phoenix.Endpoint.Cowboy2Handler, {Pleroma.Web.Endpoint, []}}
518 ]}
519 ]
520 ]
521 ]
522 end
523 end
524 end