retry and retry_timeout settings default change
[akkoma] / test / pool / connections_test.exs
1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
3 # SPDX-License-Identifier: AGPL-3.0-only
4
5 defmodule Pleroma.Pool.ConnectionsTest do
6 use ExUnit.Case
7 use Pleroma.Tests.Helpers
8 import ExUnit.CaptureLog
9 alias Pleroma.Gun.API
10 alias Pleroma.Gun.Conn
11 alias Pleroma.Pool.Connections
12
13 setup_all do
14 {:ok, _} = Registry.start_link(keys: :unique, name: API.Mock)
15 :ok
16 end
17
18 clear_config([:connections_pool, :retry]) do
19 Pleroma.Config.put([:connections_pool, :retry], 5)
20 end
21
22 setup do
23 name = :test_connections
24 adapter = Application.get_env(:tesla, :adapter)
25 Application.put_env(:tesla, :adapter, Tesla.Adapter.Gun)
26
27 {:ok, pid} = Connections.start_link({name, [max_connections: 2, checkin_timeout: 1_500]})
28
29 on_exit(fn ->
30 Application.put_env(:tesla, :adapter, adapter)
31
32 if Process.alive?(pid) do
33 GenServer.stop(name)
34 end
35 end)
36
37 {:ok, name: name}
38 end
39
40 describe "alive?/2" do
41 test "is alive", %{name: name} do
42 assert Connections.alive?(name)
43 end
44
45 test "returns false if not started" do
46 refute Connections.alive?(:some_random_name)
47 end
48 end
49
50 test "opens connection and reuse it on next request", %{name: name} do
51 url = "http://some-domain.com"
52 key = "http:some-domain.com:80"
53 refute Connections.checkin(url, name)
54 :ok = Conn.open(url, name)
55
56 conn = Connections.checkin(url, name)
57 assert is_pid(conn)
58 assert Process.alive?(conn)
59
60 self = self()
61
62 %Connections{
63 conns: %{
64 ^key => %Conn{
65 conn: ^conn,
66 gun_state: :up,
67 used_by: [{^self, _}],
68 conn_state: :active
69 }
70 }
71 } = Connections.get_state(name)
72
73 reused_conn = Connections.checkin(url, name)
74
75 assert conn == reused_conn
76
77 %Connections{
78 conns: %{
79 ^key => %Conn{
80 conn: ^conn,
81 gun_state: :up,
82 used_by: [{^self, _}, {^self, _}],
83 conn_state: :active
84 }
85 }
86 } = Connections.get_state(name)
87
88 :ok = Connections.checkout(conn, self, name)
89
90 %Connections{
91 conns: %{
92 ^key => %Conn{
93 conn: ^conn,
94 gun_state: :up,
95 used_by: [{^self, _}],
96 conn_state: :active
97 }
98 }
99 } = Connections.get_state(name)
100
101 :ok = Connections.checkout(conn, self, name)
102
103 %Connections{
104 conns: %{
105 ^key => %Conn{
106 conn: ^conn,
107 gun_state: :up,
108 used_by: [],
109 conn_state: :idle
110 }
111 }
112 } = Connections.get_state(name)
113 end
114
115 test "reuse connection for idna domains", %{name: name} do
116 url = "http://ですsome-domain.com"
117 refute Connections.checkin(url, name)
118
119 :ok = Conn.open(url, name)
120
121 conn = Connections.checkin(url, name)
122 assert is_pid(conn)
123 assert Process.alive?(conn)
124
125 self = self()
126
127 %Connections{
128 conns: %{
129 "http:ですsome-domain.com:80" => %Conn{
130 conn: ^conn,
131 gun_state: :up,
132 used_by: [{^self, _}],
133 conn_state: :active
134 }
135 }
136 } = Connections.get_state(name)
137
138 reused_conn = Connections.checkin(url, name)
139
140 assert conn == reused_conn
141 end
142
143 test "reuse for ipv4", %{name: name} do
144 url = "http://127.0.0.1"
145
146 refute Connections.checkin(url, name)
147
148 :ok = Conn.open(url, name)
149
150 conn = Connections.checkin(url, name)
151 assert is_pid(conn)
152 assert Process.alive?(conn)
153
154 self = self()
155
156 %Connections{
157 conns: %{
158 "http:127.0.0.1:80" => %Conn{
159 conn: ^conn,
160 gun_state: :up,
161 used_by: [{^self, _}],
162 conn_state: :active
163 }
164 }
165 } = Connections.get_state(name)
166
167 reused_conn = Connections.checkin(url, name)
168
169 assert conn == reused_conn
170
171 :ok = Connections.checkout(conn, self, name)
172 :ok = Connections.checkout(reused_conn, self, name)
173
174 %Connections{
175 conns: %{
176 "http:127.0.0.1:80" => %Conn{
177 conn: ^conn,
178 gun_state: :up,
179 used_by: [],
180 conn_state: :idle
181 }
182 }
183 } = Connections.get_state(name)
184 end
185
186 test "reuse for ipv6", %{name: name} do
187 url = "http://[2a03:2880:f10c:83:face:b00c:0:25de]"
188
189 refute Connections.checkin(url, name)
190
191 :ok = Conn.open(url, name)
192
193 conn = Connections.checkin(url, name)
194 assert is_pid(conn)
195 assert Process.alive?(conn)
196
197 self = self()
198
199 %Connections{
200 conns: %{
201 "http:2a03:2880:f10c:83:face:b00c:0:25de:80" => %Conn{
202 conn: ^conn,
203 gun_state: :up,
204 used_by: [{^self, _}],
205 conn_state: :active
206 }
207 }
208 } = Connections.get_state(name)
209
210 reused_conn = Connections.checkin(url, name)
211
212 assert conn == reused_conn
213 end
214
215 test "up and down ipv4", %{name: name} do
216 self = self()
217 url = "http://127.0.0.1"
218 :ok = Conn.open(url, name)
219 conn = Connections.checkin(url, name)
220 send(name, {:gun_down, conn, nil, nil, nil})
221 send(name, {:gun_up, conn, nil})
222
223 %Connections{
224 conns: %{
225 "http:127.0.0.1:80" => %Conn{
226 conn: ^conn,
227 gun_state: :up,
228 used_by: [{^self, _}],
229 conn_state: :active
230 }
231 }
232 } = Connections.get_state(name)
233 end
234
235 test "up and down ipv6", %{name: name} do
236 self = self()
237 url = "http://[2a03:2880:f10c:83:face:b00c:0:25de]"
238 :ok = Conn.open(url, name)
239 conn = Connections.checkin(url, name)
240 send(name, {:gun_down, conn, nil, nil, nil})
241 send(name, {:gun_up, conn, nil})
242
243 %Connections{
244 conns: %{
245 "http:2a03:2880:f10c:83:face:b00c:0:25de:80" => %Conn{
246 conn: ^conn,
247 gun_state: :up,
248 used_by: [{^self, _}],
249 conn_state: :active
250 }
251 }
252 } = Connections.get_state(name)
253 end
254
255 test "reuses connection based on protocol", %{name: name} do
256 http_url = "http://some-domain.com"
257 http_key = "http:some-domain.com:80"
258 https_url = "https://some-domain.com"
259 https_key = "https:some-domain.com:443"
260
261 refute Connections.checkin(http_url, name)
262 :ok = Conn.open(http_url, name)
263 conn = Connections.checkin(http_url, name)
264 assert is_pid(conn)
265 assert Process.alive?(conn)
266
267 refute Connections.checkin(https_url, name)
268 :ok = Conn.open(https_url, name)
269 https_conn = Connections.checkin(https_url, name)
270
271 refute conn == https_conn
272
273 reused_https = Connections.checkin(https_url, name)
274
275 refute conn == reused_https
276
277 assert reused_https == https_conn
278
279 %Connections{
280 conns: %{
281 ^http_key => %Conn{
282 conn: ^conn,
283 gun_state: :up
284 },
285 ^https_key => %Conn{
286 conn: ^https_conn,
287 gun_state: :up
288 }
289 }
290 } = Connections.get_state(name)
291 end
292
293 test "connection can't get up", %{name: name} do
294 url = "http://gun-not-up.com"
295
296 assert capture_log(fn ->
297 refute Conn.open(url, name)
298 refute Connections.checkin(url, name)
299 end) =~
300 "Received error on opening connection http://gun-not-up.com {:error, :timeout}"
301 end
302
303 test "process gun_down message and then gun_up", %{name: name} do
304 self = self()
305 url = "http://gun-down-and-up.com"
306 key = "http:gun-down-and-up.com:80"
307 :ok = Conn.open(url, name)
308 conn = Connections.checkin(url, name)
309
310 assert is_pid(conn)
311 assert Process.alive?(conn)
312
313 %Connections{
314 conns: %{
315 ^key => %Conn{
316 conn: ^conn,
317 gun_state: :up,
318 used_by: [{^self, _}]
319 }
320 }
321 } = Connections.get_state(name)
322
323 send(name, {:gun_down, conn, :http, nil, nil})
324
325 %Connections{
326 conns: %{
327 ^key => %Conn{
328 conn: ^conn,
329 gun_state: :down,
330 used_by: [{^self, _}]
331 }
332 }
333 } = Connections.get_state(name)
334
335 send(name, {:gun_up, conn, :http})
336
337 conn2 = Connections.checkin(url, name)
338 assert conn == conn2
339
340 assert is_pid(conn2)
341 assert Process.alive?(conn2)
342
343 %Connections{
344 conns: %{
345 ^key => %Conn{
346 conn: _,
347 gun_state: :up,
348 used_by: [{^self, _}, {^self, _}]
349 }
350 }
351 } = Connections.get_state(name)
352 end
353
354 test "async processes get same conn for same domain", %{name: name} do
355 url = "http://some-domain.com"
356 :ok = Conn.open(url, name)
357
358 tasks =
359 for _ <- 1..5 do
360 Task.async(fn ->
361 Connections.checkin(url, name)
362 end)
363 end
364
365 tasks_with_results = Task.yield_many(tasks)
366
367 results =
368 Enum.map(tasks_with_results, fn {task, res} ->
369 res || Task.shutdown(task, :brutal_kill)
370 end)
371
372 conns = for {:ok, value} <- results, do: value
373
374 %Connections{
375 conns: %{
376 "http:some-domain.com:80" => %Conn{
377 conn: conn,
378 gun_state: :up
379 }
380 }
381 } = Connections.get_state(name)
382
383 assert Enum.all?(conns, fn res -> res == conn end)
384 end
385
386 test "remove frequently used and idle", %{name: name} do
387 self = self()
388 http_url = "http://some-domain.com"
389 https_url = "https://some-domain.com"
390 :ok = Conn.open(https_url, name)
391 :ok = Conn.open(http_url, name)
392
393 conn1 = Connections.checkin(https_url, name)
394
395 [conn2 | _conns] =
396 for _ <- 1..4 do
397 Connections.checkin(http_url, name)
398 end
399
400 http_key = "http:some-domain.com:80"
401
402 %Connections{
403 conns: %{
404 ^http_key => %Conn{
405 conn: ^conn2,
406 gun_state: :up,
407 conn_state: :active,
408 used_by: [{^self, _}, {^self, _}, {^self, _}, {^self, _}]
409 },
410 "https:some-domain.com:443" => %Conn{
411 conn: ^conn1,
412 gun_state: :up,
413 conn_state: :active,
414 used_by: [{^self, _}]
415 }
416 }
417 } = Connections.get_state(name)
418
419 :ok = Connections.checkout(conn1, self, name)
420
421 another_url = "http://another-domain.com"
422 :ok = Conn.open(another_url, name)
423 conn = Connections.checkin(another_url, name)
424
425 %Connections{
426 conns: %{
427 "http:another-domain.com:80" => %Conn{
428 conn: ^conn,
429 gun_state: :up
430 },
431 ^http_key => %Conn{
432 conn: _,
433 gun_state: :up
434 }
435 }
436 } = Connections.get_state(name)
437 end
438
439 describe "integration test" do
440 @describetag :integration
441
442 clear_config(API) do
443 Pleroma.Config.put(API, Pleroma.Gun)
444 end
445
446 test "opens connection and change owner", %{name: name} do
447 url = "https://httpbin.org"
448 :ok = Conn.open(url, name)
449 conn = Connections.checkin(url, name)
450
451 pid = Process.whereis(name)
452
453 assert :gun.info(conn).owner == pid
454 end
455
456 test "opens connection and reuse it on next request", %{name: name} do
457 url = "http://httpbin.org"
458 :ok = Conn.open(url, name)
459 Process.sleep(250)
460 conn = Connections.checkin(url, name)
461
462 assert is_pid(conn)
463 assert Process.alive?(conn)
464
465 reused_conn = Connections.checkin(url, name)
466
467 assert conn == reused_conn
468
469 %Connections{
470 conns: %{
471 "http:httpbin.org:80" => %Conn{
472 conn: ^conn,
473 gun_state: :up
474 }
475 }
476 } = Connections.get_state(name)
477 end
478
479 test "opens ssl connection and reuse it on next request", %{name: name} do
480 url = "https://httpbin.org"
481 :ok = Conn.open(url, name)
482 Process.sleep(1_000)
483 conn = Connections.checkin(url, name)
484
485 assert is_pid(conn)
486 assert Process.alive?(conn)
487
488 reused_conn = Connections.checkin(url, name)
489
490 assert conn == reused_conn
491
492 %Connections{
493 conns: %{
494 "https:httpbin.org:443" => %Conn{
495 conn: ^conn,
496 gun_state: :up
497 }
498 }
499 } = Connections.get_state(name)
500 end
501
502 test "remove frequently used and idle", %{name: name} do
503 self = self()
504 https1 = "https://www.google.com"
505 https2 = "https://httpbin.org"
506
507 :ok = Conn.open(https1, name)
508 :ok = Conn.open(https2, name)
509 Process.sleep(1_500)
510 conn = Connections.checkin(https1, name)
511
512 for _ <- 1..4 do
513 Connections.checkin(https2, name)
514 end
515
516 %Connections{
517 conns: %{
518 "https:httpbin.org:443" => %Conn{
519 conn: _,
520 gun_state: :up
521 },
522 "https:www.google.com:443" => %Conn{
523 conn: _,
524 gun_state: :up
525 }
526 }
527 } = Connections.get_state(name)
528
529 :ok = Connections.checkout(conn, self, name)
530 http = "http://httpbin.org"
531 Process.sleep(1_000)
532 :ok = Conn.open(http, name)
533 conn = Connections.checkin(http, name)
534
535 %Connections{
536 conns: %{
537 "http:httpbin.org:80" => %Conn{
538 conn: ^conn,
539 gun_state: :up
540 },
541 "https:httpbin.org:443" => %Conn{
542 conn: _,
543 gun_state: :up
544 }
545 }
546 } = Connections.get_state(name)
547 end
548
549 test "remove earlier used and idle", %{name: name} do
550 self = self()
551
552 https1 = "https://www.google.com"
553 https2 = "https://httpbin.org"
554 :ok = Conn.open(https1, name)
555 :ok = Conn.open(https2, name)
556 Process.sleep(1_500)
557
558 Connections.checkin(https1, name)
559 conn = Connections.checkin(https1, name)
560
561 Process.sleep(1_000)
562 Connections.checkin(https2, name)
563 Connections.checkin(https2, name)
564
565 %Connections{
566 conns: %{
567 "https:httpbin.org:443" => %Conn{
568 conn: _,
569 gun_state: :up
570 },
571 "https:www.google.com:443" => %Conn{
572 conn: ^conn,
573 gun_state: :up
574 }
575 }
576 } = Connections.get_state(name)
577
578 :ok = Connections.checkout(conn, self, name)
579 :ok = Connections.checkout(conn, self, name)
580
581 http = "http://httpbin.org"
582 :ok = Conn.open(http, name)
583 Process.sleep(1_000)
584
585 conn = Connections.checkin(http, name)
586
587 %Connections{
588 conns: %{
589 "http:httpbin.org:80" => %Conn{
590 conn: ^conn,
591 gun_state: :up
592 },
593 "https:httpbin.org:443" => %Conn{
594 conn: _,
595 gun_state: :up
596 }
597 }
598 } = Connections.get_state(name)
599 end
600
601 test "doesn't open new conn on pool overflow", %{name: name} do
602 self = self()
603
604 https1 = "https://www.google.com"
605 https2 = "https://httpbin.org"
606 :ok = Conn.open(https1, name)
607 :ok = Conn.open(https2, name)
608 Process.sleep(1_000)
609 Connections.checkin(https1, name)
610 conn1 = Connections.checkin(https1, name)
611 conn2 = Connections.checkin(https2, name)
612
613 %Connections{
614 conns: %{
615 "https:httpbin.org:443" => %Conn{
616 conn: ^conn2,
617 gun_state: :up,
618 conn_state: :active,
619 used_by: [{^self, _}]
620 },
621 "https:www.google.com:443" => %Conn{
622 conn: ^conn1,
623 gun_state: :up,
624 conn_state: :active,
625 used_by: [{^self, _}, {^self, _}]
626 }
627 }
628 } = Connections.get_state(name)
629
630 refute Connections.checkin("http://httpbin.org", name)
631
632 %Connections{
633 conns: %{
634 "https:httpbin.org:443" => %Conn{
635 conn: ^conn2,
636 gun_state: :up,
637 conn_state: :active,
638 used_by: [{^self, _}]
639 },
640 "https:www.google.com:443" => %Conn{
641 conn: ^conn1,
642 gun_state: :up,
643 conn_state: :active,
644 used_by: [{^self, _}, {^self, _}]
645 }
646 }
647 } = Connections.get_state(name)
648 end
649
650 test "get idle connection with the smallest crf", %{
651 name: name
652 } do
653 self = self()
654
655 https1 = "https://www.google.com"
656 https2 = "https://httpbin.org"
657
658 :ok = Conn.open(https1, name)
659 :ok = Conn.open(https2, name)
660 Process.sleep(1_500)
661 Connections.checkin(https1, name)
662 Connections.checkin(https2, name)
663 Connections.checkin(https1, name)
664 conn1 = Connections.checkin(https1, name)
665 conn2 = Connections.checkin(https2, name)
666
667 %Connections{
668 conns: %{
669 "https:httpbin.org:443" => %Conn{
670 conn: ^conn2,
671 gun_state: :up,
672 conn_state: :active,
673 used_by: [{^self, _}, {^self, _}],
674 crf: crf2
675 },
676 "https:www.google.com:443" => %Conn{
677 conn: ^conn1,
678 gun_state: :up,
679 conn_state: :active,
680 used_by: [{^self, _}, {^self, _}, {^self, _}],
681 crf: crf1
682 }
683 }
684 } = Connections.get_state(name)
685
686 assert crf1 > crf2
687
688 :ok = Connections.checkout(conn1, self, name)
689 :ok = Connections.checkout(conn1, self, name)
690 :ok = Connections.checkout(conn1, self, name)
691
692 :ok = Connections.checkout(conn2, self, name)
693 :ok = Connections.checkout(conn2, self, name)
694
695 %Connections{
696 conns: %{
697 "https:httpbin.org:443" => %Conn{
698 conn: ^conn2,
699 gun_state: :up,
700 conn_state: :idle,
701 used_by: []
702 },
703 "https:www.google.com:443" => %Conn{
704 conn: ^conn1,
705 gun_state: :up,
706 conn_state: :idle,
707 used_by: []
708 }
709 }
710 } = Connections.get_state(name)
711
712 http = "http://httpbin.org"
713 :ok = Conn.open(http, name)
714 Process.sleep(1_000)
715 conn = Connections.checkin(http, name)
716
717 %Connections{
718 conns: %{
719 "https:www.google.com:443" => %Conn{
720 conn: ^conn1,
721 gun_state: :up,
722 conn_state: :idle,
723 used_by: [],
724 crf: crf1
725 },
726 "http:httpbin.org:80" => %Conn{
727 conn: ^conn,
728 gun_state: :up,
729 conn_state: :active,
730 used_by: [{^self, _}],
731 crf: crf
732 }
733 }
734 } = Connections.get_state(name)
735
736 assert crf1 > crf
737 end
738 end
739
740 describe "with proxy" do
741 test "as ip", %{name: name} do
742 url = "http://proxy-string.com"
743 key = "http:proxy-string.com:80"
744 :ok = Conn.open(url, name, proxy: {{127, 0, 0, 1}, 8123})
745
746 conn = Connections.checkin(url, name)
747
748 %Connections{
749 conns: %{
750 ^key => %Conn{
751 conn: ^conn,
752 gun_state: :up
753 }
754 }
755 } = Connections.get_state(name)
756
757 reused_conn = Connections.checkin(url, name)
758
759 assert reused_conn == conn
760 end
761
762 test "as host", %{name: name} do
763 url = "http://proxy-tuple-atom.com"
764 :ok = Conn.open(url, name, proxy: {'localhost', 9050})
765 conn = Connections.checkin(url, name)
766
767 %Connections{
768 conns: %{
769 "http:proxy-tuple-atom.com:80" => %Conn{
770 conn: ^conn,
771 gun_state: :up
772 }
773 }
774 } = Connections.get_state(name)
775
776 reused_conn = Connections.checkin(url, name)
777
778 assert reused_conn == conn
779 end
780
781 test "as ip and ssl", %{name: name} do
782 url = "https://proxy-string.com"
783
784 :ok = Conn.open(url, name, proxy: {{127, 0, 0, 1}, 8123})
785 conn = Connections.checkin(url, name)
786
787 %Connections{
788 conns: %{
789 "https:proxy-string.com:443" => %Conn{
790 conn: ^conn,
791 gun_state: :up
792 }
793 }
794 } = Connections.get_state(name)
795
796 reused_conn = Connections.checkin(url, name)
797
798 assert reused_conn == conn
799 end
800
801 test "as host and ssl", %{name: name} do
802 url = "https://proxy-tuple-atom.com"
803 :ok = Conn.open(url, name, proxy: {'localhost', 9050})
804 conn = Connections.checkin(url, name)
805
806 %Connections{
807 conns: %{
808 "https:proxy-tuple-atom.com:443" => %Conn{
809 conn: ^conn,
810 gun_state: :up
811 }
812 }
813 } = Connections.get_state(name)
814
815 reused_conn = Connections.checkin(url, name)
816
817 assert reused_conn == conn
818 end
819
820 test "with socks type", %{name: name} do
821 url = "http://proxy-socks.com"
822
823 :ok = Conn.open(url, name, proxy: {:socks5, 'localhost', 1234})
824
825 conn = Connections.checkin(url, name)
826
827 %Connections{
828 conns: %{
829 "http:proxy-socks.com:80" => %Conn{
830 conn: ^conn,
831 gun_state: :up
832 }
833 }
834 } = Connections.get_state(name)
835
836 reused_conn = Connections.checkin(url, name)
837
838 assert reused_conn == conn
839 end
840
841 test "with socks4 type and ssl", %{name: name} do
842 url = "https://proxy-socks.com"
843
844 :ok = Conn.open(url, name, proxy: {:socks4, 'localhost', 1234})
845
846 conn = Connections.checkin(url, name)
847
848 %Connections{
849 conns: %{
850 "https:proxy-socks.com:443" => %Conn{
851 conn: ^conn,
852 gun_state: :up
853 }
854 }
855 } = Connections.get_state(name)
856
857 reused_conn = Connections.checkin(url, name)
858
859 assert reused_conn == conn
860 end
861 end
862
863 describe "crf/3" do
864 setup do
865 crf = Connections.crf(1, 10, 1)
866 {:ok, crf: crf}
867 end
868
869 test "more used will have crf higher", %{crf: crf} do
870 # used 3 times
871 crf1 = Connections.crf(1, 10, crf)
872 crf1 = Connections.crf(1, 10, crf1)
873
874 # used 2 times
875 crf2 = Connections.crf(1, 10, crf)
876
877 assert crf1 > crf2
878 end
879
880 test "recently used will have crf higher on equal references", %{crf: crf} do
881 # used 3 sec ago
882 crf1 = Connections.crf(3, 10, crf)
883
884 # used 4 sec ago
885 crf2 = Connections.crf(4, 10, crf)
886
887 assert crf1 > crf2
888 end
889
890 test "equal crf on equal reference and time", %{crf: crf} do
891 # used 2 times
892 crf1 = Connections.crf(1, 10, crf)
893
894 # used 2 times
895 crf2 = Connections.crf(1, 10, crf)
896
897 assert crf1 == crf2
898 end
899
900 test "recently used will have higher crf", %{crf: crf} do
901 crf1 = Connections.crf(2, 10, crf)
902 crf1 = Connections.crf(1, 10, crf1)
903
904 crf2 = Connections.crf(3, 10, crf)
905 crf2 = Connections.crf(4, 10, crf2)
906 assert crf1 > crf2
907 end
908 end
909
910 describe "get_unused_conns/1" do
911 test "crf is equalent, sorting by reference", %{name: name} do
912 Connections.add_conn(name, "1", %Conn{
913 conn_state: :idle,
914 last_reference: now() - 1
915 })
916
917 Connections.add_conn(name, "2", %Conn{
918 conn_state: :idle,
919 last_reference: now()
920 })
921
922 assert [{"1", _unused_conn} | _others] = Connections.get_unused_conns(name)
923 end
924
925 test "reference is equalent, sorting by crf", %{name: name} do
926 Connections.add_conn(name, "1", %Conn{
927 conn_state: :idle,
928 crf: 1.999
929 })
930
931 Connections.add_conn(name, "2", %Conn{
932 conn_state: :idle,
933 crf: 2
934 })
935
936 assert [{"1", _unused_conn} | _others] = Connections.get_unused_conns(name)
937 end
938
939 test "higher crf and lower reference", %{name: name} do
940 Connections.add_conn(name, "1", %Conn{
941 conn_state: :idle,
942 crf: 3,
943 last_reference: now() - 1
944 })
945
946 Connections.add_conn(name, "2", %Conn{
947 conn_state: :idle,
948 crf: 2,
949 last_reference: now()
950 })
951
952 assert [{"2", _unused_conn} | _others] = Connections.get_unused_conns(name)
953 end
954
955 test "lower crf and lower reference", %{name: name} do
956 Connections.add_conn(name, "1", %Conn{
957 conn_state: :idle,
958 crf: 1.99,
959 last_reference: now() - 1
960 })
961
962 Connections.add_conn(name, "2", %Conn{
963 conn_state: :idle,
964 crf: 2,
965 last_reference: now()
966 })
967
968 assert [{"1", _unused_conn} | _others] = Connections.get_unused_conns(name)
969 end
970 end
971
972 test "count/1", %{name: name} do
973 assert Connections.count(name) == 0
974 Connections.add_conn(name, "1", %Conn{conn: self()})
975 assert Connections.count(name) == 1
976 Connections.remove_conn(name, "1")
977 assert Connections.count(name) == 0
978 end
979
980 defp now do
981 :os.system_time(:second)
982 end
983 end