From a3177dc91e0be2d1f9633cce62b05c10a9b5f3e2 Mon Sep 17 00:00:00 2001 From: William Bergamin Date: Thu, 7 May 2026 12:13:22 -0400 Subject: [PATCH 1/6] chore: update prod integration tests --- .gitignore | 1 + prod-server-integration-tests/.env.sample | 112 ++++++++++++++++ prod-server-integration-tests/README.md | 119 ++++++++++++++--- .../link-socket-mode.sh | 15 --- prod-server-integration-tests/link-web-api.sh | 15 --- .../manifests/README.md | 102 +++++++++++++++ .../manifests/admin-app.manifest.json | 39 ++++++ .../manifests/main-bot-app.manifest.json | 48 +++++++ .../slack-connect-receiver.manifest.json | 29 +++++ .../slack-connect-sender.manifest.json | 31 +++++ .../manifests/socket-mode-app.manifest.json | 38 ++++++ prod-server-integration-tests/package.json | 10 +- .../test/bookmarks-web-api.js | 92 ++++++------- prod-server-integration-tests/test/web-api.js | 121 +++++++++--------- 14 files changed, 617 insertions(+), 155 deletions(-) create mode 100644 prod-server-integration-tests/.env.sample delete mode 100755 prod-server-integration-tests/link-socket-mode.sh delete mode 100755 prod-server-integration-tests/link-web-api.sh create mode 100644 prod-server-integration-tests/manifests/README.md create mode 100644 prod-server-integration-tests/manifests/admin-app.manifest.json create mode 100644 prod-server-integration-tests/manifests/main-bot-app.manifest.json create mode 100644 prod-server-integration-tests/manifests/slack-connect-receiver.manifest.json create mode 100644 prod-server-integration-tests/manifests/slack-connect-sender.manifest.json create mode 100644 prod-server-integration-tests/manifests/socket-mode-app.manifest.json diff --git a/.gitignore b/.gitignore index 1d7cc1387..98378dad5 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,7 @@ lcov.info test-results.xml tmp/ .env* +!.env.sample # npm pack outputs *.tgz diff --git a/prod-server-integration-tests/.env.sample b/prod-server-integration-tests/.env.sample new file mode 100644 index 000000000..cda8f27f8 --- /dev/null +++ b/prod-server-integration-tests/.env.sample @@ -0,0 +1,112 @@ +# ============================================================================= +# Production Server Integration Tests - Environment Variables +# ============================================================================= +# Copy this file to .env and fill in the values. +# The dotenv package loads .env automatically when tests run. +# +# Each variable references which manifest/app it corresponds to. +# See manifests/ directory for app configurations. +# ============================================================================= + +# ----------------------------------------------------------------------------- +# Main Bot App (see manifests/main-bot-app.manifest.json) +# Install this app to your test workspace with all listed bot + user scopes. +# ----------------------------------------------------------------------------- + +# Bot token (xoxb-) from the main bot app +# Used by: test/web-api.js, test/bookmarks-web-api.js, scripts/conversations_invite.js +SLACK_SDK_TEST_BOT_TOKEN=xoxb-your-bot-token + +# User token (xoxp-) from the main bot app (user must authorize the app) +# Used by: test/web-api.js +SLACK_SDK_TEST_USER_TOKEN=xoxp-your-user-token + +# A public channel ID in the test workspace (bot must be a member) +# Used by: test/web-api.js (chat.scheduleMessage tests) +SLACK_SDK_TEST_WEB_TEST_CHANNEL_ID=C0123456789 + +# ----------------------------------------------------------------------------- +# Slack Connect Apps (see manifests/slack-connect-sender.manifest.json +# and manifests/slack-connect-receiver.manifest.json) +# These require TWO separate workspaces with Slack Connect enabled. +# A shared channel must be manually created between the two workspaces +# with both bots added as members before running tests. +# ----------------------------------------------------------------------------- + +# Bot token for the SENDER workspace app +# Used by: test/web-api.js (Slack Connect tests) +SLACK_SDK_TEST_CONNECT_INVITE_SENDER_BOT_TOKEN=xoxb-sender-bot-token + +# Bot token for the RECEIVER workspace app +# Used by: test/web-api.js (Slack Connect tests) +SLACK_SDK_TEST_CONNECT_INVITE_RECEIVER_BOT_TOKEN=xoxb-receiver-bot-token + +# The bot user ID (not the app ID) of the receiver bot (starts with U or W) +# Used by: test/web-api.js (Slack Connect tests) +SLACK_SDK_TEST_CONNECT_INVITE_RECEIVER_BOT_USER_ID=U0123456789 + +# ----------------------------------------------------------------------------- +# Enterprise Grid Admin Tokens (see manifests/admin-app.manifest.json) +# These are USER tokens (xoxp-) from admin users in an Enterprise Grid org. +# The app must be installed org-wide with admin scopes granted. +# ----------------------------------------------------------------------------- + +# Workspace-level admin user token (admin of a workspace within the Grid org) +# Used by: test/admin-web-api.js, test/admin-web-api-conversations-bulk.js, +# test/admin-web-api-custom-retention.js, test/admin-web-api-user-sessions.js, +# scripts/admin/usersSessionSettings.js +SLACK_SDK_TEST_GRID_WORKSPACE_ADMIN_USER_TOKEN=xoxp-workspace-admin-token + +# Org-level admin user token (org admin of the Enterprise Grid) +# Used by: test/admin-web-api.js, test/admin-web-api-analytics.js, +# test/admin-web-api-conversations-bulk.js, test/admin-web-api-custom-retention.js, +# test/admin-web-api-roles.js, test/admin-web-api-user-sessions.js, +# test/admin-web-api-users-unsupportedVersions-export.js, +# scripts/admin/usersSessionSettings.js +SLACK_SDK_TEST_GRID_ORG_ADMIN_USER_TOKEN=xoxp-org-admin-token + +# Team ID of a SECONDARY workspace in the same Enterprise Grid org +# (must differ from the workspace of the workspace admin token above) +# Used by: test/admin-web-api-conversations-bulk.js (bulkMove test) +SLACK_SDK_TEST_GRID_SECONDARY_WORKSPACE_ID=T0123456789 + +# (Optional) A user ID for auth policy tests +# Used by: test/admin-web-api.js (admin.auth.policy tests) +# SLACK_SDK_TEST_GRID_USER_ID=U0123456789 + +# ----------------------------------------------------------------------------- +# Socket Mode App (see manifests/socket-mode-app.manifest.json) +# This app must have Socket Mode enabled in app settings. +# Generate an app-level token with connections:write scope. +# ----------------------------------------------------------------------------- + +# App-level token (xapp-) with connections:write scope +# Used by: scripts/socket-mode.js +SLACK_SDK_TEST_SOCKET_MODE_APP_TOKEN=xapp-your-app-level-token + +# ----------------------------------------------------------------------------- +# Scripts - Additional Tokens +# Some scripts use alternate variable names for convenience. +# ----------------------------------------------------------------------------- + +# A bot token for simple script examples (can be same as SLACK_SDK_TEST_BOT_TOKEN) +# Used by: scripts/auth_test.js, scripts/pagination.js +SLACK_BOT_TOKEN=xoxb-your-bot-token + +# (Optional) A user ID for pagination script filtering +# Used by: scripts/pagination.js +# SLACK_USER_ID=U0123456789 + +# ----------------------------------------------------------------------------- +# App Manifest Lifecycle (tooling/configuration token) +# This is NOT an app-specific token. Generate via App Configuration Tokens: +# https://api.slack.com/authentication/config-tokens +# ----------------------------------------------------------------------------- + +# Configuration/tooling token for apps.manifest.* API methods +# Used by: scripts/apps_manifest_test.js +SLACK_TOOLING_TOKEN=xoxe-your-tooling-token + +# (Optional) Override the Slack API base URL (for dev/staging environments) +# Used by: scripts/apps_manifest_test.js +# SLACK_SDK_TEST_DEV_API_URL=https://dev.slack.com/api/ diff --git a/prod-server-integration-tests/README.md b/prod-server-integration-tests/README.md index ba8db1b81..4c4ad06c1 100644 --- a/prod-server-integration-tests/README.md +++ b/prod-server-integration-tests/README.md @@ -1,33 +1,116 @@ -First off, you need to link `@slack/*` packages to `../packages/*` to verify the latest revision's behavior. +# Production Server Integration Tests + +End-to-end integration tests that run against real Slack APIs to verify the +`@slack/web-api` and `@slack/socket-mode` packages work correctly. + +## Prerequisites + +1. **Link local packages** to test the latest code from this repository: ```bash +# For Web API tests ./link-web-api.sh + +# For Socket Mode tests +./link-socket-mode.sh ``` -If you would like to run `scripts/socket-mode.js` with the latest revision of code in this repo, run `./link-socket-mode.sh` instead. +2. **Create the required Slack apps** using the manifests in [`manifests/`](./manifests/). + See [`manifests/README.md`](./manifests/README.md) for detailed setup instructions. -### How to run tests +3. **Configure environment variables** by copying the sample file: ```bash -# with all of the bot scopes -export SLACK_SDK_TEST_BOT_TOKEN=xoxb- -# with all of the user scopes -export SLACK_SDK_TEST_USER_TOKEN=xoxp- -# admin user's token for a workspace in an Enterprise Grid org -export SLACK_SDK_TEST_GRID_WORKSPACE_ADMIN_USER_TOKEN=xoxp- -# org-level admin user's token -export SLACK_SDK_TEST_GRID_ORG_ADMIN_USER_TOKEN=xoxp- -# admin user's token for a secondary workspace in an Enterprise Grid org (must be in same org as workspace for SLACK_SDK_TEST_GRID_WORKSPACE_ADMIN_USER_TOKEN, but a different workspace) - required for admin.conversations.bulkMove test -export SLACK_SDK_TEST_GRID_SECONDARY_WORKSPACE_ID=T0123456789 +cp .env.sample .env +# Edit .env and fill in your tokens/IDs +``` + +The `dotenv` package loads `.env` automatically when tests run. + +## Running Tests + +### All tests +```bash npm test ``` -### How to run example scripts +### Individual test suites ```bash -# some examples may use different env variable names -export SLACK_BOT_TOKEN=xoxb-(your own token) -# Run the script -npx node scripts/pagination.js # or whatever you want to run +# Core Web API tests (auth, chat.scheduleMessage, Slack Connect, team.*) +npx mocha --require dotenv/config --timeout 10000 test/web-api.js + +# Bookmarks CRUD +npx mocha --require dotenv/config --timeout 10000 test/bookmarks-web-api.js + +# Admin: session settings + auth policy +npx mocha --require dotenv/config --timeout 10000 test/admin-web-api.js + +# Admin: analytics +npx mocha --require dotenv/config --timeout 10000 test/admin-web-api-analytics.js + +# Admin: bulk archive/delete/move +npx mocha --require dotenv/config --timeout 10000 test/admin-web-api-conversations-bulk.js + +# Admin: custom retention +npx mocha --require dotenv/config --timeout 10000 test/admin-web-api-custom-retention.js + +# Admin: roles +npx mocha --require dotenv/config --timeout 10000 test/admin-web-api-roles.js + +# Admin: user sessions reset +npx mocha --require dotenv/config --timeout 10000 test/admin-web-api-user-sessions.js + +# Admin: unsupported versions export +npx mocha --require dotenv/config --timeout 10000 test/admin-web-api-users-unsupportedVersions-export.js ``` + +### Example scripts + +```bash +# No token needed +npx node scripts/api_test.js + +# Requires SLACK_BOT_TOKEN +npx node scripts/auth_test.js +npx node scripts/pagination.js + +# Requires SLACK_SDK_TEST_BOT_TOKEN +npx node scripts/conversations_invite.js + +# Requires SLACK_SDK_TEST_SOCKET_MODE_APP_TOKEN +npx node scripts/socket-mode.js + +# Requires SLACK_TOOLING_TOKEN +npx node scripts/apps_manifest_test.js + +# Requires SLACK_SDK_TEST_GRID_*_TOKEN vars +npx node scripts/admin/usersSessionSettings.js +``` + +## Environment Variables + +See [`.env.sample`](./.env.sample) for the complete list with descriptions. Summary: + +| Variable | Source App | Used By | +|---|---|---| +| `SLACK_SDK_TEST_BOT_TOKEN` | main-bot-app | web-api, bookmarks, conversations_invite | +| `SLACK_SDK_TEST_USER_TOKEN` | main-bot-app (user OAuth) | web-api | +| `SLACK_SDK_TEST_WEB_TEST_CHANNEL_ID` | — (channel in workspace) | chat.scheduleMessage test | +| `SLACK_SDK_TEST_CONNECT_INVITE_SENDER_BOT_TOKEN` | slack-connect-sender | Slack Connect tests | +| `SLACK_SDK_TEST_CONNECT_INVITE_RECEIVER_BOT_TOKEN` | slack-connect-receiver | Slack Connect tests | +| `SLACK_SDK_TEST_CONNECT_INVITE_RECEIVER_BOT_USER_ID` | — (user ID) | Slack Connect tests | +| `SLACK_SDK_TEST_GRID_WORKSPACE_ADMIN_USER_TOKEN` | admin-app (user OAuth) | all admin tests | +| `SLACK_SDK_TEST_GRID_ORG_ADMIN_USER_TOKEN` | admin-app (user OAuth) | all admin tests | +| `SLACK_SDK_TEST_GRID_SECONDARY_WORKSPACE_ID` | — (team ID) | admin bulk move test | +| `SLACK_SDK_TEST_GRID_USER_ID` | — (user ID, optional) | admin auth policy test | +| `SLACK_SDK_TEST_SOCKET_MODE_APP_TOKEN` | socket-mode-app | Socket Mode script | +| `SLACK_BOT_TOKEN` | main-bot-app (or any) | auth_test, pagination scripts | +| `SLACK_TOOLING_TOKEN` | — (configuration token) | apps_manifest_test script | + +## App Manifests + +The [`manifests/`](./manifests/) directory contains ready-to-use app manifest JSON files. +Each file can be pasted directly into the Slack app creation flow at +[api.slack.com/apps](https://api.slack.com/apps) when selecting "From an app manifest." diff --git a/prod-server-integration-tests/link-socket-mode.sh b/prod-server-integration-tests/link-socket-mode.sh deleted file mode 100755 index 37c476370..000000000 --- a/prod-server-integration-tests/link-socket-mode.sh +++ /dev/null @@ -1,15 +0,0 @@ -#!/bin/bash - -current_dir=`dirname $0` -cd ${current_dir} -rm -rf package-lock.json node_modules/ -npm unlink @slack/socket-mode \ - && npm i \ - && cd ../packages/socket-mode \ - && npm link \ - && cd - \ - && cd ../packages/socket-mode \ - && npm link \ - && cd - \ - && npm i \ - && npm link @slack/socket-mode \ No newline at end of file diff --git a/prod-server-integration-tests/link-web-api.sh b/prod-server-integration-tests/link-web-api.sh deleted file mode 100755 index ca5fbb1af..000000000 --- a/prod-server-integration-tests/link-web-api.sh +++ /dev/null @@ -1,15 +0,0 @@ -#!/bin/bash - -current_dir=`dirname $0` -cd ${current_dir} -rm -rf package-lock.json node_modules/ -npm unlink @slack/web-api \ - && npm i \ - && cd ../packages/web-api \ - && npm link \ - && cd - \ - && cd ../packages/socket-mode \ - && npm link \ - && cd - \ - && npm i \ - && npm link @slack/web-api \ No newline at end of file diff --git a/prod-server-integration-tests/manifests/README.md b/prod-server-integration-tests/manifests/README.md new file mode 100644 index 000000000..a6a140e05 --- /dev/null +++ b/prod-server-integration-tests/manifests/README.md @@ -0,0 +1,102 @@ +# App Manifests for Integration Tests + +This directory contains example `manifest.json` files for the Slack apps required +to run the production integration tests. + +## Overview + +| Manifest File | Purpose | Token Env Variable(s) | +|---|---|---| +| `main-bot-app.manifest.json` | Primary bot for Web API tests | `SLACK_SDK_TEST_BOT_TOKEN`, `SLACK_SDK_TEST_USER_TOKEN` | +| `slack-connect-sender.manifest.json` | Slack Connect sender workspace bot | `SLACK_SDK_TEST_CONNECT_INVITE_SENDER_BOT_TOKEN` | +| `slack-connect-receiver.manifest.json` | Slack Connect receiver workspace bot | `SLACK_SDK_TEST_CONNECT_INVITE_RECEIVER_BOT_TOKEN` | +| `socket-mode-app.manifest.json` | Socket Mode listener | `SLACK_SDK_TEST_SOCKET_MODE_APP_TOKEN` | +| `admin-app.manifest.json` | Enterprise Grid admin operations | `SLACK_SDK_TEST_GRID_WORKSPACE_ADMIN_USER_TOKEN`, `SLACK_SDK_TEST_GRID_ORG_ADMIN_USER_TOKEN` | + +## How to Use These Manifests + +1. Go to [api.slack.com/apps](https://api.slack.com/apps) +2. Click **Create New App** > **From an app manifest** +3. Select the target workspace +4. Paste the contents of the relevant manifest file (choose JSON format) +5. Review and create the app +6. Install the app to the workspace and note the generated tokens + +### Special Setup + +**Slack Connect tests** require two apps in two *different* workspaces that both have +Slack Connect enabled. You must manually create a shared channel between the two +workspaces and add both bots as members before running the tests. + +**Admin tests** require an Enterprise Grid organization. The admin app must be +installed at the org level by an Org Admin. The user tokens (`xoxp-`) come from +admin users who authorize the app — these are not bot tokens. + +**Socket Mode app** requires generating an app-level token with `connections:write` +scope after creating the app. Go to **App Settings > Basic Information > App-Level Tokens** +to generate this token (`xapp-`). + +**Tooling token** (`SLACK_TOOLING_TOKEN`) is a configuration token not associated +with any of these manifests. Generate it via the [App Configuration Tokens](https://api.slack.com/authentication/config-tokens) flow. + +## Scope Justification + +### main-bot-app (bot scopes) + +| Scope | Reason | +|-------|--------| +| `bookmarks:read` | `bookmarks.list` in bookmarks tests | +| `bookmarks:write` | `bookmarks.add/edit/remove` in bookmarks tests | +| `channels:history` | Reading channel history | +| `channels:manage` | Creating/archiving test channels | +| `channels:read` | Listing channels | +| `chat:write` | `chat.scheduleMessage` tests | +| `conversations.connect:manage` | Slack Connect invite listing | +| `conversations.connect:read` | Slack Connect invite listing | +| `team.billing:read` | `team.billing.info` test | +| `team.preferences:read` | `team.preferences.list` test | +| `users:read` | Listing users for test setup | + +### main-bot-app (user scopes) + +| Scope | Reason | +|-------|--------| +| `channels:history` | `auth.test` with user token | +| `channels:read` | `auth.test` with user token | +| `users:read` | `auth.test` with user token | + +### slack-connect-sender + +| Scope | Reason | +|-------|--------| +| `channels:manage` | Creating channels to share | +| `conversations.connect:manage` | Managing Slack Connect invites | +| `conversations.connect:read` | Listing Slack Connect invites | +| `conversations.connect:write` | Sending Slack Connect invites | + +### slack-connect-receiver + +| Scope | Reason | +|-------|--------| +| `conversations.connect:manage` | Accepting/approving Slack Connect invites | +| `conversations.connect:read` | Reading Slack Connect invite details | + +### socket-mode-app + +| Scope | Reason | +|-------|--------| +| `app_mentions:read` | Receiving `app_mention` events | +| `channels:history` | Receiving message events | +| `chat:write` | Responding to events | + +### admin-app (user scopes) + +| Scope | Reason | +|-------|--------| +| `admin.analytics:read` | `admin.analytics.getFile` | +| `admin.conversations:read` | Reading channel retention, bulk operations | +| `admin.conversations:write` | Bulk archive/delete/move, custom retention | +| `admin.roles:read` | `admin.roles.listAssignments` | +| `admin.roles:write` | Role assignment operations | +| `admin.users:read` | Session settings, unsupported versions export | +| `admin.users:write` | Session reset/clear operations | diff --git a/prod-server-integration-tests/manifests/admin-app.manifest.json b/prod-server-integration-tests/manifests/admin-app.manifest.json new file mode 100644 index 000000000..2fe71f0a6 --- /dev/null +++ b/prod-server-integration-tests/manifests/admin-app.manifest.json @@ -0,0 +1,39 @@ +{ + "_metadata": { + "major_version": 1, + "minor_version": 1 + }, + "display_information": { + "name": "Node SDK Admin Tests", + "description": "Admin app for Enterprise Grid integration tests. Must be installed org-wide by an Org Admin." + }, + "features": { + "bot_user": { + "display_name": "SDK Admin Test Bot", + "always_online": false + } + }, + "oauth_config": { + "scopes": { + "bot": [ + "channels:manage", + "channels:read", + "users:read" + ], + "user": [ + "admin.analytics:read", + "admin.conversations:read", + "admin.conversations:write", + "admin.roles:read", + "admin.roles:write", + "admin.users:read", + "admin.users:write" + ] + } + }, + "settings": { + "org_deploy_enabled": true, + "socket_mode_enabled": false, + "token_rotation_enabled": false + } +} diff --git a/prod-server-integration-tests/manifests/main-bot-app.manifest.json b/prod-server-integration-tests/manifests/main-bot-app.manifest.json new file mode 100644 index 000000000..f4d898948 --- /dev/null +++ b/prod-server-integration-tests/manifests/main-bot-app.manifest.json @@ -0,0 +1,48 @@ +{ + "_metadata": { + "major_version": 1, + "minor_version": 1 + }, + "display_information": { + "name": "Node SDK Integration Tests", + "description": "Primary bot app for node-slack-sdk production integration tests" + }, + "features": { + "bot_user": { + "display_name": "Node SDK Test Bot", + "always_online": false + } + }, + "oauth_config": { + "scopes": { + "bot": [ + "bookmarks:read", + "bookmarks:write", + "channels:history", + "channels:manage", + "channels:read", + "channels:write", + "chat:write", + "conversations.connect:manage", + "conversations.connect:read", + "conversations.connect:write", + "groups:write", + "im:write", + "mpim:write", + "team.billing:read", + "team.preferences:read", + "users:read" + ], + "user": [ + "channels:history", + "channels:read", + "users:read" + ] + } + }, + "settings": { + "org_deploy_enabled": false, + "socket_mode_enabled": false, + "token_rotation_enabled": false + } +} diff --git a/prod-server-integration-tests/manifests/slack-connect-receiver.manifest.json b/prod-server-integration-tests/manifests/slack-connect-receiver.manifest.json new file mode 100644 index 000000000..9bb10949e --- /dev/null +++ b/prod-server-integration-tests/manifests/slack-connect-receiver.manifest.json @@ -0,0 +1,29 @@ +{ + "_metadata": { + "major_version": 1, + "minor_version": 1 + }, + "display_information": { + "name": "Node SDK Connect Receiver", + "description": "Slack Connect receiver bot for integration tests - installed in the RECEIVING workspace" + }, + "features": { + "bot_user": { + "display_name": "SDK Connect Receiver", + "always_online": false + } + }, + "oauth_config": { + "scopes": { + "bot": [ + "conversations.connect:manage", + "conversations.connect:read" + ] + } + }, + "settings": { + "org_deploy_enabled": false, + "socket_mode_enabled": false, + "token_rotation_enabled": false + } +} diff --git a/prod-server-integration-tests/manifests/slack-connect-sender.manifest.json b/prod-server-integration-tests/manifests/slack-connect-sender.manifest.json new file mode 100644 index 000000000..740df68b5 --- /dev/null +++ b/prod-server-integration-tests/manifests/slack-connect-sender.manifest.json @@ -0,0 +1,31 @@ +{ + "_metadata": { + "major_version": 1, + "minor_version": 1 + }, + "display_information": { + "name": "Node SDK Connect Sender", + "description": "Slack Connect sender bot for integration tests - installed in the SENDING workspace" + }, + "features": { + "bot_user": { + "display_name": "SDK Connect Sender", + "always_online": false + } + }, + "oauth_config": { + "scopes": { + "bot": [ + "channels:manage", + "conversations.connect:manage", + "conversations.connect:read", + "conversations.connect:write" + ] + } + }, + "settings": { + "org_deploy_enabled": false, + "socket_mode_enabled": false, + "token_rotation_enabled": false + } +} diff --git a/prod-server-integration-tests/manifests/socket-mode-app.manifest.json b/prod-server-integration-tests/manifests/socket-mode-app.manifest.json new file mode 100644 index 000000000..13b6a7d6d --- /dev/null +++ b/prod-server-integration-tests/manifests/socket-mode-app.manifest.json @@ -0,0 +1,38 @@ +{ + "_metadata": { + "major_version": 1, + "minor_version": 1 + }, + "display_information": { + "name": "Node SDK Socket Mode Test", + "description": "Socket Mode app for node-slack-sdk integration tests" + }, + "features": { + "bot_user": { + "display_name": "SDK Socket Mode Bot", + "always_online": false + } + }, + "oauth_config": { + "scopes": { + "bot": [ + "app_mentions:read", + "channels:history", + "chat:write" + ] + } + }, + "settings": { + "event_subscriptions": { + "bot_events": [ + "app_mention" + ] + }, + "interactivity": { + "is_enabled": true + }, + "org_deploy_enabled": false, + "socket_mode_enabled": true, + "token_rotation_enabled": false + } +} diff --git a/prod-server-integration-tests/package.json b/prod-server-integration-tests/package.json index 8728ff516..3c2353a2d 100644 --- a/prod-server-integration-tests/package.json +++ b/prod-server-integration-tests/package.json @@ -2,15 +2,19 @@ "name": "@slack/web-api-code-snippets", "description": "Basic examples of using @slack/web-api package", "scripts": { - "test": "mocha --timeout 10000", - "test:clean": "./link.sh && mocha --timeout 10000" + "build": "npm run build --prefix ../packages/logger && npm run build --prefix ../packages/types && npm run build --prefix ../packages/web-api && npm run build --prefix ../packages/socket-mode", + "clean": "rm -rf node_modules/ package-lock.json", + "test": "mocha --require dotenv/config --timeout 10000", + "test:clean": "npm run clean && npm run build && npm install && npm test" }, "repository": "https://github.com/slackapi/node-slack-sdk", "author": "Slack Technologies, LLC", "license": "MIT", "dependencies": { "dotenv": "^4", - "winston": "^3.3.3" + "winston": "^3.3.3", + "@slack/socket-mode": "file:../packages/socket-mode", + "@slack/web-api": "file:../packages/web-api" }, "devDependencies": { "chai": "^4.3.4", diff --git a/prod-server-integration-tests/test/bookmarks-web-api.js b/prod-server-integration-tests/test/bookmarks-web-api.js index ac9d23639..39622e642 100644 --- a/prod-server-integration-tests/test/bookmarks-web-api.js +++ b/prod-server-integration-tests/test/bookmarks-web-api.js @@ -1,6 +1,7 @@ // npx mocha --timeout 10000 test/bookmarks-web-api.js // run above from /prod-server-integration-tests require('mocha'); +const { config } = require('dotenv'); const { assert } = require('chai'); const { WebClient } = require('@slack/web-api'); @@ -12,7 +13,9 @@ const logger = winston.createLogger({ ], }); -describe('bookmarks.* Web APIs', async function() { +config(); + +describe('bookmarks.* Web APIs', async function () { // export SLACK_SDK_TEST_BOT_TOKEN=xoxb- // token used must have bookmarks:read, bookmarks:write scopes and channels:manage scopes to run tests properly @@ -20,49 +23,48 @@ describe('bookmarks.* Web APIs', async function() { describe('bookmarks.{list|add|edit|remove}', async function () { it('should work', async function () { - // prepare channel - const channelRes = await client.conversations.create({ - name: `bookmarks-${Date.now()}`, - }); - assert.isUndefined(channelRes.error); - const channelId = channelRes.channel.id; - - // get current bookmarks - const listRes = await client.bookmarks.list({ - channel_id: channelId, - }); - assert.isUndefined(listRes.error); - - // add new bookmark - const addRes = await client.bookmarks.add({ - channel_id: channelId, - title: `${Date().now}`, - type: 'link', - link: 'https://www.example.com', - }); - assert.isUndefined(addRes.error); - const bookmark_id = addRes.bookmark.id; - - assert.isNotNull(bookmark_id); - - // edit bookmark - const editRes = await client.bookmarks.edit({ - channel_id: channelId, - bookmark_id, - }); - assert.isUndefined(editRes.error); - - // remove bookmark - const removeRes = await client.bookmarks.remove({ - channel_id: channelId, - bookmark_id, - }); - assert.isUndefined(removeRes.error); - - // cleanup channel - client.conversations.archive({ - channel: channelId, - }); + let channelId; + try { + const channelRes = await client.conversations.create({ + name: `bookmarks-${Date.now()}`, + }); + assert.isUndefined(channelRes.error); + channelId = channelRes.channel.id; + + const listRes = await client.bookmarks.list({ + channel_id: channelId, + }); + assert.isUndefined(listRes.error); + + const addRes = await client.bookmarks.add({ + channel_id: channelId, + title: `${Date().now}`, + type: 'link', + link: 'https://www.example.com', + }); + assert.isUndefined(addRes.error); + const bookmark_id = addRes.bookmark.id; + + assert.isNotNull(bookmark_id); + + const editRes = await client.bookmarks.edit({ + channel_id: channelId, + bookmark_id, + }); + if (editRes.error) console.log('bookmarks.edit failed:', editRes.error); + assert.isUndefined(editRes.error); + + const removeRes = await client.bookmarks.remove({ + channel_id: channelId, + bookmark_id, + }); + if (removeRes.error) console.log('bookmarks.remove failed:', removeRes.error); + assert.isUndefined(removeRes.error); + } finally { + if (channelId) { + await client.conversations.archive({ channel: channelId }); + } + } }); }); -}); \ No newline at end of file +}); diff --git a/prod-server-integration-tests/test/web-api.js b/prod-server-integration-tests/test/web-api.js index c34b044de..32874f5a4 100644 --- a/prod-server-integration-tests/test/web-api.js +++ b/prod-server-integration-tests/test/web-api.js @@ -1,4 +1,5 @@ require('mocha'); +const { config } = require('dotenv'); const { assert } = require('chai'); const { WebClient } = require('@slack/web-api'); @@ -10,6 +11,8 @@ const logger = winston.createLogger({ ], }); +config(); + describe('Web APIs', function () { const botClient = new WebClient(process.env.SLACK_SDK_TEST_BOT_TOKEN, { logger, }); const userClient = new WebClient(process.env.SLACK_SDK_TEST_USER_TOKEN, { logger, }); @@ -30,7 +33,7 @@ describe('Web APIs', function () { }); describe('chat.scheduleMessage', function () { - it('should accept either an integer or a string value for post_at', async function() { + it('should accept either an integer or a string value for post_at', async function () { const channelId = process.env.SLACK_SDK_TEST_WEB_TEST_CHANNEL_ID; const postAt = Number.parseInt((Date.now() / 1000) + 60 * 15); try { @@ -55,7 +58,7 @@ describe('Web APIs', function () { }); }); - describe('Slack Connect conversations.* methods', async function (){ + describe('Slack Connect conversations.* methods', async function () { /* To run this test suite, we use two workspace-level bot tokens, one for the sending workspace(list and send invites) another for the receiving @@ -69,71 +72,71 @@ describe('Web APIs', function () { export SLACK_SDK_TEST_CONNECT_INVITE_RECEIVER_BOT_TOKEN= export SLACK_SDK_TEST_CONNECT_INVITE_RECEIVER_BOT_USER_ID= */ - const sender= new WebClient(process.env.SLACK_SDK_TEST_CONNECT_INVITE_SENDER_BOT_TOKEN); + const sender = new WebClient(process.env.SLACK_SDK_TEST_CONNECT_INVITE_SENDER_BOT_TOKEN); const receiver = new WebClient(process.env.SLACK_SDK_TEST_CONNECT_INVITE_RECEIVER_BOT_TOKEN) - describe('listConnectInvites', function () { - it('should list shared channel invites ', async function () { - const invites = await sender.conversations.listConnectInvites({}); - assert.isUndefined(invites.error); - }); + describe('listConnectInvites', function () { + it('should list shared channel invites ', async function () { + const invites = await sender.conversations.listConnectInvites({}); + assert.isUndefined(invites.error); }); - describe('inviteShared, acceptShared, approveShared', function () { - it('should successfully send an invite and accept it', async function () { - let channelId, inviteId = null; - let channelName = Date.now().toString().concat('-connect-test'); - try { - // creates channel to be shared - const newChannel = await sender.conversations.create({ - name: channelName, - }); - assert.isUndefined(newChannel.error); - channelId = newChannel.channel.id; - - // sends invite to reciever bot - const inviteShared = await sender.conversations.inviteShared({ - channel: channelId, - user_ids: process.env.SLACK_SDK_TEST_CONNECT_INVITE_RECEIVER_BOT_USER_ID, - }); - assert.isUndefined(inviteShared.error); - assert.isDefined(inviteShared.invite_id); - inviteId = inviteShared.invite_id; - - // accepts invite - const accepted = await receiver.conversations.acceptSharedInvite({ - channel_name: channelName, + }); + describe('inviteShared, acceptShared, approveShared', function () { + it('should successfully send an invite and accept it', async function () { + let channelId, inviteId = null; + let channelName = Date.now().toString().concat('-connect-test'); + try { + // creates channel to be shared + const newChannel = await sender.conversations.create({ + name: channelName, + }); + assert.isUndefined(newChannel.error); + channelId = newChannel.channel.id; + + // sends invite to reciever bot + const inviteShared = await sender.conversations.inviteShared({ + channel: channelId, + user_ids: process.env.SLACK_SDK_TEST_CONNECT_INVITE_RECEIVER_BOT_USER_ID, + }); + assert.isUndefined(inviteShared.error); + assert.isDefined(inviteShared.invite_id); + inviteId = inviteShared.invite_id; + + // accepts invite + const accepted = await receiver.conversations.acceptSharedInvite({ + channel_name: channelName, + invite_id: inviteId + }) + assert.isUndefined(accepted.error); + + if (!accepted.implicit_approval) { + // attempts to have receiver approve shared invite + await receiver.conversations.approveSharedInvite({ invite_id: inviteId }) - assert.isUndefined(accepted.error); - - if (!accepted.implicit_approval) { - // attempts to have receiver approve shared invite - await receiver.conversations.approveSharedInvite({ - invite_id: inviteId - }) - } - } finally { - // cleanup any created channels - if (channelId) { - const cleanup = await sender.conversations.archive({ channel: channelId }) - assert.isUndefined(cleanup.error) - } } - }); + } finally { + // cleanup any created channels + if (channelId) { + const cleanup = await sender.conversations.archive({ channel: channelId }) + assert.isUndefined(cleanup.error) + } + } }); }); + }); - describe('team.* for rtm.start migration', function () { + describe('team.* for rtm.start migration', function () { - it('should work with a bot token (team.billing.info)', async function () { - const response = await botClient.team.billing.info(); - logger.info(response); - assert.isUndefined(response.error); - }); - it('should work with a bot token (team.preferences.list)', async function () { - const response = await botClient.team.preferences.list(); - logger.info(response); - assert.isUndefined(response.error); - }); + it('should work with a bot token (team.billing.info)', async function () { + const response = await botClient.team.billing.info(); + logger.info(response); + assert.isUndefined(response.error); + }); + it('should work with a bot token (team.preferences.list)', async function () { + const response = await botClient.team.preferences.list(); + logger.info(response); + assert.isUndefined(response.error); }); - }); \ No newline at end of file + }); +}); From 80d1c9b5197af62515e7ee011fde52082e4255f5 Mon Sep 17 00:00:00 2001 From: William Bergamin Date: Thu, 7 May 2026 15:41:51 -0400 Subject: [PATCH 2/6] Update tests based on new format --- biome.json | 1 + prod-server-integration-tests/.env.sample | 23 +-- prod-server-integration-tests/README.md | 56 ++++--- .../manifests/README.md | 2 +- .../manifests/admin-app.manifest.json | 13 +- .../manifests/main-bot-app.manifest.json | 6 +- .../slack-connect-receiver.manifest.json | 5 +- .../manifests/socket-mode-app.manifest.json | 10 +- prod-server-integration-tests/package.json | 21 +-- .../scripts/admin/usersSessionSettings.js | 76 +++++----- .../scripts/api_test.js | 10 +- .../scripts/apps_manifest_test.js | 116 +++++++------- .../scripts/auth_test.js | 13 +- .../scripts/conversations_invite.js | 97 ++++++------ .../scripts/pagination.js | 22 ++- .../scripts/socket-mode.js | 15 +- .../test/admin-web-api-analytics.js | 74 --------- .../test/admin-web-api-analytics.test.js | 75 +++++++++ .../test/admin-web-api-conversations-bulk.js | 118 --------------- .../admin-web-api-conversations-bulk.test.js | 123 +++++++++++++++ ...=> admin-web-api-custom-retention.test.js} | 56 ++++--- .../test/admin-web-api-roles.js | 27 ---- .../test/admin-web-api-roles.test.js | 23 +++ .../test/admin-web-api-user-sessions.js | 54 ------- .../test/admin-web-api-user-sessions.test.js | 51 +++++++ ...eb-api-users-unsupportedVersions-export.js | 25 --- ...i-users-unsupportedVersions-export.test.js | 22 +++ .../test/admin-web-api.js | 106 ------------- .../test/admin-web-api.test.js | 97 ++++++++++++ .../test/bookmarks-web-api.js | 70 --------- .../test/bookmarks-web-api.test.js | 59 ++++++++ prod-server-integration-tests/test/web-api.js | 142 ------------------ .../test/web-api.test.js | 120 +++++++++++++++ 33 files changed, 818 insertions(+), 910 deletions(-) delete mode 100644 prod-server-integration-tests/test/admin-web-api-analytics.js create mode 100644 prod-server-integration-tests/test/admin-web-api-analytics.test.js delete mode 100644 prod-server-integration-tests/test/admin-web-api-conversations-bulk.js create mode 100644 prod-server-integration-tests/test/admin-web-api-conversations-bulk.test.js rename prod-server-integration-tests/test/{admin-web-api-custom-retention.js => admin-web-api-custom-retention.test.js} (51%) delete mode 100644 prod-server-integration-tests/test/admin-web-api-roles.js create mode 100644 prod-server-integration-tests/test/admin-web-api-roles.test.js delete mode 100644 prod-server-integration-tests/test/admin-web-api-user-sessions.js create mode 100644 prod-server-integration-tests/test/admin-web-api-user-sessions.test.js delete mode 100644 prod-server-integration-tests/test/admin-web-api-users-unsupportedVersions-export.js create mode 100644 prod-server-integration-tests/test/admin-web-api-users-unsupportedVersions-export.test.js delete mode 100644 prod-server-integration-tests/test/admin-web-api.js create mode 100644 prod-server-integration-tests/test/admin-web-api.test.js delete mode 100644 prod-server-integration-tests/test/bookmarks-web-api.js create mode 100644 prod-server-integration-tests/test/bookmarks-web-api.test.js delete mode 100644 prod-server-integration-tests/test/web-api.js create mode 100644 prod-server-integration-tests/test/web-api.test.js diff --git a/biome.json b/biome.json index ff88fccd0..f5371cb27 100644 --- a/biome.json +++ b/biome.json @@ -10,6 +10,7 @@ "files": { "includes": [ "packages/**", + "prod-server-integration-tests/**", "!!packages/client", "!!packages/events-api", "!!packages/interactive-messages" diff --git a/prod-server-integration-tests/.env.sample b/prod-server-integration-tests/.env.sample index cda8f27f8..a8366fa33 100644 --- a/prod-server-integration-tests/.env.sample +++ b/prod-server-integration-tests/.env.sample @@ -14,7 +14,8 @@ # ----------------------------------------------------------------------------- # Bot token (xoxb-) from the main bot app -# Used by: test/web-api.js, test/bookmarks-web-api.js, scripts/conversations_invite.js +# Used by: test/web-api.js, test/bookmarks-web-api.js, scripts/conversations_invite.js, +# scripts/auth_test.js, scripts/pagination.js SLACK_SDK_TEST_BOT_TOKEN=xoxb-your-bot-token # User token (xoxp-) from the main bot app (user must authorize the app) @@ -51,22 +52,18 @@ SLACK_SDK_TEST_CONNECT_INVITE_RECEIVER_BOT_USER_ID=U0123456789 # The app must be installed org-wide with admin scopes granted. # ----------------------------------------------------------------------------- -# Workspace-level admin user token (admin of a workspace within the Grid org) -# Used by: test/admin-web-api.js, test/admin-web-api-conversations-bulk.js, -# test/admin-web-api-custom-retention.js, test/admin-web-api-user-sessions.js, -# scripts/admin/usersSessionSettings.js -SLACK_SDK_TEST_GRID_WORKSPACE_ADMIN_USER_TOKEN=xoxp-workspace-admin-token +# Team ID of the PRIMARY workspace in the Enterprise Grid org +# Required by conversations.create when using Enterprise Grid tokens +# Used by: test/admin-web-api-conversations-bulk.js, test/admin-web-api-custom-retention.js +SLACK_SDK_TEST_GRID_WORKSPACE_ID=T0123456789 # Org-level admin user token (org admin of the Enterprise Grid) -# Used by: test/admin-web-api.js, test/admin-web-api-analytics.js, -# test/admin-web-api-conversations-bulk.js, test/admin-web-api-custom-retention.js, -# test/admin-web-api-roles.js, test/admin-web-api-user-sessions.js, -# test/admin-web-api-users-unsupportedVersions-export.js, +# Used by: all admin test files (test/admin-web-api*.js), # scripts/admin/usersSessionSettings.js SLACK_SDK_TEST_GRID_ORG_ADMIN_USER_TOKEN=xoxp-org-admin-token # Team ID of a SECONDARY workspace in the same Enterprise Grid org -# (must differ from the workspace of the workspace admin token above) +# (must differ from the primary workspace above) # Used by: test/admin-web-api-conversations-bulk.js (bulkMove test) SLACK_SDK_TEST_GRID_SECONDARY_WORKSPACE_ID=T0123456789 @@ -89,10 +86,6 @@ SLACK_SDK_TEST_SOCKET_MODE_APP_TOKEN=xapp-your-app-level-token # Some scripts use alternate variable names for convenience. # ----------------------------------------------------------------------------- -# A bot token for simple script examples (can be same as SLACK_SDK_TEST_BOT_TOKEN) -# Used by: scripts/auth_test.js, scripts/pagination.js -SLACK_BOT_TOKEN=xoxb-your-bot-token - # (Optional) A user ID for pagination script filtering # Used by: scripts/pagination.js # SLACK_USER_ID=U0123456789 diff --git a/prod-server-integration-tests/README.md b/prod-server-integration-tests/README.md index 4c4ad06c1..fc50b7d5f 100644 --- a/prod-server-integration-tests/README.md +++ b/prod-server-integration-tests/README.md @@ -5,28 +5,28 @@ End-to-end integration tests that run against real Slack APIs to verify the ## Prerequisites -1. **Link local packages** to test the latest code from this repository: +1. **Build local packages** that these tests depend on: ```bash -# For Web API tests -./link-web-api.sh +npm run build +``` + +2. **Install dependencies**: -# For Socket Mode tests -./link-socket-mode.sh +```bash +npm install ``` -2. **Create the required Slack apps** using the manifests in [`manifests/`](./manifests/). +3. **Create the required Slack apps** using the manifests in [`manifests/`](./manifests/). See [`manifests/README.md`](./manifests/README.md) for detailed setup instructions. -3. **Configure environment variables** by copying the sample file: +4. **Configure environment variables** by copying the sample file: ```bash cp .env.sample .env # Edit .env and fill in your tokens/IDs ``` -The `dotenv` package loads `.env` automatically when tests run. - ## Running Tests ### All tests @@ -39,54 +39,52 @@ npm test ```bash # Core Web API tests (auth, chat.scheduleMessage, Slack Connect, team.*) -npx mocha --require dotenv/config --timeout 10000 test/web-api.js +node --test-timeout=10000 --test test/web-api.test.js # Bookmarks CRUD -npx mocha --require dotenv/config --timeout 10000 test/bookmarks-web-api.js +node --test-timeout=10000 --test test/bookmarks-web-api.test.js # Admin: session settings + auth policy -npx mocha --require dotenv/config --timeout 10000 test/admin-web-api.js +node --test-timeout=10000 --test test/admin-web-api.test.js # Admin: analytics -npx mocha --require dotenv/config --timeout 10000 test/admin-web-api-analytics.js +node --test-timeout=10000 --test test/admin-web-api-analytics.test.js # Admin: bulk archive/delete/move -npx mocha --require dotenv/config --timeout 10000 test/admin-web-api-conversations-bulk.js +node --test-timeout=10000 --test test/admin-web-api-conversations-bulk.test.js # Admin: custom retention -npx mocha --require dotenv/config --timeout 10000 test/admin-web-api-custom-retention.js +node --test-timeout=10000 --test test/admin-web-api-custom-retention.test.js # Admin: roles -npx mocha --require dotenv/config --timeout 10000 test/admin-web-api-roles.js +node --test-timeout=10000 --test test/admin-web-api-roles.test.js # Admin: user sessions reset -npx mocha --require dotenv/config --timeout 10000 test/admin-web-api-user-sessions.js +node --test-timeout=10000 --test test/admin-web-api-user-sessions.test.js # Admin: unsupported versions export -npx mocha --require dotenv/config --timeout 10000 test/admin-web-api-users-unsupportedVersions-export.js +node --test-timeout=10000 --test test/admin-web-api-users-unsupportedVersions-export.test.js ``` ### Example scripts ```bash # No token needed -npx node scripts/api_test.js - -# Requires SLACK_BOT_TOKEN -npx node scripts/auth_test.js -npx node scripts/pagination.js +node scripts/api_test.js # Requires SLACK_SDK_TEST_BOT_TOKEN -npx node scripts/conversations_invite.js +node scripts/auth_test.js +node scripts/pagination.js +node scripts/conversations_invite.js # Requires SLACK_SDK_TEST_SOCKET_MODE_APP_TOKEN -npx node scripts/socket-mode.js +node scripts/socket-mode.js # Requires SLACK_TOOLING_TOKEN -npx node scripts/apps_manifest_test.js +node scripts/apps_manifest_test.js # Requires SLACK_SDK_TEST_GRID_*_TOKEN vars -npx node scripts/admin/usersSessionSettings.js +node scripts/admin/usersSessionSettings.js ``` ## Environment Variables @@ -95,18 +93,16 @@ See [`.env.sample`](./.env.sample) for the complete list with descriptions. Summ | Variable | Source App | Used By | |---|---|---| -| `SLACK_SDK_TEST_BOT_TOKEN` | main-bot-app | web-api, bookmarks, conversations_invite | +| `SLACK_SDK_TEST_BOT_TOKEN` | main-bot-app | web-api, bookmarks, conversations_invite, auth_test, pagination | | `SLACK_SDK_TEST_USER_TOKEN` | main-bot-app (user OAuth) | web-api | | `SLACK_SDK_TEST_WEB_TEST_CHANNEL_ID` | — (channel in workspace) | chat.scheduleMessage test | | `SLACK_SDK_TEST_CONNECT_INVITE_SENDER_BOT_TOKEN` | slack-connect-sender | Slack Connect tests | | `SLACK_SDK_TEST_CONNECT_INVITE_RECEIVER_BOT_TOKEN` | slack-connect-receiver | Slack Connect tests | | `SLACK_SDK_TEST_CONNECT_INVITE_RECEIVER_BOT_USER_ID` | — (user ID) | Slack Connect tests | -| `SLACK_SDK_TEST_GRID_WORKSPACE_ADMIN_USER_TOKEN` | admin-app (user OAuth) | all admin tests | | `SLACK_SDK_TEST_GRID_ORG_ADMIN_USER_TOKEN` | admin-app (user OAuth) | all admin tests | | `SLACK_SDK_TEST_GRID_SECONDARY_WORKSPACE_ID` | — (team ID) | admin bulk move test | | `SLACK_SDK_TEST_GRID_USER_ID` | — (user ID, optional) | admin auth policy test | | `SLACK_SDK_TEST_SOCKET_MODE_APP_TOKEN` | socket-mode-app | Socket Mode script | -| `SLACK_BOT_TOKEN` | main-bot-app (or any) | auth_test, pagination scripts | | `SLACK_TOOLING_TOKEN` | — (configuration token) | apps_manifest_test script | ## App Manifests diff --git a/prod-server-integration-tests/manifests/README.md b/prod-server-integration-tests/manifests/README.md index a6a140e05..588062221 100644 --- a/prod-server-integration-tests/manifests/README.md +++ b/prod-server-integration-tests/manifests/README.md @@ -11,7 +11,7 @@ to run the production integration tests. | `slack-connect-sender.manifest.json` | Slack Connect sender workspace bot | `SLACK_SDK_TEST_CONNECT_INVITE_SENDER_BOT_TOKEN` | | `slack-connect-receiver.manifest.json` | Slack Connect receiver workspace bot | `SLACK_SDK_TEST_CONNECT_INVITE_RECEIVER_BOT_TOKEN` | | `socket-mode-app.manifest.json` | Socket Mode listener | `SLACK_SDK_TEST_SOCKET_MODE_APP_TOKEN` | -| `admin-app.manifest.json` | Enterprise Grid admin operations | `SLACK_SDK_TEST_GRID_WORKSPACE_ADMIN_USER_TOKEN`, `SLACK_SDK_TEST_GRID_ORG_ADMIN_USER_TOKEN` | +| `admin-app.manifest.json` | Enterprise Grid admin operations | `SLACK_SDK_TEST_GRID_ORG_ADMIN_USER_TOKEN` | ## How to Use These Manifests diff --git a/prod-server-integration-tests/manifests/admin-app.manifest.json b/prod-server-integration-tests/manifests/admin-app.manifest.json index 2fe71f0a6..d6caf87d9 100644 --- a/prod-server-integration-tests/manifests/admin-app.manifest.json +++ b/prod-server-integration-tests/manifests/admin-app.manifest.json @@ -15,11 +15,7 @@ }, "oauth_config": { "scopes": { - "bot": [ - "channels:manage", - "channels:read", - "users:read" - ], + "bot": ["channels:manage", "channels:read", "users:read"], "user": [ "admin.analytics:read", "admin.conversations:read", @@ -27,7 +23,12 @@ "admin.roles:read", "admin.roles:write", "admin.users:read", - "admin.users:write" + "admin.users:write", + "channels:write", + "groups:write", + "mpim:write", + "im:write", + "users:read" ] } }, diff --git a/prod-server-integration-tests/manifests/main-bot-app.manifest.json b/prod-server-integration-tests/manifests/main-bot-app.manifest.json index f4d898948..aad9b583c 100644 --- a/prod-server-integration-tests/manifests/main-bot-app.manifest.json +++ b/prod-server-integration-tests/manifests/main-bot-app.manifest.json @@ -33,11 +33,7 @@ "team.preferences:read", "users:read" ], - "user": [ - "channels:history", - "channels:read", - "users:read" - ] + "user": ["channels:history", "channels:read", "users:read"] } }, "settings": { diff --git a/prod-server-integration-tests/manifests/slack-connect-receiver.manifest.json b/prod-server-integration-tests/manifests/slack-connect-receiver.manifest.json index 9bb10949e..77e92432f 100644 --- a/prod-server-integration-tests/manifests/slack-connect-receiver.manifest.json +++ b/prod-server-integration-tests/manifests/slack-connect-receiver.manifest.json @@ -15,10 +15,7 @@ }, "oauth_config": { "scopes": { - "bot": [ - "conversations.connect:manage", - "conversations.connect:read" - ] + "bot": ["conversations.connect:manage", "conversations.connect:write", "conversations.connect:read"] } }, "settings": { diff --git a/prod-server-integration-tests/manifests/socket-mode-app.manifest.json b/prod-server-integration-tests/manifests/socket-mode-app.manifest.json index 13b6a7d6d..d27a30011 100644 --- a/prod-server-integration-tests/manifests/socket-mode-app.manifest.json +++ b/prod-server-integration-tests/manifests/socket-mode-app.manifest.json @@ -15,18 +15,12 @@ }, "oauth_config": { "scopes": { - "bot": [ - "app_mentions:read", - "channels:history", - "chat:write" - ] + "bot": ["app_mentions:read", "channels:history", "chat:write"] } }, "settings": { "event_subscriptions": { - "bot_events": [ - "app_mention" - ] + "bot_events": ["app_mention"] }, "interactivity": { "is_enabled": true diff --git a/prod-server-integration-tests/package.json b/prod-server-integration-tests/package.json index 3c2353a2d..a45546a5c 100644 --- a/prod-server-integration-tests/package.json +++ b/prod-server-integration-tests/package.json @@ -1,23 +1,24 @@ { - "name": "@slack/web-api-code-snippets", - "description": "Basic examples of using @slack/web-api package", + "name": "@slack/prod-server-integration-tests", + "private": true, + "type": "module", + "description": "Integration tests running against real Slack APIs", "scripts": { "build": "npm run build --prefix ../packages/logger && npm run build --prefix ../packages/types && npm run build --prefix ../packages/web-api && npm run build --prefix ../packages/socket-mode", "clean": "rm -rf node_modules/ package-lock.json", - "test": "mocha --require dotenv/config --timeout 10000", + "lint": "npx biome check ../prod-server-integration-tests/", + "lint:fix": "npx biome check --write ../prod-server-integration-tests/", + "test": "node --test-timeout=10000 --test-reporter=spec --test-reporter-destination=stdout --test test/*.test.js", "test:clean": "npm run clean && npm run build && npm install && npm test" }, "repository": "https://github.com/slackapi/node-slack-sdk", "author": "Slack Technologies, LLC", "license": "MIT", "dependencies": { - "dotenv": "^4", - "winston": "^3.3.3", "@slack/socket-mode": "file:../packages/socket-mode", - "@slack/web-api": "file:../packages/web-api" + "@slack/web-api": "file:../packages/web-api", + "dotenv": "^16", + "winston": "^3.3.3" }, - "devDependencies": { - "chai": "^4.3.4", - "mocha": "^8.3.2" - } + "devDependencies": {} } diff --git a/prod-server-integration-tests/scripts/admin/usersSessionSettings.js b/prod-server-integration-tests/scripts/admin/usersSessionSettings.js index 36a08bfbb..2a0cbcd2a 100644 --- a/prod-server-integration-tests/scripts/admin/usersSessionSettings.js +++ b/prod-server-integration-tests/scripts/admin/usersSessionSettings.js @@ -1,48 +1,42 @@ -// export SLACK_SDK_TEST_GRID_ORG_ADMIN_USER_TOKEN= -// export SLACK_SDK_TEST_GRID_WORKSPACE_ADMIN_USER_TOKEN= +import 'dotenv/config'; +import { WebClient } from '@slack/web-api'; -const { WebClient } = require('@slack/web-api'); - -(async () => { - // fetch 3 active members from a workspace - const teamAdminClient = new WebClient(process.env.SLACK_SDK_TEST_GRID_WORKSPACE_ADMIN_USER_TOKEN); - const users = await teamAdminClient.users.list({ - exclude_archived: true, - limit: 100 - }); - const userIds = []; - for (const u of users.members) { - if (userIds.length >= 3) { - break; - } - if (!u.is_bot && !u.deleted && !u.is_app_user && !u.is_owner && u.id !== 'USLACKBOT') { - userIds.push(u.id); - } +const teamAdminClient = new WebClient(process.env.SLACK_SDK_TEST_GRID_ORG_ADMIN_USER_TOKEN); +const users = await teamAdminClient.users.list({ + exclude_archived: true, + limit: 100, +}); +const userIds = []; +for (const u of users.members) { + if (userIds.length >= 3) { + break; + } + if (!u.is_bot && !u.deleted && !u.is_app_user && !u.is_owner && u.id !== 'USLACKBOT') { + userIds.push(u.id); } +} - // call get/set/clearSettings APIs - const orgAdminClient = new WebClient(process.env.SLACK_SDK_TEST_GRID_ORG_ADMIN_USER_TOKEN, { - logLevel: 'debug' - }); +const orgAdminClient = new WebClient(process.env.SLACK_SDK_TEST_GRID_ORG_ADMIN_USER_TOKEN, { + logLevel: 'debug', +}); - const get = await orgAdminClient.admin.users.session.getSettings({ - user_ids: userIds - }); - console.log(JSON.stringify(get, null, 2)); +const get = await orgAdminClient.admin.users.session.getSettings({ + user_ids: userIds, +}); +console.log(JSON.stringify(get, null, 2)); - const set = await orgAdminClient.admin.users.session.setSettings({ - user_ids: userIds, - duration: 60 * 60 * 24 * 30 - }); - console.log(JSON.stringify(set, null, 2)); +const set = await orgAdminClient.admin.users.session.setSettings({ + user_ids: userIds, + duration: 60 * 60 * 24 * 30, +}); +console.log(JSON.stringify(set, null, 2)); - const get2 = await orgAdminClient.admin.users.session.getSettings({ - user_ids: userIds - }); - console.log(JSON.stringify(get2, null, 2)); +const get2 = await orgAdminClient.admin.users.session.getSettings({ + user_ids: userIds, +}); +console.log(JSON.stringify(get2, null, 2)); - const clear = await orgAdminClient.admin.users.session.clearSettings({ - user_ids: userIds - }); - console.log(JSON.stringify(clear, null, 2)); -})(); \ No newline at end of file +const clear = await orgAdminClient.admin.users.session.clearSettings({ + user_ids: userIds, +}); +console.log(JSON.stringify(clear, null, 2)); diff --git a/prod-server-integration-tests/scripts/api_test.js b/prod-server-integration-tests/scripts/api_test.js index bed92759a..383c6377e 100644 --- a/prod-server-integration-tests/scripts/api_test.js +++ b/prod-server-integration-tests/scripts/api_test.js @@ -1,6 +1,6 @@ -const { WebClient } = require('@slack/web-api'); +import 'dotenv/config'; +import { WebClient } from '@slack/web-api'; + const client = new WebClient(); -(async () => { - const response = await client.api.test({ foo: "bar" }); - console.log(JSON.stringify(response, null, 2)); -})(); \ No newline at end of file +const response = await client.api.test({ foo: 'bar' }); +console.log(JSON.stringify(response, null, 2)); diff --git a/prod-server-integration-tests/scripts/apps_manifest_test.js b/prod-server-integration-tests/scripts/apps_manifest_test.js index 20788485a..ba1b32675 100644 --- a/prod-server-integration-tests/scripts/apps_manifest_test.js +++ b/prod-server-integration-tests/scripts/apps_manifest_test.js @@ -1,4 +1,5 @@ -const { WebClient } = require('@slack/web-api'); +import 'dotenv/config'; +import { WebClient } from '@slack/web-api'; const options = {}; @@ -10,67 +11,62 @@ if (process.env.SLACK_SDK_TEST_DEV_API_URL !== undefined) { const client = new WebClient(options); -(async () => { - const manifest = `{ - "_metadata": { - "major_version": 1, - "minor_version": 1 - }, - "display_information": { - "name": "Test Manifest App" - }, - "features": { - "bot_user": { - "display_name": "Test Manifest App", - "always_online": false - } - }, - "oauth_config": { - "scopes": { - "bot": [ - "channels:history" - ] - } - }, - "settings": { - "event_subscriptions": { - "request_url": "https://yourhosthere.ngrok.io/slack/events", - "bot_events": [ - "message.channels" - ] - }, - "org_deploy_enabled": false, - "socket_mode_enabled": false, - "is_hosted": false, - "token_rotation_enabled": false - } - }`; +const manifest = `{ + "_metadata": { + "major_version": 1, + "minor_version": 1 + }, + "display_information": { + "name": "Test Manifest App" + }, + "features": { + "bot_user": { + "display_name": "Test Manifest App", + "always_online": false + } + }, + "oauth_config": { + "scopes": { + "bot": [ + "channels:history" + ] + } + }, + "settings": { + "event_subscriptions": { + "request_url": "https://yourhosthere.ngrok.io/slack/events", + "bot_events": [ + "message.channels" + ] + }, + "org_deploy_enabled": false, + "socket_mode_enabled": false, + "is_hosted": false, + "token_rotation_enabled": false + } +}`; - let appId; +let appId; - try { +try { + const validateRes = await client.apps.manifest.validate({ token: TOOLING_TOKEN, manifest }); + console.log(JSON.stringify(validateRes, null, 2)); - // Verify a manifest is valid - const validateRes = await client.apps.manifest.validate({ token: TOOLING_TOKEN, manifest }); - console.log(JSON.stringify(validateRes, null, 2)); + const createRes = await client.apps.manifest.create({ token: TOOLING_TOKEN, manifest }); + appId = createRes.app_id; + console.log(JSON.stringify(createRes, null, 2)); - // Create a new app using a manifest - const createRes = await client.apps.manifest.create({ token: TOOLING_TOKEN, manifest }); - appId = createRes.app_id - console.log(JSON.stringify(createRes, null, 2)); + const updatedManifest = { ...JSON.parse(manifest), display_information: { name: 'Updated Test Manifest App' } }; + const updateRes = await client.apps.manifest.update({ + token: TOOLING_TOKEN, + app_id: appId, + manifest: updatedManifest, + }); + console.log(JSON.stringify(updateRes, null, 2)); - // Update the app using an altered manifest - const updatedManifest = { ...JSON.parse(manifest), display_information: { name: 'Updated Test Manifest App' } }; - const updateRes = await client.apps.manifest.update({ token: TOOLING_TOKEN, app_id: appId, manifest: updatedManifest }); - console.log(JSON.stringify(updateRes, null, 2)); - - // Export (get) the app's manifest - const exportRes = await client.apps.manifest.export({ token: TOOLING_TOKEN, app_id: appId }); - console.log(JSON.stringify(exportRes, null, 2)); - - } finally { - // Delete the app - const deleteRes = await client.apps.manifest.delete({ token: TOOLING_TOKEN, app_id: appId }); - console.log(JSON.stringify(deleteRes, null, 2)); - }; -})(); \ No newline at end of file + const exportRes = await client.apps.manifest.export({ token: TOOLING_TOKEN, app_id: appId }); + console.log(JSON.stringify(exportRes, null, 2)); +} finally { + const deleteRes = await client.apps.manifest.delete({ token: TOOLING_TOKEN, app_id: appId }); + console.log(JSON.stringify(deleteRes, null, 2)); +} diff --git a/prod-server-integration-tests/scripts/auth_test.js b/prod-server-integration-tests/scripts/auth_test.js index 34e082a55..9e51ad9ee 100644 --- a/prod-server-integration-tests/scripts/auth_test.js +++ b/prod-server-integration-tests/scripts/auth_test.js @@ -1,7 +1,6 @@ -const { WebClient } = require('@slack/web-api'); -const token = process.env.SLACK_BOT_TOKEN; -const client = new WebClient(token); -(async () => { - const response = await client.auth.test(); - console.log(JSON.stringify(response, null, 2)); -})(); \ No newline at end of file +import 'dotenv/config'; +import { WebClient } from '@slack/web-api'; + +const client = new WebClient(process.env.SLACK_SDK_TEST_BOT_TOKEN); +const response = await client.auth.test(); +console.log(JSON.stringify(response, null, 2)); diff --git a/prod-server-integration-tests/scripts/conversations_invite.js b/prod-server-integration-tests/scripts/conversations_invite.js index 3b237492b..2a80d5f40 100644 --- a/prod-server-integration-tests/scripts/conversations_invite.js +++ b/prod-server-integration-tests/scripts/conversations_invite.js @@ -1,57 +1,50 @@ -// Simple example script demonstrating how to create a new channel and invite members using a bot token -// This example is originally created to verify if the web-api works without any issues for https://github.com/slackapi/bolt-js/issues/973 but the code should be useful for general purposes. +import 'dotenv/config'; +import { WebClient } from '@slack/web-api'; -const { WebClient } = require('@slack/web-api'); +const client = new WebClient(process.env.SLACK_SDK_TEST_BOT_TOKEN, { logLevel: 'debug' }); -const client = new WebClient( - token = process.env.SLACK_SDK_TEST_BOT_TOKEN, - { logLevel: 'debug' }, -); +let channelId = null; +try { + const teamId = (await client.auth.test()).team_id; + const creation = await client.conversations.create({ + name: `test-channel-${Date.now()}`, + team_id: teamId, + }); + console.log(JSON.stringify(creation, null, 2)); -(async () => { - let channelId = null; - try { - // Create a new public channel - const teamId = (await client.auth.test()).team_id; - const creation = await client.conversations.create({ - name: `test-channel-${Date.now()}`, - team_id: teamId, - }) - console.log(JSON.stringify(creation, null, 2)); - - channelId = creation.channel.id; + channelId = creation.channel.id; - // Randomly pick up human user IDs to invite - const userIds = (await client.users.list({ - limit: 100 - })).members.filter(m => - m.id !== 'USLACKBOT' && // USLACKBOT is a special user ID for @SlackBot - !m.is_bot && - !m.is_invited_user && - !m.is_restricted && - !m.is_ultra_restricted && - !m.deleted && - !m.is_workflow_bot - ).map(m => m.id).slice(0, 5); - - // Invite the users to the channel - const invitations = await client.conversations.invite({ - channel: channelId, - users: userIds.join(","), + const userIds = ( + await client.users.list({ + limit: 100, }) - console.log(JSON.stringify(invitations, null, 2)); - - // Verify if the channel has members as expected - const members = await client.conversations.members({ channel: channelId }) - console.log(JSON.stringify(members, null, 2)); - - } finally { - // Archive the channel if you want to remove the channel from your Slack client view - if (channelId !== null) { - const cleanup = await client.conversations.archive({ - channel: channelId, - }) - console.log(JSON.stringify(cleanup, null, 2)); - } - }; -})(); \ No newline at end of file + ).members + .filter( + (m) => + m.id !== 'USLACKBOT' && + !m.is_bot && + !m.is_invited_user && + !m.is_restricted && + !m.is_ultra_restricted && + !m.deleted && + !m.is_workflow_bot, + ) + .map((m) => m.id) + .slice(0, 5); + + const invitations = await client.conversations.invite({ + channel: channelId, + users: userIds.join(','), + }); + console.log(JSON.stringify(invitations, null, 2)); + + const members = await client.conversations.members({ channel: channelId }); + console.log(JSON.stringify(members, null, 2)); +} finally { + if (channelId !== null) { + const cleanup = await client.conversations.archive({ + channel: channelId, + }); + console.log(JSON.stringify(cleanup, null, 2)); + } +} diff --git a/prod-server-integration-tests/scripts/pagination.js b/prod-server-integration-tests/scripts/pagination.js index a733ac4bb..37340534a 100644 --- a/prod-server-integration-tests/scripts/pagination.js +++ b/prod-server-integration-tests/scripts/pagination.js @@ -1,15 +1,13 @@ -const { WebClient } = require('@slack/web-api'); -const token = process.env.SLACK_BOT_TOKEN; -const client = new WebClient(token); -const user = process.env.SLACK_USER_ID; +import 'dotenv/config'; +import { WebClient } from '@slack/web-api'; + +const client = new WebClient(process.env.SLACK_SDK_TEST_BOT_TOKEN); const parameters = { limit: 1 }; -if (user !== undefined) { - parameters.user = user; +if (process.env.SLACK_USER_ID !== undefined) { + parameters.user = process.env.SLACK_USER_ID; } -(async () => { - // https://docs.slack.dev/reference/methods/reactions.list - for await (const page of client.paginate('reactions.list', parameters)) { - console.log(JSON.stringify(page, null, 2)); - } -})(); \ No newline at end of file +// https://docs.slack.dev/reference/methods/reactions.list +for await (const page of client.paginate('reactions.list', parameters)) { + console.log(JSON.stringify(page, null, 2)); +} diff --git a/prod-server-integration-tests/scripts/socket-mode.js b/prod-server-integration-tests/scripts/socket-mode.js index 8c8336b4d..fe159676d 100644 --- a/prod-server-integration-tests/scripts/socket-mode.js +++ b/prod-server-integration-tests/scripts/socket-mode.js @@ -1,28 +1,27 @@ -const { SocketModeClient } = require('@slack/socket-mode'); +import 'dotenv/config'; +import { SocketModeClient } from '@slack/socket-mode'; + const appToken = process.env.SLACK_SDK_TEST_SOCKET_MODE_APP_TOKEN; const socketModeClient = new SocketModeClient({ appToken, logLevel: 'debug' }); -// events_api's event.type socketModeClient.on('app_mention', async (args) => { console.log(args); await args.ack(); }); -// interactivity: block_actions etc. + socketModeClient.on('interactive', async (args) => { console.log(args); await args.ack(); }); -// slash command invocations + socketModeClient.on('slash_commands', async (args) => { console.log(args); await args.ack(); }); -// events sent to bolt-js + socketModeClient.on('slack_event', async (args) => { console.log(args); await args.ack(); }); -(async () => { - await socketModeClient.start(); -})(); +await socketModeClient.start(); diff --git a/prod-server-integration-tests/test/admin-web-api-analytics.js b/prod-server-integration-tests/test/admin-web-api-analytics.js deleted file mode 100644 index 385ac8031..000000000 --- a/prod-server-integration-tests/test/admin-web-api-analytics.js +++ /dev/null @@ -1,74 +0,0 @@ -require('mocha'); -const { assert } = require('chai'); -const { WebClient } = require('@slack/web-api'); - -const winston = require('winston'); -const logger = winston.createLogger({ - level: 'debug', - transports: [ - new winston.transports.File({ filename: 'logs/console.log' }), - ], -}); - -describe('admin.analytics.* Web API', function () { - // org-level admin user's token - const orgAdminClient = new WebClient(process.env.SLACK_SDK_TEST_GRID_ORG_ADMIN_USER_TOKEN, { logger, }); - - describe('admin.analytics.getFile', function () { - it('should get an available file for a public_channel type and a date that has passed', async function () { - // This test may fail depending on the enterprise grid account you are testing on. - // To get this test to pass, please adjust the date to anywhere from 1 day after - // your enterprise grid org was created to the current date. - const body = { type: 'public_channel', date: '2022-07-06' }; - const res = await orgAdminClient.admin.analytics.getFile(body); - assert.isUndefined(res.error); - assert.isDefined(res.file_data); - assert.property(res.file_data[0], 'total_members_count'); - assert.property(res.file_data[0], 'full_members_count'); - }); - - it('should fail to get an available file for a future date', async function () { - // This test may fail depending on the current date and the enterprise grid account you are testing on. - // To get this test to pass, please adjust the date to anywhere after today's date. - const body = { type: 'public_channel', date: '2035-10-06' }; - try { - await orgAdminClient.admin.analytics.getFile(body); - } catch (error) { - assert.isFalse(error.data.ok); - assert.equal(error.data.error, 'file_not_yet_available'); - } - }); - - it('should get an available file for a member type and a date that has passed', async function () { - // This test may fail depending on the enterprise grid account you are testing on. - // To get this test to pass, please adjust the date to anywhere from 1 day after - // your enterprise grid org was created to the current date. - const body = { type: 'member', date: '2022-07-06' }; - const res1 = await orgAdminClient.admin.analytics.getFile(body); - assert.isUndefined(res1.error); - assert.isDefined(res1.file_data); - assert.property(res1.file_data[0], 'user_id'); - assert.property(res1.file_data[0], 'email_address'); - }); - - it('should get metadata when using public_channel type and metadata_only options', async function () { - const body = { type: 'public_channel', metadata_only: true }; - const res2 = await orgAdminClient.admin.analytics.getFile(body); - assert.isUndefined(res2.error); - assert.isDefined(res2.file_data); - assert.property(res2.file_data[0], 'name'); - assert.property(res2.file_data[0], 'topic'); - assert.property(res2.file_data[0], 'description'); - }); - - it('should fail to get metadata when using member type and metadata_only options', async function () { - const body = { type: 'member', metadata_only: true }; - try { - await orgAdminClient.admin.analytics.getFile(body); - } catch (error) { - assert.isFalse(error.data.ok); - assert.equal(error.data.error, 'metadata_not_available'); - } - }); - }); -}); diff --git a/prod-server-integration-tests/test/admin-web-api-analytics.test.js b/prod-server-integration-tests/test/admin-web-api-analytics.test.js new file mode 100644 index 000000000..ad810cb7f --- /dev/null +++ b/prod-server-integration-tests/test/admin-web-api-analytics.test.js @@ -0,0 +1,75 @@ +import 'dotenv/config'; +import assert from 'node:assert/strict'; +import { describe, it } from 'node:test'; +import { WebClient } from '@slack/web-api'; +import winston from 'winston'; + +const logger = winston.createLogger({ + level: 'debug', + transports: [new winston.transports.File({ filename: 'logs/console.log' })], +}); + +/** @param {Date} date @returns {string} Date formatted as YYYY-MM-DD */ +function formatDate(date) { + return date.toISOString().split('T')[0]; +} + +const today = new Date(); +const yesterday = new Date(today); +yesterday.setDate(yesterday.getDate() - 1); +const tomorrow = new Date(today); +tomorrow.setDate(tomorrow.getDate() + 1); + +describe('admin.analytics.* Web API', () => { + const orgAdminClient = new WebClient(process.env.SLACK_SDK_TEST_GRID_ORG_ADMIN_USER_TOKEN, { logger }); + + describe('admin.analytics.getFile', () => { + it('should get an available file for a public_channel type and a date that has passed', async () => { + const body = { type: 'public_channel', date: formatDate(yesterday) }; + const res = await orgAdminClient.admin.analytics.getFile(body); + assert.equal(res.error, undefined, res.error); + assert.notEqual(res.file_data, undefined); + assert.ok('total_members_count' in res.file_data[0]); + assert.ok('full_members_count' in res.file_data[0]); + }); + + it('should fail to get an available file for a future date', async () => { + const body = { type: 'public_channel', date: formatDate(tomorrow) }; + try { + await orgAdminClient.admin.analytics.getFile(body); + } catch (error) { + assert.strictEqual(error.data.ok, false); + assert.equal(error.data.error, 'file_not_yet_available'); + } + }); + + it('should get an available file for a member type and a date that has passed', async () => { + const body = { type: 'member', date: formatDate(yesterday) }; + const res1 = await orgAdminClient.admin.analytics.getFile(body); + assert.equal(res1.error, undefined, res1.error); + assert.notEqual(res1.file_data, undefined); + assert.ok('user_id' in res1.file_data[0]); + assert.ok('email_address' in res1.file_data[0]); + }); + + it('should get metadata when using public_channel type and metadata_only options', async () => { + const body = { type: 'public_channel', metadata_only: true }; + const res2 = await orgAdminClient.admin.analytics.getFile(body); + assert.equal(res2.error, undefined, res2.error); + assert.notEqual(res2.file_data, undefined); + assert.ok('name' in res2.file_data[0]); + assert.ok('topic' in res2.file_data[0]); + assert.ok('description' in res2.file_data[0]); + }); + + it('should fail to get metadata when using member type and metadata_only options', async () => { + const body = { type: 'member', metadata_only: true }; + try { + await orgAdminClient.admin.analytics.getFile(body); + } catch (error) { + assert.strictEqual(error.data.ok, false); + assert.equal(error.data.error, 'metadata_not_available'); + } + }); + }); +}); diff --git a/prod-server-integration-tests/test/admin-web-api-conversations-bulk.js b/prod-server-integration-tests/test/admin-web-api-conversations-bulk.js deleted file mode 100644 index 19eb05122..000000000 --- a/prod-server-integration-tests/test/admin-web-api-conversations-bulk.js +++ /dev/null @@ -1,118 +0,0 @@ -require('mocha'); -const { assert } = require('chai'); -const { WebClient } = require('@slack/web-api'); - -const winston = require('winston'); -const logger = winston.createLogger({ - level: 'debug', - transports: [ - new winston.transports.File({ filename: 'logs/console.log' }), - ], -}); - -// Note: to run this test, you must have 2 workspaces on your Enterprise Grid and -// you must have the SLACK_SDK_TEST_GRID_SECONDARY_WORKSPACE_ID environment variable configured. -describe('admin.* Web APIs', function () { - // admin user's token for a workspace in an Enterprise Grid org - const teamAdminClient = new WebClient(process.env.SLACK_SDK_TEST_GRID_WORKSPACE_ADMIN_USER_TOKEN, { logger, }); - // admin user's team ID for a secondary workspace in an Enterprise Grid org - // (must be in same org as workspace for teamAdminClient, but a different workspace) - const secondaryTeamId = process.env.SLACK_SDK_TEST_GRID_SECONDARY_WORKSPACE_ID; - // org-level admin user's token - const orgAdminClient = new WebClient(process.env.SLACK_SDK_TEST_GRID_ORG_ADMIN_USER_TOKEN, { logger, }); - - describe('admin.conversations.bulk{Archive|Move|Delete}', function () { - it('should bulk archive conversations using admin.conversations.bulkArchive', async function () { - const testChannel = await teamAdminClient.conversations.create({ - name: `test-bulk-archive-${new Date().getTime()}`, - }); - const channelId = testChannel.channel.id; - let archiving = null; - let isInProgress = false; - - while (archiving === null || isInProgress) { - try { - archiving = await orgAdminClient.admin.conversations.bulkArchive({ - channel_ids: [channelId] - }).catch((error) => { - if (error.data.error === 'action_already_in_progress') { - isInProgress = true; - new Promise(r => setTimeout(r, 3000)); - } else { - throw error; - } - }); - - if (archiving && archiving.ok) { - isInProgress = false; - } - } finally { - await orgAdminClient.admin.conversations.delete({ - channel_id: channelId, - }); - } - } - - logger.info(archiving); - assert.isDefined(archiving.bulk_action_id); - assert.isUndefined(archiving.error); - }); - it('should bulk delete conversations using admin.conversations.bulkDelete', async function () { - const testChannel = await teamAdminClient.conversations.create({ - name: `test-bulk-delete-${new Date().getTime()}`, - }); - const channelId = testChannel.channel.id; - let removing = null; - let isInProgress = false; - - while (removing === null || isInProgress) { - removing = await orgAdminClient.admin.conversations.bulkDelete({ - channel_ids: [channelId] - }).catch((error) => { - if (error.data.error === 'action_already_in_progress') { - isInProgress = true; - new Promise(r => setTimeout(r, 3000)); - } else { - throw error; - } - }); - - if (removing && removing.ok) { - isInProgress = false; - } - } - logger.info(removing); - assert.isDefined(removing.bulk_action_id); - assert.isUndefined(removing.error); - }); - it('should bulk move conversations using admin.conversations.bulkMove', async function () { - const testChannel = await teamAdminClient.conversations.create({ - name: `test-bulk-move-${new Date().getTime()}`, - }); - const channelId = testChannel.channel.id; - let moving = null; - let isInProgress = false; - - while (moving === null || isInProgress) { - moving = await orgAdminClient.admin.conversations.bulkMove({ - channel_ids: [channelId], - target_team_id: secondaryTeamId, - }).catch((error) => { - if (error.data.error === 'action_already_in_progress') { - isInProgress = true; - new Promise(r => setTimeout(r, 3000)); - } else { - throw error; - } - }); - - if (moving && moving.ok) { - isInProgress = false; - } - } - logger.info(moving); - assert.isDefined(moving.bulk_action_id); - assert.isUndefined(moving.error); - }); - }); -}); diff --git a/prod-server-integration-tests/test/admin-web-api-conversations-bulk.test.js b/prod-server-integration-tests/test/admin-web-api-conversations-bulk.test.js new file mode 100644 index 000000000..014e6974a --- /dev/null +++ b/prod-server-integration-tests/test/admin-web-api-conversations-bulk.test.js @@ -0,0 +1,123 @@ +import 'dotenv/config'; +import assert from 'node:assert/strict'; +import { describe, it } from 'node:test'; +import { WebClient } from '@slack/web-api'; +import winston from 'winston'; + +const logger = winston.createLogger({ + level: 'debug', + transports: [new winston.transports.File({ filename: 'logs/console.log' })], +}); + +describe('admin.* Web APIs', () => { + const teamAdminClient = new WebClient(process.env.SLACK_SDK_TEST_GRID_ORG_ADMIN_USER_TOKEN, { logger }); + const primaryTeamId = process.env.SLACK_SDK_TEST_GRID_WORKSPACE_ID; + const secondaryTeamId = process.env.SLACK_SDK_TEST_GRID_SECONDARY_WORKSPACE_ID; + const orgAdminClient = new WebClient(process.env.SLACK_SDK_TEST_GRID_ORG_ADMIN_USER_TOKEN, { logger }); + + describe('admin.conversations.bulk{Archive|Move|Delete}', () => { + it('should bulk archive conversations using admin.conversations.bulkArchive', async () => { + const testChannel = await teamAdminClient.conversations.create({ + name: `test-bulk-archive-${Date.now()}`, + team_id: primaryTeamId, + }); + const channelId = testChannel.channel.id; + let archiving = null; + let isInProgress = false; + + while (archiving === null || isInProgress) { + try { + archiving = await orgAdminClient.admin.conversations + .bulkArchive({ + channel_ids: [channelId], + }) + .catch((error) => { + if (error.data.error === 'action_already_in_progress') { + isInProgress = true; + new Promise((r) => setTimeout(r, 3000)); + } else { + throw error; + } + }); + + if (archiving?.ok) { + isInProgress = false; + } + } finally { + await orgAdminClient.admin.conversations.delete({ + channel_id: channelId, + }); + } + } + + logger.info(archiving); + assert.notEqual(archiving.bulk_action_id, undefined); + assert.equal(archiving.error, undefined, archiving.error); + }); + + it('should bulk delete conversations using admin.conversations.bulkDelete', async () => { + const testChannel = await teamAdminClient.conversations.create({ + name: `test-bulk-delete-${Date.now()}`, + team_id: primaryTeamId, + }); + const channelId = testChannel.channel.id; + let removing = null; + let isInProgress = false; + + while (removing === null || isInProgress) { + removing = await orgAdminClient.admin.conversations + .bulkDelete({ + channel_ids: [channelId], + }) + .catch((error) => { + if (error.data.error === 'action_already_in_progress') { + isInProgress = true; + new Promise((r) => setTimeout(r, 3000)); + } else { + throw error; + } + }); + + if (removing?.ok) { + isInProgress = false; + } + } + logger.info(removing); + assert.notEqual(removing.bulk_action_id, undefined); + assert.equal(removing.error, undefined, removing.error); + }); + + it('should bulk move conversations using admin.conversations.bulkMove', async () => { + const testChannel = await teamAdminClient.conversations.create({ + name: `test-bulk-move-${Date.now()}`, + team_id: primaryTeamId, + }); + const channelId = testChannel.channel.id; + let moving = null; + let isInProgress = false; + + while (moving === null || isInProgress) { + moving = await orgAdminClient.admin.conversations + .bulkMove({ + channel_ids: [channelId], + target_team_id: secondaryTeamId, + }) + .catch((error) => { + if (error.data.error === 'action_already_in_progress') { + isInProgress = true; + new Promise((r) => setTimeout(r, 3000)); + } else { + throw error; + } + }); + + if (moving?.ok) { + isInProgress = false; + } + } + logger.info(moving); + assert.notEqual(moving.bulk_action_id, undefined); + assert.equal(moving.error, undefined, moving.error); + }); + }); +}); diff --git a/prod-server-integration-tests/test/admin-web-api-custom-retention.js b/prod-server-integration-tests/test/admin-web-api-custom-retention.test.js similarity index 51% rename from prod-server-integration-tests/test/admin-web-api-custom-retention.js rename to prod-server-integration-tests/test/admin-web-api-custom-retention.test.js index 4a4fb7c71..63e0d8f91 100644 --- a/prod-server-integration-tests/test/admin-web-api-custom-retention.js +++ b/prod-server-integration-tests/test/admin-web-api-custom-retention.test.js @@ -1,35 +1,32 @@ -// npx mocha --timeout 10000 test/admin-web-api-custom-retention.js -// tail -f logs/console.log | jq -require('mocha'); -const { assert } = require('chai'); -const { WebClient } = require('@slack/web-api'); +import 'dotenv/config'; +import assert from 'node:assert/strict'; +import { describe, it } from 'node:test'; +import { WebClient } from '@slack/web-api'; +import winston from 'winston'; -const winston = require('winston'); const logger = winston.createLogger({ level: 'debug', - transports: [ - new winston.transports.File({ filename: 'logs/console.log' }), - ], + transports: [new winston.transports.File({ filename: 'logs/console.log' })], }); -describe('admin.* Web APIs', function () { - // admin user's token for a workspace in an Enterprise Grid org - const teamAdminClient = new WebClient(process.env.SLACK_SDK_TEST_GRID_WORKSPACE_ADMIN_USER_TOKEN, { logger, }); - // org-level admin user's token - const orgAdminClient = new WebClient(process.env.SLACK_SDK_TEST_GRID_ORG_ADMIN_USER_TOKEN, { logger, }); +describe('admin.* Web APIs', () => { + const teamAdminClient = new WebClient(process.env.SLACK_SDK_TEST_GRID_ORG_ADMIN_USER_TOKEN, { logger }); + const primaryTeamId = process.env.SLACK_SDK_TEST_GRID_WORKSPACE_ID; + const orgAdminClient = new WebClient(process.env.SLACK_SDK_TEST_GRID_ORG_ADMIN_USER_TOKEN, { logger }); - describe('admin.conversations.{get|set|remove}CustomRetention', function () { - it('should work', async function () { - const crewation = await teamAdminClient.conversations.create({ - name: `test-channel-${new Date().getTime()}`, + describe('admin.conversations.{get|set|remove}CustomRetention', () => { + it('should work', async () => { + const creation = await teamAdminClient.conversations.create({ + name: `test-channel-${Date.now()}`, + team_id: primaryTeamId, }); - const channelId = crewation.channel.id; + const channelId = creation.channel.id; try { let get = await orgAdminClient.admin.conversations.getCustomRetention({ - channel_id: channelId + channel_id: channelId, }); logger.info(get); - assert.isUndefined(get.error); + assert.equal(get.error, undefined, get.error); assert.equal(get.duration_days, 0); const set = await orgAdminClient.admin.conversations.setCustomRetention({ @@ -37,32 +34,31 @@ describe('admin.* Web APIs', function () { duration_days: 700, }); logger.info(set); - assert.isUndefined(set.error); + assert.equal(set.error, undefined, set.error); get = await orgAdminClient.admin.conversations.getCustomRetention({ - channel_id: channelId + channel_id: channelId, }); logger.info(get); - assert.isUndefined(get.error); + assert.equal(get.error, undefined, get.error); assert.equal(get.duration_days, 700); const remove = await orgAdminClient.admin.conversations.removeCustomRetention({ - channel_id: channelId + channel_id: channelId, }); logger.info(remove); - assert.isUndefined(remove.error); + assert.equal(remove.error, undefined, remove.error); get = await orgAdminClient.admin.conversations.getCustomRetention({ - channel_id: channelId + channel_id: channelId, }); logger.info(get); - assert.isUndefined(get.error); + assert.equal(get.error, undefined, get.error); assert.equal(get.duration_days, 0); - } finally { await orgAdminClient.admin.conversations.delete({ channel_id: channelId, - }) + }); } }); }); diff --git a/prod-server-integration-tests/test/admin-web-api-roles.js b/prod-server-integration-tests/test/admin-web-api-roles.js deleted file mode 100644 index ab39f2a29..000000000 --- a/prod-server-integration-tests/test/admin-web-api-roles.js +++ /dev/null @@ -1,27 +0,0 @@ -// npx mocha --timeout 10000 npm test test/admin-web-api-roles.js -// tail -f logs/console.log | jq -require('mocha'); -const { assert } = require('chai'); -const { WebClient } = require('@slack/web-api'); - -const winston = require('winston'); -const logger = winston.createLogger({ - level: 'debug', - transports: [ - new winston.transports.File({ filename: 'logs/console.log' }), - ], -}); - -describe('admin.roles.*', function () { - it('should work', async function () { - const orgAdminClient = new WebClient(process.env.SLACK_SDK_TEST_GRID_ORG_ADMIN_USER_TOKEN, { logger, }); - const response = await orgAdminClient.admin.roles.listAssignments({ - role_ids: ["Rl0A"], - limit: 3, - sort_dir: "desc", - }) - logger.info(response); - assert.isUndefined(response.error); - // TODO: add/removeAssignments - }); -}); diff --git a/prod-server-integration-tests/test/admin-web-api-roles.test.js b/prod-server-integration-tests/test/admin-web-api-roles.test.js new file mode 100644 index 000000000..a58ff3cb3 --- /dev/null +++ b/prod-server-integration-tests/test/admin-web-api-roles.test.js @@ -0,0 +1,23 @@ +import 'dotenv/config'; +import assert from 'node:assert/strict'; +import { describe, it } from 'node:test'; +import { WebClient } from '@slack/web-api'; +import winston from 'winston'; + +const logger = winston.createLogger({ + level: 'debug', + transports: [new winston.transports.File({ filename: 'logs/console.log' })], +}); + +describe('admin.roles.*', () => { + it('should work', async () => { + const orgAdminClient = new WebClient(process.env.SLACK_SDK_TEST_GRID_ORG_ADMIN_USER_TOKEN, { logger }); + const response = await orgAdminClient.admin.roles.listAssignments({ + role_ids: ['Rl0A'], + limit: 3, + sort_dir: 'desc', + }); + logger.info(response); + assert.equal(response.error, undefined, response.error); + }); +}); diff --git a/prod-server-integration-tests/test/admin-web-api-user-sessions.js b/prod-server-integration-tests/test/admin-web-api-user-sessions.js deleted file mode 100644 index 9e99a8f92..000000000 --- a/prod-server-integration-tests/test/admin-web-api-user-sessions.js +++ /dev/null @@ -1,54 +0,0 @@ -// npx mocha --timeout 10000 test/admin-web-api-user-sessions.js -// tail -f logs/console.log | jq -require('mocha'); -const { assert } = require('chai'); -const { WebClient } = require('@slack/web-api'); - -const winston = require('winston'); -const logger = winston.createLogger({ - level: 'debug', - transports: [ - new winston.transports.File({ filename: 'logs/console.log' }), - ], -}); - -describe('admin.users.session.* Web APIs', function () { - // admin user's token for a workspace in an Enterprise Grid org - const teamAdminClient = new WebClient(process.env.SLACK_SDK_TEST_GRID_WORKSPACE_ADMIN_USER_TOKEN, { logger, }); - // org-level admin user's token - const orgAdminClient = new WebClient(process.env.SLACK_SDK_TEST_GRID_ORG_ADMIN_USER_TOKEN, { logger, }); - - describe('admin.users.session.reset/resetBulk', function () { - it('should work', async function () { - // fetch 3 active members from a workspace - const users = await teamAdminClient.users.list({ - exclude_archived: true, - limit: 100 - }); - const userIds = []; - for (const u of users.members) { - if (userIds.length >= 3) { - break; - } - if (!u.is_bot && !u.deleted && !u.is_app_user && !u.is_owner && u.id !== 'USLACKBOT') { - userIds.push(u.id); - } - } - let single = await orgAdminClient.admin.users.session.reset({ - user_id: userIds[0], - mobile_only: true, - web_only: false, - }); - logger.info(single); - assert.isUndefined(single.error); - - let bulk = await orgAdminClient.admin.users.session.resetBulk({ - user_ids: userIds, - mobile_only: true, - web_only: false, - }); - logger.info(bulk); - assert.isUndefined(bulk.error); - }); - }); -}); diff --git a/prod-server-integration-tests/test/admin-web-api-user-sessions.test.js b/prod-server-integration-tests/test/admin-web-api-user-sessions.test.js new file mode 100644 index 000000000..786f3782c --- /dev/null +++ b/prod-server-integration-tests/test/admin-web-api-user-sessions.test.js @@ -0,0 +1,51 @@ +import 'dotenv/config'; +import assert from 'node:assert/strict'; +import { describe, it } from 'node:test'; +import { WebClient } from '@slack/web-api'; +import winston from 'winston'; + +const logger = winston.createLogger({ + level: 'debug', + transports: [new winston.transports.File({ filename: 'logs/console.log' })], +}); + +describe('admin.users.session.* Web APIs', () => { + const teamAdminClient = new WebClient(process.env.SLACK_SDK_TEST_GRID_ORG_ADMIN_USER_TOKEN, { logger }); + const primaryTeamId = process.env.SLACK_SDK_TEST_GRID_WORKSPACE_ID; + const orgAdminClient = new WebClient(process.env.SLACK_SDK_TEST_GRID_ORG_ADMIN_USER_TOKEN, { logger }); + + describe('admin.users.session.reset/resetBulk', () => { + it('should work', async () => { + const users = await teamAdminClient.users.list({ + exclude_archived: true, + limit: 100, + team_id: primaryTeamId, + }); + const userIds = []; + for (const u of users.members) { + if (userIds.length >= 3) { + break; + } + if (!u.is_bot && !u.deleted && !u.is_app_user && !u.is_owner && u.id !== 'USLACKBOT') { + userIds.push(u.id); + } + } + + const single = await orgAdminClient.admin.users.session.reset({ + user_id: userIds[0], + mobile_only: true, + web_only: false, + }); + logger.info(single); + assert.equal(single.error, undefined, single.error); + + const bulk = await orgAdminClient.admin.users.session.resetBulk({ + user_ids: userIds, + mobile_only: true, + web_only: false, + }); + logger.info(bulk); + assert.equal(bulk.error, undefined, bulk.error); + }); + }); +}); diff --git a/prod-server-integration-tests/test/admin-web-api-users-unsupportedVersions-export.js b/prod-server-integration-tests/test/admin-web-api-users-unsupportedVersions-export.js deleted file mode 100644 index f5840c5f2..000000000 --- a/prod-server-integration-tests/test/admin-web-api-users-unsupportedVersions-export.js +++ /dev/null @@ -1,25 +0,0 @@ -// npx mocha --timeout 10000 test/admin-web-api-users-unsupportedVersions-export.js -// tail -f logs/console.log | jq -require('mocha'); -const { assert } = require('chai'); -const { WebClient } = require('@slack/web-api'); - -const winston = require('winston'); -const logger = winston.createLogger({ - level: 'debug', - transports: [ - new winston.transports.File({ filename: 'logs/console.log' }), - ], -}); - -describe('admin.users.unsupportedVersions.export', function () { - it('should work', async function () { - const orgAdminClient = new WebClient(process.env.SLACK_SDK_TEST_GRID_ORG_ADMIN_USER_TOKEN, { logger, }); - const response = await orgAdminClient.admin.users.unsupportedVersions.export({ - date_end_of_support: Math.floor(+new Date() / 1000) + 60 * 60 * 24 *365, - date_sessions_started: 0, - }); - logger.info(response); - assert.isUndefined(response.error); - }); -}); diff --git a/prod-server-integration-tests/test/admin-web-api-users-unsupportedVersions-export.test.js b/prod-server-integration-tests/test/admin-web-api-users-unsupportedVersions-export.test.js new file mode 100644 index 000000000..b9995e6dd --- /dev/null +++ b/prod-server-integration-tests/test/admin-web-api-users-unsupportedVersions-export.test.js @@ -0,0 +1,22 @@ +import 'dotenv/config'; +import assert from 'node:assert/strict'; +import { describe, it } from 'node:test'; +import { WebClient } from '@slack/web-api'; +import winston from 'winston'; + +const logger = winston.createLogger({ + level: 'debug', + transports: [new winston.transports.File({ filename: 'logs/console.log' })], +}); + +describe('admin.users.unsupportedVersions.export', () => { + it('should work', async () => { + const orgAdminClient = new WebClient(process.env.SLACK_SDK_TEST_GRID_ORG_ADMIN_USER_TOKEN, { logger }); + const response = await orgAdminClient.admin.users.unsupportedVersions.export({ + date_end_of_support: Math.floor(Date.now() / 1000) + 60 * 60 * 24 * 365, + date_sessions_started: 0, + }); + logger.info(response); + assert.equal(response.error, undefined, response.error); + }); +}); diff --git a/prod-server-integration-tests/test/admin-web-api.js b/prod-server-integration-tests/test/admin-web-api.js deleted file mode 100644 index ced0163ac..000000000 --- a/prod-server-integration-tests/test/admin-web-api.js +++ /dev/null @@ -1,106 +0,0 @@ -require('mocha'); -const { assert } = require('chai'); -const { WebClient } = require('@slack/web-api'); - -const winston = require('winston'); -const logger = winston.createLogger({ - level: 'debug', - transports: [ - new winston.transports.File({ filename: 'logs/console.log' }), - ], -}); - -describe('admin.* Web APIs', function () { - // admin user's token for a workspace in an Enterprise Grid org - const teamAdminClient = new WebClient(process.env.SLACK_SDK_TEST_GRID_WORKSPACE_ADMIN_USER_TOKEN, { logger, }); - // org-level admin user's token - const orgAdminClient = new WebClient(process.env.SLACK_SDK_TEST_GRID_ORG_ADMIN_USER_TOKEN, { logger, }); - - describe('admin.users.session.{get|set|clear}Settings', function () { - it('should work', async function () { - // fetch 3 active members from a workspace - const users = await teamAdminClient.users.list({ - exclude_archived: true, - limit: 100 - }); - const userIds = []; - for (const u of users.members) { - if (userIds.length >= 3) { - break; - } - if (!u.is_bot && !u.deleted && !u.is_app_user && !u.is_owner && u.id !== 'USLACKBOT') { - userIds.push(u.id); - } - } - - // call get/set/clearSettings APIs - const get = await orgAdminClient.admin.users.session.getSettings({ - user_ids: userIds - }); - logger.info(get); - assert.isUndefined(get.error); - - const set = await orgAdminClient.admin.users.session.setSettings({ - user_ids: userIds, - duration: 60 * 60 * 24 * 30 - }); - logger.info(set); - assert.isUndefined(set.error); - - const get2 = await orgAdminClient.admin.users.session.getSettings({ - user_ids: userIds - }); - logger.info(get2); - assert.isUndefined(get2.error); - assert.equal(get2.session_settings.length, userIds.length); - - const clear = await orgAdminClient.admin.users.session.clearSettings({ - user_ids: userIds - }); - logger.info(clear); - assert.isUndefined(clear.error); - - const get3 = await orgAdminClient.admin.users.session.getSettings({ - user_ids: userIds - }); - logger.info(get3); - assert.isUndefined(get3.error); - assert.equal(get3.session_settings.length, 0); - assert.equal(get3.no_settings_applied.length, userIds.length); - }); - }); - - describe('admin.auth.policy.{assign|get|remove}Entities', function () { - /* - To run this test suite manually, you need an email_password auth policy - enabled on the Enterprise Org. For this, you need to have an Enterprise Org - with SSO enabled. You will additionally need to export a User ID for - a user managed by the IDP for testing. - - export SLACK_SDK_TEST_GRID_USER_ID= - */ - it('should assign an entity', async function () { - const res = await orgAdminClient.admin.auth.policy.assignEntities({ - entity_ids: [process.env.SLACK_SDK_TEST_GRID_USER_ID], - entity_type: "USER", - policy_name: "email_password", - }); - assert.isUndefined(res.error); - }) - it('should get entities', async function () { - const res2 = await orgAdminClient.admin.auth.policy.getEntities({ - policy_name: "email_password", - }); - logger.info(res2); - assert.isUndefined(res2.error); - }) - it('should remove entities', async function () { - const res3 = await orgAdminClient.admin.auth.policy.removeEntities({ - entity_ids: [process.env.SLACK_SDK_TEST_GRID_USER_ID], - entity_type: "USER", - policy_name: "email_password", - }) - assert.isUndefined(res3.error); - }) - }) -}); diff --git a/prod-server-integration-tests/test/admin-web-api.test.js b/prod-server-integration-tests/test/admin-web-api.test.js new file mode 100644 index 000000000..9a3d70ae6 --- /dev/null +++ b/prod-server-integration-tests/test/admin-web-api.test.js @@ -0,0 +1,97 @@ +import 'dotenv/config'; +import assert from 'node:assert/strict'; +import { describe, it } from 'node:test'; +import { WebClient } from '@slack/web-api'; +import winston from 'winston'; + +const logger = winston.createLogger({ + level: 'debug', + transports: [new winston.transports.File({ filename: 'logs/console.log' })], +}); + +describe('admin.* Web APIs', () => { + const teamAdminClient = new WebClient(process.env.SLACK_SDK_TEST_GRID_ORG_ADMIN_USER_TOKEN, { logger }); + const primaryTeamId = process.env.SLACK_SDK_TEST_GRID_WORKSPACE_ID; + const orgAdminClient = new WebClient(process.env.SLACK_SDK_TEST_GRID_ORG_ADMIN_USER_TOKEN, { logger }); + + describe('admin.users.session.{get|set|clear}Settings', () => { + it('should work', async () => { + const users = await teamAdminClient.users.list({ + exclude_archived: true, + limit: 100, + team_id: primaryTeamId, + }); + const userIds = []; + for (const u of users.members) { + if (userIds.length >= 3) { + break; + } + if (!u.is_bot && !u.deleted && !u.is_app_user && !u.is_owner && u.id !== 'USLACKBOT') { + userIds.push(u.id); + } + } + + const get = await orgAdminClient.admin.users.session.getSettings({ + user_ids: userIds, + }); + logger.info(get); + assert.equal(get.error, undefined, get.error); + + const set = await orgAdminClient.admin.users.session.setSettings({ + user_ids: userIds, + duration: 60 * 60 * 24 * 30, + }); + logger.info(set); + assert.equal(set.error, undefined, set.error); + + const get2 = await orgAdminClient.admin.users.session.getSettings({ + user_ids: userIds, + }); + logger.info(get2); + assert.equal(get2.error, undefined, get2.error); + assert.equal(get2.session_settings.length, userIds.length); + + const clear = await orgAdminClient.admin.users.session.clearSettings({ + user_ids: userIds, + }); + logger.info(clear); + assert.equal(clear.error, undefined, clear.error); + + const get3 = await orgAdminClient.admin.users.session.getSettings({ + user_ids: userIds, + }); + logger.info(get3); + assert.equal(get3.error, undefined, get3.error); + assert.equal(get3.session_settings.length, 0); + assert.equal(get3.no_settings_applied.length, userIds.length); + }); + }); + + describe('admin.auth.policy.{assign|get|remove}Entities', () => { + it('should assign an entity', async () => { + const res = await orgAdminClient.admin.auth.policy.assignEntities({ + entity_ids: [process.env.SLACK_SDK_TEST_GRID_USER_ID], + entity_type: 'USER', + policy_name: 'email_password', + }); + assert.equal(res.error, undefined, res.error); + }); + + it('should get entities', async () => { + const res2 = await orgAdminClient.admin.auth.policy.getEntities({ + policy_name: 'email_password', + }); + logger.info(res2); + assert.equal(res2.error, undefined, res2.error); + }); + + it('should remove entities', async () => { + const res3 = await orgAdminClient.admin.auth.policy.removeEntities({ + entity_ids: [process.env.SLACK_SDK_TEST_GRID_USER_ID], + entity_type: 'USER', + policy_name: 'email_password', + }); + assert.equal(res3.error, undefined, res3.error); + }); + }); +}); diff --git a/prod-server-integration-tests/test/bookmarks-web-api.js b/prod-server-integration-tests/test/bookmarks-web-api.js deleted file mode 100644 index 39622e642..000000000 --- a/prod-server-integration-tests/test/bookmarks-web-api.js +++ /dev/null @@ -1,70 +0,0 @@ -// npx mocha --timeout 10000 test/bookmarks-web-api.js -// run above from /prod-server-integration-tests -require('mocha'); -const { config } = require('dotenv'); -const { assert } = require('chai'); -const { WebClient } = require('@slack/web-api'); - -const winston = require('winston'); -const logger = winston.createLogger({ - level: 'debug', - transports: [ - new winston.transports.File({ filename: 'logs/console.log' }), - ], -}); - -config(); - -describe('bookmarks.* Web APIs', async function () { - // export SLACK_SDK_TEST_BOT_TOKEN=xoxb- - // token used must have bookmarks:read, bookmarks:write scopes and channels:manage scopes to run tests properly - - const client = new WebClient(process.env.SLACK_SDK_TEST_BOT_TOKEN, { logger, }); - - describe('bookmarks.{list|add|edit|remove}', async function () { - it('should work', async function () { - let channelId; - try { - const channelRes = await client.conversations.create({ - name: `bookmarks-${Date.now()}`, - }); - assert.isUndefined(channelRes.error); - channelId = channelRes.channel.id; - - const listRes = await client.bookmarks.list({ - channel_id: channelId, - }); - assert.isUndefined(listRes.error); - - const addRes = await client.bookmarks.add({ - channel_id: channelId, - title: `${Date().now}`, - type: 'link', - link: 'https://www.example.com', - }); - assert.isUndefined(addRes.error); - const bookmark_id = addRes.bookmark.id; - - assert.isNotNull(bookmark_id); - - const editRes = await client.bookmarks.edit({ - channel_id: channelId, - bookmark_id, - }); - if (editRes.error) console.log('bookmarks.edit failed:', editRes.error); - assert.isUndefined(editRes.error); - - const removeRes = await client.bookmarks.remove({ - channel_id: channelId, - bookmark_id, - }); - if (removeRes.error) console.log('bookmarks.remove failed:', removeRes.error); - assert.isUndefined(removeRes.error); - } finally { - if (channelId) { - await client.conversations.archive({ channel: channelId }); - } - } - }); - }); -}); diff --git a/prod-server-integration-tests/test/bookmarks-web-api.test.js b/prod-server-integration-tests/test/bookmarks-web-api.test.js new file mode 100644 index 000000000..0992448c2 --- /dev/null +++ b/prod-server-integration-tests/test/bookmarks-web-api.test.js @@ -0,0 +1,59 @@ +import 'dotenv/config'; +import assert from 'node:assert/strict'; +import { describe, it } from 'node:test'; +import { WebClient } from '@slack/web-api'; +import winston from 'winston'; + +const logger = winston.createLogger({ + level: 'debug', + transports: [new winston.transports.File({ filename: 'logs/console.log' })], +}); + +describe('bookmarks.* Web APIs', () => { + const client = new WebClient(process.env.SLACK_SDK_TEST_BOT_TOKEN, { logger }); + + describe('bookmarks.{list|add|edit|remove}', () => { + it('should work', async () => { + let channelId; + try { + const channelRes = await client.conversations.create({ + name: `bookmarks-${Date.now()}`, + }); + assert.equal(channelRes.error, undefined, channelRes.error); + channelId = channelRes.channel.id; + + const listRes = await client.bookmarks.list({ + channel_id: channelId, + }); + assert.equal(listRes.error, undefined, listRes.error); + + const addRes = await client.bookmarks.add({ + channel_id: channelId, + title: `${Date.now()}`, + type: 'link', + link: 'https://www.example.com', + }); + assert.equal(addRes.error, undefined, addRes.error); + const bookmark_id = addRes.bookmark.id; + + assert.notEqual(bookmark_id, null); + + const editRes = await client.bookmarks.edit({ + channel_id: channelId, + bookmark_id, + }); + assert.equal(editRes.error, undefined, editRes.error); + + const removeRes = await client.bookmarks.remove({ + channel_id: channelId, + bookmark_id, + }); + assert.equal(removeRes.error, undefined, removeRes.error); + } finally { + if (channelId) { + await client.conversations.archive({ channel: channelId }); + } + } + }); + }); +}); diff --git a/prod-server-integration-tests/test/web-api.js b/prod-server-integration-tests/test/web-api.js deleted file mode 100644 index 32874f5a4..000000000 --- a/prod-server-integration-tests/test/web-api.js +++ /dev/null @@ -1,142 +0,0 @@ -require('mocha'); -const { config } = require('dotenv'); -const { assert } = require('chai'); -const { WebClient } = require('@slack/web-api'); - -const winston = require('winston'); -const logger = winston.createLogger({ - level: 'debug', - transports: [ - new winston.transports.File({ filename: 'logs/console.log' }), - ], -}); - -config(); - -describe('Web APIs', function () { - const botClient = new WebClient(process.env.SLACK_SDK_TEST_BOT_TOKEN, { logger, }); - const userClient = new WebClient(process.env.SLACK_SDK_TEST_USER_TOKEN, { logger, }); - - describe('auth.test', function () { - - it('should work with a bot token', async function () { - const response = await botClient.auth.test(); - logger.info(response); - assert.isUndefined(response.error); - }); - - it('should work with a user token', async function () { - const response = await userClient.auth.test(); - logger.info(response); - assert.isUndefined(response.error); - }); - }); - - describe('chat.scheduleMessage', function () { - it('should accept either an integer or a string value for post_at', async function () { - const channelId = process.env.SLACK_SDK_TEST_WEB_TEST_CHANNEL_ID; - const postAt = Number.parseInt((Date.now() / 1000) + 60 * 15); - try { - const params = { - text: 'Hi there!', - channel: channelId, - post_at: postAt, - }; - assert.isTrue(typeof params.post_at === 'number'); - assert.isTrue(Number.isInteger(params.post_at)); - const response1 = await botClient.chat.scheduleMessage(params); - assert.isUndefined(response1.error); - - params.post_at = "" + params.post_at; - assert.isTrue(typeof params.post_at === 'string'); - const response2 = await botClient.chat.scheduleMessage(params); - assert.isUndefined(response2.error); - } catch (e) { - console.log(e.code + " / " + JSON.stringify(e.data)); - throw e; - } - }); - }); - - describe('Slack Connect conversations.* methods', async function () { - /* - To run this test suite, we use two workspace-level bot tokens, - one for the sending workspace(list and send invites) another for the receiving - workspace (accept and approve) sent invites. Before being able to run this test, - we also need to have manually created a slack connect shared channel and added - these two bots as members first. - - Required env variables: - - export SLACK_SDK_TEST_CONNECT_INVITE_SENDER_BOT_TOKEN= - export SLACK_SDK_TEST_CONNECT_INVITE_RECEIVER_BOT_TOKEN= - export SLACK_SDK_TEST_CONNECT_INVITE_RECEIVER_BOT_USER_ID= - */ - const sender = new WebClient(process.env.SLACK_SDK_TEST_CONNECT_INVITE_SENDER_BOT_TOKEN); - const receiver = new WebClient(process.env.SLACK_SDK_TEST_CONNECT_INVITE_RECEIVER_BOT_TOKEN) - - describe('listConnectInvites', function () { - it('should list shared channel invites ', async function () { - const invites = await sender.conversations.listConnectInvites({}); - assert.isUndefined(invites.error); - }); - }); - describe('inviteShared, acceptShared, approveShared', function () { - it('should successfully send an invite and accept it', async function () { - let channelId, inviteId = null; - let channelName = Date.now().toString().concat('-connect-test'); - try { - // creates channel to be shared - const newChannel = await sender.conversations.create({ - name: channelName, - }); - assert.isUndefined(newChannel.error); - channelId = newChannel.channel.id; - - // sends invite to reciever bot - const inviteShared = await sender.conversations.inviteShared({ - channel: channelId, - user_ids: process.env.SLACK_SDK_TEST_CONNECT_INVITE_RECEIVER_BOT_USER_ID, - }); - assert.isUndefined(inviteShared.error); - assert.isDefined(inviteShared.invite_id); - inviteId = inviteShared.invite_id; - - // accepts invite - const accepted = await receiver.conversations.acceptSharedInvite({ - channel_name: channelName, - invite_id: inviteId - }) - assert.isUndefined(accepted.error); - - if (!accepted.implicit_approval) { - // attempts to have receiver approve shared invite - await receiver.conversations.approveSharedInvite({ - invite_id: inviteId - }) - } - } finally { - // cleanup any created channels - if (channelId) { - const cleanup = await sender.conversations.archive({ channel: channelId }) - assert.isUndefined(cleanup.error) - } - } - }); - }); - }); - - describe('team.* for rtm.start migration', function () { - - it('should work with a bot token (team.billing.info)', async function () { - const response = await botClient.team.billing.info(); - logger.info(response); - assert.isUndefined(response.error); - }); - it('should work with a bot token (team.preferences.list)', async function () { - const response = await botClient.team.preferences.list(); - logger.info(response); - assert.isUndefined(response.error); - }); - }); -}); diff --git a/prod-server-integration-tests/test/web-api.test.js b/prod-server-integration-tests/test/web-api.test.js new file mode 100644 index 000000000..3aa7df70a --- /dev/null +++ b/prod-server-integration-tests/test/web-api.test.js @@ -0,0 +1,120 @@ +import 'dotenv/config'; +import assert from 'node:assert/strict'; +import { describe, it } from 'node:test'; +import { WebClient } from '@slack/web-api'; +import winston from 'winston'; + +const logger = winston.createLogger({ + level: 'debug', + transports: [new winston.transports.File({ filename: 'logs/console.log' })], +}); + +describe('Web APIs', () => { + const botClient = new WebClient(process.env.SLACK_SDK_TEST_BOT_TOKEN, { logger }); + const userClient = new WebClient(process.env.SLACK_SDK_TEST_USER_TOKEN, { logger }); + + describe('auth.test', () => { + it('should work with a bot token', async () => { + const response = await botClient.auth.test(); + logger.info(response); + assert.equal(response.error, undefined, response.error); + }); + + it('should work with a user token', async () => { + const response = await userClient.auth.test(); + logger.info(response); + assert.equal(response.error, undefined, response.error); + }); + }); + + describe('chat.scheduleMessage', () => { + it('should accept either an integer or a string value for post_at', async () => { + const channelId = process.env.SLACK_SDK_TEST_WEB_TEST_CHANNEL_ID; + const postAt = Number.parseInt(Date.now() / 1000 + 60 * 15, 10); + try { + const params = { + text: 'Hi there!', + channel: channelId, + post_at: postAt, + }; + assert.strictEqual(typeof params.post_at, 'number'); + assert.ok(Number.isInteger(params.post_at)); + const response1 = await botClient.chat.scheduleMessage(params); + assert.equal(response1.error, undefined, response1.error); + + params.post_at = `${params.post_at}`; + assert.strictEqual(typeof params.post_at, 'string'); + const response2 = await botClient.chat.scheduleMessage(params); + assert.equal(response2.error, undefined, response2.error); + } catch (e) { + console.log(`${e.code} / ${JSON.stringify(e.data)}`); + throw e; + } + }); + }); + + describe('Slack Connect conversations.* methods', () => { + const sender = new WebClient(process.env.SLACK_SDK_TEST_CONNECT_INVITE_SENDER_BOT_TOKEN); + const receiver = new WebClient(process.env.SLACK_SDK_TEST_CONNECT_INVITE_RECEIVER_BOT_TOKEN); + + describe('listConnectInvites', () => { + it('should list shared channel invites', async () => { + const invites = await sender.conversations.listConnectInvites({}); + assert.equal(invites.error, undefined, invites.error); + }); + }); + + describe('inviteShared, acceptShared, approveShared', () => { + it('should successfully send an invite and accept it', async () => { + let channelId = null; + const channelName = Date.now().toString().concat('-connect-test'); + try { + const newChannel = await sender.conversations.create({ + name: channelName, + }); + assert.equal(newChannel.error, undefined, newChannel.error); + channelId = newChannel.channel.id; + + const inviteShared = await sender.conversations.inviteShared({ + channel: channelId, + user_ids: process.env.SLACK_SDK_TEST_CONNECT_INVITE_RECEIVER_BOT_USER_ID, + }); + assert.equal(inviteShared.error, undefined, inviteShared.error); + assert.notEqual(inviteShared.invite_id, undefined); + const inviteId = inviteShared.invite_id; + + const accepted = await receiver.conversations.acceptSharedInvite({ + channel_name: channelName, + invite_id: inviteId, + }); + assert.equal(accepted.error, undefined, accepted.error); + + if (!accepted.implicit_approval) { + await receiver.conversations.approveSharedInvite({ + invite_id: inviteId, + }); + } + } finally { + if (channelId) { + const cleanup = await sender.conversations.archive({ channel: channelId }); + assert.equal(cleanup.error, undefined, cleanup.error); + } + } + }); + }); + }); + + describe('team.* for rtm.start migration', () => { + it('should work with a bot token (team.billing.info)', async () => { + const response = await botClient.team.billing.info(); + logger.info(response); + assert.equal(response.error, undefined, response.error); + }); + + it('should work with a bot token (team.preferences.list)', async () => { + const response = await botClient.team.preferences.list(); + logger.info(response); + assert.equal(response.error, undefined, response.error); + }); + }); +}); From 4914718eb2b975403d8f1317972681b7e4e59a62 Mon Sep 17 00:00:00 2001 From: William Bergamin Date: Thu, 7 May 2026 15:44:05 -0400 Subject: [PATCH 3/6] bump timeout --- prod-server-integration-tests/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/prod-server-integration-tests/package.json b/prod-server-integration-tests/package.json index a45546a5c..a4173e983 100644 --- a/prod-server-integration-tests/package.json +++ b/prod-server-integration-tests/package.json @@ -8,7 +8,7 @@ "clean": "rm -rf node_modules/ package-lock.json", "lint": "npx biome check ../prod-server-integration-tests/", "lint:fix": "npx biome check --write ../prod-server-integration-tests/", - "test": "node --test-timeout=10000 --test-reporter=spec --test-reporter-destination=stdout --test test/*.test.js", + "test": "node --test-timeout=15000 --test-reporter=spec --test-reporter-destination=stdout --test test/*.test.js", "test:clean": "npm run clean && npm run build && npm install && npm test" }, "repository": "https://github.com/slackapi/node-slack-sdk", From d7f2bdd0542b41b8bbd8e38ca141ac78cfd9c8d9 Mon Sep 17 00:00:00 2001 From: William Bergamin Date: Thu, 7 May 2026 15:55:13 -0400 Subject: [PATCH 4/6] we dont need winston! --- prod-server-integration-tests/logger.js | 43 +++++++++++++ .../manifests/README.md | 62 ------------------- prod-server-integration-tests/package.json | 3 +- .../test/admin-web-api-analytics.test.js | 7 +-- .../admin-web-api-conversations-bulk.test.js | 7 +-- .../admin-web-api-custom-retention.test.js | 7 +-- .../test/admin-web-api-roles.test.js | 7 +-- .../test/admin-web-api-user-sessions.test.js | 7 +-- ...i-users-unsupportedVersions-export.test.js | 7 +-- .../test/admin-web-api.test.js | 7 +-- .../test/bookmarks-web-api.test.js | 7 +-- .../test/web-api.test.js | 7 +-- 12 files changed, 62 insertions(+), 109 deletions(-) create mode 100644 prod-server-integration-tests/logger.js diff --git a/prod-server-integration-tests/logger.js b/prod-server-integration-tests/logger.js new file mode 100644 index 000000000..2ae8a3650 --- /dev/null +++ b/prod-server-integration-tests/logger.js @@ -0,0 +1,43 @@ +import { appendFileSync, mkdirSync } from 'node:fs'; +import { dirname } from 'node:path'; + +const SEVERITY = { debug: 100, info: 200, warn: 300, error: 400 }; + +export class FileLogger { + #level; + #filePath; + + constructor(filePath, level = 'debug') { + this.#filePath = filePath; + this.#level = level; + mkdirSync(dirname(filePath), { recursive: true }); + } + + getLevel() { + return this.#level; + } + setLevel(level) { + this.#level = level; + } + setName(_name) {} + + debug(...msg) { + this.#log('debug', msg); + } + info(...msg) { + this.#log('info', msg); + } + warn(...msg) { + this.#log('warn', msg); + } + error(...msg) { + this.#log('error', msg); + } + + #log(level, msg) { + if (SEVERITY[level] >= SEVERITY[this.#level]) { + const entry = JSON.stringify({ level, message: msg.length === 1 ? msg[0] : msg }); + appendFileSync(this.#filePath, `${entry}\n`); + } + } +} diff --git a/prod-server-integration-tests/manifests/README.md b/prod-server-integration-tests/manifests/README.md index 588062221..9b731318e 100644 --- a/prod-server-integration-tests/manifests/README.md +++ b/prod-server-integration-tests/manifests/README.md @@ -38,65 +38,3 @@ to generate this token (`xapp-`). **Tooling token** (`SLACK_TOOLING_TOKEN`) is a configuration token not associated with any of these manifests. Generate it via the [App Configuration Tokens](https://api.slack.com/authentication/config-tokens) flow. - -## Scope Justification - -### main-bot-app (bot scopes) - -| Scope | Reason | -|-------|--------| -| `bookmarks:read` | `bookmarks.list` in bookmarks tests | -| `bookmarks:write` | `bookmarks.add/edit/remove` in bookmarks tests | -| `channels:history` | Reading channel history | -| `channels:manage` | Creating/archiving test channels | -| `channels:read` | Listing channels | -| `chat:write` | `chat.scheduleMessage` tests | -| `conversations.connect:manage` | Slack Connect invite listing | -| `conversations.connect:read` | Slack Connect invite listing | -| `team.billing:read` | `team.billing.info` test | -| `team.preferences:read` | `team.preferences.list` test | -| `users:read` | Listing users for test setup | - -### main-bot-app (user scopes) - -| Scope | Reason | -|-------|--------| -| `channels:history` | `auth.test` with user token | -| `channels:read` | `auth.test` with user token | -| `users:read` | `auth.test` with user token | - -### slack-connect-sender - -| Scope | Reason | -|-------|--------| -| `channels:manage` | Creating channels to share | -| `conversations.connect:manage` | Managing Slack Connect invites | -| `conversations.connect:read` | Listing Slack Connect invites | -| `conversations.connect:write` | Sending Slack Connect invites | - -### slack-connect-receiver - -| Scope | Reason | -|-------|--------| -| `conversations.connect:manage` | Accepting/approving Slack Connect invites | -| `conversations.connect:read` | Reading Slack Connect invite details | - -### socket-mode-app - -| Scope | Reason | -|-------|--------| -| `app_mentions:read` | Receiving `app_mention` events | -| `channels:history` | Receiving message events | -| `chat:write` | Responding to events | - -### admin-app (user scopes) - -| Scope | Reason | -|-------|--------| -| `admin.analytics:read` | `admin.analytics.getFile` | -| `admin.conversations:read` | Reading channel retention, bulk operations | -| `admin.conversations:write` | Bulk archive/delete/move, custom retention | -| `admin.roles:read` | `admin.roles.listAssignments` | -| `admin.roles:write` | Role assignment operations | -| `admin.users:read` | Session settings, unsupported versions export | -| `admin.users:write` | Session reset/clear operations | diff --git a/prod-server-integration-tests/package.json b/prod-server-integration-tests/package.json index a4173e983..b8725c0ee 100644 --- a/prod-server-integration-tests/package.json +++ b/prod-server-integration-tests/package.json @@ -17,8 +17,7 @@ "dependencies": { "@slack/socket-mode": "file:../packages/socket-mode", "@slack/web-api": "file:../packages/web-api", - "dotenv": "^16", - "winston": "^3.3.3" + "dotenv": "^16" }, "devDependencies": {} } diff --git a/prod-server-integration-tests/test/admin-web-api-analytics.test.js b/prod-server-integration-tests/test/admin-web-api-analytics.test.js index ad810cb7f..492811d18 100644 --- a/prod-server-integration-tests/test/admin-web-api-analytics.test.js +++ b/prod-server-integration-tests/test/admin-web-api-analytics.test.js @@ -2,12 +2,9 @@ import 'dotenv/config'; import assert from 'node:assert/strict'; import { describe, it } from 'node:test'; import { WebClient } from '@slack/web-api'; -import winston from 'winston'; +import { FileLogger } from '../logger.js'; -const logger = winston.createLogger({ - level: 'debug', - transports: [new winston.transports.File({ filename: 'logs/console.log' })], -}); +const logger = new FileLogger('logs/console.log'); /** @param {Date} date @returns {string} Date formatted as YYYY-MM-DD */ function formatDate(date) { diff --git a/prod-server-integration-tests/test/admin-web-api-conversations-bulk.test.js b/prod-server-integration-tests/test/admin-web-api-conversations-bulk.test.js index 014e6974a..3b5305870 100644 --- a/prod-server-integration-tests/test/admin-web-api-conversations-bulk.test.js +++ b/prod-server-integration-tests/test/admin-web-api-conversations-bulk.test.js @@ -2,12 +2,9 @@ import 'dotenv/config'; import assert from 'node:assert/strict'; import { describe, it } from 'node:test'; import { WebClient } from '@slack/web-api'; -import winston from 'winston'; +import { FileLogger } from '../logger.js'; -const logger = winston.createLogger({ - level: 'debug', - transports: [new winston.transports.File({ filename: 'logs/console.log' })], -}); +const logger = new FileLogger('logs/console.log'); describe('admin.* Web APIs', () => { const teamAdminClient = new WebClient(process.env.SLACK_SDK_TEST_GRID_ORG_ADMIN_USER_TOKEN, { logger }); diff --git a/prod-server-integration-tests/test/admin-web-api-custom-retention.test.js b/prod-server-integration-tests/test/admin-web-api-custom-retention.test.js index 63e0d8f91..680faaaa5 100644 --- a/prod-server-integration-tests/test/admin-web-api-custom-retention.test.js +++ b/prod-server-integration-tests/test/admin-web-api-custom-retention.test.js @@ -2,12 +2,9 @@ import 'dotenv/config'; import assert from 'node:assert/strict'; import { describe, it } from 'node:test'; import { WebClient } from '@slack/web-api'; -import winston from 'winston'; +import { FileLogger } from '../logger.js'; -const logger = winston.createLogger({ - level: 'debug', - transports: [new winston.transports.File({ filename: 'logs/console.log' })], -}); +const logger = new FileLogger('logs/console.log'); describe('admin.* Web APIs', () => { const teamAdminClient = new WebClient(process.env.SLACK_SDK_TEST_GRID_ORG_ADMIN_USER_TOKEN, { logger }); diff --git a/prod-server-integration-tests/test/admin-web-api-roles.test.js b/prod-server-integration-tests/test/admin-web-api-roles.test.js index a58ff3cb3..3761dac45 100644 --- a/prod-server-integration-tests/test/admin-web-api-roles.test.js +++ b/prod-server-integration-tests/test/admin-web-api-roles.test.js @@ -2,12 +2,9 @@ import 'dotenv/config'; import assert from 'node:assert/strict'; import { describe, it } from 'node:test'; import { WebClient } from '@slack/web-api'; -import winston from 'winston'; +import { FileLogger } from '../logger.js'; -const logger = winston.createLogger({ - level: 'debug', - transports: [new winston.transports.File({ filename: 'logs/console.log' })], -}); +const logger = new FileLogger('logs/console.log'); describe('admin.roles.*', () => { it('should work', async () => { diff --git a/prod-server-integration-tests/test/admin-web-api-user-sessions.test.js b/prod-server-integration-tests/test/admin-web-api-user-sessions.test.js index 786f3782c..3da34ec25 100644 --- a/prod-server-integration-tests/test/admin-web-api-user-sessions.test.js +++ b/prod-server-integration-tests/test/admin-web-api-user-sessions.test.js @@ -2,12 +2,9 @@ import 'dotenv/config'; import assert from 'node:assert/strict'; import { describe, it } from 'node:test'; import { WebClient } from '@slack/web-api'; -import winston from 'winston'; +import { FileLogger } from '../logger.js'; -const logger = winston.createLogger({ - level: 'debug', - transports: [new winston.transports.File({ filename: 'logs/console.log' })], -}); +const logger = new FileLogger('logs/console.log'); describe('admin.users.session.* Web APIs', () => { const teamAdminClient = new WebClient(process.env.SLACK_SDK_TEST_GRID_ORG_ADMIN_USER_TOKEN, { logger }); diff --git a/prod-server-integration-tests/test/admin-web-api-users-unsupportedVersions-export.test.js b/prod-server-integration-tests/test/admin-web-api-users-unsupportedVersions-export.test.js index b9995e6dd..f1538709d 100644 --- a/prod-server-integration-tests/test/admin-web-api-users-unsupportedVersions-export.test.js +++ b/prod-server-integration-tests/test/admin-web-api-users-unsupportedVersions-export.test.js @@ -2,12 +2,9 @@ import 'dotenv/config'; import assert from 'node:assert/strict'; import { describe, it } from 'node:test'; import { WebClient } from '@slack/web-api'; -import winston from 'winston'; +import { FileLogger } from '../logger.js'; -const logger = winston.createLogger({ - level: 'debug', - transports: [new winston.transports.File({ filename: 'logs/console.log' })], -}); +const logger = new FileLogger('logs/console.log'); describe('admin.users.unsupportedVersions.export', () => { it('should work', async () => { diff --git a/prod-server-integration-tests/test/admin-web-api.test.js b/prod-server-integration-tests/test/admin-web-api.test.js index 9a3d70ae6..8acb54d6c 100644 --- a/prod-server-integration-tests/test/admin-web-api.test.js +++ b/prod-server-integration-tests/test/admin-web-api.test.js @@ -2,12 +2,9 @@ import 'dotenv/config'; import assert from 'node:assert/strict'; import { describe, it } from 'node:test'; import { WebClient } from '@slack/web-api'; -import winston from 'winston'; +import { FileLogger } from '../logger.js'; -const logger = winston.createLogger({ - level: 'debug', - transports: [new winston.transports.File({ filename: 'logs/console.log' })], -}); +const logger = new FileLogger('logs/console.log'); describe('admin.* Web APIs', () => { const teamAdminClient = new WebClient(process.env.SLACK_SDK_TEST_GRID_ORG_ADMIN_USER_TOKEN, { logger }); diff --git a/prod-server-integration-tests/test/bookmarks-web-api.test.js b/prod-server-integration-tests/test/bookmarks-web-api.test.js index 0992448c2..5e19663f6 100644 --- a/prod-server-integration-tests/test/bookmarks-web-api.test.js +++ b/prod-server-integration-tests/test/bookmarks-web-api.test.js @@ -2,12 +2,9 @@ import 'dotenv/config'; import assert from 'node:assert/strict'; import { describe, it } from 'node:test'; import { WebClient } from '@slack/web-api'; -import winston from 'winston'; +import { FileLogger } from '../logger.js'; -const logger = winston.createLogger({ - level: 'debug', - transports: [new winston.transports.File({ filename: 'logs/console.log' })], -}); +const logger = new FileLogger('logs/console.log'); describe('bookmarks.* Web APIs', () => { const client = new WebClient(process.env.SLACK_SDK_TEST_BOT_TOKEN, { logger }); diff --git a/prod-server-integration-tests/test/web-api.test.js b/prod-server-integration-tests/test/web-api.test.js index 3aa7df70a..929192f7d 100644 --- a/prod-server-integration-tests/test/web-api.test.js +++ b/prod-server-integration-tests/test/web-api.test.js @@ -2,12 +2,9 @@ import 'dotenv/config'; import assert from 'node:assert/strict'; import { describe, it } from 'node:test'; import { WebClient } from '@slack/web-api'; -import winston from 'winston'; +import { FileLogger } from '../logger.js'; -const logger = winston.createLogger({ - level: 'debug', - transports: [new winston.transports.File({ filename: 'logs/console.log' })], -}); +const logger = new FileLogger('logs/console.log'); describe('Web APIs', () => { const botClient = new WebClient(process.env.SLACK_SDK_TEST_BOT_TOKEN, { logger }); From f6d3dd9e7a726da420e662f9a19943dfef852ffb Mon Sep 17 00:00:00 2001 From: William Bergamin Date: Thu, 7 May 2026 16:00:30 -0400 Subject: [PATCH 5/6] restore deleted comments --- prod-server-integration-tests/README.md | 16 +--------------- .../scripts/socket-mode.js | 8 ++++---- .../admin-web-api-conversations-bulk.test.js | 2 ++ .../test/admin-web-api.test.js | 2 ++ .../test/bookmarks-web-api.test.js | 1 + .../test/web-api.test.js | 3 +++ 6 files changed, 13 insertions(+), 19 deletions(-) diff --git a/prod-server-integration-tests/README.md b/prod-server-integration-tests/README.md index fc50b7d5f..d189addc3 100644 --- a/prod-server-integration-tests/README.md +++ b/prod-server-integration-tests/README.md @@ -89,21 +89,7 @@ node scripts/admin/usersSessionSettings.js ## Environment Variables -See [`.env.sample`](./.env.sample) for the complete list with descriptions. Summary: - -| Variable | Source App | Used By | -|---|---|---| -| `SLACK_SDK_TEST_BOT_TOKEN` | main-bot-app | web-api, bookmarks, conversations_invite, auth_test, pagination | -| `SLACK_SDK_TEST_USER_TOKEN` | main-bot-app (user OAuth) | web-api | -| `SLACK_SDK_TEST_WEB_TEST_CHANNEL_ID` | — (channel in workspace) | chat.scheduleMessage test | -| `SLACK_SDK_TEST_CONNECT_INVITE_SENDER_BOT_TOKEN` | slack-connect-sender | Slack Connect tests | -| `SLACK_SDK_TEST_CONNECT_INVITE_RECEIVER_BOT_TOKEN` | slack-connect-receiver | Slack Connect tests | -| `SLACK_SDK_TEST_CONNECT_INVITE_RECEIVER_BOT_USER_ID` | — (user ID) | Slack Connect tests | -| `SLACK_SDK_TEST_GRID_ORG_ADMIN_USER_TOKEN` | admin-app (user OAuth) | all admin tests | -| `SLACK_SDK_TEST_GRID_SECONDARY_WORKSPACE_ID` | — (team ID) | admin bulk move test | -| `SLACK_SDK_TEST_GRID_USER_ID` | — (user ID, optional) | admin auth policy test | -| `SLACK_SDK_TEST_SOCKET_MODE_APP_TOKEN` | socket-mode-app | Socket Mode script | -| `SLACK_TOOLING_TOKEN` | — (configuration token) | apps_manifest_test script | +See [`.env.sample`](./.env.sample) for the complete list with descriptions. ## App Manifests diff --git a/prod-server-integration-tests/scripts/socket-mode.js b/prod-server-integration-tests/scripts/socket-mode.js index fe159676d..b5369827c 100644 --- a/prod-server-integration-tests/scripts/socket-mode.js +++ b/prod-server-integration-tests/scripts/socket-mode.js @@ -4,22 +4,22 @@ import { SocketModeClient } from '@slack/socket-mode'; const appToken = process.env.SLACK_SDK_TEST_SOCKET_MODE_APP_TOKEN; const socketModeClient = new SocketModeClient({ appToken, logLevel: 'debug' }); -socketModeClient.on('app_mention', async (args) => { +socketModeClient.on('app_mention', async (args) => { // events_api event console.log(args); await args.ack(); }); -socketModeClient.on('interactive', async (args) => { +socketModeClient.on('interactive', async (args) => { // block_actions, shortcuts, etc. console.log(args); await args.ack(); }); -socketModeClient.on('slash_commands', async (args) => { +socketModeClient.on('slash_commands', async (args) => { // slash command invocations console.log(args); await args.ack(); }); -socketModeClient.on('slack_event', async (args) => { +socketModeClient.on('slack_event', async (args) => { // all events (used by bolt-js) console.log(args); await args.ack(); }); diff --git a/prod-server-integration-tests/test/admin-web-api-conversations-bulk.test.js b/prod-server-integration-tests/test/admin-web-api-conversations-bulk.test.js index 3b5305870..0a5c1184e 100644 --- a/prod-server-integration-tests/test/admin-web-api-conversations-bulk.test.js +++ b/prod-server-integration-tests/test/admin-web-api-conversations-bulk.test.js @@ -1,3 +1,5 @@ +// Prerequisites: requires 2 workspaces on your Enterprise Grid and +// the SLACK_SDK_TEST_GRID_SECONDARY_WORKSPACE_ID environment variable configured. import 'dotenv/config'; import assert from 'node:assert/strict'; import { describe, it } from 'node:test'; diff --git a/prod-server-integration-tests/test/admin-web-api.test.js b/prod-server-integration-tests/test/admin-web-api.test.js index 8acb54d6c..bad46cfe3 100644 --- a/prod-server-integration-tests/test/admin-web-api.test.js +++ b/prod-server-integration-tests/test/admin-web-api.test.js @@ -64,6 +64,8 @@ describe('admin.* Web APIs', () => { }); }); + // Prerequisites: requires an email_password auth policy enabled on the Enterprise Org, + // which means having SSO enabled. SLACK_SDK_TEST_GRID_USER_ID must be a user managed by the IDP. describe('admin.auth.policy.{assign|get|remove}Entities', () => { it('should assign an entity', async () => { const res = await orgAdminClient.admin.auth.policy.assignEntities({ diff --git a/prod-server-integration-tests/test/bookmarks-web-api.test.js b/prod-server-integration-tests/test/bookmarks-web-api.test.js index 5e19663f6..55f7fdfdb 100644 --- a/prod-server-integration-tests/test/bookmarks-web-api.test.js +++ b/prod-server-integration-tests/test/bookmarks-web-api.test.js @@ -6,6 +6,7 @@ import { FileLogger } from '../logger.js'; const logger = new FileLogger('logs/console.log'); +// Bot token must have bookmarks:read, bookmarks:write, and channels:manage scopes. describe('bookmarks.* Web APIs', () => { const client = new WebClient(process.env.SLACK_SDK_TEST_BOT_TOKEN, { logger }); diff --git a/prod-server-integration-tests/test/web-api.test.js b/prod-server-integration-tests/test/web-api.test.js index 929192f7d..6c717c630 100644 --- a/prod-server-integration-tests/test/web-api.test.js +++ b/prod-server-integration-tests/test/web-api.test.js @@ -50,6 +50,9 @@ describe('Web APIs', () => { }); }); + // Prerequisites: two workspace-level bot tokens are needed — one for the sending workspace + // (list and send invites) and another for the receiving workspace (accept and approve invites). + // A Slack Connect shared channel must be manually created with both bots added as members first. describe('Slack Connect conversations.* methods', () => { const sender = new WebClient(process.env.SLACK_SDK_TEST_CONNECT_INVITE_SENDER_BOT_TOKEN); const receiver = new WebClient(process.env.SLACK_SDK_TEST_CONNECT_INVITE_RECEIVER_BOT_TOKEN); From acf8c8e8a79925f251d483beb3fb3fbf27519f22 Mon Sep 17 00:00:00 2001 From: William Bergamin Date: Thu, 7 May 2026 16:10:17 -0400 Subject: [PATCH 6/6] clean up changes --- prod-server-integration-tests/scripts/socket-mode.js | 12 ++++++++---- .../test/admin-web-api-conversations-bulk.test.js | 7 +++---- .../test/admin-web-api-custom-retention.test.js | 3 +-- .../test/admin-web-api-user-sessions.test.js | 3 +-- .../test/admin-web-api.test.js | 3 +-- 5 files changed, 14 insertions(+), 14 deletions(-) diff --git a/prod-server-integration-tests/scripts/socket-mode.js b/prod-server-integration-tests/scripts/socket-mode.js index b5369827c..c8f7470b4 100644 --- a/prod-server-integration-tests/scripts/socket-mode.js +++ b/prod-server-integration-tests/scripts/socket-mode.js @@ -4,22 +4,26 @@ import { SocketModeClient } from '@slack/socket-mode'; const appToken = process.env.SLACK_SDK_TEST_SOCKET_MODE_APP_TOKEN; const socketModeClient = new SocketModeClient({ appToken, logLevel: 'debug' }); -socketModeClient.on('app_mention', async (args) => { // events_api event +// events_api event +socketModeClient.on('app_mention', async (args) => { console.log(args); await args.ack(); }); -socketModeClient.on('interactive', async (args) => { // block_actions, shortcuts, etc. +// block_actions, shortcuts, etc. +socketModeClient.on('interactive', async (args) => { console.log(args); await args.ack(); }); -socketModeClient.on('slash_commands', async (args) => { // slash command invocations +// slash command invocations +socketModeClient.on('slash_commands', async (args) => { console.log(args); await args.ack(); }); -socketModeClient.on('slack_event', async (args) => { // all events (used by bolt-js) +// all events (used by bolt-js) +socketModeClient.on('slack_event', async (args) => { console.log(args); await args.ack(); }); diff --git a/prod-server-integration-tests/test/admin-web-api-conversations-bulk.test.js b/prod-server-integration-tests/test/admin-web-api-conversations-bulk.test.js index 0a5c1184e..48c77407e 100644 --- a/prod-server-integration-tests/test/admin-web-api-conversations-bulk.test.js +++ b/prod-server-integration-tests/test/admin-web-api-conversations-bulk.test.js @@ -9,14 +9,13 @@ import { FileLogger } from '../logger.js'; const logger = new FileLogger('logs/console.log'); describe('admin.* Web APIs', () => { - const teamAdminClient = new WebClient(process.env.SLACK_SDK_TEST_GRID_ORG_ADMIN_USER_TOKEN, { logger }); const primaryTeamId = process.env.SLACK_SDK_TEST_GRID_WORKSPACE_ID; const secondaryTeamId = process.env.SLACK_SDK_TEST_GRID_SECONDARY_WORKSPACE_ID; const orgAdminClient = new WebClient(process.env.SLACK_SDK_TEST_GRID_ORG_ADMIN_USER_TOKEN, { logger }); describe('admin.conversations.bulk{Archive|Move|Delete}', () => { it('should bulk archive conversations using admin.conversations.bulkArchive', async () => { - const testChannel = await teamAdminClient.conversations.create({ + const testChannel = await orgAdminClient.conversations.create({ name: `test-bulk-archive-${Date.now()}`, team_id: primaryTeamId, }); @@ -55,7 +54,7 @@ describe('admin.* Web APIs', () => { }); it('should bulk delete conversations using admin.conversations.bulkDelete', async () => { - const testChannel = await teamAdminClient.conversations.create({ + const testChannel = await orgAdminClient.conversations.create({ name: `test-bulk-delete-${Date.now()}`, team_id: primaryTeamId, }); @@ -87,7 +86,7 @@ describe('admin.* Web APIs', () => { }); it('should bulk move conversations using admin.conversations.bulkMove', async () => { - const testChannel = await teamAdminClient.conversations.create({ + const testChannel = await orgAdminClient.conversations.create({ name: `test-bulk-move-${Date.now()}`, team_id: primaryTeamId, }); diff --git a/prod-server-integration-tests/test/admin-web-api-custom-retention.test.js b/prod-server-integration-tests/test/admin-web-api-custom-retention.test.js index 680faaaa5..667981ec7 100644 --- a/prod-server-integration-tests/test/admin-web-api-custom-retention.test.js +++ b/prod-server-integration-tests/test/admin-web-api-custom-retention.test.js @@ -7,13 +7,12 @@ import { FileLogger } from '../logger.js'; const logger = new FileLogger('logs/console.log'); describe('admin.* Web APIs', () => { - const teamAdminClient = new WebClient(process.env.SLACK_SDK_TEST_GRID_ORG_ADMIN_USER_TOKEN, { logger }); const primaryTeamId = process.env.SLACK_SDK_TEST_GRID_WORKSPACE_ID; const orgAdminClient = new WebClient(process.env.SLACK_SDK_TEST_GRID_ORG_ADMIN_USER_TOKEN, { logger }); describe('admin.conversations.{get|set|remove}CustomRetention', () => { it('should work', async () => { - const creation = await teamAdminClient.conversations.create({ + const creation = await orgAdminClient.conversations.create({ name: `test-channel-${Date.now()}`, team_id: primaryTeamId, }); diff --git a/prod-server-integration-tests/test/admin-web-api-user-sessions.test.js b/prod-server-integration-tests/test/admin-web-api-user-sessions.test.js index 3da34ec25..411ff856f 100644 --- a/prod-server-integration-tests/test/admin-web-api-user-sessions.test.js +++ b/prod-server-integration-tests/test/admin-web-api-user-sessions.test.js @@ -7,13 +7,12 @@ import { FileLogger } from '../logger.js'; const logger = new FileLogger('logs/console.log'); describe('admin.users.session.* Web APIs', () => { - const teamAdminClient = new WebClient(process.env.SLACK_SDK_TEST_GRID_ORG_ADMIN_USER_TOKEN, { logger }); const primaryTeamId = process.env.SLACK_SDK_TEST_GRID_WORKSPACE_ID; const orgAdminClient = new WebClient(process.env.SLACK_SDK_TEST_GRID_ORG_ADMIN_USER_TOKEN, { logger }); describe('admin.users.session.reset/resetBulk', () => { it('should work', async () => { - const users = await teamAdminClient.users.list({ + const users = await orgAdminClient.users.list({ exclude_archived: true, limit: 100, team_id: primaryTeamId, diff --git a/prod-server-integration-tests/test/admin-web-api.test.js b/prod-server-integration-tests/test/admin-web-api.test.js index bad46cfe3..e68ee02d8 100644 --- a/prod-server-integration-tests/test/admin-web-api.test.js +++ b/prod-server-integration-tests/test/admin-web-api.test.js @@ -7,13 +7,12 @@ import { FileLogger } from '../logger.js'; const logger = new FileLogger('logs/console.log'); describe('admin.* Web APIs', () => { - const teamAdminClient = new WebClient(process.env.SLACK_SDK_TEST_GRID_ORG_ADMIN_USER_TOKEN, { logger }); const primaryTeamId = process.env.SLACK_SDK_TEST_GRID_WORKSPACE_ID; const orgAdminClient = new WebClient(process.env.SLACK_SDK_TEST_GRID_ORG_ADMIN_USER_TOKEN, { logger }); describe('admin.users.session.{get|set|clear}Settings', () => { it('should work', async () => { - const users = await teamAdminClient.users.list({ + const users = await orgAdminClient.users.list({ exclude_archived: true, limit: 100, team_id: primaryTeamId,