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