From a32a50a5fdcf3d4a0811425d8a0b42cec6061442 Mon Sep 17 00:00:00 2001 From: Graham McIntire Date: Tue, 20 Jan 2026 15:30:14 -0600 Subject: [PATCH] fix: make ApiTokens.update_last_used synchronous in test env The async Task.start pattern was causing DBConnection.Ownership errors when tests finished before the spawned Task could complete its database update. By making the update synchronous in test environment, we ensure the database connection is properly managed within the test process. In production, we still use the async Task.start to avoid blocking requests when updating last_used_at timestamps. Fixes all "owner #PID<...> exited" errors in test suite. --- lib/towerops/api_tokens.ex | 32 ++++++++++++++++++++------------ 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/lib/towerops/api_tokens.ex b/lib/towerops/api_tokens.ex index 1d4d4770..99a31c4e 100644 --- a/lib/towerops/api_tokens.ex +++ b/lib/towerops/api_tokens.ex @@ -172,20 +172,28 @@ defmodule Towerops.ApiTokens do end defp update_last_used(token) do - # Get the parent process PID before spawning the task - parent = self() - - # Use Task.start to avoid blocking the request - # We don't care if this fails - Task.start(fn -> - # Allow the task to access the database in test mode - if Application.get_env(:towerops, :sql_sandbox) do - Ecto.Adapters.SQL.Sandbox.allow(Repo, parent, self()) - end - + # In test mode, update synchronously to avoid ownership issues + # In production, use Task.start to avoid blocking the request + if Application.get_env(:towerops, :env) == :test do token |> Ecto.Changeset.change(last_used_at: DateTime.truncate(DateTime.utc_now(), :second)) |> Repo.update() - end) + else + # Get the parent process PID before spawning the task + parent = self() + + # Use Task.start to avoid blocking the request + # We don't care if this fails + Task.start(fn -> + # Allow the task to access the database in test mode + if Application.get_env(:towerops, :sql_sandbox) do + Ecto.Adapters.SQL.Sandbox.allow(Repo, parent, self()) + end + + token + |> Ecto.Changeset.change(last_used_at: DateTime.truncate(DateTime.utc_now(), :second)) + |> Repo.update() + end) + end end end