Merge branch 'main' into issue/mitgliederverwaltung-533
Some checks reported errors
continuous-integration/drone/push Build was killed
continuous-integration/drone/promote/production Build is passing

# Conflicts:
#	test/mv_web/member_live/index_test.exs
This commit is contained in:
Moritz 2026-06-16 18:13:03 +02:00
commit 84e1cf1cb8
118 changed files with 767 additions and 1148 deletions

View file

@ -5,10 +5,11 @@ defmodule Mv.Membership.GroupDatabaseConstraintsTest do
"""
use Mv.DataCase, async: false
import Ash.Expr
alias Mv.Membership
require Ash.Query
import Ash.Expr
setup do
system_actor = Mv.Helpers.SystemActor.get_system_actor()

View file

@ -5,10 +5,11 @@ defmodule Mv.Membership.GroupTest do
"""
use Mv.DataCase, async: true
import Ash.Expr
alias Mv.Membership
require Ash.Query
import Ash.Expr
setup do
system_actor = Mv.Helpers.SystemActor.get_system_actor()

View file

@ -8,13 +8,14 @@ defmodule Mv.Membership.JoinRequestApprovalDomainTest do
use Mv.DataCase, async: true
import Ash.Expr
require Ash.Query
alias Mv.Fixtures
alias Mv.Helpers.SystemActor
alias Mv.Membership
alias Mv.Membership.Member
require Ash.Query
defp member_count do
actor = SystemActor.get_system_actor()
{:ok, members} = Membership.list_members(actor: actor)

View file

@ -12,13 +12,14 @@ defmodule Mv.Membership.JoinRequestTest do
"""
use Mv.DataCase, async: true
require Ash.Query
import Ash.Expr
alias Mv.Fixtures
alias Mv.Membership
alias Mv.Membership.JoinRequest
require Ash.Query
# Valid minimal attributes for submit (email required; confirmation_token optional for tests)
@valid_submit_attrs %{
email: "join#{System.unique_integer([:positive])}@example.com"

View file

@ -5,10 +5,11 @@ defmodule Mv.Membership.MemberGroupTest do
"""
use Mv.DataCase, async: true
import Ash.Expr
alias Mv.Membership
require Ash.Query
import Ash.Expr
setup do
system_actor = Mv.Helpers.SystemActor.get_system_actor()

View file

@ -4,10 +4,11 @@ defmodule Mv.Membership.MemberGroupsRelationshipTest do
"""
use Mv.DataCase, async: true
import Ash.Expr
alias Mv.Membership
require Ash.Query
import Ash.Expr
setup do
system_actor = Mv.Helpers.SystemActor.get_system_actor()

View file

@ -0,0 +1,58 @@
defmodule Mv.ApplicationTest do
@moduledoc """
Guards the AshAuthentication supervisor wiring in `Mv.Application`.
The auth children (token Expunger, audit-log batcher/expunger) resolve their
configuration via `Spark.sparks(otp_app, Ash.Resource)`. With the wrong
`otp_app` they silently start against an empty resource set: the token
Expunger then runs against no token resources and becomes a no-op. This test
pins the corrected `:mv` wiring against the *running* application tree: the
supervisor and all three children are present, and the token Expunger booted
live and resolved the real `:mv` token resource.
The two audit-log children legitimately report an `:undefined` pid because no
`AuditLogResource` resources exist under `:mv` (their init returns `:ignore`);
that is a successful start, not a crash, so they are only asserted present.
"""
use ExUnit.Case, async: false
alias AshAuthentication.TokenResource.Expunger
test "AshAuthentication children boot under the :mv otp_app and resolve real config" do
# Locate the running AshAuthentication.Supervisor inside the live app tree.
auth_sup =
Mv.Supervisor
|> Supervisor.which_children()
|> Enum.find_value(fn
{AshAuthentication.Supervisor, pid, _type, _mods} when is_pid(pid) -> pid
_ -> nil
end)
assert is_pid(auth_sup), "AshAuthentication.Supervisor is not running in Mv.Supervisor"
children = Supervisor.which_children(auth_sup)
child_ids = children |> Enum.map(&elem(&1, 0)) |> Enum.sort()
# All three auth children are present in the supervision tree.
assert child_ids ==
Enum.sort([
AshAuthentication.TokenResource.Expunger,
AshAuthentication.AuditLogResource.Batcher,
AshAuthentication.AuditLogResource.Expunger
])
# The token Expunger booted as a live process and resolved the real :mv
# token resource — proving the children run against non-empty :mv config
# rather than the empty set the wrong otp_app produced.
expunger_pid =
Enum.find_value(children, fn
{Expunger, pid, _, _} when is_pid(pid) -> pid
_ -> nil
end)
assert is_pid(expunger_pid), "token Expunger did not boot as a live process under :mv"
assert Process.alive?(expunger_pid)
assert %{otp_app: :mv, resources: resources} = :sys.get_state(expunger_pid)
assert Map.has_key?(resources, Mv.Accounts.Token)
end
end

View file

@ -12,10 +12,10 @@ defmodule Mv.Authorization.Checks.HasPermissionFailClosedTest do
"""
use Mv.DataCase, async: true
alias Mv.Authorization.Checks.HasPermission
import Mv.Fixtures
alias Mv.Authorization.Checks.HasPermission
test "auto_filter deny-filter matches no records (regression for NOT IN [] allow-all bug)" do
# Arrange: create some members in DB
_m1 = member_fixture()

View file

@ -1,59 +0,0 @@
defmodule Mv.OidcRoleSyncConfigTest do
@moduledoc """
Tests for OIDC role sync configuration (OIDC_ADMIN_GROUP_NAME, OIDC_GROUPS_CLAIM).
Reads via Mv.Config (ENV first, then Settings).
"""
use Mv.DataCase, async: false
alias Mv.OidcRoleSyncConfig
describe "oidc_admin_group_name/0" do
test "returns nil when OIDC_ADMIN_GROUP_NAME is not configured" do
restore = clear_env("OIDC_ADMIN_GROUP_NAME")
on_exit(restore)
assert OidcRoleSyncConfig.oidc_admin_group_name() == nil
end
test "returns configured admin group name when set via ENV" do
restore = set_env("OIDC_ADMIN_GROUP_NAME", "mila-admin")
on_exit(restore)
assert OidcRoleSyncConfig.oidc_admin_group_name() == "mila-admin"
end
end
describe "oidc_groups_claim/0" do
test "returns default \"groups\" when OIDC_GROUPS_CLAIM is not configured" do
restore = clear_env("OIDC_GROUPS_CLAIM")
on_exit(restore)
assert OidcRoleSyncConfig.oidc_groups_claim() == "groups"
end
test "returns configured claim name when OIDC_GROUPS_CLAIM is set via ENV" do
restore = set_env("OIDC_GROUPS_CLAIM", "ak_groups")
on_exit(restore)
assert OidcRoleSyncConfig.oidc_groups_claim() == "ak_groups"
end
end
defp set_env(key, value) do
previous = System.get_env(key)
System.put_env(key, value)
fn ->
if previous, do: System.put_env(key, previous), else: System.delete_env(key)
end
end
defp clear_env(key) do
previous = System.get_env(key)
System.delete_env(key)
fn ->
if previous, do: System.put_env(key, previous)
end
end
end

View file

@ -4,7 +4,6 @@ defmodule Mv.StatisticsTest do
"""
use Mv.DataCase, async: true
require Ash.Query
import Ash.Expr
import Mv.Fixtures, only: [create_fee_type: 2]
@ -13,6 +12,8 @@ defmodule Mv.StatisticsTest do
alias Mv.MembershipFees.MembershipFeeCycle
alias Mv.Statistics
require Ash.Query
setup do
actor = Mv.Helpers.SystemActor.get_system_actor()
%{actor: actor}

View file

@ -14,9 +14,10 @@ defmodule MvWeb.Components.MemberFilterComponentTest do
# is not trivially async-safe; resolving that is a separate follow-up.
use MvWeb.ConnCase, async: false
import Phoenix.LiveViewTest
use Gettext, backend: MvWeb.Gettext
import Phoenix.LiveViewTest
alias Mv.Membership.CustomField
# Helper to create a boolean custom field (uses system_actor - only admin can create)

View file

@ -4,11 +4,11 @@ defmodule MvWeb.Helpers.MembershipFeeHelpersTest do
"""
use Mv.DataCase, async: true
require Ash.Query
alias Mv.MembershipFees.CalendarCycles
alias MvWeb.Helpers.MembershipFeeHelpers
require Ash.Query
setup do
system_actor = Mv.Helpers.SystemActor.get_system_actor()
%{actor: system_actor}

View file

@ -15,10 +15,11 @@ defmodule MvWeb.CustomFieldLive.DeletionTest do
use MvWeb.ConnCase, async: true
import Phoenix.LiveViewTest
require Ash.Query
alias Mv.Membership.{CustomField, CustomFieldValue, Member}
require Ash.Query
setup do
system_actor = Mv.Helpers.SystemActor.get_system_actor()
admin_role = Mv.Fixtures.role_fixture("admin")

View file

@ -8,10 +8,11 @@ defmodule MvWeb.CustomFieldLive.FormTest do
use MvWeb.ConnCase, async: true
import Phoenix.LiveViewTest
require Ash.Query
alias Mv.Membership.CustomField
require Ash.Query
setup do
system_actor = Mv.Helpers.SystemActor.get_system_actor()
admin_role = Mv.Fixtures.role_fixture("admin")

View file

@ -10,9 +10,10 @@ defmodule MvWeb.GroupLive.FormTest do
- Security
"""
use MvWeb.ConnCase, async: false
import Phoenix.LiveViewTest
use Gettext, backend: MvWeb.Gettext
import Phoenix.LiveViewTest
alias Mv.Fixtures
describe "create form" do

View file

@ -10,9 +10,10 @@ defmodule MvWeb.GroupLive.IndexTest do
- Edge cases
"""
use MvWeb.ConnCase, async: false
import Phoenix.LiveViewTest
use Gettext, backend: MvWeb.Gettext
import Phoenix.LiveViewTest
alias Mv.Fixtures
alias Mv.Membership

View file

@ -9,14 +9,16 @@ defmodule MvWeb.GroupLive.IntegrationTest do
- URL persistence
"""
use MvWeb.ConnCase, async: false
import Phoenix.LiveViewTest
require Ash.Query
import Ash.Expr
use Gettext, backend: MvWeb.Gettext
import Ash.Expr
import Phoenix.LiveViewTest
alias Mv.Fixtures
alias Mv.Membership
require Ash.Query
describe "complete workflow" do
test "create → view via slug → edit → view via slug (slug unchanged)", %{
conn: conn,

View file

@ -5,9 +5,10 @@ defmodule MvWeb.GroupLive.ShowAccessibilityTest do
"""
use MvWeb.ConnCase, async: false
import Phoenix.LiveViewTest
use Gettext, backend: MvWeb.Gettext
import Phoenix.LiveViewTest
alias Mv.Fixtures
alias Mv.Membership

View file

@ -5,10 +5,11 @@ defmodule MvWeb.GroupLive.ShowAddMemberTest do
"""
use MvWeb.ConnCase, async: false
import Phoenix.LiveViewTest
import MvWeb.GroupLiveHelpers
use Gettext, backend: MvWeb.Gettext
import MvWeb.GroupLiveHelpers
import Phoenix.LiveViewTest
alias Mv.Fixtures
alias Mv.Membership

View file

@ -5,9 +5,10 @@ defmodule MvWeb.GroupLive.ShowAddRemoveMembersTest do
"""
use MvWeb.ConnCase, async: false
import Phoenix.LiveViewTest
use Gettext, backend: MvWeb.Gettext
import Phoenix.LiveViewTest
alias Mv.Fixtures
alias Mv.Membership

View file

@ -5,9 +5,10 @@ defmodule MvWeb.GroupLive.ShowAuthorizationTest do
"""
use MvWeb.ConnCase, async: false
import Phoenix.LiveViewTest
use Gettext, backend: MvWeb.Gettext
import Phoenix.LiveViewTest
alias Mv.Fixtures
alias Mv.Membership

View file

@ -5,9 +5,10 @@ defmodule MvWeb.GroupLive.ShowIntegrationTest do
"""
use MvWeb.ConnCase, async: false
import Phoenix.LiveViewTest
use Gettext, backend: MvWeb.Gettext
import Phoenix.LiveViewTest
alias Mv.Fixtures
alias Mv.Membership

View file

@ -5,9 +5,10 @@ defmodule MvWeb.GroupLive.ShowMemberSearchTest do
"""
use MvWeb.ConnCase, async: false
import Phoenix.LiveViewTest
use Gettext, backend: MvWeb.Gettext
import Phoenix.LiveViewTest
alias Mv.Fixtures
alias Mv.Membership

View file

@ -5,9 +5,10 @@ defmodule MvWeb.GroupLive.ShowRemoveMemberTest do
"""
use MvWeb.ConnCase, async: false
import Phoenix.LiveViewTest
use Gettext, backend: MvWeb.Gettext
import Phoenix.LiveViewTest
alias Mv.Fixtures
alias Mv.Membership

View file

@ -11,13 +11,15 @@ defmodule MvWeb.GroupLive.ShowTest do
- Delete functionality
"""
use MvWeb.ConnCase, async: false
import Phoenix.LiveViewTest
require Ash.Query
use Gettext, backend: MvWeb.Gettext
import Phoenix.LiveViewTest
alias Mv.Fixtures
alias Mv.Membership
require Ash.Query
describe "mount and display" do
test "page renders successfully", %{conn: conn} do
group = Fixtures.group_fixture()

View file

@ -4,9 +4,10 @@ defmodule MvWeb.MemberLive.DeactivateTest do
driven through the parent LiveView (the DeactivateComponent is stateful).
"""
use MvWeb.ConnCase, async: true
import Phoenix.LiveViewTest
use Gettext, backend: MvWeb.Gettext
import Phoenix.LiveViewTest
alias Mv.Fixtures
defp reload_member(member) do

View file

@ -11,13 +11,15 @@ defmodule MvWeb.RoleLive.ShowTest do
- Delete functionality
"""
use MvWeb.ConnCase, async: false
import Phoenix.LiveViewTest
require Ash.Query
use Gettext, backend: MvWeb.Gettext
import Phoenix.LiveViewTest
alias Mv.Authorization
alias Mv.Authorization.Role
require Ash.Query
# Helper to create a role (authorize?: false for test data setup)
defp create_role(attrs \\ %{}) do
default_attrs = %{

View file

@ -10,10 +10,12 @@ defmodule MvWeb.UserLive.ShowTest do
- Error handling
"""
use MvWeb.ConnCase, async: true
import Phoenix.LiveViewTest
require Ash.Query
use Gettext, backend: MvWeb.Gettext
import Phoenix.LiveViewTest
require Ash.Query
setup do
# Create test user
user = create_test_user(%{email: "test@example.com", oidc_id: "test123"})

View file

@ -9,10 +9,11 @@ defmodule MvWeb.MemberLive.IndexCustomFieldsAccessibilityTest do
"""
use MvWeb.ConnCase, async: true
import Phoenix.LiveViewTest
require Ash.Query
alias Mv.Membership.{CustomField, CustomFieldValue}
require Ash.Query
setup do
system_actor = Mv.Helpers.SystemActor.get_system_actor()

View file

@ -15,10 +15,11 @@ defmodule MvWeb.MemberLive.IndexCustomFieldsDisplayTest do
# rather than part of the original change.
use MvWeb.ConnCase, async: false
import Phoenix.LiveViewTest
require Ash.Query
alias Mv.Membership.{CustomField, CustomFieldValue}
require Ash.Query
setup do
system_actor = Mv.Helpers.SystemActor.get_system_actor()

View file

@ -8,10 +8,11 @@ defmodule MvWeb.MemberLive.IndexCustomFieldsEdgeCasesTest do
"""
use MvWeb.ConnCase, async: true
import Phoenix.LiveViewTest
require Ash.Query
alias Mv.Membership.CustomField
require Ash.Query
@tag :slow
test "displays custom field column even when no members have values", %{conn: conn} do
system_actor = Mv.Helpers.SystemActor.get_system_actor()

View file

@ -11,10 +11,11 @@ defmodule MvWeb.MemberLive.IndexCustomFieldsSortingTest do
"""
use MvWeb.ConnCase, async: true
import Phoenix.LiveViewTest
require Ash.Query
alias Mv.Membership.{CustomField, CustomFieldValue}
require Ash.Query
setup do
system_actor = Mv.Helpers.SystemActor.get_system_actor()

View file

@ -17,10 +17,11 @@ defmodule MvWeb.MemberLive.IndexFieldVisibilityTest do
use MvWeb.ConnCase, async: false
import Phoenix.LiveViewTest
require Ash.Query
alias Mv.Membership.{CustomField, CustomFieldValue}
require Ash.Query
setup do
system_actor = Mv.Helpers.SystemActor.get_system_actor()

View file

@ -14,10 +14,11 @@ defmodule MvWeb.MemberLive.IndexGroupsAccessibilityTest do
# than part of the original change.
use MvWeb.ConnCase, async: false
import Phoenix.LiveViewTest
require Ash.Query
alias Mv.Membership.{Group, MemberGroup}
require Ash.Query
setup do
system_actor = Mv.Helpers.SystemActor.get_system_actor()

View file

@ -14,10 +14,11 @@ defmodule MvWeb.MemberLive.IndexGroupsDisplayTest do
# than part of the original change.
use MvWeb.ConnCase, async: false
import Phoenix.LiveViewTest
require Ash.Query
alias Mv.Membership.{Group, MemberGroup}
require Ash.Query
setup do
system_actor = Mv.Helpers.SystemActor.get_system_actor()

View file

@ -12,10 +12,11 @@ defmodule MvWeb.MemberLive.IndexGroupsFilterTest do
# than part of the original change.
use MvWeb.ConnCase, async: false
import Phoenix.LiveViewTest
require Ash.Query
alias Mv.Membership.{Group, MemberGroup}
require Ash.Query
setup do
system_actor = Mv.Helpers.SystemActor.get_system_actor()

View file

@ -16,12 +16,13 @@ defmodule MvWeb.MemberLive.IndexGroupsIntegrationTest do
# than part of the original change.
use MvWeb.ConnCase, async: false
import Phoenix.LiveViewTest
require Ash.Query
alias Mv.Helpers.SystemActor
alias Mv.Membership.{CustomField, CustomFieldValue, Group, MemberGroup}
alias Mv.MembershipFees.{MembershipFeeCycle, MembershipFeeType}
require Ash.Query
setup do
system_actor = SystemActor.get_system_actor()

View file

@ -14,10 +14,11 @@ defmodule MvWeb.MemberLive.IndexGroupsPerformanceTest do
# than part of the original change.
use MvWeb.ConnCase, async: false
import Phoenix.LiveViewTest
require Ash.Query
alias Mv.Membership.{Group, MemberGroup}
require Ash.Query
setup do
system_actor = Mv.Helpers.SystemActor.get_system_actor()

View file

@ -8,10 +8,11 @@ defmodule MvWeb.MemberLive.IndexGroupsSortingTest do
# than part of the original change.
use MvWeb.ConnCase, async: false
import Phoenix.LiveViewTest
require Ash.Query
alias Mv.Membership.{Group, MemberGroup}
require Ash.Query
setup do
system_actor = Mv.Helpers.SystemActor.get_system_actor()

View file

@ -15,10 +15,11 @@ defmodule MvWeb.MemberLive.IndexGroupsUrlParamsTest do
# is not trivially async-safe; resolving that is a separate follow-up.
use MvWeb.ConnCase, async: false
import Phoenix.LiveViewTest
require Ash.Query
alias Mv.Membership.{Group, MemberGroup}
require Ash.Query
setup do
system_actor = Mv.Helpers.SystemActor.get_system_actor()

View file

@ -5,7 +5,7 @@ defmodule MvWeb.MemberLive.IndexTest do
# the timing assertion flaky, so this module stays synchronous.
use MvWeb.ConnCase, async: false
import Phoenix.LiveViewTest
require Ash.Query
import Mv.Fixtures, only: [create_fee_type: 2, create_cycle: 4]
alias Mv.Helpers.SystemActor
alias Mv.Membership
@ -13,8 +13,6 @@ defmodule MvWeb.MemberLive.IndexTest do
alias Mv.Membership.CustomFieldValue
alias MvWeb.MemberLive.Index, as: MemberIndex
import Mv.Fixtures, only: [create_fee_type: 2, create_cycle: 4]
describe "desktop layout: scroll container and sticky table header" do
@describetag :ui

View file

@ -16,12 +16,14 @@ defmodule MvWeb.MemberLive.ShowGroupsDisplayTest do
than part of the original change.
"""
use MvWeb.ConnCase, async: false
import Phoenix.LiveViewTest
require Ash.Query
use Gettext, backend: MvWeb.Gettext
import Phoenix.LiveViewTest
alias Mv.Membership.{Group, MemberGroup}
require Ash.Query
describe "groups section" do
setup do
actor = Mv.Helpers.SystemActor.get_system_actor()

View file

@ -14,12 +14,14 @@ defmodule MvWeb.MemberLive.ShowTest do
- Custom field cleanup in tests ensures no interference between tests
"""
use MvWeb.ConnCase, async: true
import Phoenix.LiveViewTest
require Ash.Query
use Gettext, backend: MvWeb.Gettext
import Phoenix.LiveViewTest
alias Mv.Membership.{CustomField, CustomFieldValue}
require Ash.Query
setup do
system_actor = Mv.Helpers.SystemActor.get_system_actor()