@@ -160,5 +160,99 @@ defmodule NormTest do
160160 spec = map_of ( spec ( is_integer ( ) ) , spec ( is_atom ( ) ) )
161161 assert % { 1 => :foo , 2 => :bar } == conform! ( % { 1 => :foo , 2 => :bar } , spec )
162162 end
163+
164+ property "can be generated" do
165+ check all m <- gen ( map_of ( spec ( is_integer ( ) ) , spec ( is_atom ( ) ) ) ) do
166+ assert is_map ( m )
167+ for k <- Map . keys ( m ) , do: assert is_integer ( k )
168+ for v <- Map . values ( m ) , do: assert is_atom ( v )
169+ end
170+ end
171+ end
172+
173+ describe "coll_of/2" do
174+ test "can spec collections" do
175+ spec = coll_of ( spec ( is_atom ( ) ) )
176+ assert [ :foo , :bar , :baz ] == conform! ( [ :foo , :bar , :baz ] , spec )
177+ assert { :error , errors } = conform ( [ :foo , 1 , "test" ] , spec )
178+ assert errors == [
179+ "val: 1 in: 1 fails: is_atom()" ,
180+ "val: \" test\" in: 2 fails: is_atom()"
181+ ]
182+ end
183+
184+ test "conforming returns the conformed values" do
185+ spec = coll_of ( schema ( % { name: spec ( is_binary ( ) ) } ) )
186+ input = [
187+ % { name: "chris" , age: 31 , email: "c@keathley.io" } ,
188+ % { name: "andra" , age: 30 }
189+ ]
190+
191+ assert [ % { name: "chris" } , % { name: "andra" } ] == conform! ( input , spec )
192+
193+ input = [
194+ % { age: 31 , email: "c@keathley.io" } ,
195+ % { name: :andra , age: 30 } ,
196+ ]
197+ assert { :error , errors } = conform ( input , spec )
198+ assert errors == [
199+ "val: %{age: 31, email: \" c@keathley.io\" } in: 0/:name fails: :required" ,
200+ "val: :andra in: 1/:name fails: is_binary()"
201+ ]
202+ end
203+
204+ test "can enforce distinct elements" do
205+ spec = coll_of ( spec ( is_integer ( ) ) , distinct: true )
206+
207+ assert { :error , errors } = conform ( [ 1 , 1 , 1 ] , spec )
208+ assert errors == [ "val: [1, 1, 1] fails: distinct?" ]
209+ end
210+
211+ test "can enforce min and max counts" do
212+ spec = coll_of ( spec ( is_integer ( ) ) , min_count: 2 , max_count: 3 )
213+ assert [ 1 , 1 ] == conform! ( [ 1 , 1 ] , spec )
214+ assert [ 1 , 1 , 1 ] == conform! ( [ 1 , 1 , 1 ] , spec )
215+ assert { :error , [ "val: [1] fails: min_count: 2" ] } == conform ( [ 1 ] , spec )
216+ assert { :error , [ "val: [1, 1, 1, 1] fails: max_count: 3" ] } == conform ( [ 1 , 1 , 1 , 1 ] , spec )
217+
218+ spec = coll_of ( spec ( is_integer ( ) ) , min_count: 3 , max_count: 3 )
219+ assert [ 1 , 1 , 1 ] == conform! ( [ 1 , 1 , 1 ] , spec )
220+ end
221+
222+ test "min count must be less than or equal to max count" do
223+ assert_raise ArgumentError , fn ->
224+ coll_of ( spec ( is_integer ( ) ) , min_count: 3 , max_count: 2 )
225+ end
226+ end
227+
228+ test "can be used to spec keyword lists" do
229+ opts = one_of ( [
230+ { :name , spec ( is_atom ( ) ) } ,
231+ { :timeout , spec ( is_integer ( ) ) }
232+ ] )
233+ spec = coll_of ( opts )
234+ list = [ name: :storage , timeout: 3_000 ]
235+
236+ assert list == conform! ( list , spec )
237+ assert { :error , _errors } = conform ( [ { :foo , :bar } | list ] , spec )
238+
239+ assert list == conform! ( list , coll_of ( opts , [ min_count: 2 , distinct: true ] ) )
240+ assert { :error , errors } = conform ( [ ] , coll_of ( opts , [ min_count: 2 , distinct: true ] ) )
241+ end
242+
243+ property "can be generated" do
244+ check all is <- gen ( coll_of ( spec ( is_integer ( ) ) ) ) do
245+ for i <- is , do: assert is_integer ( i )
246+ end
247+
248+ check all is <- gen ( coll_of ( spec ( is_integer ( ) ) , min_count: 3 , max_count: 6 ) ) do
249+ length = Enum . count ( is )
250+ assert 3 <= length and length <= 6
251+ end
252+
253+ check all is <- gen ( coll_of ( spec ( is_integer ( ) ) , distinct: true ) ) do
254+ assert Enum . uniq ( is ) == is
255+ end
256+ end
163257 end
164258end
0 commit comments