Files
upstream-rocketchat/docs/features/slackbridge-manual-tests.md
upstream-mirror 30810eecd7
Some checks failed
Create issues from TODOs / todos (push) Failing after 3s
Close inactive issues / close-issues (push) Has been cancelled
Deploy GitHub Pages / deploy-preview (push) Has been cancelled
CI / ⚙️ Variables Setup (push) Has been cancelled
CI / ⚙️ Test Guard (push) Has been cancelled
CI / 🚀 Notify external services - draft (push) Has been cancelled
CI / 📦 Build Packages (push) Has been cancelled
CI / 📦 Meteor Build (coverage) (push) Has been cancelled
CI / 📦 Meteor Build (production) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [account-service presence-service omnichannel-transcript-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [account-service presence-service omnichannel-transcript-service], fips) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [authorization-service queue-worker-service ddp-streamer-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [authorization-service queue-worker-service ddp-streamer-service], fips) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], coverage) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], fips) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [account-service presence-service omnichannel-transcript-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [authorization-service queue-worker-service ddp-streamer-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [rocketchat], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [rocketchat], coverage) (push) Has been cancelled
CI / 🚢 Publish Docker Images (ghcr.io) (push) Has been cancelled
CI / 📦 Track Image Sizes (push) Has been cancelled
CI / 🔎 Code Check - Action Lint (push) Has been cancelled
CI / 🔎 Code Check (push) Has been cancelled
CI / 🔨 Test Storybook (push) Has been cancelled
CI / 🔨 Test Unit (push) Has been cancelled
CI / 🔨 Test API (CE) (push) Has been cancelled
CI / 🔨 Test API Livechat (CE) (push) Has been cancelled
CI / 🔨 Test UI (CE) (push) Has been cancelled
CI / 🔨 Test API (EE) (push) Has been cancelled
CI / 🔨 Test API Livechat (EE) (push) Has been cancelled
CI / 🔨 Test API Apps (deno-runtime - EE) (push) Has been cancelled
CI / 🔨 Test UI (EE) (push) Has been cancelled
CI / 🔨 Test API (FIPS) (push) Has been cancelled
CI / 🔨 Test API Livechat (FIPS) (push) Has been cancelled
CI / 🔨 Test UI (FIPS) (push) Has been cancelled
CI / 🔨 Test Federation Matrix (push) Has been cancelled
CI / 📊 Report Coverage (push) Has been cancelled
CI / ✅ Tests Done (push) Has been cancelled
CI / 🚀 Publish build assets (push) Has been cancelled
CI / 🚀 Publish Docker Images (DockerHub) (push) Has been cancelled
CI / 🚀 Notify external services (push) Has been cancelled
CI / Update Version Durability (push) Has been cancelled
Release / Release (push) Has been cancelled
Code scanning - action / CodeQL-Build (push) Failing after 2s
Auto-close duplicate issues / auto-close-duplicates (push) Failing after 1s
snapshot of https://github.com/RocketChat/Rocket.Chat.git (develop, 2026-09-16; blobs > 131072B omitted)
2026-09-16 01:21:02 +05:00

183 lines
12 KiB
Markdown

# SlackBridge Manual Test Plan
The SlackBridge is a bi-directional bridge between a Rocket.Chat workspace and one or more Slack
workspaces — each token line in the SlackBridge settings gets its own connection and adapter. It
has no automated end-to-end coverage: exercising it requires a real Slack workspace, a real bot
installation, and a human watching both sides at once. This document is the checklist to walk
through when touching the bridge.
**Scope:** the matrices below assume a **single** bridged Slack workspace — configure exactly one
token per setting while running them. Bridging several workspaces at once is a supported
configuration that this plan does not cover; see [Coverage gaps](#coverage-gaps).
Code lives in [apps/meteor/server/bridges/slack/](../../apps/meteor/server/bridges/slack/) —
`SlackAdapter.ts` handles Slack → Rocket.Chat, `RocketAdapter.ts` handles Rocket.Chat → Slack
(hooked in through the `afterSaveMessage`, `afterDeleteMessage`, `afterSetReaction` and
`afterUnsetReaction` callbacks). Settings are declared in
[apps/meteor/server/settings/slackbridge.ts](../../apps/meteor/server/settings/slackbridge.ts).
## Setup
Configure the bridge in **Admin → Settings → SlackBridge**:
| Setting | Notes |
| --------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------- |
| `SlackBridge_Enabled` | Master switch. Must be on. |
| `SlackBridge_UseLegacy` | Chooses the connection mode — see below. Run the matrices in the mode you are testing. |
| `SlackBridge_APIToken` | Legacy mode only (RTM). |
| `SlackBridge_BotToken`, `SlackBridge_SigningSecret`, `SlackBridge_AppToken` | App mode only (Bolt / Socket Mode). |
| `SlackBridge_Out_Enabled` | Required for anything to flow Rocket.Chat → Slack. |
| `SlackBridge_Out_All` / `SlackBridge_Out_Channels` | Which Rocket.Chat rooms are bridged out. |
| `SlackBridge_Reactions_Enabled` | Gates reaction propagation in both directions. |
| `SlackBridge_AliasFormat` | Optional, Slack → Rocket.Chat only. `%s` is the Slack username; test with `%s (via Slack)`. |
| `SlackBridge_FileUpload_Enabled` | Gates file transfer. |
Two connection modes exist and they use different Slack event plumbing, so a regression can
appear in one and not the other. **Run the full matrix in both** when changing anything in the
connect/event-subscription path:
- **Legacy (`SlackBridge_UseLegacy: true`)** — RTM connection with an API token.
- **App (`SlackBridge_UseLegacy: false`)** — Slack app using bot token, signing secret and app
token.
You also need:
- A Slack workspace where you can create channels and install/invite the bot (referred to below
as **rocketbot**).
- At least one channel that exists on both sides **with** rocketbot in it, and one that exists on
both sides **without** rocketbot in it. Several cases below hinge on that difference.
- A second Slack user, if possible — some behaviors only show up when the message author is not
the same account as the bot.
Watch `SlackBridge` logs (`slackLogger` / `rocketLogger`) while testing; several expected
failures are only visible there.
## How to read the matrices
- **Origin** — where the message being acted on was originally posted.
- **Action performed on** — the side where the tester performs the action.
- Every matrix must also be repeated **on a channel that rocketbot is not a member of**. Nothing
should propagate in either direction for those channels, and nothing should error.
## Sending messages
| # | Origin | Action performed on | Expected result |
| --- | ----------- | ------------------- | ------------------------------------------------------------------------------------------------------- |
| 1 | Slack | Slack | Message appears in the linked Rocket.Chat room, under the alias `SlackBridge_AliasFormat` produces. |
| 2 | Rocket.Chat | Rocket.Chat | Message appears in the linked Slack channel, posted by rocketbot with the sender's username and avatar. |
`SlackBridge_AliasFormat` only affects case 1: `addAliasToMsg` sets `alias` on the **Rocket.Chat**
message, so with `%s (via Slack)` a message from Slack user `alice` shows as `alice (via Slack)`.
Case 2 carries the sender's identity through `chat.postMessage`'s `username`/`icon_url` and ignores
the setting entirely. Set the format, run case 1 with it set and again with it empty, then **restore
the original value** before moving on — the later matrices assume the workspace's normal setup.
Repeat both on a channel without rocketbot: neither message crosses over.
## Editing messages
Slack does not allow an app to edit messages it did not post, and it does not allow humans to
edit bot messages. Two of the four cases are therefore **expected failures** — they are listed
because "nothing crashes and the local side stays consistent" is the requirement.
| # | Origin | Action performed on | Expected result |
| --- | ----------- | ------------------- | ------------------------------------------------------------------------------------------------------------------------------------- |
| 1 | Slack | Slack | Edit propagates to Rocket.Chat. |
| 2 | Rocket.Chat | Rocket.Chat | Edit propagates to Slack. |
| 3 | Slack | Rocket.Chat | **Expected failure.** Rocket.Chat shows the edit; Slack is unchanged. `chat.update` is rejected because the message is not the bot's. |
| 4 | Rocket.Chat | Slack | **Expected failure.** Slack does not even offer the edit affordance for a bot message. |
Repeat on a channel without rocketbot: edits stay local on both sides.
## Deleting messages
Case 3 needs care about _who_ deletes. A Rocket.Chat-origin message is authored on Slack by
rocketbot, and Slack offers no delete affordance for an app's message to a regular member, so
pick one of:
- Delete it from the Slack UI as a **workspace owner or admin** — they can delete any message,
including an app's.
- Call `chat.delete` with the very token the bridge is configured with, so the caller is
rocketbot itself: `SlackBridge_APIToken` in legacy/RTM mode, `SlackBridge_BotToken` in
app/Socket Mode. Any other token fails with `cant_delete_message`, which is case 4's failure,
not case 3's.
Both routes emit the same `message_deleted` event, so either is a valid way to run the case.
| # | Origin | Action performed on | Expected result |
| --- | ----------- | ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
| 1 | Slack | Slack | Deletion propagates to Rocket.Chat. |
| 2 | Rocket.Chat | Rocket.Chat | Deletion propagates to Slack. |
| 3 | Rocket.Chat | Slack | Deletion propagates to Rocket.Chat — deleted as a workspace admin or via `chat.delete` with rocketbot's own token, per the note above. |
| 4 | Slack | Rocket.Chat | **Expected failure.** The message is deleted in Rocket.Chat only; Slack keeps it, since `chat.delete` cannot remove another user's message. |
Repeat on a channel without rocketbot: deletions stay local on both sides.
## Reactions
Requires `SlackBridge_Reactions_Enabled`. Reactions sent to Slack are added by the bot account,
so on the Slack side every Rocket.Chat reaction shows up as rocketbot's reaction regardless of
who actually reacted.
| # | Origin | Action performed on | Expected result |
| --- | ----------- | ------------------- | ----------------------------------------------------- |
| 1 | Slack | Slack | Reaction appears on the Rocket.Chat message. |
| 2 | Rocket.Chat | Slack | Reaction appears on the Rocket.Chat message. |
| 3 | Rocket.Chat | Rocket.Chat | Reaction appears on the Slack message (as rocketbot). |
| 4 | Slack | Rocket.Chat | Reaction appears on the Slack message (as rocketbot). |
Also check removal of each reaction, and confirm a reaction is not echoed back to its origin
(the bridge de-duplicates through its reactions map — a double-count means that broke).
Repeat on a channel without rocketbot: reactions stay local.
### `SlackBridge_Reactions_Enabled` toggle
| Setting state | Expected result |
| ------------- | -------------------------------------------------------------------------------------------------------------- |
| Disabled | React to both a Slack-origin and a Rocket.Chat-origin message from both sides — nothing propagates, no errors. |
| Enabled | Re-run the same reactions — they propagate as in the matrix above, without restarting the server. |
## Channel lifecycle
These cases cover how the bridge reacts to channels appearing, disappearing, and the bot joining
or leaving. Send a message **from each side** after every step and verify propagation.
### Channel exists on both sides, rocketbot not in the Slack channel
1. Confirm nothing propagates in either direction.
2. Invite rocketbot to the Slack channel.
3. Send from Slack and from Rocket.Chat — both directions now work.
### Channel exists on Rocket.Chat only
1. Create the matching channel in Slack and invite rocketbot.
2. Send from Rocket.Chat — it reaches Slack.
3. Send from Slack — it reaches Rocket.Chat.
### Channel exists on Slack only
1. Invite rocketbot to the Slack channel.
2. Send from Slack — the Rocket.Chat channel is created and receives the message.
3. Send from Rocket.Chat — it reaches Slack.
### Channel exists on both sides with rocketbot
1. Delete the Rocket.Chat channel, then send from Slack — behaves like the "Slack only" case
above (the channel is recreated).
2. Remove rocketbot from the Slack channel, then send from Slack and from Rocket.Chat — nothing
propagates in either direction, and no errors are logged.
## Coverage gaps
The matrices above are the historical manual suite and deliberately mirror it. They do **not**
cover, and it is worth checking manually when relevant:
- File uploads (`SlackBridge_FileUpload_Enabled`).
- Slack history import (`/slackbridge-import`, `slackbridge_import.server.ts`).
- Threads (`thread_ts` mapping in `postMessage`).
- Private channels and DMs.
- `SlackBridge_ExcludeBotnames` filtering.
- The **Remove Channel Links** admin action (`SlackBridge_Remove_Channel_Links`).
- Multiple Slack workspaces bridged at once (one token per line in each token setting).