defmodule Microwaveprop.BeaconMonitorsTest do use Microwaveprop.DataCase import Microwaveprop.AccountsFixtures alias Microwaveprop.BeaconMonitors alias Microwaveprop.BeaconMonitors.BeaconMonitor describe "create_monitor/2" do test "creates a monitor with a generated token" do user = user_fixture() assert {:ok, %BeaconMonitor{} = monitor} = BeaconMonitors.create_monitor(user, %{"name" => "Shack Pi"}) assert monitor.user_id == user.id assert monitor.name == "Shack Pi" assert byte_size(monitor.token) > 0 # strong_rand_bytes(32) |> url_encode64(padding: false) is 43 chars assert String.length(monitor.token) == 43 end test "generates a unique token per monitor" do user = user_fixture() {:ok, m1} = BeaconMonitors.create_monitor(user, %{"name" => "One"}) {:ok, m2} = BeaconMonitors.create_monitor(user, %{"name" => "Two"}) refute m1.token == m2.token end test "requires a name" do user = user_fixture() assert {:error, changeset} = BeaconMonitors.create_monitor(user, %{"name" => ""}) assert %{name: ["can't be blank"]} = errors_on(changeset) end end describe "list_monitors_for_user/1" do test "returns monitors belonging to the user, newest first" do user = user_fixture() other = user_fixture() # beacon_monitors.inserted_at is :utc_datetime (second precision). # Back-date the first row one second via a direct update so the # ordering assertion is deterministic without burning ~1s on a # Process.sleep — this is a slow-test-suite footgun that also # makes the test flaky on a shared CI runner where the two # inserts can land in the same wall-clock second. {:ok, first} = BeaconMonitors.create_monitor(user, %{"name" => "First"}) backdated = DateTime.add(first.inserted_at, -1, :second) {1, _} = Microwaveprop.Repo.update_all( from(m in BeaconMonitor, where: m.id == ^first.id), set: [inserted_at: backdated] ) {:ok, second} = BeaconMonitors.create_monitor(user, %{"name" => "Second"}) {:ok, _other} = BeaconMonitors.create_monitor(other, %{"name" => "Nope"}) assert [second_result, first_result] = BeaconMonitors.list_monitors_for_user(user) assert second_result.id == second.id assert first_result.id == first.id end test "returns empty list when user has no monitors" do user = user_fixture() assert BeaconMonitors.list_monitors_for_user(user) == [] end end describe "delete_monitor/2" do test "deletes a monitor owned by the user" do user = user_fixture() {:ok, monitor} = BeaconMonitors.create_monitor(user, %{"name" => "Bye"}) assert {:ok, %BeaconMonitor{}} = BeaconMonitors.delete_monitor(user, monitor.id) assert BeaconMonitors.list_monitors_for_user(user) == [] end test "does not delete a monitor owned by another user" do user = user_fixture() other = user_fixture() {:ok, monitor} = BeaconMonitors.create_monitor(other, %{"name" => "Theirs"}) assert {:error, :not_found} = BeaconMonitors.delete_monitor(user, monitor.id) assert [_] = BeaconMonitors.list_monitors_for_user(other) end test "returns :not_found for nonexistent id" do user = user_fixture() assert {:error, :not_found} = BeaconMonitors.delete_monitor(user, "11111111-1111-1111-1111-111111111111") end end describe "get_monitor_by_token/1" do test "returns the monitor matching the token" do user = user_fixture() {:ok, monitor} = BeaconMonitors.create_monitor(user, %{"name" => "Find Me"}) found = BeaconMonitors.get_monitor_by_token(monitor.token) assert found.id == monitor.id end test "returns nil for unknown token" do refute BeaconMonitors.get_monitor_by_token("not-a-real-token") end end end