Skip to content

Commit 8bc2dcd

Browse files
committed
test: improve running e2e tests
1 parent d077902 commit 8bc2dcd

5 files changed

Lines changed: 149 additions & 41 deletions

File tree

.github/workflows/e2e.yml

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
name: E2E Tests
2+
3+
on:
4+
push:
5+
branches:
6+
- master
7+
pull_request:
8+
branches:
9+
- master
10+
11+
permissions:
12+
contents: read
13+
14+
jobs:
15+
e2e:
16+
name: E2E Tests
17+
runs-on: ubuntu-latest
18+
timeout-minutes: 10
19+
services:
20+
mock-server:
21+
image: ghcr.io/whiskeysockets-devtools/bartender:latest
22+
credentials:
23+
username: ${{ github.actor }}
24+
password: ${{ secrets.BARTENDER_GHCR_TOKEN }}
25+
ports:
26+
- 8080:8080
27+
env:
28+
CHATSTATE_TTL_SECS: "3"
29+
options: --log-driver none
30+
31+
steps:
32+
- uses: actions/checkout@v4
33+
with:
34+
ref: ${{ github.event.pull_request.head.sha || github.sha }}
35+
36+
- name: Setup Node.js and Corepack
37+
uses: actions/setup-node@v4
38+
with:
39+
node-version: 20.x
40+
41+
- name: Enable Corepack and Set Yarn Version
42+
run: |
43+
corepack enable
44+
corepack prepare yarn@4.x --activate
45+
46+
- name: Restore Yarn Cache
47+
uses: actions/cache@v4
48+
with:
49+
path: .yarn/cache
50+
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
51+
restore-keys: |
52+
${{ runner.os }}-yarn-
53+
54+
- name: Install dependencies
55+
run: yarn install --immutable
56+
57+
- name: Wait for mock server
58+
run: |
59+
for i in $(seq 1 30); do
60+
if curl -sk https://localhost:8080/ > /dev/null 2>&1; then
61+
echo "Mock server is ready"
62+
exit 0
63+
fi
64+
sleep 1
65+
done
66+
echo "Mock server failed to become ready"
67+
exit 1
68+
69+
- name: Run E2E tests
70+
run: yarn test:e2e

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@
3737
"preinstall": "node ./engine-requirements.js",
3838
"release": "release-it",
3939
"test": "node --experimental-vm-modules ./node_modules/.bin/jest --testMatch '**/*.test.ts'",
40-
"test:e2e": "node --experimental-vm-modules ./node_modules/.bin/jest --testMatch '**/*.test-e2e.ts'",
40+
"test:e2e": "NODE_TLS_REJECT_UNAUTHORIZED=0 node --experimental-vm-modules ./node_modules/.bin/jest --testMatch '**/*.test-e2e.ts'",
4141
"update:version": "tsx ./scripts/update-version.ts"
4242
},
4343
"dependencies": {

src/Socket/messages-send.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,14 @@ import {
2525
generateMessageIDV2,
2626
generateParticipantHashV2,
2727
generateWAMessage,
28+
getMediaHost,
2829
getStatusCodeForMediaRetry,
2930
getUrlFromDirectPath,
3031
getWAUploadToServer,
3132
MessageRetryManager,
3233
normalizeMessageContent,
3334
parseAndInjectE2ESessions,
35+
setMediaHost,
3436
unixTimestampSeconds
3537
} from '../Utils'
3638
import { getUrlInfo } from '../Utils/link-preview'
@@ -126,6 +128,10 @@ export const makeMessagesSocket = (config: SocketConfig) => {
126128
fetchDate: new Date()
127129
}
128130
logger.debug('fetched media conn')
131+
if (node.hosts[0]) {
132+
setMediaHost(node.hosts[0].hostname)
133+
}
134+
129135
return node
130136
})()
131137
}
@@ -1148,6 +1154,9 @@ export const makeMessagesSocket = (config: SocketConfig) => {
11481154
sendReceipts,
11491155
readMessages,
11501156
refreshMediaConn,
1157+
get mediaHost() {
1158+
return getMediaHost()
1159+
},
11511160
waUploadToServer,
11521161
fetchPrivacySettings,
11531162
sendPeerDataOperationMessage,

src/Utils/messages-media.ts

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -500,7 +500,18 @@ export const encryptedStream = async (
500500
}
501501
}
502502

503-
const DEF_HOST = 'mmg.whatsapp.net'
503+
export const DEF_MEDIA_HOST = 'mmg.whatsapp.net'
504+
505+
let _mediaHost = DEF_MEDIA_HOST
506+
507+
/** Set the media host used for downloading media. Called internally by the socket when media_conn is fetched. */
508+
export const setMediaHost = (host: string) => {
509+
_mediaHost = host
510+
}
511+
512+
/** Get the current media host used for downloading media. */
513+
export const getMediaHost = () => _mediaHost
514+
504515
const AES_CHUNK_SIZE = 16
505516

506517
const toSmallestChunkSize = (num: number) => {
@@ -513,15 +524,14 @@ export type MediaDownloadOptions = {
513524
options?: RequestInit
514525
}
515526

516-
export const getUrlFromDirectPath = (directPath: string) => `https://${DEF_HOST}${directPath}`
527+
export const getUrlFromDirectPath = (directPath: string, host: string = _mediaHost) => `https://${host}${directPath}`
517528

518529
export const downloadContentFromMessage = async (
519530
{ mediaKey, directPath, url }: DownloadableMessage,
520531
type: MediaType,
521532
opts: MediaDownloadOptions = {}
522533
) => {
523-
const isValidMediaUrl = url?.startsWith('https://mmg.whatsapp.net/')
524-
const downloadUrl = isValidMediaUrl ? url : getUrlFromDirectPath(directPath!)
534+
const downloadUrl = directPath ? getUrlFromDirectPath(directPath) : url
525535
if (!downloadUrl) {
526536
throw new Boom('No valid media URL or directPath present in message', { statusCode: 400 })
527537
}

src/__tests__/e2e/send-receive-message.test-e2e.ts

Lines changed: 55 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
import { Boom } from '@hapi/boom'
22
import { jest } from '@jest/globals'
33
import { readFileSync } from 'node:fs'
4+
import { Agent } from 'node:https'
45
import P from 'pino'
56
import makeWASocket, {
67
DisconnectReason,
78
downloadContentFromMessage,
89
downloadMediaMessage,
910
jidNormalizedUser,
11+
makeCacheableSignalKeyStore,
1012
proto,
1113
toBuffer,
1214
useMultiFileAuthState,
@@ -22,47 +24,64 @@ describe('E2E Tests', () => {
2224
let groupJid: string | undefined
2325

2426
beforeAll(async () => {
25-
const { state, saveCreds } = await useMultiFileAuthState('baileys_auth_info')
26-
const logger = P({ level: 'silent' })
27+
const logger = P({ level: 'debug' })
28+
const agent = new Agent({ rejectUnauthorized: false })
2729

28-
sock = makeWASocket({
29-
auth: state,
30-
logger
31-
})
30+
const connectSocket = async (): Promise<void> => {
31+
const { state, saveCreds } = await useMultiFileAuthState('baileys_auth_info')
3232

33-
sock.ev.on('creds.update', saveCreds)
34-
35-
await new Promise<void>((resolve, reject) => {
36-
sock.ev.on('connection.update', update => {
37-
const { connection, lastDisconnect } = update
38-
if (connection === 'open') {
39-
meJid = jidNormalizedUser(sock.user?.id)
40-
meLid = sock.user?.lid
41-
42-
sock
43-
.groupFetchAllParticipating()
44-
.then(groups => {
45-
const group = Object.values(groups).find(g => g.subject === 'Baileys Group Test')
46-
if (group) {
47-
groupJid = group.id
48-
console.log(`Found test group "${group.subject}" with JID: ${groupJid}`)
49-
}
50-
51-
resolve()
52-
})
53-
.catch(reject)
54-
} else if (connection === 'close') {
55-
const reason = (lastDisconnect?.error as Boom)?.output?.statusCode
56-
if (reason === DisconnectReason.loggedOut) {
57-
console.error('Logged out, please delete the baileys_auth_info_e2e folder and re-run the test')
58-
}
33+
if (process.env.ADV_SECRET_KEY) {
34+
state.creds.advSecretKey = process.env.ADV_SECRET_KEY ?? 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA='
35+
}
36+
37+
sock = makeWASocket({
38+
auth: {
39+
creds: state.creds,
40+
keys: makeCacheableSignalKeyStore(state.keys, logger)
41+
},
42+
waWebSocketUrl: process.env.SOCKET_URL ?? 'wss://127.0.0.1:8080/ws/chat',
43+
logger,
44+
agent,
45+
fetchAgent: agent
46+
})
5947

60-
if (lastDisconnect?.error) {
61-
reject(new Error(`Connection closed: ${DisconnectReason[reason] || 'unknown'}`))
48+
sock.ev.on('creds.update', saveCreds)
49+
50+
await new Promise<void>((resolve, reject) => {
51+
sock.ev.on('connection.update', update => {
52+
const { connection, lastDisconnect } = update
53+
if (connection === 'open') {
54+
meJid = jidNormalizedUser(sock.user?.id)
55+
meLid = sock.user?.lid
56+
57+
sock
58+
.groupFetchAllParticipating()
59+
.then(groups => {
60+
const group = Object.values(groups).find(g => g.subject === 'Baileys Group Test')
61+
if (group) {
62+
groupJid = group.id
63+
console.log(`Found test group "${group.subject}" with JID: ${groupJid}`)
64+
}
65+
66+
resolve()
67+
})
68+
.catch(reject)
69+
} else if (connection === 'close') {
70+
const reason = (lastDisconnect?.error as Boom)?.output?.statusCode
71+
if (reason === DisconnectReason.loggedOut) {
72+
reject(new Error('Logged out, please delete the baileys_auth_info folder and re-run the test'))
73+
return
74+
}
75+
76+
// Reconnect on non-logout disconnects (e.g. after pairing)
77+
console.log(`Connection closed (${DisconnectReason[reason] || reason}), reconnecting...`)
78+
connectSocket().then(resolve, reject)
6279
}
63-
}
80+
})
6481
})
65-
})
82+
}
83+
84+
await connectSocket()
6685
})
6786

6887
afterAll(async () => {

0 commit comments

Comments
 (0)