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