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

Commit

Permalink
Move variant and improve tests
Browse files Browse the repository at this point in the history
  • Loading branch information
ryanwinchester committed Oct 15, 2020
1 parent afe0095 commit 75b2cb1
Show file tree
Hide file tree
Showing 5 changed files with 146 additions and 51 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
Elixir UUID Utils
UUID Utils
===========

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

```elixir
defp deps do
[ {:uuid_utils, "~> 1.4"} ]
[ {:uuid_utils, "~> 1.5"} ]
end
```

Expand Down
55 changes: 51 additions & 4 deletions lib/uuid.ex
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,7 @@ defmodule UUID do
end

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

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

#
@doc """
Validate a RFC4122 UUID.
"""
@spec valid?(binary, 0..6 | String.t()) :: boolean
def valid?(uuid, version \\ "[0-6]")

def valid?(uuid, version) when version in 0..6 or version == "[0-6]" do
case info(uuid) do
{:ok, info} ->
~r/^[0-9a-f]{8}-[0-9a-f]{4}-#{version}[0-9a-f]{3}-[089ab][0-9a-f]{3}-[0-9a-f]{12}$/i
|> Regex.match?(uuid_to_string_default(info.binary))

{:error, _} ->
false
end
end

def valid?(_, _), do: false

This comment has been minimized.

Copy link
@ryanwinchester

ryanwinchester Oct 21, 2020

Author Member

@doc """
Identify the UUID variant according to section 4.1.1 of RFC 4122.
## Examples
iex> UUID.variant(<<1, 1, 1>>)
:reserved_future
iex> UUID.variant(<<1, 1, 0>>)
:reserved_microsoft
iex> UUID.variant(<<1, 0, 0>>)
:rfc4122
iex> UUID.variant(<<0, 1, 1>>)
:reserved_ncs
iex> UUID.variant(<<1>>)
** (ArgumentError) Invalid argument; Not valid variant bits
"""
@spec variant(binary) :: variant()
def variant(<<1, 1, 1>>), do: :reserved_future
def variant(<<1, 1, _v>>), do: :reserved_microsoft
def variant(<<1, 0, _v>>), do: :rfc4122
def variant(<<0, _v::2-binary>>), do: :reserved_ncs
def variant(_), do: raise(ArgumentError, message: "Invalid argument; Not valid variant bits")

# ----------------------------------------------------------------------------
# Internal utility functions.
#
# ----------------------------------------------------------------------------

# Convert UUID bytes to String.
defp uuid_to_string(<<_::128>> = u, :default) do
Expand Down
30 changes: 1 addition & 29 deletions lib/uuid/info.ex
Original file line number Diff line number Diff line change
Expand Up @@ -150,39 +150,11 @@ defmodule UUID.Info do
binary: <<uuid::128>>,
type: type,
version: version,
variant: variant(<<v0, v1, v2>>)
variant: UUID.variant(<<v0, v1, v2>>)
}
end

def new!(_) do
raise ArgumentError, message: "Invalid argument; Expected: String"
end

@doc """
Identify the UUID variant according to section 4.1.1 of RFC 4122.
## Examples
iex> UUID.Info.variant(<<1, 1, 1>>)
:reserved_future
iex> UUID.Info.variant(<<1, 1, 0>>)
:reserved_microsoft
iex> UUID.Info.variant(<<1, 0, 0>>)
:rfc4122
iex> UUID.Info.variant(<<0, 1, 1>>)
:reserved_ncs
iex> UUID.Info.variant(<<1>>)
** (ArgumentError) Invalid argument; Not valid variant bits
"""
@spec variant(binary) :: UUID.variant()
def variant(<<1, 1, 1>>), do: :reserved_future
def variant(<<1, 1, _v>>), do: :reserved_microsoft
def variant(<<1, 0, _v>>), do: :rfc4122
def variant(<<0, _v::2-binary>>), do: :reserved_ncs
def variant(_), do: raise(ArgumentError, message: "Invalid argument; Not valid variant bits")
end
2 changes: 1 addition & 1 deletion mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ defmodule UUID.Mixfile do
use Mix.Project

@app :uuid_utils
@version "1.4.0"
@version "1.5.0"

def project do
[
Expand Down
106 changes: 91 additions & 15 deletions test/uuid_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,15 @@ defmodule UUIDTest do

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

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

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

test "UUID.info!/1 invalid argument type" do
test "info!/1 invalid argument type" do
assert_raise(
ArgumentError,
"Invalid argument; Expected: String",
Expand All @@ -21,7 +21,7 @@ defmodule UUIDTest do
)
end

test "UUID.info!/1 invalid UUID" do
test "info!/1 invalid UUID" do
assert_raise(
ArgumentError,
"Invalid argument; Not a valid UUID: not_a_uuid",
Expand All @@ -31,14 +31,91 @@ defmodule UUIDTest do
)
end

test "UUID v1 to UUID v6 conversion" do
uuid1 = UUID.uuid1() |> validate_uuid(1)
assert uuid1 == UUID.uuid1_to_uuid6(uuid1) |> validate_uuid(6) |> UUID.uuid6_to_uuid1()
test "binary_to_string!/2 converts binaries to strings" do
for type <- [:default, :raw, :hex, :urn, :slug] do
UUID.uuid4(:raw) |> UUID.binary_to_string!(type) |> UUID.valid?()
end
end

test "binary_to_string!/2 with invalid UUID type returns error" do
for type <- [:default, :raw, :hex, :urn, :slug] do
assert_raise(
ArgumentError,
"Invalid argument; Expected: <<uuid::128>>",
fn ->
UUID.binary_to_string!(123, type)
end
)
end
end

test "binary_to_string!/2 with invalid UUID returns error" do
for type <- [:default, :hex, :urn, :slug] do
assert_raise(
ArgumentError,
"Invalid binary data; Expected: <<uuid::128>>",
fn ->
UUID.binary_to_string!("not_a_uuid", type)
end
)
end
end

test "string_to_binary!/2 converts strings to binaries" do
for type <- [:default, :raw, :hex, :urn, :slug] do
UUID.uuid4(type) |> UUID.string_to_binary!() |> UUID.valid?()
end
end

test "string_to_binary!/2 with invalid UUID type returns error" do
assert_raise(
ArgumentError,
"Invalid argument; Expected: String",
fn ->
UUID.string_to_binary!(123)
end
)
end

test "string_to_binary!/2 with invalid UUID returns error" do
assert_raise(
ArgumentError,
"Invalid argument; Not a valid UUID: foo",
fn ->
UUID.string_to_binary!("foo")
end
)
end

test "uuid1_to_uuid6/1 converts UUIDs" do
uuid1 = UUID.uuid1() |> validate(1)
assert uuid1 == UUID.uuid1_to_uuid6(uuid1) |> validate(6) |> UUID.uuid6_to_uuid1()
end

test "uuid6_to_uuid1/1 converts UUIDs" do
uuid6 = UUID.uuid6() |> validate(6)
assert uuid6 == UUID.uuid6_to_uuid1(uuid6) |> validate(1) |> UUID.uuid1_to_uuid6()
end

test "valid?/2 validates valid UUIDs" do
assert UUID.uuid1() |> UUID.valid?()
assert UUID.uuid1() |> UUID.valid?(1)

assert UUID.uuid3(:dns, "my.domain.com") |> UUID.valid?()
assert UUID.uuid3(:dns, "my.domain.com") |> UUID.valid?(3)

assert UUID.uuid4() |> UUID.valid?()
assert UUID.uuid4() |> UUID.valid?(4)

assert UUID.uuid5(:dns, "my.domain.com") |> UUID.valid?()
assert UUID.uuid5(:dns, "my.domain.com") |> UUID.valid?(5)

assert UUID.uuid6() |> UUID.valid?()
assert UUID.uuid6() |> UUID.valid?(6)
end

test "UUID v6 to UUID v1 conversion" do
uuid6 = UUID.uuid6() |> validate_uuid(6)
assert uuid6 == UUID.uuid6_to_uuid1(uuid6) |> validate_uuid(1) |> UUID.uuid1_to_uuid6()
test "valid?/2 invalidates invalid UUIDs" do
refute UUID.valid?("foo")
end

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

test "UUID.info/1 #{name}" do
{expected, []} = Code.eval_string(unquote(expected))
{:ok, result} = UUID.info(unquote(input))
assert ^expected = result
validate_uuid(UUID.binary_to_string!(result.binary), expected.version)
validate(UUID.binary_to_string!(result.binary), expected.version)
end
end

defp validate_uuid(uuid, version) when version in 1..6 do
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
assert Regex.match?(r, uuid)
defp validate(uuid, version) when version in 0..6 do
assert UUID.valid?(uuid, version)
uuid
end
end

0 comments on commit 75b2cb1

Please sign in to comment.