diff --git a/android/play-store/review-access.md b/android/play-store/review-access.md index c0cbc68..e311ca0 100644 --- a/android/play-store/review-access.md +++ b/android/play-store/review-access.md @@ -46,8 +46,18 @@ access field: second factor, one-time code, developer browser session, or location gate. After both dedicated accounts have registered, confirmed their email, and set -their fixed passwords through the production UI, an operator can create the -stable synthetic records from the production checkout: +their fixed passwords through the production UI, first run the read-only +readiness check from the production checkout: + +```bash +./scripts/prepare-play-review.sh \ + REVIEWER_EMAIL COUNTERPART_EMAIL \ + --check-only whoneedhelp.com \ + /srv/who_need_help-production/.env +``` + +Only after that check succeeds should an operator create the stable synthetic +records: ```bash ./scripts/prepare-play-review.sh \ diff --git a/lib/who_need_help/release.ex b/lib/who_need_help/release.ex index 141b8d2..2eda430 100644 --- a/lib/who_need_help/release.ex +++ b/lib/who_need_help/release.ex @@ -65,6 +65,32 @@ defmodule WhoNeedHelp.Release do @play_review_request_title "Play review — medicine pickup" @play_review_activity_title "Play review — public cinema meetup" + @doc """ + Checks whether two existing ordinary accounts can be used for Google Play + review without creating or changing any records. + """ + def play_review_readiness(reviewer_email, counterpart_email) + when is_binary(reviewer_email) and is_binary(counterpart_email) do + load_app() + + with {:ok, reviewer} <- play_review_user(reviewer_email, :reviewer), + {:ok, counterpart} <- play_review_user(counterpart_email, :counterpart), + :ok <- different_play_review_users(reviewer, counterpart), + {:ok, medicine_category} <- active_category("medicine-pickup", :help), + {:ok, cinema_category} <- active_category("cinema-meetup", :activity) do + {:ok, + %{ + reviewer: reviewer, + counterpart: counterpart, + medicine_category: medicine_category, + cinema_category: cinema_category + }} + end + end + + def play_review_readiness(_reviewer_email, _counterpart_email), + do: {:error, :invalid_email} + @doc """ Prepares stable, synthetic records for Google Play review using two existing ordinary accounts. @@ -75,13 +101,13 @@ defmodule WhoNeedHelp.Release do """ def prepare_play_review(reviewer_email, counterpart_email) when is_binary(reviewer_email) and is_binary(counterpart_email) do - load_app() - - with {:ok, reviewer} <- play_review_user(reviewer_email, :reviewer), - {:ok, counterpart} <- play_review_user(counterpart_email, :counterpart), - :ok <- different_play_review_users(reviewer, counterpart), - {:ok, medicine_category} <- active_category("medicine-pickup", :help), - {:ok, cinema_category} <- active_category("cinema-meetup", :activity), + with {:ok, + %{ + reviewer: reviewer, + counterpart: counterpart, + medicine_category: medicine_category, + cinema_category: cinema_category + }} <- play_review_readiness(reviewer_email, counterpart_email), {:ok, request} <- ensure_play_review_request(reviewer, counterpart, medicine_category), {:ok, activity} <- diff --git a/scripts/prepare-play-review.sh b/scripts/prepare-play-review.sh index b41924a..7f72dbe 100755 --- a/scripts/prepare-play-review.sh +++ b/scripts/prepare-play-review.sh @@ -6,14 +6,18 @@ EXPECTED_ROOT=/srv/who_need_help-production EXPECTED_PROJECT=who_need_help_production EXPECTED_ORIGIN=https://whoneedhelp.com -if [ "$#" -ne 5 ] || [ "$3" != "--confirm" ] || [ "$4" != "whoneedhelp.com" ]; then - echo "Usage: $0 REVIEWER_EMAIL COUNTERPART_EMAIL --confirm whoneedhelp.com ENV_FILE" >&2 +if [ "$#" -ne 5 ] || + { [ "$3" != "--check-only" ] && [ "$3" != "--confirm" ]; } || + [ "$4" != "whoneedhelp.com" ]; then + echo "Usage: $0 REVIEWER_EMAIL COUNTERPART_EMAIL --check-only whoneedhelp.com ENV_FILE" >&2 + echo " $0 REVIEWER_EMAIL COUNTERPART_EMAIL --confirm whoneedhelp.com ENV_FILE" >&2 echo "Both existing accounts must be active, confirmed, password-enabled, and non-staff." >&2 exit 1 fi REVIEWER_EMAIL=$1 COUNTERPART_EMAIL=$2 +ACTION=$3 ENV_FILE=$5 if [ "$(realpath --canonicalize-existing "$ROOT")" != "$EXPECTED_ROOT" ]; then @@ -95,6 +99,11 @@ encode() { reviewer=$(encode "$REVIEWER_EMAIL") counterpart=$(encode "$COUNTERPART_EMAIL") -expression="case WhoNeedHelp.Release.prepare_play_review(Base.decode64!(\"$reviewer\"), Base.decode64!(\"$counterpart\")) do {:ok, result} -> IO.inspect(result); {:error, reason} -> raise \"Play review preparation failed: #{inspect(reason)}\" end" + +if [ "$ACTION" = "--check-only" ]; then + expression="case WhoNeedHelp.Release.play_review_readiness(Base.decode64!(\"$reviewer\"), Base.decode64!(\"$counterpart\")) do {:ok, result} -> IO.inspect(%{reviewer_id: result.reviewer.id, counterpart_id: result.counterpart.id, medicine_category_id: result.medicine_category.id, cinema_category_id: result.cinema_category.id}, label: :play_review_ready); {:error, reason} -> raise \"Play review readiness failed: #{inspect(reason)}\" end" +else + expression="case WhoNeedHelp.Release.prepare_play_review(Base.decode64!(\"$reviewer\"), Base.decode64!(\"$counterpart\")) do {:ok, result} -> IO.inspect(result); {:error, reason} -> raise \"Play review preparation failed: #{inspect(reason)}\" end" +fi docker exec "$container" /app/bin/who_need_help rpc "$expression" diff --git a/test/who_need_help/release_test.exs b/test/who_need_help/release_test.exs index d27830d..2ac776c 100644 --- a/test/who_need_help/release_test.exs +++ b/test/who_need_help/release_test.exs @@ -20,6 +20,22 @@ defmodule WhoNeedHelp.ReleaseTest do %{reviewer: reviewer, counterpart: counterpart} end + test "checks reviewer readiness without creating review records", %{ + reviewer: reviewer, + counterpart: counterpart + } do + before_counts = review_record_counts() + + assert {:ok, readiness} = + Release.play_review_readiness(reviewer.email, counterpart.email) + + assert readiness.reviewer.id == reviewer.id + assert readiness.counterpart.id == counterpart.id + assert readiness.medicine_category.slug == "medicine-pickup" + assert readiness.cinema_category.slug == "cinema-meetup" + assert review_record_counts() == before_counts + end + test "prepares stable non-staff reviewer records idempotently", %{ reviewer: reviewer, counterpart: counterpart @@ -90,4 +106,13 @@ defmodule WhoNeedHelp.ReleaseTest do assert {:error, {:reviewer, :staff_account_forbidden}} = Release.prepare_play_review(staff.email, counterpart.email) end + + defp review_record_counts do + %{ + requests: Repo.aggregate(HelpRequest, :count), + assignments: Repo.aggregate(Assignment, :count), + activities: Repo.aggregate(Activity, :count), + participants: Repo.aggregate(Participant, :count) + } + end end