Generate URLs for Libravatar and Gravatar avatars
1# SPDX-FileCopyrightText: 2024 Łukasz Niemier <#@hauleth.dev>
2#
3# SPDX-License-Identifier: MPL-2.0
4
5defmodule AwwTest do
6 use ExUnit.Case, async: true
7 use ExUnitProperties
8
9 @subject Aww
10
11 doctest @subject
12
13 test "by default there is no query" do
14 url = @subject.avatar_url("foo@example.com")
15
16 assert "" == url.query
17 end
18
19 test "by default HTTPS is used" do
20 url = @subject.avatar_url("foo@example.com")
21
22 assert "https" == url.scheme
23 end
24
25 test "can opt out to insecure HTTP query" do
26 url = @subject.avatar_url("foo@example.com", service_opts: [secure?: false])
27
28 assert "http" == url.scheme
29 end
30
31 test "default can be changed" do
32 url = @subject.avatar_url("foo@example.com", default: "monster")
33
34 assert url.query =~ "d=monster"
35 end
36
37 test "size can be defined" do
38 url = @subject.avatar_url("foo@example.com", size: 2137)
39
40 assert url.query =~ "s=2137"
41 end
42
43 test "enforce default" do
44 url = @subject.avatar_url("foo@example.com", forcedefault?: true)
45
46 assert url.query =~ "f=y"
47 end
48
49 test "select rating" do
50 url = @subject.avatar_url("foo@example.com", rating: "R")
51
52 assert url.query =~ "r=R"
53 end
54
55 property "email is case insensitive" do
56 check all(
57 local_part <- string(:ascii, min_length: 1),
58 domain <- string(:ascii, min_length: 1)
59 ) do
60 input = "#{local_part}@#{domain}"
61 downcased = String.downcase(input)
62
63 assert @subject.avatar_url(input) == @subject.avatar_url(downcased)
64 end
65 end
66
67 property "custom host contains that host" do
68 check all(host <- string(:ascii, min_length: 1)) do
69 url = @subject.avatar_url("foo@example.com", service_opts: [host: host])
70
71 assert host == url.host
72 end
73 end
74
75 property "custom host as URI contains that host" do
76 check all(host <- string(:ascii, min_length: 1), port <- integer(0..0xFFFF)) do
77 uri = %URI{
78 host: host,
79 port: port
80 }
81
82 url = @subject.avatar_url("foo@example.com", service_opts: [host: uri])
83
84 assert host == url.host
85 assert port == url.port
86 end
87 end
88
89 defp domain do
90 part = string([?a..?z, ?A..?Z, ?0..?9, ?-], min_length: 1, max_length: 10)
91
92 gen(
93 all(
94 parts <- list_of(part, min_length: 1, max_length: 4),
95 do: Enum.join(parts, ".")
96 )
97 )
98 end
99
100 defp open_id_uri do
101 gen all(
102 scheme <- one_of([constant("http"), constant("https")]),
103 host <- domain(),
104 port <- integer(0..0xFFFF),
105 segment = string(:alphanumeric, min_length: 1),
106 path <- list_of(segment),
107 user <- string(:alphanumeric, min_length: 1),
108 pass <- string(:alphanumeric, min_length: 1)
109 ) do
110 %URI{
111 scheme: scheme,
112 host: host,
113 port: port,
114 path: "/" <> Enum.join(path, "/"),
115 userinfo: "#{user}:#{pass}"
116 }
117 end
118 end
119
120 property "normalizes OpenID addresses" do
121 check all(url <- open_id_uri()) do
122 input = URI.to_string(url)
123
124 downcased =
125 URI.to_string(%URI{
126 url
127 | host: String.downcase(url.host)
128 })
129
130 assert @subject.avatar_url(input) == @subject.avatar_url(downcased)
131 end
132 end
133
134 describe "function as a host provider" do
135 def host_func(host, _opts), do: "test.#{host}"
136
137 test "passed as MFA" do
138 url =
139 @subject.avatar_url("hello@example.com",
140 service_opts: [host: {__MODULE__, :host_func, []}]
141 )
142
143 assert url.host == "test.example.com"
144 end
145
146 test "passed as capture" do
147 url =
148 @subject.avatar_url("hello@example.com",
149 service_opts: [host: &host_func/2]
150 )
151
152 assert url.host == "test.example.com"
153 end
154 end
155end