Skip to content

Commit

Permalink
Fix modified_at comparisons to use seconds granularity
Browse files Browse the repository at this point in the history
  • Loading branch information
joshsmith committed Nov 16, 2017
1 parent 315f995 commit 592e28b
Show file tree
Hide file tree
Showing 2 changed files with 33 additions and 13 deletions.
22 changes: 17 additions & 5 deletions lib/code_corps/validators/time_validator.ex
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,32 @@ defmodule CodeCorps.Validators.TimeValidator do

@doc """
Validates the new time is not before the previous time.
Works at second-level accuracy by truncating both timestamps to the second.
"""
def validate_time_not_before(%{data: data} = changeset, field) do
previous_time = Map.get(data, field)
current_time = Changeset.get_change(changeset, field)
case current_time do
new_time = Changeset.get_change(changeset, field)
case new_time do
nil -> changeset
_ -> do_validate_time_not_before(changeset, field, previous_time, current_time)
_ -> do_validate_time_not_before(changeset, field, previous_time, new_time)
end
end

defp do_validate_time_not_before(changeset, field, previous_time, current_time) do
case Timex.before?(current_time, previous_time) do
defp do_validate_time_not_before(changeset, field, previous_time, new_time) do
previous_time = previous_time |> truncate(:second)
new_time = new_time |> truncate(:second)
case Timex.before?(new_time, previous_time) do
true -> Changeset.add_error(changeset, field, "cannot be before the last recorded time")
false -> changeset
end
end

# TODO: Replace this with DateTime.truncate/2 when Elixir 1.6 releases
@spec truncate(DateTime.t, :microsecond | :millisecond | :second) :: DateTime.t
def truncate(%DateTime{microsecond: microsecond} = datetime, precision) do
%{datetime | microsecond: do_truncate(microsecond, precision)}
end

defp do_truncate(_, :second), do: {0, 0}
end
24 changes: 16 additions & 8 deletions test/lib/code_corps/validators/time_validator_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -8,32 +8,40 @@ defmodule CodeCorps.Validators.TimeValidatorTest do
describe "validate_time_not_before/2" do
test "when the time happened before" do
# set the time to 1 day before the previous (recorded) time
current_time = @previous_time |> Timex.shift(days: -1)
changeset = cast_times(@previous_time, current_time, :modified_at)
new_time = @previous_time |> Timex.shift(days: -1)
changeset = cast_times(@previous_time, new_time, :modified_at)
changeset = changeset |> validate_time_not_before(:modified_at)
refute changeset.valid?
end

test "when the time happened at the same time" do
current_time = @previous_time
changeset = cast_times(@previous_time, current_time, :modified_at)
new_time = @previous_time
changeset = cast_times(@previous_time, new_time, :modified_at)
changeset = changeset |> validate_time_not_before(:modified_at)
assert changeset.valid?
end

test "when the time happened at the same second but with microseconds of difference" do
previous_time = @previous_time |> Timex.shift(milliseconds: 500)
new_time = previous_time |> truncate(:second)
changeset = cast_times(previous_time, new_time, :modified_at)
changeset = changeset |> validate_time_not_before(:modified_at)
assert changeset.valid?
end

test "when the time happened after" do
# set the time to 1 day after the previous (recorded) time
current_time = @previous_time |> Timex.shift(days: 1)
changeset = cast_times(@previous_time, current_time, :modified_at)
new_time = @previous_time |> Timex.shift(days: 1)
changeset = cast_times(@previous_time, new_time, :modified_at)
changeset = changeset |> validate_time_not_before(:modified_at)
assert changeset.valid?
end
end

defp cast_times(previous_time, current_time, field) do
defp cast_times(previous_time, new_time, field) do
data = Map.put(%{}, field, previous_time)
fields = Map.put(%{}, field, :utc_datetime)
params = Map.put(%{}, field, current_time)
params = Map.put(%{}, field, new_time)
Ecto.Changeset.cast({data, fields}, params, [field])
end
end

0 comments on commit 592e28b

Please sign in to comment.