Skip to content
This repository was archived by the owner on Nov 30, 2022. It is now read-only.

Commit 75b2cb1

Browse files
Move variant and improve tests
1 parent afe0095 commit 75b2cb1

File tree

5 files changed

+146
-51
lines changed

5 files changed

+146
-51
lines changed

README.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
Elixir UUID Utils
1+
UUID Utils
22
===========
33

44
[![hex.pm version](https://img.shields.io/hexpm/v/uuid_utils.svg?style=flat)](https://hex.pm/packages/uuid_utils)
@@ -19,7 +19,7 @@ as a dependency in your `mix.exs` file:
1919

2020
```elixir
2121
defp deps do
22-
[ {:uuid_utils, "~> 1.4"} ]
22+
[ {:uuid_utils, "~> 1.5"} ]
2323
end
2424
```
2525

lib/uuid.ex

+51-4
Original file line numberDiff line numberDiff line change
@@ -218,7 +218,7 @@ defmodule UUID do
218218
end
219219

220220
@doc """
221-
Generate a new UUID v3. This version uses an MD5 hash of fixed value (chosen
221+
Generate a new UUID v3. This version uses an MD5 hash of fixed value chosen
222222
based on a namespace atom - see Appendix C of
223223
[RFC 4122](http://www.ietf.org/rfc/rfc4122.txt) and a name value. Can also be
224224
given an existing UUID String instead of a namespace atom.
@@ -332,7 +332,7 @@ defmodule UUID do
332332
end
333333

334334
@doc """
335-
Generate a new UUID v5. This version uses an SHA1 hash of fixed value (chosen
335+
Generate a new UUID v5. This version uses an SHA1 hash of fixed value chosen
336336
based on a namespace atom - see Appendix C of
337337
[RFC 4122](http://www.ietf.org/rfc/rfc4122.txt) and a name value. Can also be
338338
given an existing UUID String instead of a namespace atom.
@@ -512,9 +512,56 @@ defmodule UUID do
512512
|> uuid_to_string(format)
513513
end
514514

515-
#
515+
@doc """
516+
Validate a RFC4122 UUID.
517+
"""
518+
@spec valid?(binary, 0..6 | String.t()) :: boolean
519+
def valid?(uuid, version \\ "[0-6]")
520+
521+
def valid?(uuid, version) when version in 0..6 or version == "[0-6]" do
522+
case info(uuid) do
523+
{:ok, info} ->
524+
~r/^[0-9a-f]{8}-[0-9a-f]{4}-#{version}[0-9a-f]{3}-[089ab][0-9a-f]{3}-[0-9a-f]{12}$/i
525+
|> Regex.match?(uuid_to_string_default(info.binary))
526+
527+
{:error, _} ->
528+
false
529+
end
530+
end
531+
532+
def valid?(_, _), do: false
533+
534+
@doc """
535+
Identify the UUID variant according to section 4.1.1 of RFC 4122.
536+
537+
## Examples
538+
539+
iex> UUID.variant(<<1, 1, 1>>)
540+
:reserved_future
541+
542+
iex> UUID.variant(<<1, 1, 0>>)
543+
:reserved_microsoft
544+
545+
iex> UUID.variant(<<1, 0, 0>>)
546+
:rfc4122
547+
548+
iex> UUID.variant(<<0, 1, 1>>)
549+
:reserved_ncs
550+
551+
iex> UUID.variant(<<1>>)
552+
** (ArgumentError) Invalid argument; Not valid variant bits
553+
554+
"""
555+
@spec variant(binary) :: variant()
556+
def variant(<<1, 1, 1>>), do: :reserved_future
557+
def variant(<<1, 1, _v>>), do: :reserved_microsoft
558+
def variant(<<1, 0, _v>>), do: :rfc4122
559+
def variant(<<0, _v::2-binary>>), do: :reserved_ncs
560+
def variant(_), do: raise(ArgumentError, message: "Invalid argument; Not valid variant bits")
561+
562+
# ----------------------------------------------------------------------------
516563
# Internal utility functions.
517-
#
564+
# ----------------------------------------------------------------------------
518565

519566
# Convert UUID bytes to String.
520567
defp uuid_to_string(<<_::128>> = u, :default) do

lib/uuid/info.ex

+1-29
Original file line numberDiff line numberDiff line change
@@ -150,39 +150,11 @@ defmodule UUID.Info do
150150
binary: <<uuid::128>>,
151151
type: type,
152152
version: version,
153-
variant: variant(<<v0, v1, v2>>)
153+
variant: UUID.variant(<<v0, v1, v2>>)
154154
}
155155
end
156156

157157
def new!(_) do
158158
raise ArgumentError, message: "Invalid argument; Expected: String"
159159
end
160-
161-
@doc """
162-
Identify the UUID variant according to section 4.1.1 of RFC 4122.
163-
164-
## Examples
165-
166-
iex> UUID.Info.variant(<<1, 1, 1>>)
167-
:reserved_future
168-
169-
iex> UUID.Info.variant(<<1, 1, 0>>)
170-
:reserved_microsoft
171-
172-
iex> UUID.Info.variant(<<1, 0, 0>>)
173-
:rfc4122
174-
175-
iex> UUID.Info.variant(<<0, 1, 1>>)
176-
:reserved_ncs
177-
178-
iex> UUID.Info.variant(<<1>>)
179-
** (ArgumentError) Invalid argument; Not valid variant bits
180-
181-
"""
182-
@spec variant(binary) :: UUID.variant()
183-
def variant(<<1, 1, 1>>), do: :reserved_future
184-
def variant(<<1, 1, _v>>), do: :reserved_microsoft
185-
def variant(<<1, 0, _v>>), do: :rfc4122
186-
def variant(<<0, _v::2-binary>>), do: :reserved_ncs
187-
def variant(_), do: raise(ArgumentError, message: "Invalid argument; Not valid variant bits")
188160
end

mix.exs

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ defmodule UUID.Mixfile do
22
use Mix.Project
33

44
@app :uuid_utils
5-
@version "1.4.0"
5+
@version "1.5.0"
66

77
def project do
88
[

test/uuid_test.exs

+91-15
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,15 @@ defmodule UUIDTest do
33

44
doctest UUID, except: [uuid1: 1, uuid1: 3, uuid4: 0, uuid4: 1, uuid4: 2, uuid6: 2, uuid6: 3]
55

6-
test "UUID.info/1 invalid argument type" do
6+
test "info/1 invalid argument type" do
77
assert UUID.info(:not_a_uuid) == {:error, "Invalid argument; Expected: String"}
88
end
99

10-
test "UUID.info/1 invalid UUID" do
10+
test "info/1 invalid UUID" do
1111
assert UUID.info("not_a_uuid") == {:error, "Invalid argument; Not a valid UUID: not_a_uuid"}
1212
end
1313

14-
test "UUID.info!/1 invalid argument type" do
14+
test "info!/1 invalid argument type" do
1515
assert_raise(
1616
ArgumentError,
1717
"Invalid argument; Expected: String",
@@ -21,7 +21,7 @@ defmodule UUIDTest do
2121
)
2222
end
2323

24-
test "UUID.info!/1 invalid UUID" do
24+
test "info!/1 invalid UUID" do
2525
assert_raise(
2626
ArgumentError,
2727
"Invalid argument; Not a valid UUID: not_a_uuid",
@@ -31,14 +31,91 @@ defmodule UUIDTest do
3131
)
3232
end
3333

34-
test "UUID v1 to UUID v6 conversion" do
35-
uuid1 = UUID.uuid1() |> validate_uuid(1)
36-
assert uuid1 == UUID.uuid1_to_uuid6(uuid1) |> validate_uuid(6) |> UUID.uuid6_to_uuid1()
34+
test "binary_to_string!/2 converts binaries to strings" do
35+
for type <- [:default, :raw, :hex, :urn, :slug] do
36+
UUID.uuid4(:raw) |> UUID.binary_to_string!(type) |> UUID.valid?()
37+
end
38+
end
39+
40+
test "binary_to_string!/2 with invalid UUID type returns error" do
41+
for type <- [:default, :raw, :hex, :urn, :slug] do
42+
assert_raise(
43+
ArgumentError,
44+
"Invalid argument; Expected: <<uuid::128>>",
45+
fn ->
46+
UUID.binary_to_string!(123, type)
47+
end
48+
)
49+
end
50+
end
51+
52+
test "binary_to_string!/2 with invalid UUID returns error" do
53+
for type <- [:default, :hex, :urn, :slug] do
54+
assert_raise(
55+
ArgumentError,
56+
"Invalid binary data; Expected: <<uuid::128>>",
57+
fn ->
58+
UUID.binary_to_string!("not_a_uuid", type)
59+
end
60+
)
61+
end
62+
end
63+
64+
test "string_to_binary!/2 converts strings to binaries" do
65+
for type <- [:default, :raw, :hex, :urn, :slug] do
66+
UUID.uuid4(type) |> UUID.string_to_binary!() |> UUID.valid?()
67+
end
68+
end
69+
70+
test "string_to_binary!/2 with invalid UUID type returns error" do
71+
assert_raise(
72+
ArgumentError,
73+
"Invalid argument; Expected: String",
74+
fn ->
75+
UUID.string_to_binary!(123)
76+
end
77+
)
78+
end
79+
80+
test "string_to_binary!/2 with invalid UUID returns error" do
81+
assert_raise(
82+
ArgumentError,
83+
"Invalid argument; Not a valid UUID: foo",
84+
fn ->
85+
UUID.string_to_binary!("foo")
86+
end
87+
)
88+
end
89+
90+
test "uuid1_to_uuid6/1 converts UUIDs" do
91+
uuid1 = UUID.uuid1() |> validate(1)
92+
assert uuid1 == UUID.uuid1_to_uuid6(uuid1) |> validate(6) |> UUID.uuid6_to_uuid1()
93+
end
94+
95+
test "uuid6_to_uuid1/1 converts UUIDs" do
96+
uuid6 = UUID.uuid6() |> validate(6)
97+
assert uuid6 == UUID.uuid6_to_uuid1(uuid6) |> validate(1) |> UUID.uuid1_to_uuid6()
98+
end
99+
100+
test "valid?/2 validates valid UUIDs" do
101+
assert UUID.uuid1() |> UUID.valid?()
102+
assert UUID.uuid1() |> UUID.valid?(1)
103+
104+
assert UUID.uuid3(:dns, "my.domain.com") |> UUID.valid?()
105+
assert UUID.uuid3(:dns, "my.domain.com") |> UUID.valid?(3)
106+
107+
assert UUID.uuid4() |> UUID.valid?()
108+
assert UUID.uuid4() |> UUID.valid?(4)
109+
110+
assert UUID.uuid5(:dns, "my.domain.com") |> UUID.valid?()
111+
assert UUID.uuid5(:dns, "my.domain.com") |> UUID.valid?(5)
112+
113+
assert UUID.uuid6() |> UUID.valid?()
114+
assert UUID.uuid6() |> UUID.valid?(6)
37115
end
38116

39-
test "UUID v6 to UUID v1 conversion" do
40-
uuid6 = UUID.uuid6() |> validate_uuid(6)
41-
assert uuid6 == UUID.uuid6_to_uuid1(uuid6) |> validate_uuid(1) |> UUID.uuid1_to_uuid6()
117+
test "valid?/2 invalidates invalid UUIDs" do
118+
refute UUID.valid?("foo")
42119
end
43120

44121
# Expand the lines in info_tests.txt into individual tests for the
@@ -52,20 +129,19 @@ defmodule UUIDTest do
52129
{expected, []} = Code.eval_string(unquote(expected))
53130
result = UUID.info!(unquote(input))
54131
assert ^expected = result
55-
validate_uuid(UUID.binary_to_string!(result.binary), expected.version)
132+
validate(UUID.binary_to_string!(result.binary), expected.version)
56133
end
57134

58135
test "UUID.info/1 #{name}" do
59136
{expected, []} = Code.eval_string(unquote(expected))
60137
{:ok, result} = UUID.info(unquote(input))
61138
assert ^expected = result
62-
validate_uuid(UUID.binary_to_string!(result.binary), expected.version)
139+
validate(UUID.binary_to_string!(result.binary), expected.version)
63140
end
64141
end
65142

66-
defp validate_uuid(uuid, version) when version in 1..6 do
67-
r = ~r/^[0-9a-f]{8}-[0-9a-f]{4}-#{version}[0-9a-f]{3}-[089ab][0-9a-f]{3}-[0-9a-f]{12}$/i
68-
assert Regex.match?(r, uuid)
143+
defp validate(uuid, version) when version in 0..6 do
144+
assert UUID.valid?(uuid, version)
69145
uuid
70146
end
71147
end

0 commit comments

Comments
 (0)