test(e2e): added TelegramClient e2e tests
This commit is contained in:
parent
ca1916c5cb
commit
9ff6a628e5
13 changed files with 390 additions and 20 deletions
|
@ -9,15 +9,20 @@ module.exports = {
|
|||
},
|
||||
ts: {
|
||||
getFiles: () => 'tests/**/*.ts',
|
||||
beforeAll: () => [
|
||||
'tsc',
|
||||
'node build-esm.cjs',
|
||||
],
|
||||
runFile: (file) => [
|
||||
beforeAll: () => ['tsc', 'node build-esm.cjs'],
|
||||
runFile: (file) => {
|
||||
if (file.startsWith('tests/packaging/')) {
|
||||
// packaging tests - we need to make sure everything imports and works
|
||||
return [
|
||||
`mocha -r ts-node/register ${file}`,
|
||||
`mocha dist/${file.replace(/\.ts$/, '.js')}`,
|
||||
`node run-esm.cjs ${file}`,
|
||||
`mocha dist/esm/${file.replace(/\.ts$/, '.js')}`,
|
||||
],
|
||||
]
|
||||
}
|
||||
|
||||
// normal e2e tests - testing features etc
|
||||
return `mocha dist/${file.replace(/\.ts$/, '.js')}`
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
|
@ -70,6 +70,7 @@ function runForDir(dir) {
|
|||
}
|
||||
|
||||
const files = glob.sync(getFiles(), { cwd: path.join(__dirname, dir) })
|
||||
files.sort()
|
||||
|
||||
for (const file of files) {
|
||||
runForFile(dir, file, false)
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
/* eslint-disable no-restricted-globals */
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
const cp = require('child_process')
|
||||
|
|
77
e2e/ts/tests/01.auth.ts
Normal file
77
e2e/ts/tests/01.auth.ts
Normal file
|
@ -0,0 +1,77 @@
|
|||
import { expect } from 'chai'
|
||||
import { describe, it } from 'mocha'
|
||||
|
||||
import { MtUnsupportedError, TelegramClient } from '@mtcute/client'
|
||||
|
||||
import { getApiParams } from '../utils.js'
|
||||
|
||||
const getAccountId = () =>
|
||||
Math.floor(Math.random() * 10000)
|
||||
.toString()
|
||||
.padStart(4, '0')
|
||||
|
||||
describe('1. authorization', function () {
|
||||
this.timeout(300_000)
|
||||
|
||||
it('should authorize in default dc', async () => {
|
||||
const tg = new TelegramClient(getApiParams('dc2.session'))
|
||||
|
||||
// reset storage just in case
|
||||
await tg.storage.load?.()
|
||||
await tg.storage.reset(true)
|
||||
|
||||
while (true) {
|
||||
const phone = `999662${getAccountId()}`
|
||||
let user
|
||||
|
||||
try {
|
||||
user = await tg.start({
|
||||
phone,
|
||||
code: () => '22222',
|
||||
})
|
||||
} catch (e) {
|
||||
if (e instanceof MtUnsupportedError && e.message.includes('Signup is no longer supported')) {
|
||||
// retry with another number
|
||||
continue
|
||||
} else throw e
|
||||
}
|
||||
|
||||
await tg.close()
|
||||
|
||||
expect(user.isSelf).to.be.true
|
||||
expect(user.phoneNumber).to.equal(phone)
|
||||
break
|
||||
}
|
||||
})
|
||||
|
||||
it('should authorize in dc 1', async () => {
|
||||
const tg = new TelegramClient(getApiParams('dc1.session'))
|
||||
|
||||
// reset storage just in case
|
||||
await tg.storage.load?.()
|
||||
await tg.storage.reset(true)
|
||||
|
||||
while (true) {
|
||||
const phone = `999661${getAccountId()}`
|
||||
let user
|
||||
|
||||
try {
|
||||
user = await tg.start({
|
||||
phone,
|
||||
code: () => '11111',
|
||||
})
|
||||
} catch (e) {
|
||||
if (e instanceof MtUnsupportedError && e.message.includes('Signup is no longer supported')) {
|
||||
// retry with another number
|
||||
continue
|
||||
} else throw e
|
||||
}
|
||||
|
||||
await tg.close()
|
||||
|
||||
expect(user.isSelf).to.be.true
|
||||
expect(user.phoneNumber).to.equal(phone)
|
||||
break
|
||||
}
|
||||
})
|
||||
})
|
48
e2e/ts/tests/02.methods.ts
Normal file
48
e2e/ts/tests/02.methods.ts
Normal file
|
@ -0,0 +1,48 @@
|
|||
import { expect } from 'chai'
|
||||
import { describe, it } from 'mocha'
|
||||
|
||||
import { TelegramClient } from '@mtcute/client'
|
||||
|
||||
import { getApiParams } from '../utils.js'
|
||||
|
||||
describe('2. calling methods', function () {
|
||||
this.timeout(300_000)
|
||||
const tg = new TelegramClient(getApiParams('dc2.session'))
|
||||
|
||||
this.beforeAll(() => tg.connect())
|
||||
this.afterAll(() => tg.close())
|
||||
|
||||
it('getUsers(@BotFather)', async () => {
|
||||
const [user] = await tg.getUsers('botfather')
|
||||
|
||||
expect(user?.isBot).to.be.true
|
||||
expect(user?.displayName).to.equal('BotFather')
|
||||
})
|
||||
|
||||
it('getUsers(@BotFather) - cached', async () => {
|
||||
const [user] = await tg.getUsers('botfather')
|
||||
|
||||
expect(user?.isBot).to.be.true
|
||||
expect(user?.displayName).to.equal('BotFather')
|
||||
})
|
||||
|
||||
it('getHistory(777000)', async () => {
|
||||
const history = await tg.getHistory(777000, { limit: 5 })
|
||||
|
||||
expect(history[0].chat.chatType).to.equal('private')
|
||||
expect(history[0].chat.id).to.equal(777000)
|
||||
expect(history[0].chat.firstName).to.equal('Telegram')
|
||||
})
|
||||
|
||||
it('updateProfile', async () => {
|
||||
const bio = `mtcute e2e ${new Date().toISOString()}`
|
||||
|
||||
const oldSelf = await tg.getFullChat('self')
|
||||
const res = await tg.updateProfile({ bio })
|
||||
const newSelf = await tg.getFullChat('self')
|
||||
|
||||
expect(res.isSelf).to.be.true
|
||||
expect(oldSelf.bio).to.not.equal(newSelf.bio)
|
||||
expect(newSelf.bio).to.equal(bio)
|
||||
})
|
||||
})
|
167
e2e/ts/tests/03.files.ts
Normal file
167
e2e/ts/tests/03.files.ts
Normal file
|
@ -0,0 +1,167 @@
|
|||
/* eslint-disable no-restricted-imports */
|
||||
import { expect } from 'chai'
|
||||
import { createHash } from 'crypto'
|
||||
import { describe, it } from 'mocha'
|
||||
|
||||
import { FileDownloadLocation, TelegramClient, Thumbnail } from '@mtcute/client'
|
||||
import { sleep } from '@mtcute/core/utils.js'
|
||||
|
||||
import { getApiParams } from '../utils.js'
|
||||
|
||||
const CINNAMOROLL_PFP_CHAT = 'test_file_dc2'
|
||||
const CINNAMOROLL_PFP_THUMB_SHA256 = '3e6f220235a12547c16129f50c19ed3224d39b827414d1d500f79569a3431eae'
|
||||
const CINNAMOROLL_PFP_SHA256 = '4d9836a71ac039f5656cde55b83525871549bfbff9cfb658c3f8381c5ba89ce8'
|
||||
|
||||
const UWU_MSG = 'https://t.me/test_file_dc2/8'
|
||||
const UWU_SHA256 = '357b78c9f9d20e813f729a19dd90c6727f30ebd4c8c83557022285f283a705b9'
|
||||
|
||||
const SHREK_MSG = 'https://t.me/test_file_dc2/11'
|
||||
const SHREK_SHA256 = 'd3e6434e027f3d31dc3e05c6ea2eaf84fdd1fb00774a215f89d9ed8b56f86258'
|
||||
|
||||
const LARGE_MSG = 'https://t.me/test_file_dc2/12'
|
||||
|
||||
async function downloadAsSha256(client: TelegramClient, location: FileDownloadLocation): Promise<string> {
|
||||
const sha = createHash('sha256')
|
||||
|
||||
for await (const chunk of client.downloadAsIterable(location)) {
|
||||
sha.update(chunk)
|
||||
}
|
||||
|
||||
return sha.digest('hex')
|
||||
}
|
||||
|
||||
describe('3. working with files', function () {
|
||||
this.timeout(300_000)
|
||||
// sometimes test dcs are overloaded and we get FILE_REFERENCE_EXPIRED
|
||||
// because we got multiple -500:No workers running errors in a row
|
||||
// we currently don't have file references database, so we can just retry the test for now
|
||||
this.retries(2)
|
||||
|
||||
describe('same-dc', () => {
|
||||
const tg = new TelegramClient(getApiParams('dc2.session'))
|
||||
|
||||
this.beforeAll(() => tg.connect())
|
||||
this.afterAll(() => tg.close())
|
||||
|
||||
it('should download pfp thumbs', async () => {
|
||||
const chat = await tg.getChat(CINNAMOROLL_PFP_CHAT)
|
||||
if (!chat.photo) expect.fail('Chat has no photo')
|
||||
|
||||
expect(await downloadAsSha256(tg, chat.photo.big)).to.equal(CINNAMOROLL_PFP_THUMB_SHA256)
|
||||
})
|
||||
|
||||
it('should download animated pfps', async () => {
|
||||
const chat = await tg.getFullChat(CINNAMOROLL_PFP_CHAT)
|
||||
const thumb = chat.fullPhoto?.getThumbnail(Thumbnail.THUMB_VIDEO_PROFILE)
|
||||
if (!thumb) expect.fail('Chat has no animated pfp')
|
||||
|
||||
expect(await downloadAsSha256(tg, thumb)).to.equal(CINNAMOROLL_PFP_SHA256)
|
||||
})
|
||||
|
||||
it('should download photos', async () => {
|
||||
const msg = await tg.getMessageByLink(UWU_MSG)
|
||||
|
||||
if (msg?.media?.type !== 'photo') {
|
||||
expect.fail('Message not found or not a photo')
|
||||
}
|
||||
|
||||
expect(await downloadAsSha256(tg, msg.media)).to.equal(UWU_SHA256)
|
||||
})
|
||||
|
||||
it('should download documents', async () => {
|
||||
const msg = await tg.getMessageByLink(SHREK_MSG)
|
||||
|
||||
if (msg?.media?.type !== 'document') {
|
||||
expect.fail('Message not found or not a document')
|
||||
}
|
||||
|
||||
expect(await downloadAsSha256(tg, msg.media)).to.equal(SHREK_SHA256)
|
||||
})
|
||||
|
||||
it('should cancel downloads', async () => {
|
||||
const msg = await tg.getMessageByLink(LARGE_MSG)
|
||||
|
||||
if (msg?.media?.type !== 'document') {
|
||||
expect.fail('Message not found or not a document')
|
||||
}
|
||||
|
||||
const media = msg.media
|
||||
|
||||
const abort = new AbortController()
|
||||
|
||||
let downloaded = 0
|
||||
|
||||
async function download() {
|
||||
const dl = tg.downloadAsIterable(media, { abortSignal: abort.signal })
|
||||
|
||||
try {
|
||||
for await (const chunk of dl) {
|
||||
downloaded += chunk.length
|
||||
}
|
||||
} catch (e) {
|
||||
if (!(e instanceof DOMException && e.name === 'AbortError')) throw e
|
||||
}
|
||||
}
|
||||
|
||||
const promise = download()
|
||||
|
||||
// let it download for 10 seconds
|
||||
await sleep(10000)
|
||||
abort.abort()
|
||||
// abort and snap the downloaded amount
|
||||
const downloadedBefore = downloaded
|
||||
|
||||
const avgSpeed = downloaded / 10
|
||||
// eslint-disable-next-line no-console
|
||||
console.log('Average speed: %d KiB/s', avgSpeed / 1024)
|
||||
|
||||
// wait a bit more to make sure it's aborted
|
||||
await sleep(2000)
|
||||
await promise
|
||||
|
||||
expect(downloaded).to.equal(downloadedBefore, 'nothing should be downloaded after abort')
|
||||
})
|
||||
})
|
||||
|
||||
describe('cross-dc', () => {
|
||||
const tg = new TelegramClient(getApiParams('dc1.session'))
|
||||
|
||||
this.beforeAll(() => tg.connect())
|
||||
this.afterAll(() => tg.close())
|
||||
|
||||
it('should download pfp thumbs', async () => {
|
||||
const chat = await tg.getChat(CINNAMOROLL_PFP_CHAT)
|
||||
if (!chat.photo) expect.fail('Chat has no photo')
|
||||
|
||||
expect(await downloadAsSha256(tg, chat.photo.big)).to.equal(CINNAMOROLL_PFP_THUMB_SHA256)
|
||||
})
|
||||
|
||||
it('should download animated pfps', async () => {
|
||||
const chat = await tg.getFullChat(CINNAMOROLL_PFP_CHAT)
|
||||
const thumb = chat.fullPhoto?.getThumbnail(Thumbnail.THUMB_VIDEO_PROFILE)
|
||||
if (!thumb) expect.fail('Chat has no animated pfp')
|
||||
|
||||
expect(await downloadAsSha256(tg, thumb)).to.equal(CINNAMOROLL_PFP_SHA256)
|
||||
})
|
||||
|
||||
it('should download photos', async () => {
|
||||
const msg = await tg.getMessageByLink(UWU_MSG)
|
||||
|
||||
if (msg?.media?.type !== 'photo') {
|
||||
expect.fail('Message not found or not a photo')
|
||||
}
|
||||
|
||||
expect(await downloadAsSha256(tg, msg.media)).to.equal(UWU_SHA256)
|
||||
})
|
||||
|
||||
it('should download documents', async () => {
|
||||
const msg = await tg.getMessageByLink(SHREK_MSG)
|
||||
|
||||
if (msg?.media?.type !== 'document') {
|
||||
expect.fail('Message not found or not a document')
|
||||
}
|
||||
|
||||
expect(await downloadAsSha256(tg, msg.media)).to.equal(SHREK_SHA256)
|
||||
})
|
||||
})
|
||||
})
|
49
e2e/ts/tests/04.updates.ts
Normal file
49
e2e/ts/tests/04.updates.ts
Normal file
|
@ -0,0 +1,49 @@
|
|||
import { expect } from 'chai'
|
||||
import { describe, it } from 'mocha'
|
||||
|
||||
import { Message, TelegramClient } from '@mtcute/client'
|
||||
|
||||
import { getApiParams, waitFor } from '../utils.js'
|
||||
|
||||
describe('4. handling updates', async function () {
|
||||
this.timeout(300_000)
|
||||
|
||||
const tg1 = new TelegramClient(getApiParams('dc1.session'))
|
||||
tg1.log.prefix = '[tg1] '
|
||||
const tg2 = new TelegramClient(getApiParams('dc2.session'))
|
||||
tg2.log.prefix = '[tg2] '
|
||||
|
||||
this.beforeAll(async () => {
|
||||
await tg1.connect()
|
||||
await tg1.startUpdatesLoop()
|
||||
await tg2.connect()
|
||||
})
|
||||
this.afterAll(async () => {
|
||||
await tg1.close()
|
||||
await tg2.close()
|
||||
})
|
||||
|
||||
it('should send and receive messages', async () => {
|
||||
const tg1Messages: Message[] = []
|
||||
|
||||
tg1.on('new_message', (msg) => tg1Messages.push(msg))
|
||||
|
||||
const [tg1User] = await tg1.getUsers('self')
|
||||
let username = tg1User!.username
|
||||
|
||||
if (!username) {
|
||||
username = `mtcute_e2e_${Math.random().toString(36).slice(2)}`
|
||||
await tg1.setMyUsername(username)
|
||||
}
|
||||
|
||||
const messageText = `mtcute test message ${Math.random().toString(36).slice(2)}`
|
||||
const sentMsg = await tg2.sendText(username, messageText)
|
||||
|
||||
expect(sentMsg.text).to.equal(messageText)
|
||||
expect(sentMsg.chat.id).to.equal(tg1User!.id)
|
||||
|
||||
await waitFor(() => {
|
||||
expect(tg1Messages.find((msg) => msg.text === messageText)).to.exist
|
||||
})
|
||||
})
|
||||
})
|
|
@ -4,7 +4,7 @@ import { describe, it } from 'mocha'
|
|||
import { BaseTelegramClient } from '@mtcute/core'
|
||||
|
||||
// @fix-import
|
||||
import { getApiParams } from '../utils'
|
||||
import { getApiParams } from '../../utils'
|
||||
|
||||
describe('@mtcute/core', function () {
|
||||
this.timeout(300_000)
|
|
@ -81,7 +81,7 @@ describe('TlBinaryWriter', () => {
|
|||
w.bytes(obj.pq)
|
||||
w.vector(w.long, obj.serverPublicKeyFingerprints)
|
||||
},
|
||||
_staticSize: {} as any
|
||||
_staticSize: {} as any,
|
||||
}
|
||||
|
||||
it('should work with Buffers', () => {
|
|
@ -1,8 +1,12 @@
|
|||
import { BaseTelegramClientOptions } from '@mtcute/core'
|
||||
import { MemoryStorage } from '@mtcute/core/storage/memory.js'
|
||||
import { LogManager } from '@mtcute/core/utils.js'
|
||||
// eslint-disable-next-line no-restricted-imports
|
||||
import { join } from 'path'
|
||||
|
||||
export const getApiParams = (): BaseTelegramClientOptions => {
|
||||
import { BaseTelegramClientOptions, MaybeAsync } from '@mtcute/core'
|
||||
import { MemoryStorage } from '@mtcute/core/storage/memory.js'
|
||||
import { LogManager, sleep } from '@mtcute/core/utils.js'
|
||||
import { SqliteStorage } from '@mtcute/sqlite'
|
||||
|
||||
export const getApiParams = (storage?: string): BaseTelegramClientOptions => {
|
||||
if (!process.env.API_ID || !process.env.API_HASH) {
|
||||
throw new Error('API_ID and API_HASH env variables must be set')
|
||||
}
|
||||
|
@ -11,7 +15,25 @@ export const getApiParams = (): BaseTelegramClientOptions => {
|
|||
apiId: parseInt(process.env.API_ID),
|
||||
apiHash: process.env.API_HASH,
|
||||
testMode: true,
|
||||
storage: new MemoryStorage(),
|
||||
logLevel: LogManager.DEBUG,
|
||||
storage: storage ? new SqliteStorage(join(__dirname, storage)) : new MemoryStorage(),
|
||||
logLevel: LogManager.VERBOSE,
|
||||
}
|
||||
}
|
||||
|
||||
export async function waitFor(condition: () => MaybeAsync<void>, timeout = 5000): Promise<void> {
|
||||
const start = Date.now()
|
||||
let lastError
|
||||
|
||||
while (Date.now() - start < timeout) {
|
||||
try {
|
||||
await condition()
|
||||
|
||||
return
|
||||
} catch (e) {
|
||||
lastError = e
|
||||
await sleep(100)
|
||||
}
|
||||
}
|
||||
|
||||
throw lastError
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ import { describe, expect, it, vi } from 'vitest'
|
|||
import { Long, toggleChannelIdMark } from '@mtcute/core'
|
||||
import { createStub, StubTelegramClient } from '@mtcute/test'
|
||||
|
||||
import { getAuthState } from '../auth/_state.js'
|
||||
import { getAuthState, setupAuthState } from '../auth/_state.js'
|
||||
import { sendText } from './send-text.js'
|
||||
|
||||
const stubUser = createStub('user', {
|
||||
|
@ -104,6 +104,7 @@ describe('sendText', () => {
|
|||
const client = new StubTelegramClient()
|
||||
|
||||
await client.registerPeers(stubUser)
|
||||
setupAuthState(client)
|
||||
getAuthState(client).userId = stubUser.id
|
||||
|
||||
client.respondWith('messages.sendMessage', () =>
|
||||
|
@ -127,6 +128,7 @@ describe('sendText', () => {
|
|||
it('should correctly handle updateShortSentMessage without cached peer', async () => {
|
||||
const client = new StubTelegramClient()
|
||||
|
||||
setupAuthState(client)
|
||||
getAuthState(client).userId = stubUser.id
|
||||
|
||||
const getUsersFn = client.respondWith(
|
||||
|
|
Loading…
Reference in a new issue