From 0fa6b688af16489a6e3ae9fae56a52cac96e9235 Mon Sep 17 00:00:00 2001 From: Adam Rutkowski Date: Mon, 24 Oct 2022 09:34:02 +0200 Subject: [PATCH] Google APIs integration improvements (#2358) * Make TestUtils module available in all tests * Add macros patching the application env in tests Unfortunately a lot of existing functionality relies on certain application env setup. This isn't ideal because the app config is a shared state that prevents us from running the tests in parallel. Those macros encapsulate setting up new env for test purposes and make sure the changes are reverted when the test finishes. * Allow passing request opts to HTTPClient.post/4 We need this to swap custom request building in Google Analytics import. * Unify errors when listing sites * React: propagate backend error messages if available * React: catch API errors in Search Terms component * Propagate google API errors on referrer drilldown * Handle verified properties errors in SC settings * Add missing tests for SC settings controller * Unify errors for fetching search analytics queries (list stats) * Unify errors refreshing Google Auth Token * Test fetch_stats/3 errors and replace Double with Mox * Fixup makrup * s/class/className * Simplify Search Terms display in case of errors * Fix warnings --- assets/js/dashboard/api.js | 9 +- .../dashboard/stats/sources/search-terms.js | 12 +- lib/plausible/google/api.ex | 3 +- lib/plausible/google/http.ex | 132 ++++++------- lib/plausible/http_client.ex | 16 +- .../controllers/api/stats_controller.ex | 16 +- .../site/settings_search_console.html.eex | 22 ++- test/plausible/billing/billing_test.exs | 1 - test/plausible/google/api_test.exs | 173 ++++++++++++++---- test/plausible/google/buffer_test.exs | 7 +- test/plausible/google/vcr_test.exs | 3 +- test/plausible/http_client_test.exs | 12 ++ test/plausible/imported/imported_test.exs | 1 - test/plausible/purge_test.exs | 1 - test/plausible/site/admin_test.exs | 2 +- test/plausible/site/sites_test.exs | 2 +- test/plausible_web/captcha_test.exs | 9 +- .../api/external_sites_controller_test.exs | 1 - .../aggregate_test.exs | 1 - .../external_stats_controller/auth_test.exs | 8 +- .../breakdown_test.exs | 2 +- .../timeseries_test.exs | 1 - .../api/internal_controller_test.exs | 1 - .../stats_controller/authorization_test.exs | 1 - .../api/stats_controller/browsers_test.exs | 1 - .../api/stats_controller/cities_test.exs | 1 - .../api/stats_controller/conversions_test.exs | 2 +- .../api/stats_controller/countries_test.exs | 1 - .../current_visitors_test.exs | 1 - .../api/stats_controller/main_graph_test.exs | 2 +- .../operating_systems_test.exs | 1 - .../api/stats_controller/pages_test.exs | 2 +- .../api/stats_controller/regions_test.exs | 1 - .../stats_controller/screen_sizes_test.exs | 1 - .../api/stats_controller/sources_test.exs | 2 +- .../api/stats_controller/suggestions_test.exs | 1 - .../api/stats_controller/top_stats_test.exs | 2 +- .../controllers/auth_controller_test.exs | 1 - .../controllers/billing_controller_test.exs | 1 - .../invitation_controller_test.exs | 1 - .../site/membership_controller_test.exs | 1 - .../controllers/site_controller_test.exs | 117 +++++++++++- .../controllers/stats_controller_test.exs | 1 - .../plugs/authorise_site_access_test.exs | 1 - test/support/conn_case.ex | 1 + test/support/data_case.ex | 1 + test/support/test_utils.ex | 28 +++ test/workers/check_usage_test.exs | 2 +- test/workers/import_google_analytics_test.exs | 2 +- test/workers/notify_annual_renewal_test.exs | 2 +- test/workers/send_email_report_test.exs | 1 - test/workers/send_site_setup_emails_test.exs | 2 +- .../workers/send_trial_notifications_test.exs | 2 +- 53 files changed, 427 insertions(+), 191 deletions(-) diff --git a/assets/js/dashboard/api.js b/assets/js/dashboard/api.js index 6722c52d90..cdc624176d 100644 --- a/assets/js/dashboard/api.js +++ b/assets/js/dashboard/api.js @@ -4,9 +4,10 @@ let abortController = new AbortController() let SHARED_LINK_AUTH = null class ApiError extends Error { - constructor(message) { - super(message); - this.name = "ApiError"; + constructor(message, payload) { + super(message) + this.name = "ApiError" + this.payload = payload } } @@ -56,7 +57,7 @@ export function get(url, query={}, ...extraQuery) { .then( response => { if (!response.ok) { return response.json().then((msg) => { - throw new ApiError(msg.error) + throw new ApiError(msg.error, msg) }) } return response.json() diff --git a/assets/js/dashboard/stats/sources/search-terms.js b/assets/js/dashboard/stats/sources/search-terms.js index fe2d41b576..c237107397 100644 --- a/assets/js/dashboard/stats/sources/search-terms.js +++ b/assets/js/dashboard/stats/sources/search-terms.js @@ -30,7 +30,11 @@ export default class SearchTerms extends React.Component { searchTerms: res.search_terms || [], notConfigured: res.not_configured, isAdmin: res.is_admin - })) + })).catch((error) => + { + this.setState({ loading: false, searchTerms: [], notConfigured: true, error: true, isAdmin: error.payload.is_admin }) + } + ) } renderSearchTerm(term) { @@ -67,8 +71,10 @@ export default class SearchTerms extends React.Component { return (
-
The site is not connected to Google Search Keywords
-
Cannot show search terms
+
+ This site is not connected to Search Console so we cannot show the search phrases. + {this.state.isAdmin && this.state.error && <>

Please click below to connect your Search Console account.

} +
{this.state.isAdmin && Connect with Google }
) diff --git a/lib/plausible/google/api.ex b/lib/plausible/google/api.ex index 2e58ccd794..bfc6a1e839 100644 --- a/lib/plausible/google/api.ex +++ b/lib/plausible/google/api.ex @@ -182,9 +182,8 @@ defmodule Plausible.Google.Api do buffer_pid = Keyword.get(opts, :buffer) attempt = Keyword.get(opts, :attempt, 1) sleep_time = Keyword.get(opts, :sleep_time, 1000) - http_client = Keyword.get(opts, :http_client, Finch) - case HTTP.get_report(http_client, report_request) do + case HTTP.get_report(report_request) do {:ok, {rows, next_page_token}} -> records = Plausible.Imported.from_google_analytics(rows, site.id, report_request.dataset) :ok = Plausible.Google.Buffer.insert_many(buffer_pid, report_request.dataset, records) diff --git a/lib/plausible/google/http.ex b/lib/plausible/google/http.ex index 6b32513793..4eb75e08bb 100644 --- a/lib/plausible/google/http.ex +++ b/lib/plausible/google/http.ex @@ -2,82 +2,65 @@ defmodule Plausible.Google.HTTP do require Logger alias Plausible.HTTPClient - @spec get_report(module(), Plausible.Google.ReportRequest.t()) :: + @spec get_report(Plausible.Google.ReportRequest.t()) :: {:ok, {[map()], String.t() | nil}} | {:error, any()} - def get_report(http_client, %Plausible.Google.ReportRequest{} = report_request) do - params = - Jason.encode!(%{ - reportRequests: [ - %{ - viewId: report_request.view_id, - dateRanges: [ - %{ - startDate: report_request.date_range.first, - endDate: report_request.date_range.last - } - ], - dimensions: Enum.map(report_request.dimensions, &%{name: &1, histogramBuckets: []}), - metrics: Enum.map(report_request.metrics, &%{expression: &1}), - hideTotals: true, - hideValueRanges: true, - orderBys: [%{fieldName: "ga:date", sortOrder: "DESCENDING"}], - pageSize: report_request.page_size, - pageToken: report_request.page_token - } - ] - }) + def get_report(%Plausible.Google.ReportRequest{} = report_request) do + params = %{ + reportRequests: [ + %{ + viewId: report_request.view_id, + dateRanges: [ + %{ + startDate: report_request.date_range.first, + endDate: report_request.date_range.last + } + ], + dimensions: Enum.map(report_request.dimensions, &%{name: &1, histogramBuckets: []}), + metrics: Enum.map(report_request.metrics, &%{expression: &1}), + hideTotals: true, + hideValueRanges: true, + orderBys: [%{fieldName: "ga:date", sortOrder: "DESCENDING"}], + pageSize: report_request.page_size, + pageToken: report_request.page_token + } + ] + } response = - :post - |> Finch.build( + HTTPClient.impl().post( "#{reporting_api_url()}/v4/reports:batchGet", [{"Authorization", "Bearer #{report_request.access_token}"}], - params + params, + receive_timeout: 60_000 ) - |> http_client.request(Plausible.Finch, receive_timeout: 60_000) - with {:ok, %{status: 200, body: body}} <- response, + with {:ok, %{body: body}} <- response, {:ok, report} <- parse_report_from_response(body), token <- Map.get(report, "nextPageToken"), {:ok, report} <- convert_to_maps(report) do {:ok, {report, token}} else - {:ok, %{status: _non_http_200, body: _body} = response} -> - report_failed_request_to_sentry(response) + {:error, %{reason: %{status: status, body: body}}} -> + Sentry.Context.set_extra_context(%{ga_response: %{body: body, status: status}}) {:error, :request_failed} - {:error, cause} -> - {:error, cause} + {:error, _} -> + {:error, :request_failed} end end - defp report_failed_request_to_sentry(%{status: status, body: body}) do - case Jason.decode(body) do - {:ok, %{} = body} -> - Sentry.Context.set_extra_context(%{ga_response: %{body: body, status: status}}) - - _error -> - Sentry.Context.set_extra_context(%{ga_response: %{body: body, status: status}}) - end - end - - defp parse_report_from_response(raw_body) do - with {:ok, map} <- Jason.decode(raw_body), - %{"reports" => [report | _]} <- map do + defp parse_report_from_response(body) do + with %{"reports" => [report | _]} <- body do {:ok, report} else - {:error, cause} -> - Logger.error("Google Analytics: Failed to parse JSON. Reason: #{inspect(cause)}") - Sentry.Context.set_extra_context(%{google_analytics_response: raw_body}) - {:error, cause} + _ -> + Sentry.Context.set_extra_context(%{google_analytics_response: body}) - %{} = response -> Logger.error( - "Google Analytics: Failed to find report in response. Reason: #{inspect(response)}" + "Google Analytics: Failed to find report in response. Reason: #{inspect(body)}" ) - Sentry.Context.set_extra_context(%{google_analytics_response: response}) - {:error, {:invalid_response, response}} + {:error, {:invalid_response, body}} end end @@ -114,13 +97,19 @@ defmodule Plausible.Google.HTTP do url = "#{api_url()}/webmasters/v3/sites" headers = [{"Content-Type", "application/json"}, {"Authorization", "Bearer #{access_token}"}] - case HTTPClient.get(url, headers) do + case HTTPClient.impl().get(url, headers) do {:ok, %{body: body}} -> {:ok, body} - {:error, reason} = e -> + {:error, %{reason: %{status: s}}} when s in [401, 403] -> + {:error, "google_auth_error"} + + {:error, %{reason: %{body: %{"error" => error}}}} -> + {:error, error} + + {:error, reason} -> Logger.error("Google Analytics: failed to list sites: #{inspect(reason)}") - e + {:error, "failed_to_list_sites"} end end @@ -182,25 +171,20 @@ defmodule Plausible.Google.HTTP do url = "#{api_url()}/webmasters/v3/sites/#{property}/searchAnalytics/query" headers = [{"Authorization", "Bearer #{access_token}"}] - case HTTPClient.post(url, headers, params) do + case HTTPClient.impl().post(url, headers, params) do {:ok, %Finch.Response{body: body, status: 200}} -> {:ok, body} - {:error, %{reason: %Finch.Response{body: body, status: 401}}} -> - Sentry.capture_message("Error fetching Google queries", extra: %{body: inspect(body)}) - {:error, :invalid_credentials} + {:error, %{reason: %Finch.Response{body: _body, status: status}}} + when status in [401, 403] -> + {:error, "google_auth_error"} - {:error, %{reason: %Finch.Response{body: body, status: 403}}} -> - Sentry.capture_message("Error fetching Google queries", extra: %{body: inspect(body)}) - {:error, get_in(body, ["error", "message"])} + {:error, %{reason: %{body: %{"error" => error}}}} -> + {:error, error} - {:error, %{reason: %Finch.Response{body: body}}} -> - Sentry.capture_message("Error fetching Google queries", extra: %{body: inspect(body)}) - {:error, :unknown} - - {:error, %{reason: _} = e} -> - Sentry.capture_message("Error fetching Google queries", extra: %{error: inspect(e)}) - {:error, :unknown} + {:error, reason} -> + Logger.error("Google Analytics: failed to list stats: #{inspect(reason)}") + {:error, "failed_to_list_stats"} end end @@ -223,14 +207,12 @@ defmodule Plausible.Google.HTTP do {:ok, %Finch.Response{body: body, status: 200}} -> {:ok, body} - {:error, %{reason: %Finch.Response{body: body, status: _non_http_200}}} -> - body - |> Map.get("error") - |> then(&{:error, &1}) + {:error, %{reason: %Finch.Response{body: %{"error" => error}, status: _non_http_200}}} -> + {:error, error} {:error, %{reason: _} = e} -> Sentry.capture_message("Error fetching Google queries", extra: %{error: inspect(e)}) - {:error, :unknown} + {:error, :unknown_error} end end diff --git a/lib/plausible/http_client.ex b/lib/plausible/http_client.ex index 55a3fc0e5b..ed83168989 100644 --- a/lib/plausible/http_client.ex +++ b/lib/plausible/http_client.ex @@ -11,6 +11,7 @@ defmodule Plausible.HTTPClient.Non200Error do end defmodule Plausible.HTTPClient.Interface do + @type finch_request_opts() :: Keyword.t() @type url() :: Finch.Request.url() @type headers() :: Finch.Request.headers() @type params() :: Finch.Request.body() | map() @@ -21,6 +22,7 @@ defmodule Plausible.HTTPClient.Interface do @callback get(url(), headers()) :: response() @callback get(url()) :: response() @callback post(url(), headers(), params()) :: response() + @callback post(url(), headers(), params(), finch_request_opts()) :: response() end defmodule Plausible.HTTPClient do @@ -40,8 +42,8 @@ defmodule Plausible.HTTPClient do @behaviour Plausible.HTTPClient.Interface @impl Plausible.HTTPClient.Interface - def post(url, headers \\ [], params \\ nil) do - call(:post, url, headers, params) + def post(url, headers \\ [], params \\ nil, finch_req_opts \\ []) do + call(:post, url, headers, params, finch_req_opts) end @doc """ @@ -59,12 +61,12 @@ defmodule Plausible.HTTPClient do Application.get_env(:plausible, :http_impl, __MODULE__) end - defp call(method, url, headers, params) do + defp call(method, url, headers, params, finch_req_opts \\ []) do {params, headers} = maybe_encode_params(params, headers) method |> build_request(url, headers, params) - |> do_request() + |> do_request(finch_req_opts) |> maybe_decode_body() |> tag_error() end @@ -73,8 +75,8 @@ defmodule Plausible.HTTPClient do Finch.build(method, url, headers, params) end - defp do_request(request) do - Finch.request(request, Plausible.Finch) + defp do_request(request, finch_req_opts) do + Finch.request(request, Plausible.Finch, finch_req_opts) end defp maybe_encode_params(params, headers) when is_binary(params) or is_nil(params) do @@ -123,7 +125,7 @@ defmodule Plausible.HTTPClient do end end - defp maybe_decode_body(resp), do: resp + defp maybe_decode_body(response), do: response defp json?(headers) do found = diff --git a/lib/plausible_web/controllers/api/stats_controller.ex b/lib/plausible_web/controllers/api/stats_controller.ex index 01ad08db0c..04cf3237db 100644 --- a/lib/plausible_web/controllers/api/stats_controller.ex +++ b/lib/plausible_web/controllers/api/stats_controller.ex @@ -429,18 +429,24 @@ defmodule PlausibleWeb.Api.StatsController do %{:visitors => %{value: total_visitors}} = Stats.aggregate(site, query, [:visitors]) + user_id = get_session(conn, :current_user_id) + is_admin = user_id && Plausible.Sites.has_admin_access?(user_id, site) + case search_terms do nil -> - user_id = get_session(conn, :current_user_id) - is_admin = user_id && Plausible.Sites.has_admin_access?(user_id, site) json(conn, %{not_configured: true, is_admin: is_admin, total_visitors: total_visitors}) {:ok, terms} -> json(conn, %{search_terms: terms, total_visitors: total_visitors}) - {:error, e} -> - put_status(conn, 500) - |> json(%{error: e}) + {:error, _} -> + conn + |> put_status(502) + |> json(%{ + not_configured: true, + is_admin: is_admin, + total_visitors: total_visitors + }) end end diff --git a/lib/plausible_web/templates/site/settings_search_console.html.eex b/lib/plausible_web/templates/site/settings_search_console.html.eex index f024a1b093..d24dd32603 100644 --- a/lib/plausible_web/templates/site/settings_search_console.html.eex +++ b/lib/plausible_web/templates/site/settings_search_console.html.eex @@ -36,12 +36,24 @@ <%= submit "Save", class: "button" %> <% end %> <% {:error, error} -> %> -

The following error happened when fetching your Google Search Console domains.

+

The following error happened when fetching your Google Search Console domains:

- <%= if error == "invalid_grant" do %> -

Invalid Grant error returned from Google. See here on how to fix it.

- <% else %> -

Something went wrong

+ <%= case error do %> + <% "invalid_grant" -> %> +

+ + Invalid Grant error returned from Google. See here on how to fix it. + +

+ <% "google_auth_error" -> %> +

+ Your Search Console account hasn't been connected successfully. Please unlink your Google account and try linking it again. +

+ + <% _ -> %> +

+ Something went wrong, but looks temporary. If the problem persists, try re-linking your Google account. +

<% end %> <% end %> <% else %> diff --git a/test/plausible/billing/billing_test.exs b/test/plausible/billing/billing_test.exs index 61984dc668..9ca1111f1d 100644 --- a/test/plausible/billing/billing_test.exs +++ b/test/plausible/billing/billing_test.exs @@ -2,7 +2,6 @@ defmodule Plausible.BillingTest do use Plausible.DataCase use Bamboo.Test, shared: true alias Plausible.Billing - import Plausible.TestUtils describe "usage" do test "is 0 with no events" do diff --git a/test/plausible/google/api_test.exs b/test/plausible/google/api_test.exs index 01c86aede2..e0d2c54477 100644 --- a/test/plausible/google/api_test.exs +++ b/test/plausible/google/api_test.exs @@ -2,13 +2,16 @@ defmodule Plausible.Google.ApiTest do use Plausible.DataCase, async: true use ExVCR.Mock, adapter: ExVCR.Adapter.Finch alias Plausible.Google.Api - import Plausible.TestUtils - import Double + + import ExUnit.CaptureLog + import Mox + setup :verify_on_exit! setup [:create_user, :create_new_site] describe "fetch_and_persist/4" do - @ok_response File.read!("fixture/ga_batch_report.json") + @ok_response Jason.decode!(File.read!("fixture/ga_batch_report.json")) + @no_report_response Jason.decode!(File.read!("fixture/ga_report_empty_rows.json")) setup do {:ok, pid} = Plausible.Google.Buffer.start_link() @@ -16,12 +19,6 @@ defmodule Plausible.Google.ApiTest do end test "will fetch and persist import data from Google Analytics", %{site: site, buffer: buffer} do - finch_double = - Finch - |> stub(:request, fn _, _, _ -> - {:ok, %Finch.Response{status: 200, body: @ok_response}} - end) - request = %Plausible.Google.ReportRequest{ dataset: "imported_exit_pages", view_id: "123", @@ -33,8 +30,36 @@ defmodule Plausible.Google.ApiTest do page_size: 10_000 } + expect( + Plausible.HTTPClient.Mock, + :post, + fn + "https://analyticsreporting.googleapis.com/v4/reports:batchGet", + [{"Authorization", "Bearer fake-token"}], + %{ + reportRequests: [ + %{ + dateRanges: [%{endDate: ~D[2022-02-01], startDate: ~D[2022-01-01]}], + dimensions: [ + %{histogramBuckets: [], name: "ga:date"}, + %{histogramBuckets: [], name: "ga:exitPagePath"} + ], + hideTotals: true, + hideValueRanges: true, + metrics: [%{expression: "ga:users"}, %{expression: "ga:exits"}], + orderBys: [%{fieldName: "ga:date", sortOrder: "DESCENDING"}], + pageSize: 10000, + pageToken: nil, + viewId: "123" + } + ] + }, + [receive_timeout: 60_000] -> + {:ok, %Finch.Response{status: 200, body: @ok_response}} + end + ) + Api.fetch_and_persist(site, request, - http_client: finch_double, sleep_time: 0, buffer: buffer ) @@ -52,13 +77,21 @@ defmodule Plausible.Google.ApiTest do site: site, buffer: buffer } do - finch_double = - Finch - |> stub(:request, fn _, _, _ -> {:error, :timeout} end) - |> stub(:request, fn _, _, _ -> {:error, :nx_domain} end) - |> stub(:request, fn _, _, _ -> {:error, :closed} end) - |> stub(:request, fn _, _, _ -> {:ok, %Finch.Response{status: 503}} end) - |> stub(:request, fn _, _, _ -> {:ok, %Finch.Response{status: 502}} end) + expect( + Plausible.HTTPClient.Mock, + :post, + 5, + fn + "https://analyticsreporting.googleapis.com/v4/reports:batchGet", + _, + _, + [receive_timeout: 60_000] -> + Enum.random([ + {:error, %Mint.TransportError{reason: :nxdomain}}, + {:error, %{reason: %Finch.Response{status: 500}}} + ]) + end + ) request = %Plausible.Google.ReportRequest{ view_id: "123", @@ -72,25 +105,23 @@ defmodule Plausible.Google.ApiTest do assert {:error, :request_failed} = Api.fetch_and_persist(site, request, - http_client: finch_double, sleep_time: 0, buffer: buffer ) - - assert_receive({Finch, :request, [_, _, [receive_timeout: 60_000]]}) - assert_receive({Finch, :request, [_, _, [receive_timeout: 60_000]]}) - assert_receive({Finch, :request, [_, _, [receive_timeout: 60_000]]}) - assert_receive({Finch, :request, [_, _, [receive_timeout: 60_000]]}) - assert_receive({Finch, :request, [_, _, [receive_timeout: 60_000]]}) end test "does not fail when report does not have rows key", %{site: site, buffer: buffer} do - finch_double = - Finch - |> stub(:request, fn _, _, _ -> - {:ok, - %Finch.Response{status: 200, body: File.read!("fixture/ga_report_empty_rows.json")}} - end) + expect( + Plausible.HTTPClient.Mock, + :post, + fn + "https://analyticsreporting.googleapis.com/v4/reports:batchGet", + _, + _, + [receive_timeout: 60_000] -> + {:ok, %Finch.Response{status: 200, body: @no_report_response}} + end + ) request = %Plausible.Google.ReportRequest{ dataset: "imported_exit_pages", @@ -105,14 +136,92 @@ defmodule Plausible.Google.ApiTest do assert :ok == Api.fetch_and_persist(site, request, - http_client: finch_double, sleep_time: 0, buffer: buffer ) end end - describe "fetch_stats/3" do + describe "fetch_stats/3 errors" do + setup %{user: user, site: site} do + insert(:google_auth, + user: user, + site: site, + property: "sc-domain:dummy.test", + expires: NaiveDateTime.add(NaiveDateTime.utc_now(), 3600) + ) + + :ok + end + + test "returns generic google_auth_error on 401/403", %{site: site} do + expect( + Plausible.HTTPClient.Mock, + :post, + fn + "https://www.googleapis.com/webmasters/v3/sites/sc-domain%3Adummy.test/searchAnalytics/query", + [{"Authorization", "Bearer 123"}], + %{ + dimensionFilterGroups: %{}, + dimensions: ["query"], + endDate: "2022-01-05", + rowLimit: 5, + startDate: "2022-01-01" + } -> + {:error, %{reason: %Finch.Response{status: Enum.random([401, 403])}}} + end + ) + + query = %Plausible.Stats.Query{date_range: Date.range(~D[2022-01-01], ~D[2022-01-05])} + + assert {:error, "google_auth_error"} = Plausible.Google.Api.fetch_stats(site, query, 5) + end + + test "returns whatever error code google returns on API client error", %{site: site} do + expect( + Plausible.HTTPClient.Mock, + :post, + fn + "https://www.googleapis.com/webmasters/v3/sites/sc-domain%3Adummy.test/searchAnalytics/query", + _, + _ -> + {:error, %{reason: %Finch.Response{status: 400, body: %{"error" => "some_error"}}}} + end + ) + + query = %Plausible.Stats.Query{date_range: Date.range(~D[2022-01-01], ~D[2022-01-05])} + + assert {:error, "some_error"} = Plausible.Google.Api.fetch_stats(site, query, 5) + end + + test "returns generic HTTP error and logs it", %{site: site} do + expect( + Plausible.HTTPClient.Mock, + :post, + fn + "https://www.googleapis.com/webmasters/v3/sites/sc-domain%3Adummy.test/searchAnalytics/query", + _, + _ -> + {:error, Finch.Error.exception(:some_reason)} + end + ) + + query = %Plausible.Stats.Query{date_range: Date.range(~D[2022-01-01], ~D[2022-01-05])} + + log = + capture_log(fn -> + assert {:error, "failed_to_list_stats"} = + Plausible.Google.Api.fetch_stats(site, query, 5) + end) + + assert log =~ "Google Analytics: failed to list stats: %Finch.Error{reason: :some_reason}" + end + end + + describe "fetch_stats/3 with VCR cassetes" do + # We need real HTTP Client for VCR tests + setup_patch_env(:http_impl, Plausible.HTTPClient) + test "returns name and visitor count", %{user: user, site: site} do use_cassette "google_analytics_stats", match_requests_on: [:request_body] do insert(:google_auth, diff --git a/test/plausible/google/buffer_test.exs b/test/plausible/google/buffer_test.exs index 2bcd483f72..bb14f1212c 100644 --- a/test/plausible/google/buffer_test.exs +++ b/test/plausible/google/buffer_test.exs @@ -1,6 +1,6 @@ defmodule Plausible.Google.BufferTest do use Plausible.DataCase, async: false - import Plausible.TestUtils + import Ecto.Query alias Plausible.Google.Buffer @@ -8,10 +8,7 @@ defmodule Plausible.Google.BufferTest do defp set_buffer_size(_setup_args) do google_setting = Application.get_env(:plausible, :google) - on_exit(fn -> Application.put_env(:plausible, :google, google_setting) end) - test_setting = Keyword.put(google_setting, :max_buffer_size, 10) - Application.put_env(:plausible, :google, test_setting) - + patch_env(:google, Keyword.put(google_setting, :max_buffer_size, 10)) :ok end diff --git a/test/plausible/google/vcr_test.exs b/test/plausible/google/vcr_test.exs index 0e1918730e..1eff00be41 100644 --- a/test/plausible/google/vcr_test.exs +++ b/test/plausible/google/vcr_test.exs @@ -2,9 +2,10 @@ defmodule Plausible.Google.Api.VCRTest do use Plausible.DataCase, async: false use ExVCR.Mock, adapter: ExVCR.Adapter.Finch require Ecto.Query - import Plausible.TestUtils setup [:create_user, :create_site] + # We need real HTTP Client for VCR tests + setup_patch_env(:http_impl, Plausible.HTTPClient) test "imports page views from Google Analytics", %{site: site} do use_cassette "google_analytics_import#1", match_requests_on: [:request_body] do diff --git a/test/plausible/http_client_test.exs b/test/plausible/http_client_test.exs index 37cf25766b..a7abcaf27b 100644 --- a/test/plausible/http_client_test.exs +++ b/test/plausible/http_client_test.exs @@ -102,6 +102,18 @@ defmodule Plausible.HTTPClientTest do HTTPClient.post(bypass_url(bypass, path: "/any"), headers_no_content_type, params) end + test "post/4 accepts finch request opts", %{bypass: bypass} do + Bypass.expect_once(bypass, "POST", "/timeout", fn conn -> + Process.sleep(500) + Conn.resp(conn, 200, "ok") + end) + + assert {:error, %Mint.TransportError{reason: :timeout}} == + HTTPClient.post(bypass_url(bypass, path: "/timeout"), [], %{}, receive_timeout: 100) + + Bypass.down(bypass) + end + test "non-200 responses are tagged as errors", %{bypass: bypass} do Bypass.expect_once(bypass, "GET", "/get", fn conn -> Conn.resp(conn, 300, "oops") diff --git a/test/plausible/imported/imported_test.exs b/test/plausible/imported/imported_test.exs index ed55bc15ca..3b7285ee79 100644 --- a/test/plausible/imported/imported_test.exs +++ b/test/plausible/imported/imported_test.exs @@ -1,7 +1,6 @@ defmodule Plausible.ImportedTest do use PlausibleWeb.ConnCase use Timex - import Plausible.TestUtils @user_id 123 diff --git a/test/plausible/purge_test.exs b/test/plausible/purge_test.exs index 78b0218a04..ce19544bef 100644 --- a/test/plausible/purge_test.exs +++ b/test/plausible/purge_test.exs @@ -1,6 +1,5 @@ defmodule Plausible.PurgeTest do use Plausible.DataCase - import Plausible.TestUtils setup do site = insert(:site, stats_start_date: ~D[2020-01-01]) diff --git a/test/plausible/site/admin_test.exs b/test/plausible/site/admin_test.exs index d7c87f362e..1c3bba27ca 100644 --- a/test/plausible/site/admin_test.exs +++ b/test/plausible/site/admin_test.exs @@ -1,6 +1,6 @@ defmodule Plausible.SiteAdminTest do use Plausible.DataCase, async: true - import Plausible.TestUtils + alias Plausible.{SiteAdmin, ClickhouseRepo, ClickhouseEvent, ClickhouseSession} describe "transfer_data" do diff --git a/test/plausible/site/sites_test.exs b/test/plausible/site/sites_test.exs index 0c37aea0f4..2f69d5501f 100644 --- a/test/plausible/site/sites_test.exs +++ b/test/plausible/site/sites_test.exs @@ -1,6 +1,6 @@ defmodule Plausible.SitesTest do use Plausible.DataCase - import Plausible.TestUtils + alias Plausible.Sites describe "is_member?" do diff --git a/test/plausible_web/captcha_test.exs b/test/plausible_web/captcha_test.exs index fc458d3cc0..7308283bf3 100644 --- a/test/plausible_web/captcha_test.exs +++ b/test/plausible_web/captcha_test.exs @@ -50,14 +50,7 @@ defmodule PlausibleWeb.CaptchaTest do end describe "with patched application env" do - setup do - original_env = Application.get_env(:plausible, :hcaptcha) - Application.put_env(:plausible, :hcaptcha, sitekey: nil) - - on_exit(fn -> - Application.put_env(:plausible, :hcaptcha, original_env) - end) - end + setup_patch_env(:hcaptcha, sitekey: nil) test "returns true when disabled" do assert Captcha.verify("disabled") diff --git a/test/plausible_web/controllers/api/external_sites_controller_test.exs b/test/plausible_web/controllers/api/external_sites_controller_test.exs index a61cf4a555..d2b3d0ef79 100644 --- a/test/plausible_web/controllers/api/external_sites_controller_test.exs +++ b/test/plausible_web/controllers/api/external_sites_controller_test.exs @@ -1,7 +1,6 @@ defmodule PlausibleWeb.Api.ExternalSitesControllerTest do use PlausibleWeb.ConnCase use Plausible.Repo - import Plausible.TestUtils setup %{conn: conn} do user = insert(:user) diff --git a/test/plausible_web/controllers/api/external_stats_controller/aggregate_test.exs b/test/plausible_web/controllers/api/external_stats_controller/aggregate_test.exs index 1be469279b..d9c6c3c039 100644 --- a/test/plausible_web/controllers/api/external_stats_controller/aggregate_test.exs +++ b/test/plausible_web/controllers/api/external_stats_controller/aggregate_test.exs @@ -1,6 +1,5 @@ defmodule PlausibleWeb.Api.ExternalStatsController.AggregateTest do use PlausibleWeb.ConnCase - import Plausible.TestUtils setup [:create_user, :create_new_site, :create_api_key, :use_api_key] @user_id 123 diff --git a/test/plausible_web/controllers/api/external_stats_controller/auth_test.exs b/test/plausible_web/controllers/api/external_stats_controller/auth_test.exs index d47c9c2595..70df17c3fa 100644 --- a/test/plausible_web/controllers/api/external_stats_controller/auth_test.exs +++ b/test/plausible_web/controllers/api/external_stats_controller/auth_test.exs @@ -1,6 +1,5 @@ defmodule PlausibleWeb.Api.ExternalStatsController.AuthTest do use PlausibleWeb.ConnCase - import Plausible.TestUtils setup [:create_user, :create_api_key] @@ -69,12 +68,7 @@ defmodule PlausibleWeb.Api.ExternalStatsController.AuthTest do describe "super admin access" do setup %{user: user} do - original_env = Application.get_env(:plausible, :super_admin_user_ids) - Application.put_env(:plausible, :super_admin_user_ids, [user.id]) - - on_exit(fn -> - Application.put_env(:plausible, :super_admin_user_ids, original_env) - end) + patch_env(:super_admin_user_ids, [user.id]) end test "can access as a super admin", %{conn: conn, api_key: api_key} do diff --git a/test/plausible_web/controllers/api/external_stats_controller/breakdown_test.exs b/test/plausible_web/controllers/api/external_stats_controller/breakdown_test.exs index cffdc7c8ca..dba6d36d1d 100644 --- a/test/plausible_web/controllers/api/external_stats_controller/breakdown_test.exs +++ b/test/plausible_web/controllers/api/external_stats_controller/breakdown_test.exs @@ -1,6 +1,6 @@ defmodule PlausibleWeb.Api.ExternalStatsController.BreakdownTest do use PlausibleWeb.ConnCase - import Plausible.TestUtils + @user_id 1231 setup [:create_user, :create_new_site, :create_api_key, :use_api_key] diff --git a/test/plausible_web/controllers/api/external_stats_controller/timeseries_test.exs b/test/plausible_web/controllers/api/external_stats_controller/timeseries_test.exs index 23048bd4bb..300e8b7df2 100644 --- a/test/plausible_web/controllers/api/external_stats_controller/timeseries_test.exs +++ b/test/plausible_web/controllers/api/external_stats_controller/timeseries_test.exs @@ -1,6 +1,5 @@ defmodule PlausibleWeb.Api.ExternalStatsController.TimeseriesTest do use PlausibleWeb.ConnCase - import Plausible.TestUtils setup [:create_user, :create_new_site, :create_api_key, :use_api_key] diff --git a/test/plausible_web/controllers/api/internal_controller_test.exs b/test/plausible_web/controllers/api/internal_controller_test.exs index d18089976e..f16bf8e5d1 100644 --- a/test/plausible_web/controllers/api/internal_controller_test.exs +++ b/test/plausible_web/controllers/api/internal_controller_test.exs @@ -1,7 +1,6 @@ defmodule PlausibleWeb.Api.InternalControllerTest do use PlausibleWeb.ConnCase use Plausible.Repo - import Plausible.TestUtils describe "GET /api/:domain/status" do setup [:create_user, :log_in] diff --git a/test/plausible_web/controllers/api/stats_controller/authorization_test.exs b/test/plausible_web/controllers/api/stats_controller/authorization_test.exs index af2e30b068..854b4976ae 100644 --- a/test/plausible_web/controllers/api/stats_controller/authorization_test.exs +++ b/test/plausible_web/controllers/api/stats_controller/authorization_test.exs @@ -1,6 +1,5 @@ defmodule PlausibleWeb.Api.StatsController.AuthorizationTest do use PlausibleWeb.ConnCase - import Plausible.TestUtils describe "API authorization - as anonymous user" do test "Sends 404 Not found for a site that doesn't exist", %{conn: conn} do diff --git a/test/plausible_web/controllers/api/stats_controller/browsers_test.exs b/test/plausible_web/controllers/api/stats_controller/browsers_test.exs index e55c734c12..ad318720fe 100644 --- a/test/plausible_web/controllers/api/stats_controller/browsers_test.exs +++ b/test/plausible_web/controllers/api/stats_controller/browsers_test.exs @@ -1,6 +1,5 @@ defmodule PlausibleWeb.Api.StatsController.BrowsersTest do use PlausibleWeb.ConnCase - import Plausible.TestUtils describe "GET /api/stats/:domain/browsers" do setup [:create_user, :log_in, :create_new_site, :add_imported_data] diff --git a/test/plausible_web/controllers/api/stats_controller/cities_test.exs b/test/plausible_web/controllers/api/stats_controller/cities_test.exs index 70e22ffe71..71a3bdbb4b 100644 --- a/test/plausible_web/controllers/api/stats_controller/cities_test.exs +++ b/test/plausible_web/controllers/api/stats_controller/cities_test.exs @@ -1,6 +1,5 @@ defmodule PlausibleWeb.Api.StatsController.CitiesTest do use PlausibleWeb.ConnCase - import Plausible.TestUtils describe "GET /api/stats/:domain/cities" do defp seed(%{site: site}) do diff --git a/test/plausible_web/controllers/api/stats_controller/conversions_test.exs b/test/plausible_web/controllers/api/stats_controller/conversions_test.exs index 8f41f08375..7dbe29b765 100644 --- a/test/plausible_web/controllers/api/stats_controller/conversions_test.exs +++ b/test/plausible_web/controllers/api/stats_controller/conversions_test.exs @@ -1,6 +1,6 @@ defmodule PlausibleWeb.Api.StatsController.ConversionsTest do use PlausibleWeb.ConnCase - import Plausible.TestUtils + @user_id 123 describe "GET /api/stats/:domain/conversions" do diff --git a/test/plausible_web/controllers/api/stats_controller/countries_test.exs b/test/plausible_web/controllers/api/stats_controller/countries_test.exs index bdf2df11f1..12f3f48596 100644 --- a/test/plausible_web/controllers/api/stats_controller/countries_test.exs +++ b/test/plausible_web/controllers/api/stats_controller/countries_test.exs @@ -1,6 +1,5 @@ defmodule PlausibleWeb.Api.StatsController.CountriesTest do use PlausibleWeb.ConnCase - import Plausible.TestUtils describe "GET /api/stats/:domain/countries" do setup [:create_user, :log_in, :create_new_site, :add_imported_data] diff --git a/test/plausible_web/controllers/api/stats_controller/current_visitors_test.exs b/test/plausible_web/controllers/api/stats_controller/current_visitors_test.exs index ccc62f6303..23b1b26528 100644 --- a/test/plausible_web/controllers/api/stats_controller/current_visitors_test.exs +++ b/test/plausible_web/controllers/api/stats_controller/current_visitors_test.exs @@ -1,6 +1,5 @@ defmodule PlausibleWeb.Api.StatsController.CurrentVisitorsTest do use PlausibleWeb.ConnCase - import Plausible.TestUtils describe "GET /api/stats/:domain/current-visitors" do setup [:create_user, :log_in, :create_site] diff --git a/test/plausible_web/controllers/api/stats_controller/main_graph_test.exs b/test/plausible_web/controllers/api/stats_controller/main_graph_test.exs index 02fecaa338..81def8a272 100644 --- a/test/plausible_web/controllers/api/stats_controller/main_graph_test.exs +++ b/test/plausible_web/controllers/api/stats_controller/main_graph_test.exs @@ -1,6 +1,6 @@ defmodule PlausibleWeb.Api.StatsController.MainGraphTest do use PlausibleWeb.ConnCase - import Plausible.TestUtils + @user_id 123 describe "GET /api/stats/main-graph - plot" do diff --git a/test/plausible_web/controllers/api/stats_controller/operating_systems_test.exs b/test/plausible_web/controllers/api/stats_controller/operating_systems_test.exs index ef8ee5467d..b760b6bc28 100644 --- a/test/plausible_web/controllers/api/stats_controller/operating_systems_test.exs +++ b/test/plausible_web/controllers/api/stats_controller/operating_systems_test.exs @@ -1,6 +1,5 @@ defmodule PlausibleWeb.Api.StatsController.OperatingSystemsTest do use PlausibleWeb.ConnCase - import Plausible.TestUtils describe "GET /api/stats/:domain/operating_systems" do setup [:create_user, :log_in, :create_new_site, :add_imported_data] diff --git a/test/plausible_web/controllers/api/stats_controller/pages_test.exs b/test/plausible_web/controllers/api/stats_controller/pages_test.exs index 8d4eef0d06..ee04e1c3c4 100644 --- a/test/plausible_web/controllers/api/stats_controller/pages_test.exs +++ b/test/plausible_web/controllers/api/stats_controller/pages_test.exs @@ -1,6 +1,6 @@ defmodule PlausibleWeb.Api.StatsController.PagesTest do use PlausibleWeb.ConnCase - import Plausible.TestUtils + @user_id 123 describe "GET /api/stats/:domain/pages" do diff --git a/test/plausible_web/controllers/api/stats_controller/regions_test.exs b/test/plausible_web/controllers/api/stats_controller/regions_test.exs index acde79d2c7..decf947ecf 100644 --- a/test/plausible_web/controllers/api/stats_controller/regions_test.exs +++ b/test/plausible_web/controllers/api/stats_controller/regions_test.exs @@ -1,6 +1,5 @@ defmodule PlausibleWeb.Api.StatsController.RegionsTest do use PlausibleWeb.ConnCase - import Plausible.TestUtils describe "GET /api/stats/:domain/regions" do defp seed(%{site: site}) do diff --git a/test/plausible_web/controllers/api/stats_controller/screen_sizes_test.exs b/test/plausible_web/controllers/api/stats_controller/screen_sizes_test.exs index 6582027d79..7b3ecab52c 100644 --- a/test/plausible_web/controllers/api/stats_controller/screen_sizes_test.exs +++ b/test/plausible_web/controllers/api/stats_controller/screen_sizes_test.exs @@ -1,6 +1,5 @@ defmodule PlausibleWeb.Api.StatsController.ScreenSizesTest do use PlausibleWeb.ConnCase - import Plausible.TestUtils describe "GET /api/stats/:domain/browsers" do setup [:create_user, :log_in, :create_new_site, :add_imported_data] diff --git a/test/plausible_web/controllers/api/stats_controller/sources_test.exs b/test/plausible_web/controllers/api/stats_controller/sources_test.exs index 0ece9bca90..836711b2c5 100644 --- a/test/plausible_web/controllers/api/stats_controller/sources_test.exs +++ b/test/plausible_web/controllers/api/stats_controller/sources_test.exs @@ -1,6 +1,6 @@ defmodule PlausibleWeb.Api.StatsController.SourcesTest do use PlausibleWeb.ConnCase - import Plausible.TestUtils + @user_id 123 describe "GET /api/stats/:domain/sources" do diff --git a/test/plausible_web/controllers/api/stats_controller/suggestions_test.exs b/test/plausible_web/controllers/api/stats_controller/suggestions_test.exs index acccbc89df..b5513f84ba 100644 --- a/test/plausible_web/controllers/api/stats_controller/suggestions_test.exs +++ b/test/plausible_web/controllers/api/stats_controller/suggestions_test.exs @@ -1,6 +1,5 @@ defmodule PlausibleWeb.Api.StatsController.SuggestionsTest do use PlausibleWeb.ConnCase - import Plausible.TestUtils describe "GET /api/stats/:domain/suggestions/:filter_name" do setup [:create_user, :log_in, :create_site] diff --git a/test/plausible_web/controllers/api/stats_controller/top_stats_test.exs b/test/plausible_web/controllers/api/stats_controller/top_stats_test.exs index e7ebb65b47..6a9a79a189 100644 --- a/test/plausible_web/controllers/api/stats_controller/top_stats_test.exs +++ b/test/plausible_web/controllers/api/stats_controller/top_stats_test.exs @@ -1,6 +1,6 @@ defmodule PlausibleWeb.Api.StatsController.TopStatsTest do use PlausibleWeb.ConnCase - import Plausible.TestUtils + @user_id 123 describe "GET /api/stats/top-stats - default" do diff --git a/test/plausible_web/controllers/auth_controller_test.exs b/test/plausible_web/controllers/auth_controller_test.exs index f3734fc89f..ad5cea0587 100644 --- a/test/plausible_web/controllers/auth_controller_test.exs +++ b/test/plausible_web/controllers/auth_controller_test.exs @@ -2,7 +2,6 @@ defmodule PlausibleWeb.AuthControllerTest do use PlausibleWeb.ConnCase use Bamboo.Test use Plausible.Repo - import Plausible.TestUtils import Mox setup :verify_on_exit! diff --git a/test/plausible_web/controllers/billing_controller_test.exs b/test/plausible_web/controllers/billing_controller_test.exs index 06b456a5f9..7daedae035 100644 --- a/test/plausible_web/controllers/billing_controller_test.exs +++ b/test/plausible_web/controllers/billing_controller_test.exs @@ -1,6 +1,5 @@ defmodule PlausibleWeb.BillingControllerTest do use PlausibleWeb.ConnCase - import Plausible.TestUtils describe "GET /upgrade" do setup [:create_user, :log_in] diff --git a/test/plausible_web/controllers/invitation_controller_test.exs b/test/plausible_web/controllers/invitation_controller_test.exs index 5e920fef4e..41640f377c 100644 --- a/test/plausible_web/controllers/invitation_controller_test.exs +++ b/test/plausible_web/controllers/invitation_controller_test.exs @@ -2,7 +2,6 @@ defmodule PlausibleWeb.Site.InvitationControllerTest do use PlausibleWeb.ConnCase use Plausible.Repo use Bamboo.Test - import Plausible.TestUtils setup [:create_user, :log_in] diff --git a/test/plausible_web/controllers/site/membership_controller_test.exs b/test/plausible_web/controllers/site/membership_controller_test.exs index 79680715b2..b97f5e789b 100644 --- a/test/plausible_web/controllers/site/membership_controller_test.exs +++ b/test/plausible_web/controllers/site/membership_controller_test.exs @@ -2,7 +2,6 @@ defmodule PlausibleWeb.Site.MembershipControllerTest do use PlausibleWeb.ConnCase use Plausible.Repo use Bamboo.Test - import Plausible.TestUtils setup [:create_user, :log_in] diff --git a/test/plausible_web/controllers/site_controller_test.exs b/test/plausible_web/controllers/site_controller_test.exs index 205b43fa46..f638d79c25 100644 --- a/test/plausible_web/controllers/site_controller_test.exs +++ b/test/plausible_web/controllers/site_controller_test.exs @@ -3,7 +3,10 @@ defmodule PlausibleWeb.SiteControllerTest do use Plausible.Repo use Bamboo.Test use Oban.Testing, repo: Plausible.Repo - import Plausible.TestUtils + + import ExUnit.CaptureLog + import Mox + setup :verify_on_exit! describe "GET /sites/new" do setup [:create_user, :log_in] @@ -384,6 +387,118 @@ defmodule PlausibleWeb.SiteControllerTest do end end + describe "GET /:webiste/settings/search-console for self-hosting" do + setup [:create_user, :log_in, :create_site] + + test "display search console settings", %{conn: conn, site: site} do + conn = get(conn, "/#{site.domain}/settings/search-console") + resp = html_response(conn, 200) + assert resp =~ "An extra step is needed" + assert resp =~ "Google Search Console integration" + assert resp =~ "self-hosting-configuration" + end + end + + describe "GET /:webiste/settings/search-console" do + setup [:create_user, :log_in, :create_site] + + setup_patch_env(:google, client_id: "some", api_url: "https://www.googleapis.com") + + setup %{site: site, user: user} = context do + insert(:google_auth, user: user, site: site, property: "sc-domain:#{site.domain}") + context + end + + test "displays appropriate error in case of google account `google_auth_error`", %{ + conn: conn, + site: site + } do + expect( + Plausible.HTTPClient.Mock, + :get, + fn + "https://www.googleapis.com/webmasters/v3/sites", + [{"Content-Type", "application/json"}, {"Authorization", "Bearer 123"}] -> + {:error, %{reason: %Finch.Response{status: Enum.random([401, 403])}}} + end + ) + + conn = get(conn, "/#{site.domain}/settings/search-console") + resp = html_response(conn, 200) + assert resp =~ "Your Search Console account hasn't been connected successfully" + assert resp =~ "Please unlink your Google account and try linking it again" + end + + test "displays docs link error in case of `invalid_grant`", %{ + conn: conn, + site: site + } do + expect( + Plausible.HTTPClient.Mock, + :get, + fn + "https://www.googleapis.com/webmasters/v3/sites", + [{"Content-Type", "application/json"}, {"Authorization", "Bearer 123"}] -> + {:error, %{reason: %Finch.Response{status: 400, body: %{"error" => "invalid_grant"}}}} + end + ) + + conn = get(conn, "/#{site.domain}/settings/search-console") + resp = html_response(conn, 200) + + assert resp =~ + "https://plausible.io/docs/google-search-console-integration#i-get-the-invalid-grant-error" + end + + test "displays generic error in case of random error code returned by google", %{ + conn: conn, + site: site + } do + expect( + Plausible.HTTPClient.Mock, + :get, + fn + "https://www.googleapis.com/webmasters/v3/sites", + [{"Content-Type", "application/json"}, {"Authorization", "Bearer 123"}] -> + {:error, %{reason: %Finch.Response{status: 503, body: %{"error" => "some_error"}}}} + end + ) + + conn = get(conn, "/#{site.domain}/settings/search-console") + resp = html_response(conn, 200) + + assert resp =~ "Something went wrong, but looks temporary" + assert resp =~ "try re-linking your Google account" + end + + test "displays generic error and logs a message, in case of random HTTP failure calling google", + %{ + conn: conn, + site: site + } do + expect( + Plausible.HTTPClient.Mock, + :get, + fn + "https://www.googleapis.com/webmasters/v3/sites", + [{"Content-Type", "application/json"}, {"Authorization", "Bearer 123"}] -> + {:error, :nxdomain} + end + ) + + log = + capture_log(fn -> + conn = get(conn, "/#{site.domain}/settings/search-console") + resp = html_response(conn, 200) + + assert resp =~ "Something went wrong, but looks temporary" + assert resp =~ "try re-linking your Google account" + end) + + assert log =~ "Google Analytics: failed to list sites: :nxdomain" + end + end + describe "GET /:website/goals/new" do setup [:create_user, :log_in, :create_site] diff --git a/test/plausible_web/controllers/stats_controller_test.exs b/test/plausible_web/controllers/stats_controller_test.exs index 138deefa6d..2d822f21e7 100644 --- a/test/plausible_web/controllers/stats_controller_test.exs +++ b/test/plausible_web/controllers/stats_controller_test.exs @@ -1,7 +1,6 @@ defmodule PlausibleWeb.StatsControllerTest do use PlausibleWeb.ConnCase, async: true use Plausible.Repo - import Plausible.TestUtils describe "GET /:website - anonymous user" do test "public site - shows site stats", %{conn: conn} do diff --git a/test/plausible_web/plugs/authorise_site_access_test.exs b/test/plausible_web/plugs/authorise_site_access_test.exs index 984c36ddf8..d8fb8bbd47 100644 --- a/test/plausible_web/plugs/authorise_site_access_test.exs +++ b/test/plausible_web/plugs/authorise_site_access_test.exs @@ -2,7 +2,6 @@ defmodule PlausibleWeb.AuthorizeSiteAccessTest do use PlausibleWeb.ConnCase, async: true alias PlausibleWeb.AuthorizeSiteAccess - import Plausible.TestUtils setup [:create_user, :log_in] test "doesn't allow :website bypass with :domain in body", %{conn: conn, user: me} do diff --git a/test/support/conn_case.ex b/test/support/conn_case.ex index 86d3fef0a5..1ca2449263 100644 --- a/test/support/conn_case.ex +++ b/test/support/conn_case.ex @@ -18,6 +18,7 @@ defmodule PlausibleWeb.ConnCase do using do quote do # Import conveniences for testing with connections + use Plausible.TestUtils import Plug.Conn import Phoenix.ConnTest alias PlausibleWeb.Router.Helpers, as: Routes diff --git a/test/support/data_case.ex b/test/support/data_case.ex index 411c83ec35..6add172007 100644 --- a/test/support/data_case.ex +++ b/test/support/data_case.ex @@ -17,6 +17,7 @@ defmodule Plausible.DataCase do using do quote do use Plausible.Repo + use Plausible.TestUtils import Ecto.Changeset import Plausible.DataCase diff --git a/test/support/test_utils.ex b/test/support/test_utils.ex index ebd49752a9..f9c6015e88 100644 --- a/test/support/test_utils.ex +++ b/test/support/test_utils.ex @@ -2,6 +2,34 @@ defmodule Plausible.TestUtils do use Plausible.Repo alias Plausible.Factory + defmacro __using__(_) do + quote do + require Plausible.TestUtils + import Plausible.TestUtils + end + end + + defmacro patch_env(env_key, value) do + quote do + original_env = Application.get_env(:plausible, unquote(env_key)) + Application.put_env(:plausible, unquote(env_key), unquote(value)) + + on_exit(fn -> + Application.put_env(:plausible, unquote(env_key), original_env) + end) + + {:ok, %{patched_env: true}} + end + end + + defmacro setup_patch_env(env_key, value) do + quote do + setup do + patch_env(unquote(env_key), unquote(value)) + end + end + end + def create_user(_) do {:ok, user: Factory.insert(:user)} end diff --git a/test/workers/check_usage_test.exs b/test/workers/check_usage_test.exs index 9b79ae6f39..a35e7e7bc0 100644 --- a/test/workers/check_usage_test.exs +++ b/test/workers/check_usage_test.exs @@ -2,7 +2,7 @@ defmodule Plausible.Workers.CheckUsageTest do use Plausible.DataCase, async: true use Bamboo.Test import Double - import Plausible.TestUtils + alias Plausible.Workers.CheckUsage setup [:create_user, :create_site] diff --git a/test/workers/import_google_analytics_test.exs b/test/workers/import_google_analytics_test.exs index 996408187a..dbea18b991 100644 --- a/test/workers/import_google_analytics_test.exs +++ b/test/workers/import_google_analytics_test.exs @@ -2,7 +2,7 @@ defmodule Plausible.Workers.ImportGoogleAnalyticsTest do use Plausible.DataCase use Bamboo.Test import Double - import Plausible.TestUtils + alias Plausible.Workers.ImportGoogleAnalytics @imported_data %Plausible.Site.ImportedData{ diff --git a/test/workers/notify_annual_renewal_test.exs b/test/workers/notify_annual_renewal_test.exs index 9b14ef2294..3d43df8171 100644 --- a/test/workers/notify_annual_renewal_test.exs +++ b/test/workers/notify_annual_renewal_test.exs @@ -1,7 +1,7 @@ defmodule Plausible.Workers.NotifyAnnualRenewalTest do use Plausible.DataCase, async: true use Bamboo.Test - import Plausible.TestUtils + alias Plausible.Workers.NotifyAnnualRenewal setup [:create_user, :create_site] diff --git a/test/workers/send_email_report_test.exs b/test/workers/send_email_report_test.exs index fb8b626784..9b70110c92 100644 --- a/test/workers/send_email_report_test.exs +++ b/test/workers/send_email_report_test.exs @@ -1,5 +1,4 @@ defmodule Plausible.Workers.SendEmailReportTest do - import Plausible.TestUtils use Plausible.DataCase use Bamboo.Test use Oban.Testing, repo: Plausible.Repo diff --git a/test/workers/send_site_setup_emails_test.exs b/test/workers/send_site_setup_emails_test.exs index 9d479a3b99..15f203f9d1 100644 --- a/test/workers/send_site_setup_emails_test.exs +++ b/test/workers/send_site_setup_emails_test.exs @@ -2,7 +2,7 @@ defmodule Plausible.Workers.SendSiteSetupEmailsTest do use Plausible.DataCase, async: true use Bamboo.Test use Oban.Testing, repo: Plausible.Repo - import Plausible.TestUtils + alias Plausible.Workers.SendSiteSetupEmails describe "when user has not managed to set up the site" do diff --git a/test/workers/send_trial_notifications_test.exs b/test/workers/send_trial_notifications_test.exs index b647ca2c9f..c056f919f7 100644 --- a/test/workers/send_trial_notifications_test.exs +++ b/test/workers/send_trial_notifications_test.exs @@ -2,7 +2,7 @@ defmodule Plausible.Workers.SendTrialNotificationsTest do use Plausible.DataCase use Bamboo.Test use Oban.Testing, repo: Plausible.Repo - import Plausible.TestUtils + alias Plausible.Workers.SendTrialNotifications test "does not send a notification if user didn't create a site" do