defmodule Mv.SeedsTest do use Mv.DataCase, async: false require Ash.Query setup do system_actor = Mv.Helpers.SystemActor.get_system_actor() %{actor: system_actor} end describe "Seeds script" do test "runs successfully without errors", %{actor: actor} do # Run the seeds script - should not raise any errors assert Code.eval_file("priv/repo/seeds.exs") # Basic smoke test: ensure some data was created {:ok, users} = Ash.read(Mv.Accounts.User, actor: actor) {:ok, members} = Ash.read(Mv.Membership.Member, actor: actor) {:ok, custom_fields} = Ash.read(Mv.Membership.CustomField, actor: actor) assert not Enum.empty?(users), "Seeds should create at least one user" assert not Enum.empty?(members), "Seeds should create at least one member" assert not Enum.empty?(custom_fields), "Seeds should create at least one custom field" end test "can be run multiple times (idempotent)", %{actor: actor} do # Run seeds first time assert Code.eval_file("priv/repo/seeds.exs") # Count records {:ok, users_count_1} = Ash.read(Mv.Accounts.User, actor: actor) {:ok, members_count_1} = Ash.read(Mv.Membership.Member, actor: actor) {:ok, custom_fields_count_1} = Ash.read(Mv.Membership.CustomField, actor: actor) # Run seeds second time - should not raise errors assert Code.eval_file("priv/repo/seeds.exs") # Count records again - should be the same (upsert, not duplicate) {:ok, users_count_2} = Ash.read(Mv.Accounts.User, actor: actor) {:ok, members_count_2} = Ash.read(Mv.Membership.Member, actor: actor) {:ok, custom_fields_count_2} = Ash.read(Mv.Membership.CustomField, actor: actor) assert length(users_count_1) == length(users_count_2), "Users count should remain same after re-running seeds" assert length(members_count_1) == length(members_count_2), "Members count should remain same after re-running seeds" assert length(custom_fields_count_1) == length(custom_fields_count_2), "CustomFields count should remain same after re-running seeds" end test "at least one member has no membership fee type assigned", %{actor: actor} do # Run the seeds script assert Code.eval_file("priv/repo/seeds.exs") # Get all members {:ok, members} = Ash.read(Mv.Membership.Member, actor: actor) # At least one member should have no membership_fee_type_id members_without_fee_type = Enum.filter(members, fn member -> member.membership_fee_type_id == nil end) assert not Enum.empty?(members_without_fee_type), "At least one member should have no membership fee type assigned" end test "each membership fee type has at least one member", %{actor: actor} do # Run the seeds script assert Code.eval_file("priv/repo/seeds.exs") # Get all fee types and members {:ok, fee_types} = Ash.read(Mv.MembershipFees.MembershipFeeType, actor: actor) {:ok, members} = Ash.read(Mv.Membership.Member, actor: actor) # Group members by fee type (excluding nil) members_by_fee_type = members |> Enum.filter(&(&1.membership_fee_type_id != nil)) |> Enum.group_by(& &1.membership_fee_type_id) # Each fee type should have at least one member Enum.each(fee_types, fn fee_type -> members_for_type = Map.get(members_by_fee_type, fee_type.id, []) assert not Enum.empty?(members_for_type), "Membership fee type #{fee_type.name} should have at least one member assigned" end) end test "members with fee types have cycles with various statuses", %{actor: actor} do # Run the seeds script assert Code.eval_file("priv/repo/seeds.exs") # Get all members with fee types {:ok, members} = Ash.read(Mv.Membership.Member, actor: actor) members_with_fee_types = members |> Enum.filter(&(&1.membership_fee_type_id != nil)) # At least one member should have cycles assert not Enum.empty?(members_with_fee_types), "At least one member should have a membership fee type" # Check that cycles exist and have various statuses all_cycle_statuses = members_with_fee_types |> Enum.flat_map(fn member -> Mv.MembershipFees.MembershipFeeCycle |> Ash.Query.filter(member_id == ^member.id) |> Ash.read!(actor: actor) end) |> Enum.map(& &1.status) # At least one cycle should be paid assert :paid in all_cycle_statuses, "At least one cycle should be paid" # At least one cycle should be unpaid assert :unpaid in all_cycle_statuses, "At least one cycle should be unpaid" # At least one cycle should be suspended assert :suspended in all_cycle_statuses, "At least one cycle should be suspended" end end describe "Authorization roles (from seeds)" do test "creates all 5 authorization roles with correct permission sets" do # Run seeds once for this test Code.eval_file("priv/repo/seeds.exs") {:ok, roles} = Ash.read(Mv.Authorization.Role) assert length(roles) >= 5, "Should have at least 5 roles" # Check each role role_configs = [ {"Mitglied", "own_data", true}, {"Vorstand", "read_only", false}, {"Kassenwart", "normal_user", false}, {"Buchhaltung", "read_only", false}, {"Admin", "admin", false} ] Enum.each(role_configs, fn {name, perm_set, is_system} -> role = Enum.find(roles, &(&1.name == name)) assert role, "Role #{name} should exist" assert role.permission_set_name == perm_set assert role.is_system_role == is_system end) end test "Mitglied role is marked as system role" do Code.eval_file("priv/repo/seeds.exs") {:ok, mitglied} = Mv.Authorization.Role |> Ash.Query.filter(name == "Mitglied") |> Ash.read_one() assert mitglied.is_system_role == true end test "all roles have valid permission_set_names" do Code.eval_file("priv/repo/seeds.exs") {:ok, roles} = Ash.read(Mv.Authorization.Role) valid_sets = Mv.Authorization.PermissionSets.all_permission_sets() |> Enum.map(&Atom.to_string/1) Enum.each(roles, fn role -> assert role.permission_set_name in valid_sets, "Role #{role.name} has invalid permission_set_name: #{role.permission_set_name}" end) end test "assigns Admin role to ADMIN_EMAIL user" do Code.eval_file("priv/repo/seeds.exs") admin_email = System.get_env("ADMIN_EMAIL") || "admin@localhost" {:ok, admin_user} = Mv.Accounts.User |> Ash.Query.filter(email == ^admin_email) |> Ash.read_one(domain: Mv.Accounts, authorize?: false) assert admin_user != nil, "Admin user should exist after seeds run" {:ok, admin_user_with_role} = Ash.load(admin_user, :role, domain: Mv.Accounts, authorize?: false) assert admin_user_with_role.role != nil, "Admin user should have a role assigned" assert admin_user_with_role.role.name == "Admin" assert admin_user_with_role.role.permission_set_name == "admin" end end describe "Authorization role assignment" do test "does not change role of users who already have a role" do # Seeds once (creates Admin with Admin role) Code.eval_file("priv/repo/seeds.exs") admin_email = System.get_env("ADMIN_EMAIL") || "admin@localhost" {:ok, admin_user} = Mv.Accounts.User |> Ash.Query.filter(email == ^admin_email) |> Ash.read_one(domain: Mv.Accounts, authorize?: false) assert admin_user != nil, "Admin user should exist after seeds run" {:ok, admin_user_with_role} = Ash.load(admin_user, :role, domain: Mv.Accounts, authorize?: false) assert admin_user_with_role.role != nil, "Admin user should have a role assigned" original_role_id = admin_user_with_role.role_id assert admin_user_with_role.role.name == "Admin" # Seeds again Code.eval_file("priv/repo/seeds.exs") # Admin reloaded {:ok, admin_reloaded} = Mv.Accounts.User |> Ash.Query.filter(email == ^admin_email) |> Ash.read_one(domain: Mv.Accounts, authorize?: false) assert admin_reloaded != nil, "Admin user should still exist after re-running seeds" {:ok, admin_reloaded_with_role} = Ash.load(admin_reloaded, :role, domain: Mv.Accounts, authorize?: false) assert admin_reloaded_with_role.role != nil, "Admin user should still have a role after re-running seeds" assert admin_reloaded_with_role.role_id == original_role_id assert admin_reloaded_with_role.role.name == "Admin" end test "role creation is idempotent" do Code.eval_file("priv/repo/seeds.exs") {:ok, roles_1} = Ash.read(Mv.Authorization.Role) Code.eval_file("priv/repo/seeds.exs") {:ok, roles_2} = Ash.read(Mv.Authorization.Role) assert length(roles_1) == length(roles_2), "Role count should remain same after re-running seeds" # Each role should appear exactly once role_names = Enum.map(roles_2, & &1.name) assert length(role_names) == length(Enum.uniq(role_names)), "Each role name should appear exactly once" end end end