From 67fd3feba7861cb8a02217c8561d59a1348bb79c Mon Sep 17 00:00:00 2001 From: SimpleTest Date: Thu, 13 Aug 2026 05:53:38 +0300 Subject: [PATCH] Scope support E2E job cleanup --- lib/mix/tasks/wnh.staging_full_e2e.ex | 55 ++++++++- .../staging_full_e2e_cleanup_test.exs | 105 ++++++++++++++++++ 2 files changed, 154 insertions(+), 6 deletions(-) create mode 100644 test/who_need_help/staging_full_e2e_cleanup_test.exs diff --git a/lib/mix/tasks/wnh.staging_full_e2e.ex b/lib/mix/tasks/wnh.staging_full_e2e.ex index f629d98..039c939 100644 --- a/lib/mix/tasks/wnh.staging_full_e2e.ex +++ b/lib/mix/tasks/wnh.staging_full_e2e.ex @@ -43,6 +43,12 @@ defmodule Mix.Tasks.Wnh.StagingFullE2e do @confirmation "public-staging-full-e2e" @precreated_roles ~w(requester helper replacement-helper activity-organizer activity-participant admin) @registered_roles ~w(auth-user auth-user-changed) + @support_job_workers [ + "WhoNeedHelp.Mail.SupportConfirmationWorker", + "WhoNeedHelp.Mail.SupportOperatorAlertWorker", + "WhoNeedHelp.Mail.SupportUpdateWorker" + ] + @content_removal_job_workers ["WhoNeedHelp.Mail.ContentRemovalEmailWorker"] @target_schemas %{ users: User, @@ -309,7 +315,14 @@ defmodule Mix.Tasks.Wnh.StagingFullE2e do content_removal_notices: content_removal_notice_ids }) - job_ids = fixture_job_ids(user_ids, request_ids, notification_ids) + job_ids = + fixture_job_ids( + user_ids, + request_ids, + notification_ids, + support_request_ids, + content_removal_notice_ids + ) targets = %{ users: user_ids, @@ -392,7 +405,15 @@ defmodule Mix.Tasks.Wnh.StagingFullE2e do end assert_targets_absent!(targets) - assert_no_late_fixture_jobs!(user_ids, request_ids, notification_ids) + + assert_no_late_fixture_jobs!( + user_ids, + request_ids, + notification_ids, + support_request_ids, + content_removal_notice_ids + ) + assert_prefix_unused!(context) manifest = @@ -583,7 +604,13 @@ defmodule Mix.Tasks.Wnh.StagingFullE2e do end end - defp fixture_job_ids(user_ids, request_ids, notification_ids) do + defp fixture_job_ids( + user_ids, + request_ids, + notification_ids, + support_request_ids, + content_removal_notice_ids + ) do recipients = Enum.map(user_ids, &"user:#{&1}") Job @@ -591,7 +618,11 @@ defmodule Mix.Tasks.Wnh.StagingFullE2e do [job], fragment("?->>'recipient' = ANY(?)", job.args, ^recipients) or fragment("?->>'request_id' = ANY(?)", job.args, ^request_ids) or - fragment("?->>'notification_id' = ANY(?)", job.args, ^notification_ids) + fragment("?->>'notification_id' = ANY(?)", job.args, ^notification_ids) or + (job.worker in ^@support_job_workers and + fragment("?->>'request_id' = ANY(?)", job.args, ^support_request_ids)) or + (job.worker in ^@content_removal_job_workers and + fragment("?->>'notice_id' = ANY(?)", job.args, ^content_removal_notice_ids)) ) |> select([job], job.id) |> Repo.all() @@ -614,8 +645,20 @@ defmodule Mix.Tasks.Wnh.StagingFullE2e do end end - defp assert_no_late_fixture_jobs!(user_ids, request_ids, notification_ids) do - case fixture_job_ids(user_ids, request_ids, notification_ids) do + defp assert_no_late_fixture_jobs!( + user_ids, + request_ids, + notification_ids, + support_request_ids, + content_removal_notice_ids + ) do + case fixture_job_ids( + user_ids, + request_ids, + notification_ids, + support_request_ids, + content_removal_notice_ids + ) do [] -> :ok ids -> Mix.raise("run-scoped Oban jobs appeared during cleanup: #{inspect(ids)}") end diff --git a/test/who_need_help/staging_full_e2e_cleanup_test.exs b/test/who_need_help/staging_full_e2e_cleanup_test.exs new file mode 100644 index 0000000..bc14039 --- /dev/null +++ b/test/who_need_help/staging_full_e2e_cleanup_test.exs @@ -0,0 +1,105 @@ +defmodule WhoNeedHelp.StagingFullE2ECleanupTest do + use WhoNeedHelp.DataCase, async: false + use Oban.Testing, repo: WhoNeedHelp.Repo + + import ExUnit.CaptureIO + + alias Oban.Job + alias WhoNeedHelp.Accounts.{Scope, User} + alias WhoNeedHelp.{ContentRemoval, Repo, Support} + alias WhoNeedHelp.Mail.{ContentRemovalEmailWorker, SupportConfirmationWorker} + alias WhoNeedHelp.Push.NearbyMatchWorker + + test "cleanup removes only run-owned support and legal mail jobs" do + run_id = "cleanup-#{System.unique_integer([:positive])}" + manifest_path = "/output/wnh-staging-full-e2e-#{run_id}.json" + password = "staging-cleanup-#{System.unique_integer([:positive])}" + + %Postgrex.Result{rows: [[database]]} = + Repo.query!("SELECT current_database()", [], log: false) + + env = %{ + "WNH_STAGING_E2E_CONFIRM" => "public-staging-full-e2e", + "WNH_STAGING_E2E_EXPECTED_DATABASE" => database, + "WNH_STAGING_E2E_MANIFEST_PATH" => manifest_path, + "WNH_STAGING_E2E_PASSWORD" => password, + "WNH_STAGING_E2E_RUN_ID" => run_id + } + + previous_env = Map.new(env, fn {name, _value} -> {name, System.get_env(name)} end) + Enum.each(env, fn {name, value} -> System.put_env(name, value) end) + + on_exit(fn -> + File.rm(manifest_path) + + Enum.each(previous_env, fn + {name, nil} -> System.delete_env(name) + {name, value} -> System.put_env(name, value) + end) + end) + + run_task("prepare") + + requester = + Repo.get_by!(User, email: "wnh-staging-e2e-#{run_id}-requester@example.invalid") + + scope = Scope.for_user(requester) + + assert {:ok, support_request} = + Support.create_request(scope, %{ + "kind" => "technical_issue", + "subject" => "Run-scoped support cleanup", + "details" => "Verify that the exact support mail job is removed with its fixture." + }) + + support_job = + %{request_id: support_request.id} + |> SupportConfirmationWorker.new() + |> Repo.insert!() + + assert {:ok, notice} = + ContentRemoval.create_notice(scope, :general, %{ + "category" => "privacy_violation", + "submitter_name" => "Fixture requester", + "relationship" => "self", + "content_locations" => "https://example.test/requests/run-scoped-cleanup", + "explanation" => + "Verify that the exact content-removal mail job is removed with its fixture.", + "electronic_signature" => "Fixture requester", + "good_faith" => "true", + "accurate_complete" => "true" + }) + + legal_job = + Repo.get_by!(Job, + worker: inspect(ContentRemovalEmailWorker), + args: %{"notice_id" => notice.id, "kind" => "received"} + ) + + unrelated_job = + %{request_id: support_request.id, event_key: "created"} + |> NearbyMatchWorker.new() + |> Repo.insert!() + + run_task("cleanup") + + manifest = manifest_path |> File.read!() |> Jason.decode!() + cleaned_job_ids = manifest["cleanup_targets"]["push_jobs"] + + assert support_job.id in cleaned_job_ids + assert legal_job.id in cleaned_job_ids + refute unrelated_job.id in cleaned_job_ids + refute Repo.get(Job, support_job.id) + refute Repo.get(Job, legal_job.id) + assert Repo.get(Job, unrelated_job.id) + assert manifest["cleanup_verified"] == true + end + + defp run_task(action) do + Mix.Task.reenable("wnh.staging_full_e2e") + + capture_io(fn -> + Mix.Tasks.Wnh.StagingFullE2e.run([action]) + end) + end +end