Add actor parameter to all tests requiring authorization
All checks were successful
continuous-integration/drone/push Build is passing

This commit adds actor: system_actor to all Ash operations in tests that
require authorization.
This commit is contained in:
Moritz 2026-01-23 20:00:24 +01:00
parent 4c846f8bba
commit a6cdeaa18d
Signed by: moritz
GPG key ID: 1020A035E5DD0824
75 changed files with 4649 additions and 2865 deletions

View file

@ -8,9 +8,15 @@ defmodule MvWeb.OidcPasswordLinkingTest do
use MvWeb.ConnCase, async: true
require Ash.Query
setup do
system_actor = Mv.Helpers.SystemActor.get_system_actor()
%{actor: system_actor}
end
describe "OIDC login with existing email (no oidc_id) - Email Collision" do
@tag :test_proposal
test "OIDC register with existing password user email fails with PasswordVerificationRequired" do
test "OIDC register with existing password user email fails with PasswordVerificationRequired",
%{actor: actor} do
# Create password-only user
existing_user =
create_test_user(%{
@ -26,10 +32,13 @@ defmodule MvWeb.OidcPasswordLinkingTest do
}
result =
Mv.Accounts.create_register_with_rauthy(%{
user_info: user_info,
oauth_tokens: %{}
})
Mv.Accounts.create_register_with_rauthy(
%{
user_info: user_info,
oauth_tokens: %{}
},
actor: actor
)
# Should fail with PasswordVerificationRequired error
assert {:error, %Ash.Error.Invalid{errors: errors}} = result
@ -47,7 +56,7 @@ defmodule MvWeb.OidcPasswordLinkingTest do
end
@tag :test_proposal
test "PasswordVerificationRequired error contains necessary context" do
test "PasswordVerificationRequired error contains necessary context", %{actor: actor} do
existing_user =
create_test_user(%{
email: "test@example.com",
@ -61,10 +70,13 @@ defmodule MvWeb.OidcPasswordLinkingTest do
}
{:error, %Ash.Error.Invalid{errors: errors}} =
Mv.Accounts.create_register_with_rauthy(%{
user_info: user_info,
oauth_tokens: %{}
})
Mv.Accounts.create_register_with_rauthy(
%{
user_info: user_info,
oauth_tokens: %{}
},
actor: actor
)
password_error =
Enum.find(errors, fn err ->
@ -78,7 +90,7 @@ defmodule MvWeb.OidcPasswordLinkingTest do
end
@tag :test_proposal
test "after successful password verification, oidc_id can be set" do
test "after successful password verification, oidc_id can be set", %{actor: actor} do
# Create password user
user =
create_test_user(%{
@ -97,12 +109,12 @@ defmodule MvWeb.OidcPasswordLinkingTest do
{:ok, updated_user} =
Mv.Accounts.User
|> Ash.Query.filter(id == ^user.id)
|> Ash.read_one!()
|> Ash.read_one!(actor: actor)
|> Ash.Changeset.for_update(:link_oidc_id, %{
oidc_id: user_info["sub"],
oidc_user_info: user_info
})
|> Ash.update()
|> Ash.update(actor: actor)
assert updated_user.id == user.id
assert updated_user.oidc_id == "linked_oidc_555"
@ -112,7 +124,7 @@ defmodule MvWeb.OidcPasswordLinkingTest do
end
@tag :test_proposal
test "password verification with wrong password keeps oidc_id as nil" do
test "password verification with wrong password keeps oidc_id as nil", %{actor: actor} do
# This test verifies that if password verification fails,
# the oidc_id should NOT be set
@ -131,7 +143,7 @@ defmodule MvWeb.OidcPasswordLinkingTest do
# before link_oidc_id is called, so here we just verify the user state
# User should still have no oidc_id (no linking happened)
{:ok, unchanged_user} = Ash.get(Mv.Accounts.User, user.id)
{:ok, unchanged_user} = Ash.get(Mv.Accounts.User, user.id, actor: actor)
assert is_nil(unchanged_user.oidc_id)
assert unchanged_user.hashed_password == user.hashed_password
end
@ -139,7 +151,7 @@ defmodule MvWeb.OidcPasswordLinkingTest do
describe "OIDC login with email of user having different oidc_id - Account Conflict" do
@tag :test_proposal
test "OIDC register with email of user having different oidc_id fails" do
test "OIDC register with email of user having different oidc_id fails", %{actor: actor} do
# User already linked to OIDC provider A
_existing_user =
create_test_user(%{
@ -155,10 +167,13 @@ defmodule MvWeb.OidcPasswordLinkingTest do
}
result =
Mv.Accounts.create_register_with_rauthy(%{
user_info: user_info,
oauth_tokens: %{}
})
Mv.Accounts.create_register_with_rauthy(
%{
user_info: user_info,
oauth_tokens: %{}
},
actor: actor
)
# Should fail - cannot link different OIDC account to same email
assert {:error, %Ash.Error.Invalid{errors: errors}} = result
@ -171,7 +186,7 @@ defmodule MvWeb.OidcPasswordLinkingTest do
end
@tag :test_proposal
test "existing OIDC user email remains unchanged when oidc_id matches" do
test "existing OIDC user email remains unchanged when oidc_id matches", %{actor: actor} do
user =
create_test_user(%{
email: "oidc@example.com",
@ -186,10 +201,13 @@ defmodule MvWeb.OidcPasswordLinkingTest do
# This should work via upsert
{:ok, updated_user} =
Mv.Accounts.create_register_with_rauthy(%{
user_info: user_info,
oauth_tokens: %{}
})
Mv.Accounts.create_register_with_rauthy(
%{
user_info: user_info,
oauth_tokens: %{}
},
actor: actor
)
assert updated_user.id == user.id
assert updated_user.oidc_id == "oidc_stable_789"
@ -199,7 +217,7 @@ defmodule MvWeb.OidcPasswordLinkingTest do
describe "Email update during OIDC linking" do
@tag :test_proposal
test "linking OIDC to password account updates email if different in OIDC" do
test "linking OIDC to password account updates email if different in OIDC", %{actor: actor} do
# Password user with old email
user =
create_test_user(%{
@ -218,19 +236,19 @@ defmodule MvWeb.OidcPasswordLinkingTest do
{:ok, updated_user} =
Mv.Accounts.User
|> Ash.Query.filter(id == ^user.id)
|> Ash.read_one!()
|> Ash.read_one!(actor: actor)
|> Ash.Changeset.for_update(:link_oidc_id, %{
oidc_id: user_info["sub"],
oidc_user_info: user_info
})
|> Ash.update()
|> Ash.update(actor: actor)
assert updated_user.oidc_id == "oidc_link_999"
assert to_string(updated_user.email) == "newemail@example.com"
end
@tag :test_proposal
test "email change during linking triggers member email sync" do
test "email change during linking triggers member email sync", %{actor: actor} do
# Create member
member =
Ash.Seed.seed!(Mv.Membership.Member, %{
@ -257,25 +275,25 @@ defmodule MvWeb.OidcPasswordLinkingTest do
{:ok, updated_user} =
Mv.Accounts.User
|> Ash.Query.filter(id == ^user.id)
|> Ash.read_one!()
|> Ash.read_one!(actor: actor)
|> Ash.Changeset.for_update(:link_oidc_id, %{
oidc_id: user_info["sub"],
oidc_user_info: user_info
})
|> Ash.update()
|> Ash.update(actor: actor)
# Verify user email changed
assert to_string(updated_user.email) == "newemail@example.com"
# Verify member email was synced
{:ok, updated_member} = Ash.get(Mv.Membership.Member, member.id)
{:ok, updated_member} = Ash.get(Mv.Membership.Member, member.id, actor: actor)
assert to_string(updated_member.email) == "newemail@example.com"
end
end
describe "Edge cases" do
@tag :test_proposal
test "user with empty string oidc_id is treated as password-only user" do
test "user with empty string oidc_id is treated as password-only user", %{actor: actor} do
_user =
create_test_user(%{
email: "empty@example.com",
@ -290,10 +308,13 @@ defmodule MvWeb.OidcPasswordLinkingTest do
}
result =
Mv.Accounts.create_register_with_rauthy(%{
user_info: user_info,
oauth_tokens: %{}
})
Mv.Accounts.create_register_with_rauthy(
%{
user_info: user_info,
oauth_tokens: %{}
},
actor: actor
)
# Should trigger PasswordVerificationRequired (empty string = no OIDC)
assert {:error, %Ash.Error.Invalid{errors: errors}} = result
@ -307,7 +328,7 @@ defmodule MvWeb.OidcPasswordLinkingTest do
end
@tag :test_proposal
test "cannot link same oidc_id to multiple users" do
test "cannot link same oidc_id to multiple users", %{actor: actor} do
# User 1 with OIDC
_user1 =
create_test_user(%{
@ -323,7 +344,7 @@ defmodule MvWeb.OidcPasswordLinkingTest do
email: "user2@example.com"
})
|> Ash.Changeset.force_change_attribute(:oidc_id, "shared_oidc_333")
|> Ash.create()
|> Ash.create(actor: actor)
# Should fail due to unique constraint on oidc_id
assert match?({:error, %Ash.Error.Invalid{}}, result)
@ -337,14 +358,16 @@ defmodule MvWeb.OidcPasswordLinkingTest do
end
describe "OIDC login with passwordless user - Requires Linking Flow" do
test "user without password and without oidc_id triggers PasswordVerificationRequired" do
test "user without password and without oidc_id triggers PasswordVerificationRequired", %{
actor: actor
} do
# Create user without password (e.g., invited user)
{:ok, existing_user} =
Mv.Accounts.User
|> Ash.Changeset.for_create(:create_user, %{
email: "invited@example.com"
})
|> Ash.create()
|> Ash.create(actor: actor)
# Verify user has no password and no oidc_id
assert is_nil(existing_user.hashed_password)
@ -372,14 +395,14 @@ defmodule MvWeb.OidcPasswordLinkingTest do
end)
end
test "user without password but WITH password later requires verification" do
test "user without password but WITH password later requires verification", %{actor: actor} do
# Create user without password first
{:ok, user} =
Mv.Accounts.User
|> Ash.Changeset.for_create(:create_user, %{
email: "added-password@example.com"
})
|> Ash.create()
|> Ash.create(actor: actor)
# User sets password later (using admin action)
{:ok, user_with_password} =
@ -387,7 +410,7 @@ defmodule MvWeb.OidcPasswordLinkingTest do
|> Ash.Changeset.for_update(:admin_set_password, %{
password: "newpassword123"
})
|> Ash.update()
|> Ash.update(actor: actor)
assert not is_nil(user_with_password.hashed_password)
@ -398,10 +421,13 @@ defmodule MvWeb.OidcPasswordLinkingTest do
}
result =
Mv.Accounts.create_register_with_rauthy(%{
user_info: user_info,
oauth_tokens: %{"access_token" => "test_token"}
})
Mv.Accounts.create_register_with_rauthy(
%{
user_info: user_info,
oauth_tokens: %{"access_token" => "test_token"}
},
actor: actor
)
# Should fail with PasswordVerificationRequired
assert {:error, %Ash.Error.Invalid{}} = result
@ -414,7 +440,7 @@ defmodule MvWeb.OidcPasswordLinkingTest do
end
describe "OIDC login with different oidc_id - Hard Error" do
test "user with different oidc_id cannot be linked (hard error)" do
test "user with different oidc_id cannot be linked (hard error)", %{actor: actor} do
# Create user with existing OIDC ID
{:ok, existing_user} =
Mv.Accounts.User
@ -422,7 +448,7 @@ defmodule MvWeb.OidcPasswordLinkingTest do
email: "already-linked@example.com"
})
|> Ash.Changeset.force_change_attribute(:oidc_id, "original_oidc_999")
|> Ash.create()
|> Ash.create(actor: actor)
assert existing_user.oidc_id == "original_oidc_999"
@ -433,10 +459,13 @@ defmodule MvWeb.OidcPasswordLinkingTest do
}
result =
Mv.Accounts.create_register_with_rauthy(%{
user_info: user_info,
oauth_tokens: %{"access_token" => "test_token"}
})
Mv.Accounts.create_register_with_rauthy(
%{
user_info: user_info,
oauth_tokens: %{"access_token" => "test_token"}
},
actor: actor
)
# Should fail with hard error (not PasswordVerificationRequired)
assert {:error, %Ash.Error.Invalid{}} = result
@ -459,7 +488,7 @@ defmodule MvWeb.OidcPasswordLinkingTest do
end)
end
test "cannot link different oidc_id even with password verification" do
test "cannot link different oidc_id even with password verification", %{actor: actor} do
# Create user with password AND existing OIDC ID
existing_user =
create_test_user(%{
@ -478,10 +507,13 @@ defmodule MvWeb.OidcPasswordLinkingTest do
}
result =
Mv.Accounts.create_register_with_rauthy(%{
user_info: user_info,
oauth_tokens: %{"access_token" => "test_token"}
})
Mv.Accounts.create_register_with_rauthy(
%{
user_info: user_info,
oauth_tokens: %{"access_token" => "test_token"}
},
actor: actor
)
# Should fail - cannot link different OIDC ID
assert {:error, %Ash.Error.Invalid{}} = result