Skip to content

fix: migrate federation add user tests from DDP to REST responses#40740

Open
yummyPancake2607 wants to merge 1 commit into
RocketChat:developfrom
yummyPancake2607:fix/ddp-removal-federation-tests
Open

fix: migrate federation add user tests from DDP to REST responses#40740
yummyPancake2607 wants to merge 1 commit into
RocketChat:developfrom
yummyPancake2607:fix/ddp-removal-federation-tests

Conversation

@yummyPancake2607
Copy link
Copy Markdown

@yummyPancake2607 yummyPancake2607 commented May 29, 2026

Proposed changes (including videos or screenshots)

fixes: #40709

This PR migrates federation add-user tests from the legacy DDP-style response assertions to the newer REST response envelope.

Changes made

  • Updated addUserToRoom helper in apps/meteor/tests/data/rooms.helper.ts

    • Migrated from:

      • /api/v1/method.call/addUsersToRoom
    • To:

      • /api/v1/channels.invite
      • /api/v1/groups.invite
  • Updated federation end-to-end tests to assert REST response structures instead of DDP message payloads.

Updated assertions

Changed checks like:

JSON.parse(response.body.message)

and:

expect(response.body.message).toMatch(...)

to REST-style assertions such as:

expect(response.body).toHaveProperty('error', ...)

and:

expect(response.body).toHaveProperty('success', false)

Files updated

  • apps/meteor/tests/data/rooms.helper.ts
  • ee/packages/federation-matrix/tests/end-to-end/permissions.spec.ts
  • ee/packages/federation-matrix/tests/end-to-end/dms.spec.ts

The remaining usage in room.spec.ts was intentionally left unchanged because the slash-command flow still uses DDP ephemeral messaging behavior.

Issue(s)

Related to the DDP removal migration TODOs for federation invite flows.

Steps to test or reproduce

  1. Run federation matrix end-to-end tests:

    yarn test-ee -- federation-matrix
  2. Verify:

    • Federated room invite tests pass
    • Non-federated room restrictions still behave correctly
    • REST responses return expected success and error fields
    • Slash-command ephemeral message flow still works correctly
  3. Run lint checks:

    yarn eslint ee/packages/federation-matrix/tests/end-to-end/permissions.spec.ts
    yarn eslint ee/packages/federation-matrix/tests/end-to-end/dms.spec.ts
    yarn eslint apps/meteor/tests/data/rooms.helper.ts

Further comments

This PR only migrates the direct add-user helper and related federation assertions to REST responses.

The slash-command flow (addUserToRoomSlashCommand) was intentionally not migrated because it still depends on DDP ephemeral message broadcasting behavior and does not yet have equivalent REST-based handling.

Summary by CodeRabbit

  • Tests
    • Updated test infrastructure for room management and federation scenarios to use REST-based approaches.
    • Enhanced test assertions for error handling and response validation in federation and direct message flows.
    • Improved test helpers for room user invitations with support for channel and private room types.

Review Change Stack

@dionisio-bot
Copy link
Copy Markdown
Contributor

dionisio-bot Bot commented May 29, 2026

Looks like this PR is not ready to merge, because of the following issues:

  • This PR is missing the 'stat: QA assured' label
  • This PR is missing the required milestone or project

Please fix the issues and try again

If you have any trouble, please check the PR guidelines

@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented May 29, 2026

⚠️ No Changeset found

Latest commit: 8dcdc4a

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@CLAassistant
Copy link
Copy Markdown

CLAassistant commented May 29, 2026

CLA assistant check
All committers have signed the CLA.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 29, 2026

Walkthrough

This PR migrates test helpers and E2E test assertions from DDP method calls to REST API endpoints for user room invitations. The addUserToRoom helper refactors to use REST channels.invite or groups.invite based on a room type discriminator, while federation E2E tests are updated to validate error responses against the new response.body.error field instead of parsing response.body.message.

Changes

REST API Migration for User Room Invitations

Layer / File(s) Summary
Helper refactoring to REST invite endpoints
apps/meteor/tests/data/rooms.helper.ts
addUserToRoom now requires type discriminator ('c' | 'p') to select between channels.invite and groups.invite, validates inputs, and invites each username in parallel. addUserToRoomSlashCommand switches from DDP slashCommand method to REST commands.run endpoint with REST command parameters.
DM federation test assertions
ee/packages/federation-matrix/tests/end-to-end/dms.spec.ts
Test assertions updated to check response.body.error directly (error-not-allowed, error-cant-invite-for-direct-room) instead of parsing response.body.message JSON for multiple DM permission scenarios.
Permissions federation test assertions
ee/packages/federation-matrix/tests/end-to-end/permissions.spec.ts
Test assertions updated to validate unauthorized outcomes via response.body.error (error-not-authorized-federation) instead of body.message pattern matching. Success paths simplified to rely on status/success checks only.
Room federation test assertions
ee/packages/federation-matrix/tests/end-to-end/room.spec.ts
Test for adding federated users to non-federated rooms updated to destructure addUserToRoom response and assert error via response.body.error instead of parsing nested error fields in response.body.message.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related issues

Possibly related PRs

  • RocketChat/Rocket.Chat#40704: Broader "migrate DDP callers to REST endpoints" initiative that aligns with this PR's refactoring of the same addUserToRoom/addUserToRoomSlashCommand call paths from DDP to REST.

Suggested labels

type: bug

Suggested reviewers

  • tassoevan
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately captures the main change: migrating federation add-user tests and helpers from DDP to REST responses, which is the primary objective of the PR.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ 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.

@yummyPancake2607 yummyPancake2607 marked this pull request as draft May 29, 2026 17:57
Copy link
Copy Markdown
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 (2)
ee/packages/federation-matrix/tests/end-to-end/permissions.spec.ts (1)

525-541: ⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Undefined variable causes test to fail at runtime.

The test references addUserResponse on lines 538-540, but this variable is never assigned. The createRoom call on line 528 discards its return value.

🐛 Proposed fix
 it('should NOT be able to be added to a federated room during creation', async () => {
 	const channelName = `federated-room-${Date.now()}`;
 
-	await createRoom({
+	const createResponse = await createRoom({
 		type: 'p',
 		name: channelName,
 		members: [userWithNonMatchingEmail.username],
 		extraData: {
 			federated: true,
 		},
 		config: userRequestConfig,
 	});
 
-	expect(addUserResponse.status).toBe(400);
-	expect(addUserResponse.body).toHaveProperty('success', false);
-	expect(addUserResponse.body).toHaveProperty('error', 'error-not-authorized-federation');
+	expect(createResponse.status).toBe(400);
+	expect(createResponse.body).toHaveProperty('success', false);
+	expect(createResponse.body).toHaveProperty('error', 'error-not-authorized-federation');
 });
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@ee/packages/federation-matrix/tests/end-to-end/permissions.spec.ts` around
lines 525 - 541, The test references an undefined addUserResponse because
createRoom(...)'s return value was discarded; update the test to capture the
response from createRoom by assigning its result to addUserResponse (or a newly
declared variable) and then assert on that variable; locate the createRoom call
in the test and ensure the returned response object from createRoom is awaited
and stored (e.g., const addUserResponse = await createRoom(...)) before the
expect(...) assertions that check status/body.
apps/meteor/tests/data/rooms.helper.ts (1)

119-131: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Stale doc comment: still describes the removed DDP path.

This JSDoc was not updated with the migration — it references the addUsersToRoom "DDP method call" and a "method call response," and omits the new type parameter, which now misdescribes the function.

📝 Suggested doc update
-/**
- * Adds users to a room using the addUsersToRoom method.
- *
- * Invites one or more users to join a room using the DDP method call.
- * Supports both local and federated users, with proper error handling
- * for federation restrictions.
- *
- * `@param` usernames - Array of usernames to add to the room
- * `@param` rid - The unique identifier of the room
- * `@param` userCredentials - Optional credentials for the request (deprecated, use config instead)
- * `@param` config - Optional request configuration for custom domains
- * `@returns` Promise resolving to the method call response
- */
+/**
+ * Adds users to a room using the REST channels.invite / groups.invite endpoints.
+ *
+ * Invites one or more users to join a room by issuing one REST request per
+ * username in parallel. Supports both local and federated users, with proper
+ * error handling for federation restrictions.
+ *
+ * `@param` usernames - Array of usernames to add to the room
+ * `@param` rid - The unique identifier of the room
+ * `@param` type - Room type discriminator: 'c' (channel) or 'p' (private group)
+ * `@param` userCredentials - Optional credentials for the request (deprecated, use config instead)
+ * `@param` config - Optional request configuration for custom domains
+ * `@returns` Promise resolving to an array of REST invite responses (one per username)
+ */
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/meteor/tests/data/rooms.helper.ts` around lines 119 - 131, Update the
stale JSDoc for the addUsersToRoom helper to remove references to a "DDP method
call" and "method call response", and instead describe the current
implementation and return value; explicitly document the new type parameter
(e.g., invite type) along with existing parameters userCredentials and config,
and state the actual Promise return (e.g., Promise<void> or the resolved
payload) and behavior for local vs federated users and federation restrictions;
ensure parameter names match the function signature (addUsersToRoom, type,
usernames, rid, userCredentials, config) and keep the comment concise and
accurate.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@apps/meteor/tests/data/rooms.helper.ts`:
- Around line 202-211: The REST helper in rooms.helper.ts posts to
api('commands.run') which returns API.v1.success({ result }) (no DDP `message`
envelope), so the federation test in
ee/packages/federation-matrix/tests/end-to-end/room.spec.ts must stop parsing
response.body.message; update the test to assert against response.body.result
(or switch to a DDP-based helper), specifically replace
JSON.parse(response.body.message) and checks for msg:'result' with direct
assertions on response.body.result to match the /v1/commands.run implementation.
- Around line 132-166: The addUserToRoom helper now breaks callers by requiring
type and always returning an array; make the helper backward-compatible by
making the type parameter optional (default to 'c' when missing), update the
return shape to return the single request response when usernames.length === 1
and an array only when multiple usernames are provided, and update the JSDoc for
addUserToRoom to document the optional type and return shape; also fix the
affected federation-matrix tests (e.g., permissions.spec.ts, dms.spec.ts,
room.spec.ts) that parse commands.run responses to read response.body.result
(not response.body.message) so they match the server route that returns
API.v1.success({ result }).

---

Outside diff comments:
In `@apps/meteor/tests/data/rooms.helper.ts`:
- Around line 119-131: Update the stale JSDoc for the addUsersToRoom helper to
remove references to a "DDP method call" and "method call response", and instead
describe the current implementation and return value; explicitly document the
new type parameter (e.g., invite type) along with existing parameters
userCredentials and config, and state the actual Promise return (e.g.,
Promise<void> or the resolved payload) and behavior for local vs federated users
and federation restrictions; ensure parameter names match the function signature
(addUsersToRoom, type, usernames, rid, userCredentials, config) and keep the
comment concise and accurate.

In `@ee/packages/federation-matrix/tests/end-to-end/permissions.spec.ts`:
- Around line 525-541: The test references an undefined addUserResponse because
createRoom(...)'s return value was discarded; update the test to capture the
response from createRoom by assigning its result to addUserResponse (or a newly
declared variable) and then assert on that variable; locate the createRoom call
in the test and ensure the returned response object from createRoom is awaited
and stored (e.g., const addUserResponse = await createRoom(...)) before the
expect(...) assertions that check status/body.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: e1a16805-42d7-4fe4-a397-1cbde6bb5489

📥 Commits

Reviewing files that changed from the base of the PR and between b92bcc7 and 8dcdc4a.

📒 Files selected for processing (4)
  • apps/meteor/tests/data/rooms.helper.ts
  • ee/packages/federation-matrix/tests/end-to-end/dms.spec.ts
  • ee/packages/federation-matrix/tests/end-to-end/permissions.spec.ts
  • ee/packages/federation-matrix/tests/end-to-end/room.spec.ts
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: cubic · AI code reviewer
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx,js}

📄 CodeRabbit inference engine (.cursor/rules/playwright.mdc)

**/*.{ts,tsx,js}: Write concise, technical TypeScript/JavaScript with accurate typing in Playwright tests
Avoid code comments in the implementation

Files:

  • ee/packages/federation-matrix/tests/end-to-end/room.spec.ts
  • ee/packages/federation-matrix/tests/end-to-end/dms.spec.ts
  • apps/meteor/tests/data/rooms.helper.ts
  • ee/packages/federation-matrix/tests/end-to-end/permissions.spec.ts
**/*.spec.ts

📄 CodeRabbit inference engine (.cursor/rules/playwright.mdc)

**/*.spec.ts: Use descriptive test names that clearly communicate expected behavior in Playwright tests
Use .spec.ts extension for test files (e.g., login.spec.ts)

Files:

  • ee/packages/federation-matrix/tests/end-to-end/room.spec.ts
  • ee/packages/federation-matrix/tests/end-to-end/dms.spec.ts
  • ee/packages/federation-matrix/tests/end-to-end/permissions.spec.ts
🧠 Learnings (6)
📚 Learning: 2025-12-09T20:01:00.324Z
Learnt from: sampaiodiego
Repo: RocketChat/Rocket.Chat PR: 37532
File: ee/packages/federation-matrix/src/FederationMatrix.ts:920-927
Timestamp: 2025-12-09T20:01:00.324Z
Learning: When reviewing federation invite handling in Rocket.Chat (specifically under ee/packages/federation-matrix), understand that rejecting an invite via federationSDK.rejectInvite() triggers an event-driven cleanup: a leave event is emitted and handled by handleLeave() in ee/packages/federation-matrix/src/events/member.ts, which calls Room.performUserRemoval() to remove the subscription. Do not add explicit cleanup in the reject branch of handleInvite(); rely on the existing leave-event flow for cleanup. If making changes, ensure this invariant remains and that any related paths still funnel cleanup through the leave event to avoid duplicate or missing removals.

Applied to files:

  • ee/packages/federation-matrix/tests/end-to-end/room.spec.ts
  • ee/packages/federation-matrix/tests/end-to-end/dms.spec.ts
  • ee/packages/federation-matrix/tests/end-to-end/permissions.spec.ts
📚 Learning: 2026-02-24T19:22:48.358Z
Learnt from: juliajforesti
Repo: RocketChat/Rocket.Chat PR: 38493
File: apps/meteor/tests/e2e/omnichannel/omnichannel-send-pdf-transcript.spec.ts:66-67
Timestamp: 2026-02-24T19:22:48.358Z
Learning: In Playwright end-to-end tests (e.g., under apps/meteor/tests/e2e/...), prefer locating elements by translated text (getByText) and ARIA roles (getByRole) over data-qa attributes. If translation values change, update the corresponding test locators accordingly. Never use data-qa locators. This guideline applies to all Playwright e2e test specs in the repository and helps keep tests robust to UI text changes and accessible semantics.

Applied to files:

  • ee/packages/federation-matrix/tests/end-to-end/room.spec.ts
  • ee/packages/federation-matrix/tests/end-to-end/dms.spec.ts
  • ee/packages/federation-matrix/tests/end-to-end/permissions.spec.ts
📚 Learning: 2026-02-26T19:25:44.063Z
Learnt from: gabriellsh
Repo: RocketChat/Rocket.Chat PR: 38778
File: packages/ui-voip/src/providers/useMediaSession.ts:192-192
Timestamp: 2026-02-26T19:25:44.063Z
Learning: In the Rocket.Chat repository, do not reference Biome lint rules in code review feedback. Biome is not used even if biome.json exists; only reference Biome rules if there is explicit, project-wide usage documented. For TypeScript files, review lint implications without Biome guidance unless the project enables Biome rules.

Applied to files:

  • ee/packages/federation-matrix/tests/end-to-end/room.spec.ts
  • ee/packages/federation-matrix/tests/end-to-end/dms.spec.ts
  • apps/meteor/tests/data/rooms.helper.ts
  • ee/packages/federation-matrix/tests/end-to-end/permissions.spec.ts
📚 Learning: 2026-02-26T19:25:44.063Z
Learnt from: gabriellsh
Repo: RocketChat/Rocket.Chat PR: 38778
File: packages/ui-voip/src/providers/useMediaSession.ts:192-192
Timestamp: 2026-02-26T19:25:44.063Z
Learning: In this repository (RocketChat/Rocket.Chat), Biome lint rules are not used even if a biome.json exists. When reviewing TypeScript files (e.g., packages/ui-voip/src/providers/useMediaSession.ts), ensure lint suggestions do not reference Biome-specific rules. Rely on general ESLint/TypeScript lint rules and project conventions instead.

Applied to files:

  • ee/packages/federation-matrix/tests/end-to-end/room.spec.ts
  • ee/packages/federation-matrix/tests/end-to-end/dms.spec.ts
  • apps/meteor/tests/data/rooms.helper.ts
  • ee/packages/federation-matrix/tests/end-to-end/permissions.spec.ts
📚 Learning: 2026-03-06T18:10:15.268Z
Learnt from: tassoevan
Repo: RocketChat/Rocket.Chat PR: 39397
File: packages/gazzodown/src/code/CodeBlock.spec.tsx:47-68
Timestamp: 2026-03-06T18:10:15.268Z
Learning: In tests (especially those using testing-library/dom/jsdom) for Rocket.Chat components, the HTML <code> element has an implicit ARIA role of 'code'. Therefore, screen.getByRole('code') or screen.findByRole('code') will locate <code> elements even without a role attribute. Do not flag findByRole('code') as invalid in reviews; prefer using the implicit role instead of adding role="code" unless necessary for accessibility.

Applied to files:

  • ee/packages/federation-matrix/tests/end-to-end/room.spec.ts
  • ee/packages/federation-matrix/tests/end-to-end/dms.spec.ts
  • ee/packages/federation-matrix/tests/end-to-end/permissions.spec.ts
📚 Learning: 2026-05-06T12:21:44.083Z
Learnt from: juliajforesti
Repo: RocketChat/Rocket.Chat PR: 40256
File: apps/meteor/client/components/CreateDiscussion/CreateDiscussion.tsx:121-149
Timestamp: 2026-05-06T12:21:44.083Z
Learning: Field wrappers in rocket.chat/fuselage-forms (Field, FieldLabel, FieldRow, FieldError, FieldHint) auto-create htmlFor/id associations, aria-describedby, and role="alert" for errors. Do not manually set htmlFor, id, aria-describedby, or role attributes when using these wrappers. This automatic wiring does not apply to plain rocket.chat/fuselage components, which require explicit ID wiring per the accessibility docs. In code reviews, prefer using fuselage-forms wrappers for form fields and verify there is no unnecessary manual ID/aria wiring in files that use these wrappers. If a component uses plain fuselage components, ensure proper id wiring as per docs.

Applied to files:

  • ee/packages/federation-matrix/tests/end-to-end/room.spec.ts
  • ee/packages/federation-matrix/tests/end-to-end/dms.spec.ts
  • apps/meteor/tests/data/rooms.helper.ts
  • ee/packages/federation-matrix/tests/end-to-end/permissions.spec.ts
🔇 Additional comments (9)
ee/packages/federation-matrix/tests/end-to-end/dms.spec.ts (3)

690-699: LGTM!


1176-1185: LGTM!


1743-1751: LGTM!

ee/packages/federation-matrix/tests/end-to-end/permissions.spec.ts (4)

182-192: LGTM!


500-523: LGTM!


583-606: LGTM!


647-670: LGTM!

ee/packages/federation-matrix/tests/end-to-end/room.spec.ts (1)

229-256: LGTM!

apps/meteor/tests/data/rooms.helper.ts (1)

156-165: /v1/channels.invite and /v1/groups.invite accept username in the request body

  • ChannelsInviteProps allows username?: string with { roomId: string }.
  • GroupsInviteProps (via WithUserId in packages/rest-typings/src/v1/groups/BaseProps.ts) includes variants where username: string is accepted (as an alternative to userId/user) with additionalProperties: false.

Comment on lines 132 to 166
export const addUserToRoom = ({
usernames,
rid,
type,
userCredentials,
config,
}: {
usernames: string[];
rid: IRoom['_id'];
type: 'c' | 'p';
userCredentials?: Credentials;
config?: IRequestConfig;
}) => {
if (!usernames || usernames.length === 0) {
throw new Error('"usernames" is required in "addUserToRoom" test helper');
}

if (!rid) {
throw new Error('"rid" is required in "addUserToRoom" test helper');
}

const requestInstance = config?.request || request;
const credentialsInstance = config?.credentials || userCredentials || credentials;

return requestInstance
.post(methodCall('addUsersToRoom'))
.set(credentialsInstance)
.send({
message: JSON.stringify({
method: 'addUsersToRoom',
params: [{ rid, users: usernames }],
id: 'id',
msg: 'method',
const endpoint = type === 'c' ? 'channels.invite' : 'groups.invite';

return Promise.all(
usernames.map((username) =>
requestInstance.post(api(endpoint)).set(credentialsInstance).send({
roomId: rid,
username,
}),
});
),
);
};
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Find all call sites of addUserToRoom to confirm they pass the new required `type`
# and handle the new array (Promise.all) return shape.
rg -nP --type=ts -C4 '\baddUserToRoom\s*\(' -g '!**/rooms.helper.ts'

Repository: RocketChat/Rocket.Chat

Length of output: 35419


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# 1) Inspect rooms.helper.ts around the referenced regions
sed -n '90,240p' apps/meteor/tests/data/rooms.helper.ts

Repository: RocketChat/Rocket.Chat

Length of output: 4425


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# 2) Find how addUserToRoom is imported/used in federation-matrix tests
for f in \
  ee/packages/federation-matrix/tests/end-to-end/permissions.spec.ts \
  ee/packages/federation-matrix/tests/end-to-end/dms.spec.ts \
  ee/packages/federation-matrix/tests/end-to-end/room.spec.ts \
  apps/meteor/tests/end-to-end/api/chat.ts
do
  echo "===== $f (imports) ====="
  rg -n "addUserToRoom" "$f" | head -n 20
  echo
  echo "===== $f (around first call) ====="
  # print 25 lines around first occurrence
  line=$(rg -n "addUserToRoom" "$f" | head -n 1 | cut -d: -f1)
  start=$((line-15)); end=$((line+25))
  sed -n "${start},${end}p" "$f"
  echo
done

Repository: RocketChat/Rocket.Chat

Length of output: 2671


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# 3) Inspect slash-command usage around the helper usage in room.spec.ts
file="ee/packages/federation-matrix/tests/end-to-end/room.spec.ts"
rg -n "addUserToRoomSlashCommand|commands\\.run|invite" "$file" | head -n 80

# show around occurrences of addUserToRoomSlashCommand
for line in $(rg -n "addUserToRoomSlashCommand" "$file" | cut -d: -f1); do
  start=$((line-20)); end=$((line+30))
  echo "===== $file around addUserToRoomSlashCommand at $line ====="
  sed -n "${start},${end}p" "$file"
done

Repository: RocketChat/Rocket.Chat

Length of output: 7233


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Search for route handlers for channels.invite / groups.invite and see expected body fields
rg -n "channels\\.invite|groups\\.invite" apps/meteor/app apps/meteor/server apps/meteor/lib apps/meteor -S --hidden --glob '!**/node_modules/**' | head -n 50

Repository: RocketChat/Rocket.Chat

Length of output: 4878


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Narrow to API route definitions (common pattern: 'channels.invite' string)
rg -n "\"channels\\.invite\"|\"groups\\.invite\"" apps/meteor --glob '*.{ts,js}' | head -n 50

Repository: RocketChat/Rocket.Chat

Length of output: 48


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Find specific files likely handling invites and then inspect for username usage
files=$(rg -l "\"channels\\.invite\"|\"groups\\.invite\"" apps/meteor --glob '*.{ts,js}')
echo "$files" | head -n 20

Repository: RocketChat/Rocket.Chat

Length of output: 48


🏁 Script executed:

#!/bin/bash
set -euo pipefail

sed -n '90,240p' apps/meteor/tests/data/rooms.helper.ts

Repository: RocketChat/Rocket.Chat

Length of output: 4425


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# 1) Inspect rooms.helper.ts around the referenced regions
sed -n '90,240p' apps/meteor/tests/data/rooms.helper.ts

Repository: RocketChat/Rocket.Chat

Length of output: 4425


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# 2) Inspect how addUserToRoom is imported/used in federation-matrix tests and a couple other callers
for f in \
  ee/packages/federation-matrix/tests/end-to-end/permissions.spec.ts \
  ee/packages/federation-matrix/tests/end-to-end/dms.spec.ts \
  ee/packages/federation-matrix/tests/end-to-end/room.spec.ts \
  apps/meteor/tests/end-to-end/api/chat.ts
do
  echo "===== $f (addUserToRoom occurrences) ====="
  rg -n "addUserToRoom" "$f" | head -n 30
  echo
  echo "===== $f (around first occurrence) ====="
  line=$(rg -n "addUserToRoom" "$f" | head -n 1 | cut -d: -f1)
  start=$((line-15)); end=$((line+35))
  sed -n "${start},${end}p" "$f"
  echo
done

Repository: RocketChat/Rocket.Chat

Length of output: 2695


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# 3) Inspect slash-command usage in room.spec.ts
file="ee/packages/federation-matrix/tests/end-to-end/room.spec.ts"
rg -n "addUserToRoomSlashCommand|commands\\.run\\(|response\\.body\\.message|msg:'result'|room\\.spec\\.ts" "$file" | head -n 120

for line in $(rg -n "addUserToRoomSlashCommand" "$file" | cut -d: -f1 | head -n 10); do
  start=$((line-25)); end=$((line+35))
  echo "===== $file around addUserToRoomSlashCommand at $line ====="
  sed -n "${start},${end}p" "$file"
done

Repository: RocketChat/Rocket.Chat

Length of output: 2380


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# 4) Locate API handlers for channels.invite / groups.invite
rg -n "\"channels\\.invite\"|\"groups\\.invite\"" apps/meteor --glob '*.{ts,js}' | head -n 50

Repository: RocketChat/Rocket.Chat

Length of output: 48


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# 5) List candidate files implementing those handlers and inspect for 'username' usage
files=$(rg -l "\"channels\\.invite\"|\"groups\\.invite\"" apps/meteor --glob '*.{ts,js}' | head -n 20)
echo "$files"

Repository: RocketChat/Rocket.Chat

Length of output: 48


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# 5b) If we found candidate files, print a small window around each match for 'username'
files=$(rg -l "\"channels\\.invite\"|\"groups\\.invite\"" apps/meteor --glob '*.{ts,js}' | head -n 20)
for f in $files; do
  echo "===== $f matches (channels.invite/groups.invite) ====="
  rg -n "\"channels\\.invite\"|\"groups\\.invite\"" "$f" | head -n 5
  echo "===== $f around match ====="
  line=$(rg -n "\"channels\\.invite\"|\"groups\\.invite\"" "$f" | head -n 1 | cut -d: -f1)
  start=$((line-20)); end=$((line+60))
  sed -n "${start},${end}p" "$f"
  echo
done

Repository: RocketChat/Rocket.Chat

Length of output: 48


🏁 Script executed:

#!/bin/bash
set -euo pipefail

file="ee/packages/federation-matrix/tests/end-to-end/permissions.spec.ts"

# Extract around the first few addUserToRoom calls (use line numbers from previous rg output)
# We'll print a wider window around each known line: 183,195,266,419,514,597,661,870
for line in 183 195 266 419 514 597 661 870; do
  start=$((line-15)); end=$((line+25))
  echo "===== $file around line $line ====="
  awk -v s=$start -v e=$end 'NR>=s && NR<=e {printf "%5d:%s\n", NR, $0}' "$file"
  echo
done

Repository: RocketChat/Rocket.Chat

Length of output: 12331


🏁 Script executed:

#!/bin/bash
set -euo pipefail

file="ee/packages/federation-matrix/tests/end-to-end/dms.spec.ts"
for line in 691 1177 1743 1789; do
  start=$((line-15)); end=$((line+25))
  echo "===== $file around line $line ====="
  awk -v s=$start -v e=$end 'NR>=s && NR<=e {printf "%5d:%s\n", NR, $0}' "$file"
  echo
done

Repository: RocketChat/Rocket.Chat

Length of output: 7109


🏁 Script executed:

#!/bin/bash
set -euo pipefail

file="ee/packages/federation-matrix/tests/end-to-end/room.spec.ts"

# Extract around the /invite slash-command parsing lines we saw: 259-293 and 278
for line in 230 250 259 278 1226 1259 1362; do
  start=$((line-15)); end=$((line+35))
  echo "===== $file around line $line ====="
  awk -v s=$start -v e=$end 'NR>=s && NR<=e {printf "%5d:%s\n", NR, $0}' "$file"
  echo
done

Repository: RocketChat/Rocket.Chat

Length of output: 17861


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Find server handlers /v1/channels.invite and /v1/groups.invite
# First list candidate files containing those exact route strings.
rg -n --glob '*.{ts,js}' '\/v1\/(channels\.invite|groups\.invite)' apps/meteor --hidden | head -n 50

Repository: RocketChat/Rocket.Chat

Length of output: 172


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# If previous search isn't enough, search for "channels.invite" / "groups.invite" route constants in REST API code
rg -n --glob '*.{ts,js}' '"channels\.invite"|"groups\.invite"' apps/meteor/app apps/meteor/server apps/meteor/lib --hidden | head -n 80

Repository: RocketChat/Rocket.Chat

Length of output: 48


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect commands.run endpoint implementation to understand response shape
rg -n --glob '*.{ts,js}' "commands\\.run" apps/meteor | head -n 50

Repository: RocketChat/Rocket.Chat

Length of output: 2935


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Print code around commands.run handler if found
# Try common patterns: /api/v1/commands.run or 'commands/run' etc.
rg -n --glob '*.{ts,js}' "(api/v1/commands\\.run|commands\\.run.*handler|commands\\.run)" apps/meteor --hidden | head -n 20

Repository: RocketChat/Rocket.Chat

Length of output: 1816


🏁 Script executed:

#!/bin/bash
set -euo pipefail

file="ee/packages/federation-matrix/tests/end-to-end/permissions.spec.ts"

# Extract around the first few addUserToRoom calls (line numbers from previous rg output)
for line in 183 195 266 419 514 597 661 870; do
  start=$((line-15)); end=$((line+25))
  echo "===== $file around line $line ====="
  awk -v s=$start -v e=$end 'NR>=s && NR<=e {printf "%5d:%s\n", NR, $0}' "$file"
  echo
done

Repository: RocketChat/Rocket.Chat

Length of output: 12331


🏁 Script executed:

#!/bin/bash
set -euo pipefail

file="ee/packages/federation-matrix/tests/end-to-end/dms.spec.ts"
for line in 691 1177 1743 1789; do
  start=$((line-15)); end=$((line+25))
  echo "===== $file around line $line ====="
  awk -v s=$start -v e=$end 'NR>=s && NR<=e {printf "%5d:%s\n", NR, $0}' "$file"
  echo
done

Repository: RocketChat/Rocket.Chat

Length of output: 7109


🏁 Script executed:

#!/bin/bash
set -euo pipefail

file="ee/packages/federation-matrix/tests/end-to-end/room.spec.ts"

for line in 230 250 259 278 1226 1259 1362; do
  start=$((line-15)); end=$((line+35))
  echo "===== $file around line $line ====="
  awk -v s=$start -v e=$end 'NR>=s && NR<=e {printf "%5d:%s\n", NR, $0}' "$file"
  echo
done

Repository: RocketChat/Rocket.Chat

Length of output: 17861


🏁 Script executed:

#!/bin/bash
set -euo pipefail

rg -n --glob '*.{ts,js}' '\/v1\/(channels\.invite|groups\.invite)' apps/meteor --hidden | head -n 50

Repository: RocketChat/Rocket.Chat

Length of output: 172


🏁 Script executed:

#!/bin/bash
set -euo pipefail

rg -n --glob '*.{ts,js}' '"channels\.invite"|"groups\.invite"' apps/meteor/app apps/meteor/server apps/meteor/lib --hidden | head -n 80

Repository: RocketChat/Rocket.Chat

Length of output: 48


🏁 Script executed:

#!/bin/bash
set -euo pipefail

rg -n --glob '*.{ts,js}' "commands\.run" apps/meteor | head -n 50

Repository: RocketChat/Rocket.Chat

Length of output: 2935


🏁 Script executed:

#!/bin/bash
set -euo pipefail

file="ee/packages/federation-matrix/tests/end-to-end/permissions.spec.ts"

# Extract around the first few addUserToRoom calls (line numbers from previous rg output)
for line in 183 195 266 419 514 597 661 870; do
  start=$((line-15)); end=$((line+25))
  echo "===== $file around line $line ====="
  awk -v s=$start -v e=$end 'NR>=s && NR<=e {printf "%5d:%s\n", NR, $0}' "$file"
  echo
done

Repository: RocketChat/Rocket.Chat

Length of output: 12331


🏁 Script executed:

#!/bin/bash
set -euo pipefail

file="ee/packages/federation-matrix/tests/end-to-end/dms.spec.ts"
for line in 691 1177 1743 1789; do
  start=$((line-15)); end=$((line+25))
  echo "===== $file around line $line ====="
  awk -v s=$start -v e=$end 'NR>=s && NR<=e {printf "%5d:%s\n", NR, $0}' "$file"
  echo
done

Repository: RocketChat/Rocket.Chat

Length of output: 7109


🏁 Script executed:

#!/bin/bash
set -euo pipefail

file="ee/packages/federation-matrix/tests/end-to-end/room.spec.ts"

for line in 230 250 259 278 1226 1259 1362; do
  start=$((line-15)); end=$((line+35))
  echo "===== $file around line $line ====="
  awk -v s=$start -v e=$end 'NR>=s && NR<=e {printf "%5d:%s\n", NR, $0}' "$file"
  echo
done

Repository: RocketChat/Rocket.Chat

Length of output: 17861


🏁 Script executed:

#!/bin/bash
set -euo pipefail

rg -n --glob '*.{ts,js}' '\/v1\/(channels\.invite|groups\.invite)' apps/meteor --hidden | head -n 50

Repository: RocketChat/Rocket.Chat

Length of output: 172


🏁 Script executed:

#!/bin/bash
set -euo pipefail

rg -n --glob '*.{ts,js}' '"channels\.invite"|"groups\.invite"' apps/meteor/app apps/meteor/server apps/meteor/lib --hidden | head -n 80

Repository: RocketChat/Rocket.Chat

Length of output: 48


🏁 Script executed:

#!/bin/bash
set -euo pipefail

rg -n --glob '*.{ts,js}' "commands\.run" apps/meteor | head -n 50

Repository: RocketChat/Rocket.Chat

Length of output: 2935


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# 1) Check whether federation-matrix specs destructure addUserToRoom return value
for f in \
  ee/packages/federation-matrix/tests/end-to-end/permissions.spec.ts \
  ee/packages/federation-matrix/tests/end-to-end/dms.spec.ts \
  ee/packages/federation-matrix/tests/end-to-end/room.spec.ts
do
  echo "===== $f destructuring addUserToRoom ====="
  rg -n "const \\[.*\\].*= *await addUserToRoom" "$f" || true
  rg -n "await addUserToRoom\\(" "$f" || true
  echo
done

Repository: RocketChat/Rocket.Chat

Length of output: 1273


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# 2) Find API route handlers for /v1/channels.invite and /v1/groups.invite
# First, list occurrences of those route strings in the server API code
rg -n "\"channels\\.invite\"|\"groups\\.invite\"" apps/meteor/app apps/meteor/server apps/meteor/lib --glob '*.{ts,js}' --hidden | head -n 50

Repository: RocketChat/Rocket.Chat

Length of output: 48


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# 3) Also search for 'channels.invite' / 'groups.invite' without quotes
rg -n "channels\\.invite|groups\\.invite" apps/meteor/app apps/meteor/server apps/meteor/lib --glob '*.{ts,js}' --hidden | head -n 50

Repository: RocketChat/Rocket.Chat

Length of output: 519


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# 4) Inspect the invite controller/service if we can locate it
# Try to locate addUserToRoom / invite usage patterns
# We look for "req.body.*username" and "req.body.*userId" near invite endpoints
rg -n "invite.*(username|userId)" apps/meteor/app apps/meteor/server apps/meteor/lib --glob '*.{ts,js}' --hidden | head -n 80

Repository: RocketChat/Rocket.Chat

Length of output: 2800


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# 5) Specifically inspect likely handler files for invite: channels/groups api endpoints.
# We'll search for function names or modules: "channels.invite" may be defined in routes.
candidates=$(rg -l "\"channels\\.invite\"|\"groups\\.invite\"" apps/meteor/app apps/meteor/server apps/meteor/lib --glob '*.{ts,js}' --hidden | tr '\n' ' ')
echo "Candidates: $candidates" | head -n 1

Repository: RocketChat/Rocket.Chat

Length of output: 48


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# 6) If we have candidates, print the relevant sections around first match in each
# (Only for up to 3 files to keep output small)
files=$(rg -l "\"channels\\.invite\"|\"groups\\.invite\"" apps/meteor/app apps/meteor/server apps/meteor/lib --glob '*.{ts,js}' --hidden | head -n 3)
for f in $files; do
  echo "===== $f matches ====="
  line=$(rg -n "\"channels\\.invite\"|\"groups\\.invite\"" "$f" | head -n 1 | cut -d: -f1)
  start=$((line-40)); end=$((line+80))
  echo "around line $line:"
  awk -v s=$start -v e=$end 'NR>=s && NR<=e {printf "%5d:%s\n", NR, $0}' "$f"
  echo
done

Repository: RocketChat/Rocket.Chat

Length of output: 48


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# 1) Check whether federation-matrix specs destructure addUserToRoom return value
for f in \
  ee/packages/federation-matrix/tests/end-to-end/permissions.spec.ts \
  ee/packages/federation-matrix/tests/end-to-end/dms.spec.ts \
  ee/packages/federation-matrix/tests/end-to-end/room.spec.ts
do
  echo "===== $f destructuring addUserToRoom ====="
  rg -n "const \\[.*\\].*= *await addUserToRoom" "$f" || true
  rg -n "await addUserToRoom\\(" "$f" || true
  echo
done

Repository: RocketChat/Rocket.Chat

Length of output: 1273


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# 2) Find API route handlers for /v1/channels.invite and /v1/groups.invite
rg -n "\"channels\\.invite\"|\"groups\\.invite\"" apps/meteor/app apps/meteor/server apps/meteor/lib --glob '*.{ts,js}' --hidden | head -n 50

Repository: RocketChat/Rocket.Chat

Length of output: 48


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# 3) Also search for 'channels.invite' / 'groups.invite' without quotes
rg -n "channels\\.invite|groups\\.invite" apps/meteor/app apps/meteor/server apps/meteor/lib --glob '*.{ts,js}' --hidden | head -n 50

Repository: RocketChat/Rocket.Chat

Length of output: 519


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# 4) Search for invite handlers reading either username or userId
rg -n "invite.*(username|userId)" apps/meteor/app apps/meteor/server apps/meteor/lib --glob '*.{ts,js}' --hidden | head -n 80

Repository: RocketChat/Rocket.Chat

Length of output: 2800


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# 5) Print the sections around the first match for up to 3 located files
files=$(rg -l "\"channels\\.invite\"|\"groups\\.invite\"" apps/meteor/app apps/meteor/server apps/meteor/lib --glob '*.{ts,js}' --hidden | head -n 3)
for f in $files; do
  echo "===== $f matches ====="
  line=$(rg -n "\"channels\\.invite\"|\"groups\\.invite\"" "$f" | head -n 1 | cut -d: -f1)
  start=$((line-40)); end=$((line+120))
  awk -v s=$start -v e=$end 'NR>=s && NR<=e {printf "%5d:%s\n", NR, $0}' "$f"
  echo
done

Repository: RocketChat/Rocket.Chat

Length of output: 48


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect invite handlers in channels/groups API files around the discovered route registration lines
echo "=== apps/meteor/app/api/server/v1/channels.ts around 'channels.invite' ==="
file="apps/meteor/app/api/server/v1/channels.ts"
line=$(rg -n "'channels\\.invite'" "$file" | head -n 1 | cut -d: -f1)
start=$((line-80)); end=$((line+120))
awk -v s=$start -v e=$end 'NR>=s && NR<=e {printf "%5d:%s\n", NR, $0}' "$file"

echo
echo "=== apps/meteor/app/api/server/v1/groups.ts around 'groups.invite' ==="
file="apps/meteor/app/api/server/v1/groups.ts"
line=$(rg -n "'groups\\.invite'" "$file" | head -n 1 | cut -d: -f1)
start=$((line-80)); end=$((line+120))
awk -v s=$start -v e=$end 'NR>=s && NR<=e {printf "%5d:%s\n", NR, $0}' "$file"

Repository: RocketChat/Rocket.Chat

Length of output: 12578


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the channels/groups API route implementation to find request body parsing
# Specifically search within those files for 'req.body', 'username', 'userId', 'roomId'
for file in apps/meteor/app/api/server/v1/channels.ts apps/meteor/app/api/server/v1/groups.ts; do
  echo "=== body parsing in $file ==="
  rg -n "channels\\.invite|groups\\.invite|req\\.body|username|userId|roomId" "$file" | head -n 120
  echo
done

Repository: RocketChat/Rocket.Chat

Length of output: 16040


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect /v1/commands.run implementation to see what it returns in body.message
file="apps/meteor/app/api/server/v1/commands.ts"
line=$(rg -n "commands\\.run" "$file" | head -n 1 | cut -d: -f1)
start=$((line-120)); end=$((line+220))
awk -v s=$start -v e=$end 'NR>=s && NR<=e {printf "%5d:%s\n", NR, $0}' "$file"

Repository: RocketChat/Rocket.Chat

Length of output: 8689


🏁 Script executed:

#!/bin/bash
set -euo pipefail

rg -n "v1\\.success\\(|success\\(\\{?\\s*.*\\)\\s*\\{|class API|API\\.v1" apps/meteor/app/api/server --hidden --glob '*.{ts,js}' | head -n 50

Repository: RocketChat/Rocket.Chat

Length of output: 4357


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Try to locate the API helper source directly
rg -n "static success|success\\(data\\)|success\\(payload\\)" apps/meteor/app --hidden --glob '*.{ts,js}' | head -n 50

Repository: RocketChat/Rocket.Chat

Length of output: 234


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Narrow to common API implementation file names
for f in $(fd -t f "api.*\\.ts" apps/meteor/app -a 2>/dev/null | head -n 20); do
  echo "FILE $f"
  rg -n "success\\(" "$f" | head -n 5 || true
done

Repository: RocketChat/Rocket.Chat

Length of output: 1281


🏁 Script executed:

#!/bin/bash
set -euo pipefail

file="apps/meteor/app/api/server/ApiClass.ts"
# show around the success() implementation
rg -n "success\\(" "$file" | head -n 20

Repository: RocketChat/Rocket.Chat

Length of output: 143


🏁 Script executed:

#!/bin/bash
set -euo pipefail

file="apps/meteor/app/api/server/ApiClass.ts"

# Print a focused window around ApiClass.success implementation by locating the first occurrence of "public success"
line=$(rg -n "public success" "$file" | head -n 1 | cut -d: -f1)
start=$((line-80)); end=$((line+120))
awk -v s=$start -v e=$end 'NR>=s && NR<=e {printf "%5d:%s\n", NR, $0}' "$file"

Repository: RocketChat/Rocket.Chat

Length of output: 6206


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Find all imports of addUserToRoom from rooms.helper.ts
rg -n "from ['\"].*apps/meteor/tests/data/rooms\.helper['\"]|from ['\"].*rooms\.helper['\"]" . --glob '*.{ts,tsx,js,jsx}' --hidden \
  | rg -n "rooms\.helper|addUserToRoom" \
  | head -n 200

Repository: RocketChat/Rocket.Chat

Length of output: 4317


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Find all call sites of the *imported helper* by matching the property access pattern.
# This is less precise than import analysis, but we'll constrain by looking for:
#   addUserToRoom({ ... usernames: [...] ... })
# and then inspect nearby code for `type:` or destructuring.
rg -n "addUserToRoom\\s*\\(\\s*\\{" ee apps/meteor/tests --glob '*.{ts,tsx}' --hidden \
  | head -n 200

Repository: RocketChat/Rocket.Chat

Length of output: 2127


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# For the call sites, check within a small context window for `type:` presence.
# Report line numbers where addUserToRoom is called but no `type:` appears near it.
# (Heuristic; mainly to catch missing type in TS callers.)
rg -n "addUserToRoom\\s*\\(\\s*\\{" ee packages apps/meteor/tests --glob '*.{ts,tsx}' --hidden > /tmp/addUserToRoom_calls.txt

while IFS=: read -r file line rest; do
  start=$((line-5)); end=$((line+15))
  window=$(awk -v s=$start -v e=$end 'NR>=s && NR<=e {print}' "$file")
  if ! echo "$window" | rg -q "\btype\s*:"; then
    echo "$file:$line"
  fi
done < /tmp/addUserToRoom_calls.txt | head -n 100

Repository: RocketChat/Rocket.Chat

Length of output: 986


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Show addUserToRoom usage in the two non-federation-matrix call sites
awk 'NR>=2335 && NR<=2375 {print NR ":" $0}' apps/meteor/tests/end-to-end/api/chat.ts
echo "----"
awk 'NR>=300 && NR<=360 {print NR ":" $0}' apps/meteor/tests/data/uploads.helper.ts

Repository: RocketChat/Rocket.Chat

Length of output: 3932


Fix breaking helper contract inconsistencies for addUserToRoom and /invite REST responses

  • apps/meteor/tests/data/rooms.helper.ts (addUserToRoom) now requires type: 'c' | 'p' and returns Promise.all(...) (array), but federation-matrix callers like ee/packages/federation-matrix/tests/end-to-end/permissions.spec.ts and .../dms.spec.ts call it without type and treat the return value as a single response (addUserResponse.status / response.body...) instead of indexing/destructuring [response].
  • ee/packages/federation-matrix/tests/end-to-end/room.spec.ts parses addUserToRoomSlashCommand as if commands.run returns response.body.message, but the server route commands.run returns API.v1.success({ result }) (i.e., response.body.result), so the test parsing needs to align with that shape.
  • The addUserToRoom JSDoc is stale (mentions the old DDP/addUsersToRoom behavior and omits the new required type parameter).
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/meteor/tests/data/rooms.helper.ts` around lines 132 - 166, The
addUserToRoom helper now breaks callers by requiring type and always returning
an array; make the helper backward-compatible by making the type parameter
optional (default to 'c' when missing), update the return shape to return the
single request response when usernames.length === 1 and an array only when
multiple usernames are provided, and update the JSDoc for addUserToRoom to
document the optional type and return shape; also fix the affected
federation-matrix tests (e.g., permissions.spec.ts, dms.spec.ts, room.spec.ts)
that parse commands.run responses to read response.body.result (not
response.body.message) so they match the server route that returns
API.v1.success({ result }).

Comment on lines 202 to 211
return requestInstance
.post(methodCall('slashCommand'))
.post(api('commands.run'))
.set(credentialsInstance)
.send({
message: JSON.stringify({
method: 'slashCommand',
params: [
{
cmd: 'invite',
params: usernames.join(' '),
msg: {
rid,
_id: `test-${Date.now()}`,
},
triggerId: `test-trigger-${Date.now()}`,
},
],
id: 'id',
msg: 'method',
}),
command: 'invite',
params: usernames.join(' '),
roomId: rid,
triggerId: `test-trigger-${Date.now()}`,
});
};
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Confirm room.spec.ts still expects a DDP `message` envelope from addUserToRoomSlashCommand...
fd 'room.spec.ts' | xargs rg -nP -C6 'addUserToRoomSlashCommand|response\.body\.message'
# ...and inspect the commands.run REST endpoint response shape.
rg -nP -C12 "'/v1/commands.run'|addEndpoint\(\s*'commands.run'"

Repository: RocketChat/Rocket.Chat

Length of output: 13489


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# 1) Inspect the helper implementation around the referenced lines
echo "---- apps/meteor/tests/data/rooms.helper.ts (lines ~180-240) ----"
sed -n '180,240p' apps/meteor/tests/data/rooms.helper.ts

# 2) Find the server implementation / route wiring for /v1/commands.run
echo
echo "---- Search for commands.run route/handler implementation ----"
rg -n --hidden --no-ignore \
  "(addEndpoint\\(.*'commands\\.run'|/v1/commands\\.run|commands\\.run')" \
  apps packages/rest-typings packages/rest-implementation packages/meteor 2>/dev/null || true

# 3) Narrow to server code under apps/meteor if present
echo
echo "---- Search specifically under apps/meteor for commands.run ----"
rg -n "commands\\.run" apps/meteor 2>/dev/null || true

# 4) Inspect the federation-matrix test assertions context (room.spec.ts)
echo
echo "---- ee/packages/federation-matrix/tests/end-to-end/room.spec.ts (lines ~240-310) ----"
sed -n '240,310p' ee/packages/federation-matrix/tests/end-to-end/room.spec.ts

Repository: RocketChat/Rocket.Chat

Length of output: 10464


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# 1) Inspect /v1/commands.run handler implementation in apps/meteor
echo "---- apps/meteor/app/api/server/v1/commands.ts around commands.run registration ----"
sed -n '330,460p' apps/meteor/app/api/server/v1/commands.ts

# 2) Locate the exact handler function for commands.run (named/anonymous) and print its return shape
echo
echo "---- Search within commands.ts for 'commands.run' handler function / implementation ----"
rg -n "commands\\.run|/v1/commands\\.run" apps/meteor/app/api/server/v1/commands.ts

# 3) Inspect the server return object fields (especially 'message') near the handler
# (Use a wider window around the matches)
echo
echo "---- Print larger window around the commands.run handler ----"
rg -n "commands\\.run" -n apps/meteor/app/api/server/v1/commands.ts | head -n 20 | awk -F: '{print $1}' | head -n 1 >/tmp/match_line.txt
LINE=$(cat /tmp/match_line.txt)
START=$((LINE-120))
END=$((LINE+200))
sed -n "${START},${END}p" apps/meteor/app/api/server/v1/commands.ts

# 4) Inspect rest-typings return type for /v1/commands.run to confirm whether 'message' exists
echo
echo "---- packages/rest-typings/src/v1/commands.ts ----"
sed -n '1,120p' packages/rest-typings/src/v1/commands.ts

Repository: RocketChat/Rocket.Chat

Length of output: 11411


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect existing /commands.run endpoint tests for expectations about response fields (message vs result)
echo "---- apps/meteor/tests/end-to-end/api/commands.ts (commands.run assertions) ----"
rg -n "commands\\.run" apps/meteor/tests/end-to-end/api/commands.ts
echo
# Show a representative window near the first commands.run test block
MATCH_LINE=$(rg -n "describe\\('\\[/commands\\.run\\]" apps/meteor/tests/end-to-end/api/commands.ts | head -n 1 | cut -d: -f1)
START=$((MATCH_LINE-40))
END=$((MATCH_LINE+240))
sed -n "${START},${END}p" apps/meteor/tests/end-to-end/api/commands.ts

# Search within that test file for ".body.message" usage
echo
echo "---- Search for response.body.message in apps/meteor/tests/end-to-end/api/commands.ts ----"
rg -n "body\\.message" apps/meteor/tests/end-to-end/api/commands.ts || true

# Inspect API.v1.success implementation to see exact success payload fields
echo
echo "---- Search for API.v1.success definition ----"
rg -n "v1\\.success\\(" apps/meteor packages/rest-implementation apps/packages 2>/dev/null || true
rg -n "API\\.v1\\.success" . | head -n 20

Repository: RocketChat/Rocket.Chat

Length of output: 50378


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# 1) Locate the implementation of API.v1.success (formatting/wrapping of response bodies)
echo "---- Locate API.v1.success implementation ----"
rg -n "API\\.v1\\.success\\s*=|function\\s+success\\(|v1\\.success\\s*\\(" apps/meteor/app packages/rest-implementation packages/rest-typings packages -S -g'*.ts' -g'*.js' 2>/dev/null | head -n 50

# Try a more targeted search for the API object itself
echo
echo "---- Search for 'class API' / 'const API' definition ----"
rg -n "class\\s+API\\b|const\\s+API\\b|export\\s+(class|const)\\s+API\\b" apps/meteor/app packages/rest-implementation packages -S -g'*.ts' 2>/dev/null | head -n 50

# 2) Once found, print the relevant portion around API.v1.success
# (We use a fallback glob to print likely candidates if paths are found.)
echo
echo "---- Candidate files containing 'API.v1.success' ----"
rg -n "API\\.v1\\.success" apps/meteor/app/api apps/meteor/app/livechat/server packages/rest-implementation -S -g'*.ts' 2>/dev/null | head -n 30

# 3) If the API implementation file is identified by name in results, we’ll print it next turn.

Repository: RocketChat/Rocket.Chat

Length of output: 4785


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Find where API.v1 is defined (rest implementation)
echo "---- Find API.v1 definition / API object ----"
rg -n --no-ignore --hidden "API\\.v1\\s*=" apps/meteor/app packages/rest-implementation -S -g'*.ts' -g'*.js' 2>/dev/null || true

# Look for a success helper definition in rest-implementation
echo
echo "---- Find API.v1.success implementation candidates ----"
rg -n --no-ignore --hidden "success\\s*\\(.*\\)\\s*\\{|function\\s+success\\b|\\bv1\\.success\\b" apps/meteor/app packages/rest-implementation -S -g'*.ts' -g'*.js' 2>/dev/null | head -n 50

# If not found, search for "API.v1.failure" alongside it
echo
echo "---- Find API.v1.failure definition candidates (paired with success) ----"
rg -n --no-ignore --hidden "API\\.v1\\.failure|v1\\.failure\\b|failure\\s*\\(.*\\)\\s*\\{" apps/meteor/app packages/rest-implementation -S -g'*.ts' -g'*.js' 2>/dev/null | head -n 50

# Search in the generic rest API utility module (often named api.ts)
echo
echo "---- Search for 'class API' or 'const API' within rest-implementation ----"
rg -n --no-ignore --hidden "class\\s+API\\b|const\\s+API\\b|export\\s+const\\s+API\\b" packages/rest-implementation apps/meteor/app -S -g'*.ts' 2>/dev/null | head -n 50

Repository: RocketChat/Rocket.Chat

Length of output: 5119


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect imports/definition source for `API` in commands.run handler
echo "---- apps/meteor/app/api/server/v1/commands.ts imports (top) ----"
sed -n '1,80p' apps/meteor/app/api/server/v1/commands.ts

# Extract likely API import paths from the file
echo
echo "---- Search for 'from .*api' imports around API usage ----"
rg -n "from ['\"][^'\"]*api[^'\"]*['\"]|from ['\"][^'\"]*rest[^'\"]*['\"]" apps/meteor/app/api/server/v1/commands.ts

# Locate the actual module exporting `API` by searching for `export.*API` near those import paths
# (We don't know the exact path yet; we do a broad search for API.v1.post/success patterns.)
echo
echo "---- Search for definition of API.v1.post in codebase ----"
rg -n "v1\\.post\\(|API\\.v1\\.post\\(" apps/meteor/app packages/rest-implementation -S -g'*.ts' 2>/dev/null | head -n 50

echo
echo "---- Search for API.success signature implementation (generic) ----"
rg -n "success\\s*\\(.*\\)\\s*\\{" packages/rest-implementation packages/rest-typings apps/meteor/app -S -g'*.ts' 2>/dev/null | head -n 50

# Also search for the exact string used in this file
echo
echo "---- Search for 'API\\.v1\\.success' definition (not just usages) ----"
rg -n "export (const|function) success\\b|function success\\b|success\\s*:\\s*\\(" packages/rest-implementation apps/meteor/app -S -g'*.ts' 2>/dev/null | head -n 50

Repository: RocketChat/Rocket.Chat

Length of output: 5950


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Find the API module imported as '../api' from apps/meteor/app/api/server/v1/commands.ts
echo "---- Resolve ../api path targets (expect apps/meteor/app/api/server/api.*) ----"
ls -la apps/meteor/app/api/server | head
ls -la apps/meteor/app/api/server/api* 2>/dev/null || true
ls -la apps/meteor/app/api/server/*api* 2>/dev/null | head -n 50 || true

# Search for the exported API symbol definition near that module
echo
echo "---- Search for export of API in apps/meteor/app/api/server ----"
rg -n "export\\s+(const|class)\\s+API\\b|export\\s+\\{\\s*API\\s*\\}" apps/meteor/app/api/server -S -g'*.ts' || true

# Inspect the API module that defines API.v1.success
echo
echo "---- Search for 'v1.success' or 'success(' in apps/meteor/app/api/server/api* ----"
rg -n "v1\\.success|API\\.v1\\.success|success\\s*\\(" apps/meteor/app/api/server -S -g'api*.ts' -g'*.ts' || true

# Once we find it, print the relevant portion.
echo
echo "---- Candidate files containing 'API.v1' ----"
rg -n "API\\.v1" apps/meteor/app/api/server -S -g'*.ts' | head -n 50

Repository: RocketChat/Rocket.Chat

Length of output: 36458


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the ApiClass success implementation that builds the response payload
echo "---- apps/meteor/app/api/server/ApiClass.ts (SuccessResult + success helper) ----"
rg -n "type\\s+SuccessResult|public\\s+success\\(|success\\(payload" apps/meteor/app/api/server/ApiClass.ts
echo
# Print a window around the first success method we find
LINE=$(rg -n "public success\\(" apps/meteor/app/api/server/ApiClass.ts | head -n 1 | cut -d: -f1)
START=$((LINE-40))
END=$((LINE+120))
sed -n "${START},${END}p" apps/meteor/app/api/server/ApiClass.ts

# Also print the API wrapper that is used by commands.ts (apps/meteor/app/api/server/api.ts)
echo
echo "---- apps/meteor/app/api/server/api.ts around export const API definition ----"
sed -n '1,120p' apps/meteor/app/api/server/api.ts

Repository: RocketChat/Rocket.Chat

Length of output: 7980


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect slashCommands.run implementation/return shape
echo "---- apps/meteor/app/utils/server/slashCommand.ts (search slashCommands.run definition) ----"
rg -n "slashCommands\\.run|run\\s*\\(" apps/meteor/app/utils/server/slashCommand.ts

echo
echo "---- Print region around slashCommands.run ----"
rg -n "slashCommands\\.run" apps/meteor/app/utils/server/slashCommand.ts | head -n 1 | cut -d: -f1 > /tmp/line.txt
LINE=$(cat /tmp/line.txt)
START=$((LINE-80))
END=$((LINE+160))
sed -n "${START},${END}p" apps/meteor/app/utils/server/slashCommand.ts

Repository: RocketChat/Rocket.Chat

Length of output: 2710


🏁 Script executed:

#!/bin/bash
set -euo pipefail
rg -n "response\\.body\\.message|\\.body\\.message" ee/packages/federation-matrix/tests/end-to-end/room.spec.ts

Repository: RocketChat/Rocket.Chat

Length of output: 130


Update the federation slash-command test assertion: REST /v1/commands.run doesn’t return a DDP message envelope.

apps/meteor/tests/data/rooms.helper.ts posts the invite via REST api('commands.run'), but ee/packages/federation-matrix/tests/end-to-end/room.spec.ts still parses response.body.message as JSON and asserts msg: 'result'. The REST endpoint implementation (apps/meteor/app/api/server/v1/commands.ts /v1/commands.run) returns API.v1.success({ result }), so the response body is { success: true, result: ... } (no message field), making JSON.parse(response.body.message) invalid.

Either keep a DDP-based helper/flow for this assertion or change room.spec.ts to assert on response.body.result instead.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/meteor/tests/data/rooms.helper.ts` around lines 202 - 211, The REST
helper in rooms.helper.ts posts to api('commands.run') which returns
API.v1.success({ result }) (no DDP `message` envelope), so the federation test
in ee/packages/federation-matrix/tests/end-to-end/room.spec.ts must stop parsing
response.body.message; update the test to assert against response.body.result
(or switch to a DDP-based helper), specifically replace
JSON.parse(response.body.message) and checks for msg:'result' with direct
assertions on response.body.result to match the /v1/commands.run implementation.

Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

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

2 issues found across 4 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="apps/meteor/tests/data/rooms.helper.ts">

<violation number="1" location="apps/meteor/tests/data/rooms.helper.ts:158">
P1: `addUserToRoom` now returns an array of per-user REST responses, breaking existing callers that still expect one response object and changing multi-user invite semantics to partial-success-per-user.</violation>

<violation number="2" location="apps/meteor/tests/data/rooms.helper.ts:203">
P1: Switching `addUserToRoomSlashCommand` from DDP (`methodCall('slashCommand')`) to REST (`api('commands.run')`) changes the response shape from a DDP envelope (`{ message: '{"msg":"result",...}' }`) to the standard REST envelope (`{ success: true }` or `{ success: false, error: '...' }`). Consumers in `room.spec.ts` still call `JSON.parse(response.body.message)` which will throw since `response.body.message` is `undefined` with the REST endpoint.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

msg: 'method',
const endpoint = type === 'c' ? 'channels.invite' : 'groups.invite';

return Promise.all(
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P1: addUserToRoom now returns an array of per-user REST responses, breaking existing callers that still expect one response object and changing multi-user invite semantics to partial-success-per-user.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/meteor/tests/data/rooms.helper.ts, line 158:

<comment>`addUserToRoom` now returns an array of per-user REST responses, breaking existing callers that still expect one response object and changing multi-user invite semantics to partial-success-per-user.</comment>

<file context>
@@ -129,55 +129,56 @@ export const getSubscriptionByRoomId = (roomId: IRoom['_id'], userCredentials =
-				msg: 'method',
+	const endpoint = type === 'c' ? 'channels.invite' : 'groups.invite';
+
+	return Promise.all(
+		usernames.map((username) =>
+			requestInstance.post(api(endpoint)).set(credentialsInstance).send({
</file context>


return requestInstance
.post(methodCall('slashCommand'))
.post(api('commands.run'))
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P1: Switching addUserToRoomSlashCommand from DDP (methodCall('slashCommand')) to REST (api('commands.run')) changes the response shape from a DDP envelope ({ message: '{"msg":"result",...}' }) to the standard REST envelope ({ success: true } or { success: false, error: '...' }). Consumers in room.spec.ts still call JSON.parse(response.body.message) which will throw since response.body.message is undefined with the REST endpoint.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/meteor/tests/data/rooms.helper.ts, line 203:

<comment>Switching `addUserToRoomSlashCommand` from DDP (`methodCall('slashCommand')`) to REST (`api('commands.run')`) changes the response shape from a DDP envelope (`{ message: '{"msg":"result",...}' }`) to the standard REST envelope (`{ success: true }` or `{ success: false, error: '...' }`). Consumers in `room.spec.ts` still call `JSON.parse(response.body.message)` which will throw since `response.body.message` is `undefined` with the REST endpoint.</comment>

<file context>
@@ -198,25 +200,13 @@ export const addUserToRoomSlashCommand = ({
 
 	return requestInstance
-		.post(methodCall('slashCommand'))
+		.post(api('commands.run'))
 		.set(credentialsInstance)
 		.send({
</file context>

@yummyPancake2607 yummyPancake2607 marked this pull request as ready for review May 29, 2026 18:07
@yummyPancake2607
Copy link
Copy Markdown
Author

This PR migrates federation add-user tests from legacy DDP response assertions to REST response envelopes as part of the DDP removal cleanup.

I’ve verified:

  • ESLint passes
  • federation tests pass locally
  • slash command DDP behavior remains unchanged intentionally

Could someone please review and approve the workflows when available? Thanks!

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

(ddp-removal): swap /api/v1/method.call/addUsersToRoom for the per-type

2 participants