mirror of
https://github.com/we-promise/sure.git
synced 2026-09-05 06:41:08 +00:00
* Add admin family management features and tests
- Implement FamiliesController with destroy action to delete unused families.
- Add localization for success and error messages related to family deletion.
- Create FamiliesControllerTest to ensure proper functionality of family deletion.
- Update UserPolicyTest to include permissions for super admins to delete users.
- Enhance UsersControllerTest with tests for user family management, including moving users between families and creating new families.
* feat(users): enhance user management with family transfer validation and improved delete warnings
* Simplify user management actions column and combine family options
Move heavy user edit forms from table rows into a DS::Popover action
menu, add role badges to the user column, combine family migration and
creation inputs with a Stimulus controller, enable self-family
transfer for super admins, and add safety guards against demoting the
last super admin in the system.
* feat: add authentication type pills to admin user index to display SSO and local login status
* Add set password feature for local users in admin user management
- Add password field in action popover for users with local password login
- Enforce all registration password criteria (min 8 chars, mixed case, digit, special char)
- Block simultaneous family and password updates with clear error
- Show descriptive success notifications (role, password, both, family)
- Ignore password param for SSO-only users
- Add comprehensive tests for all password validation paths
* Resolve DS Drift Patrol findings and CI scan failures
- Wrap auth-type pills in DS::Tooltip instead of native title= attribute
- Add actions.manage_user key to locale and drop redundant default: fallbacks
- Fix RuboCop style offenses in Admin::UsersController
- Update Brakeman ignore entry fingerprint for Admin::UsersController#user_params
* fix: update badge query to target DS::Pill structure
* Fix DS::Tooltip misuse hiding SSO auth-type pill in admin users view
The SSO pill was passed as a block to DS::Tooltip, which caused it to
render inside the hidden div[role="tooltip"] instead of being visible.
The text: option ("SSO Provider: ...") was also silently ignored because
tooltip_content returns content (the block) over @text when a block is
given.
Fix: render the SSO/Local+SSO pill directly as visible content and pass
DS::Tooltip with no block so text: is used as the tooltip popup. An info
icon now appears next to the pill and shows the provider name on hover.
Fixes test: Admin::UsersControllerTest#test_index_renders_auth_type_pills_for_local_and_sso_users
* Remove redundant default: fallback from role pill i18n lookup
All admin.users.index.roles.{guest,member,admin,super_admin} keys are
defined in the locale file and used elsewhere in the same view without
a default:. The fallback was redundant for every valid role and would
silently mask a missing or renamed key instead of raising in
development.
Drop the default: user.role.humanize argument so that any future
missing key surfaces immediately as I18n::MissingTranslationData.
* Revert unrelated JS/schema/split churn; fix transfer_to_family! default role
- Revert 62 JS files (Biome formatter and unrelated controller changes)
- Revert db/schema.rb dump churn (no new migrations in this branch)
- Revert unrelated split transaction view changes (edit/new.html.erb)
- Fix User#transfer_to_family! role default: role: role evaluates to nil
when omitted; use explicit self.role to read model attribute
Keeps the PR focused on user/family management (~18-20 files).
* Fix last login and session count in admin user management
Store last_login_at and sessions_count directly on the users table
so they remain accurate after a user logs out.
- Add migration to add last_login_at (datetime) and sessions_count
(integer, default 0) columns to users, with backfill from sessions
- Add counter_cache: :sessions_count to Session#belongs_to :user so
the count auto-increments/decrements on session create/destroy
- Add after_create callback on Session to stamp user.last_login_at
- Update Admin::UsersController to read both values from users table
instead of aggregating Session rows (which disappear on logout)
* Fix user management PR pending CI items
* Keep test current session after sign in
* Address PR review comments for user transfers
* refactor: update user removal label to "Delete User" and standardize component attribute naming
* Address PR Review Feedback for User Management
* test: Fix families and users controller tests for user management PR
* Limit PR 2868 schema diff
* Fix PR 2868 user management CI failures
---------
Signed-off-by: Juan José Mata <juanjo.mata@gmail.com>
Co-authored-by: sure-admin <sure-admin@splashblot.com>
Co-authored-by: Juan José Mata <juanjo.mata@gmail.com>
189 lines
6.2 KiB
Ruby
189 lines
6.2 KiB
Ruby
require "test_helper"
|
|
|
|
class RegistrationsControllerTest < ActionDispatch::IntegrationTest
|
|
test "new" do
|
|
get new_registration_url
|
|
assert_response :success
|
|
end
|
|
|
|
test "create redirects to correct URL" do
|
|
post registration_url, params: { user: {
|
|
email: "john@example.com",
|
|
password: "Password1!" } }
|
|
|
|
assert_redirected_to root_url
|
|
end
|
|
|
|
test "create rolls back registration when session creation fails" do
|
|
RegistrationsController.any_instance.stubs(:create_session_for).returns(false)
|
|
|
|
assert_no_difference "User.count" do
|
|
post registration_url, params: { user: {
|
|
email: "session-failure@example.com",
|
|
password: "Password1!" } }
|
|
end
|
|
|
|
assert_response :unprocessable_entity
|
|
assert_nil User.find_by(email: "session-failure@example.com")
|
|
end
|
|
|
|
test "first user of instance becomes super_admin" do
|
|
# Clear all users to simulate fresh instance
|
|
User.connection.disable_referential_integrity { User.delete_all }
|
|
|
|
assert_difference "User.count", +1 do
|
|
post registration_url, params: { user: {
|
|
email: "firstuser@example.com",
|
|
password: "Password1!" } }
|
|
end
|
|
|
|
first_user = User.find_by(email: "firstuser@example.com")
|
|
assert first_user.super_admin?, "First user should be super_admin"
|
|
end
|
|
|
|
test "subsequent users become admin not super_admin" do
|
|
# Ensure users exist from fixtures
|
|
assert User.exists?
|
|
|
|
assert_difference "User.count", +1 do
|
|
post registration_url, params: { user: {
|
|
email: "seconduser@example.com",
|
|
password: "Password1!" } }
|
|
end
|
|
|
|
new_user = User.find_by(email: "seconduser@example.com")
|
|
assert new_user.admin?, "Subsequent user should be admin"
|
|
assert_not new_user.super_admin?, "Subsequent user should not be super_admin"
|
|
end
|
|
|
|
test "create when hosted requires an invite code" do
|
|
with_env_overrides REQUIRE_INVITE_CODE: "true" do
|
|
assert_no_difference "User.count" do
|
|
post registration_url, params: { user: {
|
|
email: "john@example.com",
|
|
password: "Password1!" } }
|
|
assert_redirected_to new_registration_url
|
|
|
|
post registration_url, params: { user: {
|
|
email: "john@example.com",
|
|
password: "Password1!",
|
|
invite_code: "foo" } }
|
|
assert_redirected_to new_registration_url
|
|
end
|
|
|
|
assert_difference "User.count", +1 do
|
|
invite_code = InviteCode.generate!
|
|
post registration_url, params: { user: {
|
|
email: "john@example.com",
|
|
password: "Password1!",
|
|
invite_code: invite_code } }
|
|
assert_redirected_to root_url
|
|
assert_not InviteCode.exists?(token: invite_code)
|
|
end
|
|
end
|
|
end
|
|
|
|
test "invite code is not consumed when signup fails validation" do
|
|
with_env_overrides REQUIRE_INVITE_CODE: "true" do
|
|
invite_code = InviteCode.generate!
|
|
|
|
assert_no_difference "User.count" do
|
|
post registration_url, params: { user: {
|
|
email: "validationfail@example.com",
|
|
password: "weak",
|
|
invite_code: invite_code } }
|
|
end
|
|
|
|
assert_response :unprocessable_entity
|
|
assert InviteCode.exists?(token: invite_code)
|
|
end
|
|
end
|
|
|
|
test "invalid invite code does not create a user" do
|
|
with_env_overrides REQUIRE_INVITE_CODE: "true" do
|
|
assert_no_difference "User.count" do
|
|
post registration_url, params: { user: {
|
|
email: "valid@example.com",
|
|
password: "Password1!",
|
|
invite_code: "invalid-token-that-does-not-exist" } }
|
|
end
|
|
|
|
assert_redirected_to new_registration_url
|
|
end
|
|
end
|
|
|
|
test "creating account from guest invitation assigns guest role and intro layout" do
|
|
invitation = invitations(:one)
|
|
invitation.update!(role: "guest", email: "guest-signup@example.com")
|
|
|
|
assert_difference "User.count", +1 do
|
|
post registration_url, params: { user: {
|
|
email: invitation.email,
|
|
password: "Password1!",
|
|
invitation: invitation.token
|
|
} }
|
|
end
|
|
|
|
created_user = User.find_by(email: invitation.email)
|
|
assert_equal "guest", created_user.role
|
|
assert created_user.ui_layout_intro?
|
|
assert_not created_user.show_sidebar?
|
|
assert_not created_user.show_ai_sidebar?
|
|
assert created_user.ai_enabled?
|
|
end
|
|
|
|
test "creating account from invitation shares existing family accounts when family shares by default" do
|
|
invitation = invitations(:one)
|
|
invitation.family.update!(default_account_sharing: "shared")
|
|
|
|
post registration_url, params: { user: {
|
|
email: invitation.email,
|
|
password: "Password1!",
|
|
invitation: invitation.token
|
|
} }
|
|
|
|
created_user = User.find_by(email: invitation.email)
|
|
assert_not_nil created_user
|
|
assert_equal invitation.family_id, created_user.family_id
|
|
assert_equal invitation.family.accounts.pluck(:id).sort,
|
|
AccountShare.where(user: created_user).pluck(:account_id).sort
|
|
end
|
|
|
|
test "creating account in invite-only default family shares existing family accounts" do
|
|
family = families(:dylan_family)
|
|
family.update!(default_account_sharing: "shared")
|
|
Setting.onboarding_state = "invite_only"
|
|
Setting.invite_only_default_family_id = family.id
|
|
|
|
assert_difference "User.count", +1 do
|
|
post registration_url, params: { user: {
|
|
email: "default-family-signup@example.com",
|
|
password: "Password1!"
|
|
} }
|
|
end
|
|
|
|
created_user = User.find_by(email: "default-family-signup@example.com")
|
|
assert_not_nil created_user
|
|
assert_equal family.id, created_user.family_id
|
|
assert_equal "member", created_user.role
|
|
assert_equal family.accounts.pluck(:id).sort,
|
|
AccountShare.where(user: created_user).pluck(:account_id).sort
|
|
assert AccountShare.where(user: created_user).all?(&:read_write?)
|
|
end
|
|
|
|
test "creating account from invitation shares nothing when family sharing is private" do
|
|
invitation = invitations(:one)
|
|
invitation.family.update!(default_account_sharing: "private")
|
|
|
|
post registration_url, params: { user: {
|
|
email: invitation.email,
|
|
password: "Password1!",
|
|
invitation: invitation.token
|
|
} }
|
|
|
|
created_user = User.find_by(email: invitation.email)
|
|
assert_not_nil created_user
|
|
assert_equal 0, AccountShare.where(user: created_user).count
|
|
end
|
|
end
|