defmodule Plausible.Workers.ImportAnalyticsTest do use Plausible.DataCase use Bamboo.Test alias Plausible.Imported.SiteImport alias Plausible.Workers.ImportAnalytics require Plausible.Imported.SiteImport @moduletag capture_log: true describe "perform/1" do setup do %{ import_opts: [ start_date: Timex.today() |> Timex.shift(days: -7), end_date: Timex.today() ] } end test "updates site import after successful import", %{ import_opts: import_opts } do user = insert(:user, trial_expiry_date: Timex.today() |> Timex.shift(days: 1)) site = insert(:site, members: [user]) {:ok, job} = Plausible.Imported.NoopImporter.new_import(site, user, import_opts) assert [%{status: SiteImport.pending()}] = Plausible.Imported.list_all_imports(site) # before_start callback triggered assert_received {:before_start, import_id} assert :ok = job |> Repo.reload!() |> ImportAnalytics.perform() assert [%{id: ^import_id, status: SiteImport.completed()}] = Plausible.Imported.list_all_imports(site) # on_success callback triggered assert_received {:on_success, ^import_id} end test "clears stats_start_date field for the site after successful import", %{ import_opts: import_opts } do user = insert(:user, trial_expiry_date: Timex.today() |> Timex.shift(days: 1)) site = insert(:site, members: [user], stats_start_date: ~D[2005-01-01]) {:ok, job} = Plausible.Imported.NoopImporter.new_import(site, user, import_opts) assert :ok = job |> Repo.reload!() |> ImportAnalytics.perform() site = Repo.reload!(site) assert site.stats_start_date == nil assert Plausible.Sites.stats_start_date(site) == import_opts[:start_date] assert Repo.reload!(site).stats_start_date == import_opts[:start_date] end test "sends email to owner after successful import", %{import_opts: import_opts} do user = insert(:user, trial_expiry_date: Timex.today() |> Timex.shift(days: 1)) site = insert(:site, members: [user]) {:ok, job} = Plausible.Imported.NoopImporter.new_import(site, user, import_opts) assert :ok = job |> Repo.reload!() |> ImportAnalytics.perform() assert_email_delivered_with( to: [user], subject: "Noop data imported for #{site.domain}" ) end test "send email after successful import only to the user who ran the import", %{ import_opts: import_opts } do owner = insert(:user, trial_expiry_date: Timex.today() |> Timex.shift(days: 1)) site = insert(:site, members: [owner]) importing_user = insert(:user) insert(:site_membership, site: site, user: importing_user, role: :admin) {:ok, job} = Plausible.Imported.NoopImporter.new_import(site, importing_user, import_opts) assert :ok = job |> Repo.reload!() |> ImportAnalytics.perform() assert_email_delivered_with( to: [importing_user], subject: "Noop data imported for #{site.domain}" ) refute_email_delivered_with( to: [owner], subject: "Noop data imported for #{site.domain}" ) end test "updates site import record after failed import", %{import_opts: import_opts} do user = insert(:user, trial_expiry_date: Timex.today() |> Timex.shift(days: 1)) site = insert(:site, members: [user]) import_opts = Keyword.put(import_opts, :error, true) {:ok, job} = Plausible.Imported.NoopImporter.new_import(site, user, import_opts) assert {:discard, "Something went wrong"} = job |> Repo.reload!() |> ImportAnalytics.perform() assert [%{status: SiteImport.failed()}] = Plausible.Imported.list_all_imports(site) end test "clears any orphaned data during import", %{import_opts: import_opts} do user = insert(:user, trial_expiry_date: Timex.today() |> Timex.shift(days: 1)) site = insert(:site, members: [user]) import_opts = Keyword.put(import_opts, :error, true) {:ok, job} = Plausible.Imported.NoopImporter.new_import(site, user, import_opts) populate_stats(site, [ build(:imported_visitors, import_id: job.args.import_id, pageviews: 10) ]) assert {:discard, _} = job |> Repo.reload!() |> ImportAnalytics.perform() assert eventually(fn -> count = Plausible.Stats.Clickhouse.imported_pageview_count(site) {count == 0, count} end) # on_failure callback triggered assert_received {:on_failure, _import_id} end test "sends email to owner after failed import", %{import_opts: import_opts} do user = insert(:user, trial_expiry_date: Timex.today() |> Timex.shift(days: 1)) site = insert(:site, members: [user]) import_opts = Keyword.put(import_opts, :error, true) {:ok, job} = Plausible.Imported.NoopImporter.new_import(site, user, import_opts) assert {:discard, _} = job |> Repo.reload!() |> ImportAnalytics.perform() assert_email_delivered_with( to: [user], subject: "Noop import failed for #{site.domain}" ) end test "sends email after failed import only to the user who ran the import", %{ import_opts: import_opts } do owner = insert(:user, trial_expiry_date: Timex.today() |> Timex.shift(days: 1)) site = insert(:site, members: [owner]) import_opts = Keyword.put(import_opts, :error, true) importing_user = insert(:user) insert(:site_membership, site: site, user: importing_user, role: :admin) {:ok, job} = Plausible.Imported.NoopImporter.new_import(site, importing_user, import_opts) assert {:discard, _} = job |> Repo.reload!() |> ImportAnalytics.perform() assert_email_delivered_with( to: [importing_user], subject: "Noop import failed for #{site.domain}" ) refute_email_delivered_with( to: [owner], subject: "Noop import failed for #{site.domain}" ) end end describe "perform/1 notifications" do setup do on_exit(fn -> Ecto.Adapters.SQL.Sandbox.unboxed_run(Plausible.Repo, fn -> Repo.delete_all(Plausible.Site) Repo.delete_all(Plausible.Auth.User) Repo.delete_all(Oban.Job) end) :ok end) %{ import_opts: [ start_date: Timex.today() |> Timex.shift(days: -7), end_date: Timex.today() ] } end test "sends oban notification to calling process on completion when instructed", %{ import_opts: import_opts } do Ecto.Adapters.SQL.Sandbox.unboxed_run(Plausible.Repo, fn -> user = insert(:user, trial_expiry_date: Timex.today() |> Timex.shift(days: 1)) site = insert(:site, members: [user]) site_id = site.id import_opts = Keyword.put(import_opts, :listen?, true) {:ok, job} = Plausible.Imported.NoopImporter.new_import(site, user, import_opts) import_id = job.args[:import_id] job |> Repo.reload!() |> ImportAnalytics.perform() assert_receive {:notification, :analytics_imports_jobs, %{"event" => "complete", "import_id" => ^import_id, "site_id" => ^site_id}} end) end test "sends oban notification to calling process on permanent failure when instructed", %{ import_opts: import_opts } do Ecto.Adapters.SQL.Sandbox.unboxed_run(Plausible.Repo, fn -> user = insert(:user, trial_expiry_date: Timex.today() |> Timex.shift(days: 1)) site = insert(:site, members: [user]) site_id = site.id import_opts = import_opts |> Keyword.put(:listen?, true) |> Keyword.put(:error, true) {:ok, job} = Plausible.Imported.NoopImporter.new_import(site, user, import_opts) import_id = job.args[:import_id] job |> Repo.reload!() |> ImportAnalytics.perform() assert_receive {:notification, :analytics_imports_jobs, %{"event" => "fail", "import_id" => ^import_id, "site_id" => ^site_id}} end) end test "sends oban notification to calling process on transient failure when instructed", %{ import_opts: import_opts } do Ecto.Adapters.SQL.Sandbox.unboxed_run(Plausible.Repo, fn -> user = insert(:user, trial_expiry_date: Timex.today() |> Timex.shift(days: 1)) site = insert(:site, members: [user]) site_id = site.id import_opts = import_opts |> Keyword.put(:listen?, true) |> Keyword.put(:crash, true) {:ok, job} = Plausible.Imported.NoopImporter.new_import(site, user, import_opts) import_id = job.args[:import_id] site_import = Repo.get(Plausible.Imported.SiteImport, import_id) # emulate oban error handler try do job |> Repo.reload!() |> ImportAnalytics.perform() rescue _ -> ImportAnalytics.import_fail_transient(site_import) end assert_receive {:notification, :analytics_imports_jobs, %{ "event" => "transient_fail", "import_id" => ^import_id, "site_id" => ^site_id }} end) end test "sends oban notification to calling process on completion when listener setup separately", %{ import_opts: import_opts } do Ecto.Adapters.SQL.Sandbox.unboxed_run(Plausible.Repo, fn -> user = insert(:user, trial_expiry_date: Timex.today() |> Timex.shift(days: 1)) site = insert(:site, members: [user]) site_id = site.id {:ok, job} = Plausible.Imported.NoopImporter.new_import(site, user, import_opts) import_id = job.args[:import_id] :ok = Plausible.Imported.Importer.listen() job |> Repo.reload!() |> ImportAnalytics.perform() assert_receive {:notification, :analytics_imports_jobs, %{"event" => "complete", "import_id" => ^import_id, "site_id" => ^site_id}} end) end end end