fix(nav): show the player game-data groups to staff
All checks were successful
PR Checks / android-build (pull_request) Successful in 6m8s
All checks were successful
PR Checks / android-build (pull_request) Successful in 6m8s
Staff are a superset of players (all player abilities plus their staff tools), and the backend's player self-service surface is role-agnostic, but MenuAccess.PLAYER gated "My characters/vendors/houses" on role == player — so a signed-in admin/editor/moderator saw neither the menu items nor, via the greyed personal streams, their own notification options, even with linked characters. Gate MenuAccess.PLAYER on isPlayer OR isStaff. The notifications screen needs no change: once the backend returns the caller's linked accounts (paired with RunicGateway/website), hasLinkedAccount resolves and the personal streams enable themselves. Tests: MenuAccessTest now asserts every staff role sees the player game-data groups and a PLAYER entry, and an unrecognized role / anon still cannot. Full unit suite passes. Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -21,7 +21,12 @@ enum class MenuAccess {
|
|||||||
/** Visible to any signed-in account (§5, "My Account"). */
|
/** Visible to any signed-in account (§5, "My Account"). */
|
||||||
SIGNED_IN,
|
SIGNED_IN,
|
||||||
|
|
||||||
/** Visible only to a player — the linked game-data groups (§6.3). */
|
/**
|
||||||
|
* The linked game-data groups (§6.3). Visible to any player **or** staff:
|
||||||
|
* staff are a superset of players (all player abilities plus their staff
|
||||||
|
* tools), and the backend's player self-service surface is role-agnostic, so
|
||||||
|
* a signed-in admin/editor/moderator sees + uses their own characters too.
|
||||||
|
*/
|
||||||
PLAYER,
|
PLAYER,
|
||||||
|
|
||||||
/** Visible to any staff role (admin/editor/moderator) — the M10 staff surface (§1). */
|
/** Visible to any staff role (admin/editor/moderator) — the M10 staff surface (§1). */
|
||||||
@@ -70,7 +75,7 @@ fun visibleEntries(entries: List<MenuEntry>, session: Session): List<MenuEntry>
|
|||||||
when (entry.access) {
|
when (entry.access) {
|
||||||
MenuAccess.PUBLIC -> true
|
MenuAccess.PUBLIC -> true
|
||||||
MenuAccess.SIGNED_IN -> session is Session.SignedIn
|
MenuAccess.SIGNED_IN -> session is Session.SignedIn
|
||||||
MenuAccess.PLAYER -> session is Session.SignedIn && session.user.isPlayer
|
MenuAccess.PLAYER -> session is Session.SignedIn && (session.user.isPlayer || session.user.isStaff)
|
||||||
MenuAccess.STAFF -> session is Session.SignedIn && session.user.isStaff
|
MenuAccess.STAFF -> session is Session.SignedIn && session.user.isStaff
|
||||||
MenuAccess.MODERATOR -> session is Session.SignedIn && session.user.isModerator
|
MenuAccess.MODERATOR -> session is Session.SignedIn && session.user.isModerator
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,12 +37,16 @@ class MenuAccessTest {
|
|||||||
assertTrue(visible.contains(Routes.HOME))
|
assertTrue(visible.contains(Routes.HOME))
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test fun staffSeeAccountButNoPlayerOnlyGroups() {
|
@Test fun staffSeeThePlayerGameDataGroups() {
|
||||||
val visible = routes(signedIn(Role.EDITOR))
|
// Staff are a superset of players: every staff role sees the PLAYER-access
|
||||||
assertTrue(visible.contains(Routes.ACCOUNT))
|
// game-data groups too (their own linked characters, via the role-agnostic
|
||||||
// No PLAYER-access entry (the M4 game-data groups) leaks to staff.
|
// /player self-service surface), on top of their staff entries.
|
||||||
val playerOnly = APP_MENU.filter { it.access == MenuAccess.PLAYER }.map { it.route }
|
val playerGroups = APP_MENU.filter { it.access == MenuAccess.PLAYER }.map { it.route }
|
||||||
assertTrue(playerOnly.none { visible.contains(it) })
|
for (role in listOf(Role.ADMIN, Role.EDITOR, Role.MODERATOR)) {
|
||||||
|
val visible = routes(signedIn(role))
|
||||||
|
assertTrue("$role should see Account", visible.contains(Routes.ACCOUNT))
|
||||||
|
assertTrue("$role should see the player game-data groups", playerGroups.all { visible.contains(it) })
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test fun publicEntryCountIsStableAcrossSessions() {
|
@Test fun publicEntryCountIsStableAcrossSessions() {
|
||||||
@@ -58,10 +62,13 @@ class MenuAccessTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test fun playerAccessGatedFunction() {
|
@Test fun playerAccessGatedFunction() {
|
||||||
// A synthetic PLAYER-gated entry is visible to a player, hidden from staff/anon.
|
// A PLAYER-gated entry is visible to a player AND to every staff role
|
||||||
|
// (staff superset), hidden only from an unrecognized role and anon.
|
||||||
val entries = listOf(MenuEntry("game", 0, MenuAccess.PLAYER))
|
val entries = listOf(MenuEntry("game", 0, MenuAccess.PLAYER))
|
||||||
assertTrue(visibleEntries(entries, signedIn(Role.PLAYER)).isNotEmpty())
|
for (role in listOf(Role.PLAYER, Role.ADMIN, Role.EDITOR, Role.MODERATOR)) {
|
||||||
assertTrue(visibleEntries(entries, signedIn(Role.ADMIN)).isEmpty())
|
assertTrue("$role should see a PLAYER entry", visibleEntries(entries, signedIn(role)).isNotEmpty())
|
||||||
|
}
|
||||||
|
assertTrue(visibleEntries(entries, signedIn(Role.UNKNOWN)).isEmpty())
|
||||||
assertTrue(visibleEntries(entries, Session.SignedOut).isEmpty())
|
assertTrue(visibleEntries(entries, Session.SignedOut).isEmpty())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user