feat(seeds): add an opt-in bulk member seed for load testing
This commit is contained in:
parent
c482a2630b
commit
95c25142f0
2 changed files with 134 additions and 0 deletions
5
Justfile
5
Justfile
|
|
@ -30,6 +30,11 @@ reset-database:
|
|||
seed-database:
|
||||
mix run priv/repo/seeds.exs
|
||||
|
||||
# Seed a large batch of members for load / infinite-scroll testing (opt-in; NOT
|
||||
# part of `seed-database`). Set BULK_MEMBER_COUNT to change the count (default 300).
|
||||
seed-members-bulk:
|
||||
mix run priv/repo/seeds_bulk_members.exs
|
||||
|
||||
start-database:
|
||||
docker compose up -d
|
||||
|
||||
|
|
|
|||
129
priv/repo/seeds_bulk_members.exs
Normal file
129
priv/repo/seeds_bulk_members.exs
Normal file
|
|
@ -0,0 +1,129 @@
|
|||
# Optional BULK member seed for load / infinite-scroll testing.
|
||||
#
|
||||
# This is NOT part of the default dev seed (`mix run priv/repo/seeds.exs`), so the
|
||||
# normal dev database stays small. Run it explicitly when you need many members:
|
||||
#
|
||||
# just seed-members-bulk
|
||||
# BULK_MEMBER_COUNT=500 mix run priv/repo/seeds_bulk_members.exs
|
||||
#
|
||||
# Requires the bootstrap seeds (admin user + fee types) to have run first. Members
|
||||
# are upserted by email ("bulk-member-<n>@example.de"), so re-running is idempotent
|
||||
# and does not duplicate members or their fee cycles.
|
||||
|
||||
alias Mv.Accounts
|
||||
alias Mv.Membership
|
||||
alias Mv.MembershipFees.CycleGenerator
|
||||
alias Mv.MembershipFees.MembershipFeeType
|
||||
|
||||
require Ash.Query
|
||||
|
||||
admin_email = System.get_env("ADMIN_EMAIL") || "admin@localhost"
|
||||
|
||||
admin_user_with_role =
|
||||
case Accounts.User
|
||||
|> Ash.Query.filter(email == ^admin_email)
|
||||
|> Ash.read_one(domain: Mv.Accounts, authorize?: false) do
|
||||
{:ok, user} when not is_nil(user) ->
|
||||
user |> Ash.load!(:role, authorize?: false)
|
||||
|
||||
_ ->
|
||||
raise "Bulk seed requires bootstrap: admin user not found (#{admin_email})"
|
||||
end
|
||||
|
||||
all_fee_types =
|
||||
MembershipFeeType
|
||||
|> Ash.Query.sort(name: :asc)
|
||||
|> Ash.read!(actor: admin_user_with_role, domain: Mv.MembershipFees)
|
||||
|> Enum.to_list()
|
||||
|
||||
count =
|
||||
case Integer.parse(System.get_env("BULK_MEMBER_COUNT") || "300") do
|
||||
{n, _} when n > 0 -> n
|
||||
_ -> 300
|
||||
end
|
||||
|
||||
first_names =
|
||||
~w(Anna Bruno Clara David Elena Felix Greta Henrik Ines Jakob Laura Max Nina
|
||||
Oliver Paula Quirin Rosa Stefan Thea Uwe Vera Wolf Xenia Yannick Zoe Ada
|
||||
Ben Carl Dora Emil)
|
||||
|
||||
last_names =
|
||||
~w(Schmidt Müller Fischer Weber Wagner Becker Schulz Hoffmann Koch Richter Klein
|
||||
Wolf Neumann Schwarz Zimmermann Braun Krüger Hartmann Lange Werner Krause
|
||||
Lehmann Köhler Herrmann Walter)
|
||||
|
||||
cities = [
|
||||
{"München", "80331"},
|
||||
{"Hamburg", "20095"},
|
||||
{"Berlin", "10435"},
|
||||
{"Köln", "50667"},
|
||||
{"Frankfurt", "60313"},
|
||||
{"Stuttgart", "70173"},
|
||||
{"Düsseldorf", "40212"},
|
||||
{"Dortmund", "44137"},
|
||||
{"Essen", "45127"},
|
||||
{"Dresden", "01069"},
|
||||
{"Nürnberg", "90402"},
|
||||
{"Bremen", "28195"}
|
||||
]
|
||||
|
||||
statuses = [:paid, :unpaid, :suspended]
|
||||
|
||||
IO.puts("Seeding #{count} bulk members (set BULK_MEMBER_COUNT to change)…")
|
||||
|
||||
Enum.each(1..count, fn i ->
|
||||
{city, postal_code} = Enum.at(cities, rem(i, length(cities)))
|
||||
|
||||
attrs = %{
|
||||
first_name: Enum.at(first_names, rem(i, length(first_names))),
|
||||
last_name: Enum.at(last_names, rem(div(i, length(first_names)) + i, length(last_names))),
|
||||
email: "bulk-member-#{i}@example.de",
|
||||
join_date: Date.add(~D[2020-01-01], rem(i * 7, 2000)),
|
||||
city: city,
|
||||
street: "Teststraße",
|
||||
house_number: Integer.to_string(rem(i, 200) + 1),
|
||||
postal_code: postal_code,
|
||||
country: "Deutschland"
|
||||
}
|
||||
|
||||
attrs =
|
||||
case all_fee_types do
|
||||
[] -> attrs
|
||||
types -> Map.put(attrs, :membership_fee_type_id, Enum.at(types, rem(i, length(types))).id)
|
||||
end
|
||||
|
||||
member =
|
||||
Membership.create_member!(attrs,
|
||||
upsert?: true,
|
||||
upsert_identity: :unique_email,
|
||||
actor: admin_user_with_role
|
||||
)
|
||||
|
||||
# Give the member fee cycles + a round-robin status so the fee-status column is
|
||||
# populated at scale. Only generate when none exist yet (idempotent re-runs).
|
||||
if not is_nil(member.membership_fee_type_id) do
|
||||
member = Ash.load!(member, :membership_fee_cycles, actor: admin_user_with_role)
|
||||
|
||||
if Enum.empty?(member.membership_fee_cycles) do
|
||||
{:ok, cycles, _} =
|
||||
CycleGenerator.generate_cycles_for_member(member.id,
|
||||
skip_lock?: true,
|
||||
actor: admin_user_with_role
|
||||
)
|
||||
|
||||
status = Enum.at(statuses, rem(i, length(statuses)))
|
||||
|
||||
Enum.each(cycles, fn cycle ->
|
||||
if cycle.status != status do
|
||||
cycle
|
||||
|> Ash.Changeset.for_update(:update, %{status: status})
|
||||
|> Ash.update!(actor: admin_user_with_role, domain: Mv.MembershipFees)
|
||||
end
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
if rem(i, 50) == 0, do: IO.puts(" … #{i}/#{count}")
|
||||
end)
|
||||
|
||||
IO.puts("✅ Bulk seed complete: #{count} members.")
|
||||
Loading…
Add table
Add a link
Reference in a new issue