Skip to content

Comments

Tornilobby#3254

Draft
ryanbarlow97 wants to merge 7 commits intomainfrom
tornilobby
Draft

Tornilobby#3254
ryanbarlow97 wants to merge 7 commits intomainfrom
tornilobby

Conversation

@ryanbarlow97
Copy link
Contributor

If this PR fixes an issue, link it below. If not, delete these two lines.
Resolves #(issue number)

Description:

Describe the PR.

Please complete the following:

  • I have added screenshots for all UI updates
  • I process any text displayed to the user through translateText() and I've added it to the en.json file
  • I have added relevant tests to the test directory
  • I confirm I have thoroughly tested these changes and take full responsibility for any bugs introduced

Please put your Discord username so you can be contacted if a bug or regression is found:

DISCORD_USERNAME

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 19, 2026

Walkthrough

Adds a Discord allowlist and role-requirement feature across UI, schemas, server enforcement, worker join flow, client model, translations, and tests; enforces checks on join/rejoin and config updates and sanitizes lobby_info for non-creators.

Changes

Cohort / File(s) Summary
Localization
resources/lang/en.json
Added Discord-related translation keys under public_lobby and new kick_reason keys (discord_not_allowed, discord_role_not_allowed).
Client — Host UI
src/client/HostLobbyModal.ts
Added allowlist and required-role UI, local state for allowedDiscordIds / requiredDiscordRoles, validation via DISCORD_ID_REGEX, debounced commits, and includes these fields in update-game-config payloads.
Schemas & API
src/core/Schemas.ts, src/core/ApiSchemas.ts
Introduced DISCORD_ID_REGEX; extended GameConfigSchema with allowedDiscordIds, requiredDiscordRoles, and legacy single-role fields; added guild->roles map schema and extended UserMe discord shapes to include guild role mappings.
Server — Client model
src/server/Client.ts
Added discordId and discordGuildRoles as public readonly constructor properties on Client instances.
Server — Core logic
src/server/GameServer.ts, src/server/Worker.ts
Implemented normalization/storage of allowlist and role requirements, enforcement on join/rejoin and config updates (kicks/rejects), builder for per-guild role maps, propagation of discord identity/roles from Worker to Client, and sanitization of lobby_info for non-creators.
Tests
tests/server/DiscordAllowlist.test.ts
New test suite covering allowlist and role enforcement, rejoin/kick behavior after config changes, broadcast sanitization, and positive/negative role checks.

Sequence Diagram(s)

sequenceDiagram
    participant Player
    participant Worker
    participant GameServer
    participant ClientInstance as Client

    Player->>Worker: Join request (token or anon)
    Note over Worker: extract discordId and guildRoles from UserMe
    alt GameConfig has allowedDiscordIds or requiredRoles
        alt Player anonymous and allowlist exists
            Worker-->>Player: Reject (discord_login_required)
        else if discordId missing or not in allowlist
            Worker-->>Player: Reject (kick_reason.discord_not_allowed)
        else if missing required role
            Worker-->>Player: Reject (kick_reason.discord_role_not_allowed)
        else
            Worker->>GameServer: Create client (include discordId, guildRoles)
            GameServer->>ClientInstance: store discord info
            GameServer-->>Player: Join accepted
        end
    else
        Worker->>GameServer: Create client (no discord constraints)
        GameServer-->>Player: Join accepted
    end
Loading
sequenceDiagram
    participant Creator
    participant GameServer
    participant ConnectedClient

    Creator->>GameServer: Update gameConfig.allowedDiscordIds / requiredDiscordRoles
    GameServer->>GameServer: Normalize & dedupe lists
    GameServer->>GameServer: Build guild->roles map
    loop for each connected client
        alt client is creator OR matches allowlist/roles
            Note over GameServer: keep connected
        else
            GameServer->>ConnectedClient: Kick (discord_not_allowed / discord_role_not_allowed)
        end
    end
    GameServer-->>All: Broadcast sanitized lobby_info (hide discord lists for non-creators)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🔐 Small chips of IDs in a row,
Gates of roles where secrets flow,
Creator sets the careful key,
Server checks who may be free,
Tests nod yes — the lobby grows. 🎉

🚥 Pre-merge checks | ❌ 3

❌ Failed checks (2 warnings, 1 inconclusive)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description contains only the default template text with no actual implementation details, making it impossible to understand the PR's purpose or scope. Replace template text with a clear description of the Discord allowlist feature, explain the implementation changes across files, and complete the checklist items.
Docstring Coverage ⚠️ Warning Docstring coverage is 25.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title 'Tornilobby' is vague and does not clearly describe the actual changes in this PR, which involve Discord allowlist and role-based access control features. Use a descriptive title that summarizes the main change, such as 'Add Discord allowlist and role-based access control for lobbies'.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/server/Worker.ts (1)

366-435: ⚠️ Potential issue | 🟡 Minor

Worker discord allowlist check does not exempt the lobby creator.

The GameServer exempts the creator in isDiscordAllowed() (line 143), but the Worker's fresh join path (lines 418–427) has no way to check if the user is the creator. The creatorPersistentID is private in GameServer and not exposed via any public method.

Normally this is not a problem: reconnecting clients go through rejoinClient() (line 360), which calls isDiscordAllowed() and exempts the creator. However, if rejoinClient() fails (e.g., game session was cleaned up), the creator hits the Worker's fresh join path and gets blocked if they are not in the allowlist they set.

The inconsistency matters because:

  1. GameServer allows the creator to set any allowlist (even one excluding themselves) via updateGameConfig().
  2. If the creator disconnects and rejoins after their client is cleaned up, the Worker blocks them.

Consider adding a public method to GameServer to check if a persistentID is the creator, so the Worker can exempt them at line 418:

public isLobbyCreator(persistentID: string): boolean {
  return persistentID === this.creatorPersistentID;
}

Then in Worker, before the discord check at line 418, verify:

if (allowedDiscordIdSet !== null && !gameServer?.isLobbyCreator(persistentId)) {
  // ... perform discord check
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/server/Worker.ts` around lines 366 - 435, Worker rejects fresh-joining
lobby creators because it checks allowedDiscordIdSet without exempting the
creator like GameServer.isDiscordAllowed does; add a public
GameServer.isLobbyCreator(persistentID: string): boolean that returns
persistentID === this.creatorPersistentID, then update Worker in the fresh-join
path (where it currently inspects allowedDiscordIdSet and discordId) to skip the
Discord allowlist checks when gameServer?.isLobbyCreator(persistentId) is true
(i.e., only enforce the allowlist if allowedDiscordIdSet !== null &&
!gameServer?.isLobbyCreator(persistentId)); keep existing log/close behavior for
non-creators.
🧹 Nitpick comments (2)
tests/server/DiscordAllowlist.test.ts (1)

126-149: Test 2: direct mutation of activeClients couples the test to internal state.

game.activeClients = []; directly overwrites the public array to simulate disconnection. This works today because enforceDiscordAllowlist iterates allClients, not activeClients. If that internal detail changes, this test will silently break. Consider using the WebSocket close flow instead (call ws.close() and trigger the close event on the mock).

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/server/DiscordAllowlist.test.ts` around lines 126 - 149, The test
mutates the GameServer.internal public array (game.activeClients = []) which
couples the test to implementation; instead simulate a real disconnect by
closing the client's socket so GameServer's normal removal path (e.g. the
WebSocket close handler that updates activeClients and triggers
enforceDiscordAllowlist) runs. Update the test to call the mock client's
ws.close() (or trigger its 'close' event) after joinClient so the server's
existing logic in enforceDiscordAllowlist and getClientIdForPersistentId is
exercised, keeping createClient and GameServer behavior intact without directly
setting activeClients.
src/client/HostLobbyModal.ts (1)

751-777: Silent discard of invalid Discord IDs — no user feedback.

If the user pastes a string that doesn't match the 17–20 digit pattern, it is silently ignored and the input clears. The user gets no indication that their input was rejected.

Consider adding a brief visual hint (e.g., shake the input, show a red border momentarily, or keep the invalid text in the input so the user can see it wasn't accepted).

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/client/HostLobbyModal.ts` around lines 751 - 777, commitDiscordIdInput
currently swallows invalid tokens and clears discordIdInput with no feedback;
change it to collect invalid tokens and surface a UI error instead of silently
clearing: in commitDiscordIdInput, build two lists (validIds and invalidTokens)
by testing each token against DISCORD_ID_REGEX, add validIds to nextIds and only
clear or update this.allowedDiscordIds/ call putGameConfig if there was a
change, but if invalidTokens is non-empty set a component state flag (e.g.,
this.discordIdError or this.invalidDiscordTokens) and do not clear
this.discordIdInput so the user can see/fix them; update the template/renderer
to show a red border/brief shake or inline message when
this.discordIdError/invalidDiscordTokens is set and clear that flag on next
input or successful commit.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/server/GameServer.ts`:
- Around line 151-160: Kicked clients are never unblocked because entries in
kickedPersistentIds persist after the allowlist is removed; modify
setAllowedDiscordIds/enforceDiscordAllowlist handling so that when the allowlist
is cleared (allowedDiscordIdsSet set to null or empty) you remove any
kickedPersistentIds that were added due solely to the Discord allowlist (or
reset kickedPersistentIds entirely), and/or track a allowlistVersion on kicks
and only consult kickedPersistentIds entries that match the current
allowlistVersion; update getClientIdForPersistentId accordingly to prevent stale
permanent bans. Ensure changes reference the existing symbols:
setAllowedDiscordIds, enforceDiscordAllowlist, kickedPersistentIds, and
getClientIdForPersistentId.

In `@tests/server/DiscordAllowlist.test.ts`:
- Around line 151-196: The test is a false positive because
startLobbyInfoBroadcast()/broadcastLobbyInfo() only run via an interval set when
the creator joins, but with vi.useFakeTimers() the interval callback never fires
after guest join; advance the fake timer after
expect(game.joinClient(guestClient)) so the interval executes and
guestWs.sentMessages contains a real "lobby_info" to inspect. Specifically, in
the test around joinClient and inspection of guestWs.sentMessages, call the
fake-timer advance (e.g., vi.advanceTimersByTime or equivalent) so
broadcastLobbyInfo is invoked, then re-query guestWs.sentMessages for a
"lobby_info" and assert allowedDiscordIds is absent; this touches GameServer
startLobbyInfoBroadcast, broadcastLobbyInfo, joinClient and the test's
guestWs.sentMessages usage.

---

Outside diff comments:
In `@src/server/Worker.ts`:
- Around line 366-435: Worker rejects fresh-joining lobby creators because it
checks allowedDiscordIdSet without exempting the creator like
GameServer.isDiscordAllowed does; add a public
GameServer.isLobbyCreator(persistentID: string): boolean that returns
persistentID === this.creatorPersistentID, then update Worker in the fresh-join
path (where it currently inspects allowedDiscordIdSet and discordId) to skip the
Discord allowlist checks when gameServer?.isLobbyCreator(persistentId) is true
(i.e., only enforce the allowlist if allowedDiscordIdSet !== null &&
!gameServer?.isLobbyCreator(persistentId)); keep existing log/close behavior for
non-creators.

---

Nitpick comments:
In `@src/client/HostLobbyModal.ts`:
- Around line 751-777: commitDiscordIdInput currently swallows invalid tokens
and clears discordIdInput with no feedback; change it to collect invalid tokens
and surface a UI error instead of silently clearing: in commitDiscordIdInput,
build two lists (validIds and invalidTokens) by testing each token against
DISCORD_ID_REGEX, add validIds to nextIds and only clear or update
this.allowedDiscordIds/ call putGameConfig if there was a change, but if
invalidTokens is non-empty set a component state flag (e.g., this.discordIdError
or this.invalidDiscordTokens) and do not clear this.discordIdInput so the user
can see/fix them; update the template/renderer to show a red border/brief shake
or inline message when this.discordIdError/invalidDiscordTokens is set and clear
that flag on next input or successful commit.

In `@tests/server/DiscordAllowlist.test.ts`:
- Around line 126-149: The test mutates the GameServer.internal public array
(game.activeClients = []) which couples the test to implementation; instead
simulate a real disconnect by closing the client's socket so GameServer's normal
removal path (e.g. the WebSocket close handler that updates activeClients and
triggers enforceDiscordAllowlist) runs. Update the test to call the mock
client's ws.close() (or trigger its 'close' event) after joinClient so the
server's existing logic in enforceDiscordAllowlist and
getClientIdForPersistentId is exercised, keeping createClient and GameServer
behavior intact without directly setting activeClients.

@github-project-automation github-project-automation bot moved this from Triage to Development in OpenFront Release Management Feb 19, 2026
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (4)
src/core/Schemas.ts (1)

243-247: Add .max() to allowedDiscordIds and requiredDiscordRoles arrays.

Both arrays are included in UpdateGameConfigIntentSchema via GameConfigSchema.partial(), so a lobby creator can send an arbitrarily large array over WebSocket (there is no WS frame size limit configured in the wss setup). This could cause a memory spike on setAllowedDiscordIds (Set construction) or enforceDiscordRestrictions (iteration).

♻️ Proposed fix
-  allowedDiscordIds: z.array(DiscordIDSchema).optional(),
-  requiredDiscordRoles: z.array(DiscordRoleRequirementSchema).optional(),
+  allowedDiscordIds: z.array(DiscordIDSchema).max(500).optional(),
+  requiredDiscordRoles: z.array(DiscordRoleRequirementSchema).max(50).optional(),
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/core/Schemas.ts` around lines 243 - 247, The arrays allowedDiscordIds and
requiredDiscordRoles in GameConfigSchema can be arbitrarily large when
partial-ed into UpdateGameConfigIntentSchema, risking memory/CPU spikes in
setAllowedDiscordIds and enforceDiscordRestrictions; update the schema by adding
a reasonable .max(n) to both DiscordIDSchema arrays (and similarly to
requiredDiscordRoles' array) so the partialed UpdateGameConfigIntentSchema
enforces an upper bound on input size and prevents huge payloads being accepted
over the WebSocket.
tests/server/DiscordAllowlist.test.ts (1)

106-154: Add a test for the lobby-creator bypass.

None of the existing tests cover the case where the creator's own Discord ID is not in the allowlist — getDiscordRejectionReason returns undefined for the creator via isLobbyCreatorPersistentId. This is a critical rule in the enforcement logic and should be explicitly tested to prevent regressions.

Do you want me to draft this test case?

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/server/DiscordAllowlist.test.ts` around lines 106 - 154, Add a test
that verifies the lobby creator is exempt from Discord allowlist checks: create
a GameServer with a private game and an allowlist that does NOT include the
creator's Discord ID, then call joinClient for a client whose persistentId
equals the creator's persistent id and assert it is allowed (e.g., joinClient
returns "joined"); also assert that getDiscordRejectionReason (or the path that
calls isLobbyCreatorPersistentId) returns undefined for the creator while still
rejecting non-creator clients with blocked Discord IDs. Ensure you reference
GameServer, joinClient, getDiscordRejectionReason and isLobbyCreatorPersistentId
in the new test to cover the bypass rule.
src/server/Worker.ts (1)

58-94: normalizeRequiredDiscordRoles is duplicated in three places — extract to a shared utility.

The same normalization logic (dedup by guildId:roleId key, merge legacy fields) exists in:

  • src/server/Worker.ts lines 58–94 (standalone function)
  • src/server/GameServer.ts lines 154–179 (inside setRequiredDiscordRoleRequirements)
  • src/client/HostLobbyModal.ts lines 914–952 (instance method)

The server-side copies are especially brittle to diverge. Extracting to a shared utility (e.g., src/core/DiscordUtil.ts) removes the duplication and gives a single place to update if requirements change.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/server/Worker.ts` around lines 58 - 94, Extract the duplicated
normalizeRequiredDiscordRoles implementation into a single exported utility
function (e.g., export function normalizeRequiredDiscordRoles(...) in a new
src/core/DiscordUtil.ts) preserving the current signature and behavior (trim,
skip empty, dedupe by `${guildId}:${roleId}`, and merge
legacyGuildId/legacyRoleId), then replace the local definitions in Worker.ts
(function normalizeRequiredDiscordRoles), GameServer.ts (inside
setRequiredDiscordRoleRequirements), and HostLobbyModal.ts (the instance method)
with imports that call the shared utility; ensure each callsite imports the
utility and that types/signatures match so behavior remains identical.
src/client/HostLobbyModal.ts (1)

954-980: Invalid Discord IDs are silently dropped — consider user feedback.

In commitDiscordIdInput, tokens that fail DISCORD_ID_REGEX.test(id) are skipped without any visible indication. If a user pastes a username instead of an ID, the input field clears and nothing is added. A small validation message (e.g., highlighting the field red or showing a tooltip) would prevent confusion.

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/client/HostLobbyModal.ts`:
- Around line 941-948: The legacy guild/role branch adds a normalized
requirement but forgets to record its key in the dedupe set; update the block
that checks normalizedLegacyGuildId and normalizedLegacyRoleId to call
uniqueRequirementKeys.add(key) (using the same key computed from
normalizedLegacyGuildId:normalizedLegacyRoleId) before pushing into
normalizedRequirements so the dedupe set (uniqueRequirementKeys) stays
consistent with normalizedRequirements and matches the behavior in Worker.ts and
GameServer.ts.

---

Duplicate comments:
In `@src/server/GameServer.ts`:
- Around line 117-141: setAllowedDiscordIds currently clears
allowedDiscordIdsSet but leaves kickedPersistentIds entries from prior Discord
kicks, so clients stay blocked; update setAllowedDiscordIds (and/or the related
setter for requiredDiscordRoles if present) to remove Discord-related entries
from kickedPersistentIds when the allowlist is removed and there are no
requiredDiscordRoles (i.e., when allowedDiscordIdsSet becomes null and
requiredDiscordRoles is empty): either clear kickedPersistentIds entirely for
Discord-originated kicks or remove only entries tracked as Discord kicks (you
can add a small sentinel/reason tracking or remove by checking entries that were
added by enforceDiscordRestrictions), and ensure getClientIdForPersistentId will
then allow rejoining once restrictions are lifted.

In `@tests/server/DiscordAllowlist.test.ts`:
- Around line 156-201: The test never advances fake timers, so the lobby info
interval callback (started by startLobbyInfoBroadcast which calls
broadcastLobbyInfo) never runs after the guest joins; update the test (after
expect(game.joinClient(guestClient)).toBe("joined")) to advance the fake timers
(e.g., run pending timers or advance by the broadcast interval) so
guestWs.sentMessages receives a lobby_info message and the allowedDiscordIds
sanitization is actually exercised.

---

Nitpick comments:
In `@src/core/Schemas.ts`:
- Around line 243-247: The arrays allowedDiscordIds and requiredDiscordRoles in
GameConfigSchema can be arbitrarily large when partial-ed into
UpdateGameConfigIntentSchema, risking memory/CPU spikes in setAllowedDiscordIds
and enforceDiscordRestrictions; update the schema by adding a reasonable .max(n)
to both DiscordIDSchema arrays (and similarly to requiredDiscordRoles' array) so
the partialed UpdateGameConfigIntentSchema enforces an upper bound on input size
and prevents huge payloads being accepted over the WebSocket.

In `@src/server/Worker.ts`:
- Around line 58-94: Extract the duplicated normalizeRequiredDiscordRoles
implementation into a single exported utility function (e.g., export function
normalizeRequiredDiscordRoles(...) in a new src/core/DiscordUtil.ts) preserving
the current signature and behavior (trim, skip empty, dedupe by
`${guildId}:${roleId}`, and merge legacyGuildId/legacyRoleId), then replace the
local definitions in Worker.ts (function normalizeRequiredDiscordRoles),
GameServer.ts (inside setRequiredDiscordRoleRequirements), and HostLobbyModal.ts
(the instance method) with imports that call the shared utility; ensure each
callsite imports the utility and that types/signatures match so behavior remains
identical.

In `@tests/server/DiscordAllowlist.test.ts`:
- Around line 106-154: Add a test that verifies the lobby creator is exempt from
Discord allowlist checks: create a GameServer with a private game and an
allowlist that does NOT include the creator's Discord ID, then call joinClient
for a client whose persistentId equals the creator's persistent id and assert it
is allowed (e.g., joinClient returns "joined"); also assert that
getDiscordRejectionReason (or the path that calls isLobbyCreatorPersistentId)
returns undefined for the creator while still rejecting non-creator clients with
blocked Discord IDs. Ensure you reference GameServer, joinClient,
getDiscordRejectionReason and isLobbyCreatorPersistentId in the new test to
cover the bypass rule.

Comment on lines +941 to +948
if (normalizedLegacyGuildId && normalizedLegacyRoleId) {
const key = `${normalizedLegacyGuildId}:${normalizedLegacyRoleId}`;
if (!uniqueRequirementKeys.has(key)) {
normalizedRequirements.push({
guildId: normalizedLegacyGuildId,
roleId: normalizedLegacyRoleId,
});
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Missing uniqueRequirementKeys.add(key) for the legacy pair.

When the legacy {guildId, roleId} pair is added to normalizedRequirements (line 944), the key is not added to uniqueRequirementKeys. The equivalent code in Worker.ts (line 85) and GameServer.ts (line 173) both call uniqueRequirementKeys.add(key) before pushing. While the missing add causes no bug today (the function returns immediately after), it is inconsistent and risks producing a duplicate if more processing is added later.

🐛 Proposed fix
     if (!uniqueRequirementKeys.has(key)) {
+      uniqueRequirementKeys.add(key);
       normalizedRequirements.push({
         guildId: normalizedLegacyGuildId,
         roleId: normalizedLegacyRoleId,
       });
     }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (normalizedLegacyGuildId && normalizedLegacyRoleId) {
const key = `${normalizedLegacyGuildId}:${normalizedLegacyRoleId}`;
if (!uniqueRequirementKeys.has(key)) {
normalizedRequirements.push({
guildId: normalizedLegacyGuildId,
roleId: normalizedLegacyRoleId,
});
}
if (normalizedLegacyGuildId && normalizedLegacyRoleId) {
const key = `${normalizedLegacyGuildId}:${normalizedLegacyRoleId}`;
if (!uniqueRequirementKeys.has(key)) {
uniqueRequirementKeys.add(key);
normalizedRequirements.push({
guildId: normalizedLegacyGuildId,
roleId: normalizedLegacyRoleId,
});
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/client/HostLobbyModal.ts` around lines 941 - 948, The legacy guild/role
branch adds a normalized requirement but forgets to record its key in the dedupe
set; update the block that checks normalizedLegacyGuildId and
normalizedLegacyRoleId to call uniqueRequirementKeys.add(key) (using the same
key computed from normalizedLegacyGuildId:normalizedLegacyRoleId) before pushing
into normalizedRequirements so the dedupe set (uniqueRequirementKeys) stays
consistent with normalizedRequirements and matches the behavior in Worker.ts and
GameServer.ts.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
tests/server/DiscordAllowlist.test.ts (2)

77-80: Add vi.useRealTimers() to afterEach to avoid timer leak.

vi.restoreAllMocks() and vi.clearAllTimers() do not restore native timer implementations. vi.useFakeTimers() wraps all timer calls until vi.useRealTimers() is called — without it, fake timers persist into other test files sharing the same worker.

🔧 Proposed fix
  afterEach(() => {
    vi.restoreAllMocks();
    vi.clearAllTimers();
+   vi.useRealTimers();
  });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/server/DiscordAllowlist.test.ts` around lines 77 - 80, The afterEach
block in DiscordAllowlist.test.ts currently calls vi.restoreAllMocks() and
vi.clearAllTimers() but doesn't restore native timers; add a call to
vi.useRealTimers() inside the same afterEach so fake timers are reverted after
each test (i.e., update the afterEach that contains vi.restoreAllMocks() and
vi.clearAllTimers() to also call vi.useRealTimers()).

245-261: Use createClient instead of duplicating Client construction inline.

This block repeats the exact same 12-argument Client construction pattern already encapsulated in the createClient helper.

♻️ Proposed refactor
-    const ws = new MockWebSocket();
-    const client = new Client(
-      "allowed-client" as any,
-      "allowed-pid",
-      null,
-      undefined,
-      undefined,
-      "127.0.0.1",
-      "allowed-client",
-      "allowed-client",
-      ws as unknown as WebSocket,
-      undefined,
-      ALLOWED_DISCORD_ID,
-      {
-        [REQUIRED_GUILD_ID]: [REQUIRED_ROLE_ID],
-      },
-    );
+    const { client } = createClient(
+      "allowed-client",
+      "allowed-pid",
+      ALLOWED_DISCORD_ID,
+      { [REQUIRED_GUILD_ID]: [REQUIRED_ROLE_ID] },
+    );
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/server/DiscordAllowlist.test.ts` around lines 245 - 261, Replace the
duplicated 12-argument Client construction with the existing createClient test
helper to avoid repetition: locate the inline new Client(...) instantiation in
DiscordAllowlist.test.ts and call createClient(...) passing the same
MockWebSocket instance (ws), the ALLOWED_DISCORD_ID, and the guild->roles
mapping ({ [REQUIRED_GUILD_ID]: [REQUIRED_ROLE_ID] }) so the created client
matches the original; ensure any additional expectations in the test use the
returned client from createClient instead of the inline new Client.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@tests/server/DiscordAllowlist.test.ts`:
- Around line 77-80: The afterEach block in DiscordAllowlist.test.ts currently
calls vi.restoreAllMocks() and vi.clearAllTimers() but doesn't restore native
timers; add a call to vi.useRealTimers() inside the same afterEach so fake
timers are reverted after each test (i.e., update the afterEach that contains
vi.restoreAllMocks() and vi.clearAllTimers() to also call vi.useRealTimers()).
- Around line 245-261: Replace the duplicated 12-argument Client construction
with the existing createClient test helper to avoid repetition: locate the
inline new Client(...) instantiation in DiscordAllowlist.test.ts and call
createClient(...) passing the same MockWebSocket instance (ws), the
ALLOWED_DISCORD_ID, and the guild->roles mapping ({ [REQUIRED_GUILD_ID]:
[REQUIRED_ROLE_ID] }) so the created client matches the original; ensure any
additional expectations in the test use the returned client from createClient
instead of the inline new Client.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Development

Development

Successfully merging this pull request may close these issues.

1 participant