fix(test): isolate synthetic E2E side effects

This commit is contained in:
SimpleTest 2026-07-24 11:51:49 +03:00
parent f3cfc4e215
commit b742a98979
7 changed files with 72 additions and 20 deletions

View File

@ -10,6 +10,7 @@ defmodule Mix.Tasks.Wnh.StagingFullE2e do
alias WhoNeedHelp.Catalog.{CategoryProposal, CategoryVote} alias WhoNeedHelp.Catalog.{CategoryProposal, CategoryVote}
alias WhoNeedHelp.Help.{Assignment, HelpRequest} alias WhoNeedHelp.Help.{Assignment, HelpRequest}
alias WhoNeedHelp.Messaging.Message alias WhoNeedHelp.Messaging.Message
alias WhoNeedHelp.Notifications.Notification
alias WhoNeedHelp.Repo alias WhoNeedHelp.Repo
alias WhoNeedHelp.Tracking.{Position, TrackingSession} alias WhoNeedHelp.Tracking.{Position, TrackingSession}
@ -149,6 +150,7 @@ defmodule Mix.Tasks.Wnh.StagingFullE2e do
message_ids = ids(Message, :assignment_id, assignment_ids) message_ids = ids(Message, :assignment_id, assignment_ids)
tracking_session_ids = ids(TrackingSession, :assignment_id, assignment_ids) tracking_session_ids = ids(TrackingSession, :assignment_id, assignment_ids)
notification_ids = ids(Notification, :user_id, user_ids)
activity_ids = ids(Activity, :creator_id, user_ids) activity_ids = ids(Activity, :creator_id, user_ids)
validate_activity_membership!(activity_ids, user_ids) validate_activity_membership!(activity_ids, user_ids)
@ -192,12 +194,13 @@ defmodule Mix.Tasks.Wnh.StagingFullE2e do
signals: abuse_signal_ids signals: abuse_signal_ids
}) })
job_ids = fixture_push_job_ids(user_ids) job_ids = fixture_job_ids(user_ids, request_ids, notification_ids)
{:ok, deleted} = {:ok, deleted} =
Repo.transaction(fn -> Repo.transaction(fn ->
%{ %{
push_jobs: delete_ids(Job, job_ids), push_jobs: delete_ids(Job, job_ids),
notifications: delete_ids(Notification, notification_ids),
audit_events: audit_events:
AuditEvent AuditEvent
|> where([event], event.actor_id in ^user_ids) |> where([event], event.actor_id in ^user_ids)
@ -384,14 +387,15 @@ defmodule Mix.Tasks.Wnh.StagingFullE2e do
end end
end end
defp fixture_push_job_ids(user_ids) do defp fixture_job_ids(user_ids, request_ids, notification_ids) do
recipients = Enum.map(user_ids, &"user:#{&1}") recipients = Enum.map(user_ids, &"user:#{&1}")
Job Job
|> where( |> where(
[job], [job],
job.worker == "WhoNeedHelp.Push.DeliveryWorker" and fragment("?->>'recipient' = ANY(?)", job.args, ^recipients) or
fragment("?->>'recipient' = ANY(?)", job.args, ^recipients) fragment("?->>'request_id' = ANY(?)", job.args, ^request_ids) or
fragment("?->>'notification_id' = ANY(?)", job.args, ^notification_ids)
) )
|> select([job], job.id) |> select([job], job.id)
|> Repo.all() |> Repo.all()

View File

@ -289,7 +289,13 @@ defmodule WhoNeedHelp.Help do
end end
with {:ok, request} <- result do with {:ok, request} <- result do
_ = ProductAnalytics.increment("request.created", to_string(request.urgency)) _ =
ProductAnalytics.increment_for_user(
user,
"request.created",
to_string(request.urgency)
)
broadcast({:request_created, get_request!(request.id)}) broadcast({:request_created, get_request!(request.id)})
{:ok, request} {:ok, request}
end end
@ -364,7 +370,7 @@ defmodule WhoNeedHelp.Help do
case result do case result do
{:ok, assignment} -> {:ok, assignment} ->
_ = ProductAnalytics.increment("request.accepted") _ = ProductAnalytics.increment_for_user(helper, "request.accepted")
request = get_request!(assignment.request_id) request = get_request!(assignment.request_id)
broadcast({:request_updated, request}) broadcast({:request_updated, request})
@ -482,7 +488,11 @@ defmodule WhoNeedHelp.Help do
|> case do |> case do
{:ok, request} -> {:ok, request} ->
_ = _ =
ProductAnalytics.increment("request.cancelled", to_string(request.cancellation_reason)) ProductAnalytics.increment_for_user(
user,
"request.cancelled",
to_string(request.cancellation_reason)
)
WhoNeedHelp.Tracking.cleanup_finished_sessions() WhoNeedHelp.Tracking.cleanup_finished_sessions()
request = get_request!(request.id) request = get_request!(request.id)
@ -541,7 +551,7 @@ defmodule WhoNeedHelp.Help do
|> after_transition() |> after_transition()
|> case do |> case do
{:ok, _assignment} = result -> {:ok, _assignment} = result ->
_ = ProductAnalytics.increment("assignment.withdrawn") _ = ProductAnalytics.increment_for_user(user, "assignment.withdrawn")
WhoNeedHelp.Tracking.cleanup_finished_sessions() WhoNeedHelp.Tracking.cleanup_finished_sessions()
result result
@ -749,7 +759,7 @@ defmodule WhoNeedHelp.Help do
:error -> {:error, :not_found} :error -> {:error, :not_found}
end end
|> after_transition() |> after_transition()
|> record_assignment_metric(action) |> record_assignment_metric(user, action)
end end
defp maybe_complete(changeset, request) do defp maybe_complete(changeset, request) do
@ -952,22 +962,26 @@ defmodule WhoNeedHelp.Help do
defp maybe_notify_completion(_assignment, _request_id), do: {:ok, :not_completed} defp maybe_notify_completion(_assignment, _request_id), do: {:ok, :not_completed}
defp record_assignment_metric({:ok, _assignment} = result, :start) do defp record_assignment_metric({:ok, _assignment} = result, user, :start) do
_ = ProductAnalytics.increment("assignment.started") _ = ProductAnalytics.increment_for_user(user, "assignment.started")
result result
end end
defp record_assignment_metric({:ok, _assignment} = result, :arrive) do defp record_assignment_metric({:ok, _assignment} = result, user, :arrive) do
_ = ProductAnalytics.increment("assignment.arrived") _ = ProductAnalytics.increment_for_user(user, "assignment.arrived")
result result
end end
defp record_assignment_metric({:ok, %Assignment{status: :completed}} = result, :confirm) do defp record_assignment_metric(
_ = ProductAnalytics.increment("request.completed") {:ok, %Assignment{status: :completed}} = result,
user,
:confirm
) do
_ = ProductAnalytics.increment_for_user(user, "request.completed")
result result
end end
defp record_assignment_metric(result, _action), do: result defp record_assignment_metric(result, _user, _action), do: result
defp broadcast(event) do defp broadcast(event) do
Phoenix.PubSub.broadcast(WhoNeedHelp.PubSub, @topic, event) Phoenix.PubSub.broadcast(WhoNeedHelp.PubSub, @topic, event)

View File

@ -105,11 +105,12 @@ defmodule WhoNeedHelp.Notifications do
:ok :ok
end end
def notification_opened(%Scope{} = scope, notification_id) do def notification_opened(%Scope{user: user} = scope, notification_id) do
case mark_read(scope, notification_id) do case mark_read(scope, notification_id) do
{:ok, notification} = result -> {:ok, notification} = result ->
_ = _ =
WhoNeedHelp.ProductAnalytics.increment( WhoNeedHelp.ProductAnalytics.increment_for_user(
user,
"notification.opened", "notification.opened",
to_string(notification.kind) to_string(notification.kind)
) )

View File

@ -31,6 +31,7 @@ defmodule WhoNeedHelp.ProductAnalytics do
} }
@allowed_metrics Map.keys(@allowed_dimensions) @allowed_metrics Map.keys(@allowed_dimensions)
@synthetic_email_suffix "@example.invalid"
def increment(metric, dimension \\ "all") def increment(metric, dimension \\ "all")
@ -57,6 +58,18 @@ defmodule WhoNeedHelp.ProductAnalytics do
def increment(_metric, _dimension), do: {:error, :invalid_metric} def increment(_metric, _dimension), do: {:error, :invalid_metric}
def increment_for_user(user, metric, dimension \\ "all")
def increment_for_user(%{email: email}, metric, dimension) when is_binary(email) do
if synthetic_email?(email) do
{:ok, :synthetic_account_skipped}
else
increment(metric, dimension)
end
end
def increment_for_user(_user, _metric, _dimension), do: {:error, :invalid_user}
def paginate(%Scope{user: user}, options \\ []) do def paginate(%Scope{user: user}, options \\ []) do
if Accounts.moderator_authorized?(user) do if Accounts.moderator_authorized?(user) do
limit = Pagination.limit(options) limit = Pagination.limit(options)
@ -75,6 +88,13 @@ defmodule WhoNeedHelp.ProductAnalytics do
def allowed_metrics, do: @allowed_metrics def allowed_metrics, do: @allowed_metrics
defp synthetic_email?(email) do
email
|> String.trim()
|> String.downcase()
|> String.ends_with?(@synthetic_email_suffix)
end
defp before(query, nil), do: query defp before(query, nil), do: query
defp before(query, {inserted_at, id}) do defp before(query, {inserted_at, id}) do

View File

@ -98,7 +98,7 @@ defmodule WhoNeedHelpWeb.GoogleAuthController do
"locale" => normalize_locale(params["locale"] || pending.locale), "locale" => normalize_locale(params["locale"] || pending.locale),
"terms_accepted" => true "terms_accepted" => true
}) do }) do
_ = ProductAnalytics.increment("account.registered", "google") _ = ProductAnalytics.increment_for_user(user, "account.registered", "google")
conn conn
|> GoogleAuthPending.delete() |> GoogleAuthPending.delete()

View File

@ -27,7 +27,7 @@ defmodule WhoNeedHelpWeb.UserRegistrationController do
result <- Accounts.register_user(user_params) do result <- Accounts.register_user(user_params) do
case result do case result do
{:ok, user} -> {:ok, user} ->
_ = ProductAnalytics.increment("account.registered", "email") _ = ProductAnalytics.increment_for_user(user, "account.registered", "email")
deliver_registration_instructions(conn, user) deliver_registration_instructions(conn, user)
registration_response(conn) registration_response(conn)

View File

@ -31,4 +31,17 @@ defmodule WhoNeedHelp.ProductAnalyticsTest do
assert [%DailyMetric{metric: "request.created", count: 2}] = assert [%DailyMetric{metric: "request.created", count: 2}] =
ProductAnalytics.paginate(moderator).entries ProductAnalytics.paginate(moderator).entries
end end
test "does not count actions from reserved synthetic fixture accounts" do
synthetic_user = %{email: "browser-run@example.invalid"}
assert {:ok, :synthetic_account_skipped} =
ProductAnalytics.increment_for_user(
synthetic_user,
"request.created",
"now"
)
refute Repo.get_by(DailyMetric, metric: "request.created", dimension: "now")
end
end end