prop/test/support/fixtures/contacts_fixtures.ex
Graham McIntire 581955bd69
Some checks failed
Build and Push / Build and Push Docker Image (push) Has been cancelled
fix: prevent contacts_dedup_idx collisions in parallel async tests
Create shared ContactsFixtures module with globally-unique qso_timestamp
seconds to prevent unique-constraint violations when async test modules
run in parallel and insert contacts with identical dedup-key columns.
The qso_timestamp column is timestamp(0), so microsecond offsets were
truncated — use System.unique_integer monotonic seconds instead.

Also make count-asserting tests resilient to sandbox-leaked contacts
from prior tests by using >= assertions or status-based checks rather
than exact counts.

Includes automated DateTime.add → DateTime.shift migration from
mix format.
2026-08-04 17:05:16 -05:00

53 lines
1.6 KiB
Elixir

defmodule Microwaveprop.ContactsFixtures do
@moduledoc """
Factory helpers for creating Contact records in tests.
Uses globally-unique `qso_timestamp` microseconds (via
`System.unique_integer([:positive, :monotonic])`) to prevent
`contacts_dedup_idx` unique-constraint violations when async
tests across different modules run in parallel and would
otherwise insert contacts with identical dedup-key columns.
"""
alias Microwaveprop.Radio.Contact
alias Microwaveprop.Repo
@doc """
Creates a contact with unique dedup-key column values.
Override any field by passing it in `attrs`. Pass `:user_id` to
associate the contact with a user.
Returns the created `%Contact{}`.
"""
@spec create_contact(map()) :: Contact.t()
def create_contact(attrs \\ %{}) do
# Globally-unique second offset so every contact has a distinct
# dedup key (band, qso_timestamp, stations, grids). The column is
# timestamp(0), so microsecond offsets are truncated.
unique_s = System.unique_integer([:positive, :monotonic])
default = %{
station1: "W5XD",
station2: "K5TR",
qso_timestamp: DateTime.shift(~U[2026-03-28 18:00:00Z], second: unique_s),
mode: "CW",
band: Decimal.new("1296"),
grid1: "EM12",
grid2: "EM00",
pos1: %{"lat" => 32.9, "lon" => -97.0},
pos2: %{"lat" => 30.3, "lon" => -97.7},
distance_km: Decimal.new("295")
}
merged = Map.merge(default, attrs)
{user_id, changeset_attrs} = Map.pop(merged, :user_id)
{:ok, contact} =
%Contact{user_id: user_id}
|> Contact.changeset(changeset_attrs)
|> Repo.insert()
contact
end
end