test(e2e-deno): ported tests from e2e-node
This commit is contained in:
parent
3553abc534
commit
ad9ad041ab
9 changed files with 451 additions and 6 deletions
3
e2e/deno/.gitignore
vendored
3
e2e/deno/.gitignore
vendored
|
@ -1,3 +1,4 @@
|
|||
/.jsr-data
|
||||
.env
|
||||
/deno.lock
|
||||
/deno.lock
|
||||
/.sessions
|
83
e2e/deno/tests/01.auth.ts
Normal file
83
e2e/deno/tests/01.auth.ts
Normal file
|
@ -0,0 +1,83 @@
|
|||
import { assertEquals } from 'https://deno.land/std@0.223.0/assert/mod.ts'
|
||||
|
||||
import { MtcuteError } from '@mtcute/core'
|
||||
import { BaseTelegramClient, TelegramClient } from '@mtcute/core/client.js'
|
||||
|
||||
import { getApiParams } from '../utils.ts'
|
||||
|
||||
const getAccountId = () =>
|
||||
Math.floor(Math.random() * 10000)
|
||||
.toString()
|
||||
.padStart(4, '0')
|
||||
|
||||
Deno.test('1. authorization', { sanitizeResources: false }, async (t) => {
|
||||
await t.step('should authorize in default dc', async () => {
|
||||
const base = new BaseTelegramClient(getApiParams('dc2.session'))
|
||||
const tg = new TelegramClient({ client: base })
|
||||
|
||||
// reset storage just in case
|
||||
await base.mt.storage.load()
|
||||
await base.storage.clear(true)
|
||||
|
||||
while (true) {
|
||||
const phone = `999662${getAccountId()}`
|
||||
let user
|
||||
|
||||
try {
|
||||
user = await tg.start({
|
||||
phone,
|
||||
code: () => '22222',
|
||||
})
|
||||
} catch (e) {
|
||||
if (e instanceof MtcuteError && e.message.match(/Signup is no longer supported|2FA is enabled/)) {
|
||||
// retry with another number
|
||||
continue
|
||||
} else {
|
||||
await tg.close()
|
||||
throw e
|
||||
}
|
||||
}
|
||||
|
||||
await tg.close()
|
||||
|
||||
assertEquals(user.isSelf, true)
|
||||
assertEquals(user.phoneNumber, phone)
|
||||
break
|
||||
}
|
||||
})
|
||||
|
||||
await t.step('should authorize in dc 1', async () => {
|
||||
const base = new BaseTelegramClient(getApiParams('dc1.session'))
|
||||
const tg = new TelegramClient({ client: base })
|
||||
|
||||
// reset storage just in case
|
||||
await base.mt.storage.load()
|
||||
await base.mt.storage.clear(true)
|
||||
|
||||
while (true) {
|
||||
const phone = `999661${getAccountId()}`
|
||||
let user
|
||||
|
||||
try {
|
||||
user = await tg.start({
|
||||
phone,
|
||||
code: () => '11111',
|
||||
})
|
||||
} catch (e) {
|
||||
if (e instanceof MtcuteError && e.message.match(/Signup is no longer supported|2FA is enabled/)) {
|
||||
// retry with another number
|
||||
continue
|
||||
} else {
|
||||
await tg.close()
|
||||
throw e
|
||||
}
|
||||
}
|
||||
|
||||
await tg.close()
|
||||
|
||||
assertEquals(user.isSelf, true)
|
||||
assertEquals(user.phoneNumber, phone)
|
||||
break
|
||||
}
|
||||
})
|
||||
})
|
47
e2e/deno/tests/02.methods.ts
Normal file
47
e2e/deno/tests/02.methods.ts
Normal file
|
@ -0,0 +1,47 @@
|
|||
import { assertEquals, assertNotEquals } from 'https://deno.land/std@0.223.0/assert/mod.ts'
|
||||
|
||||
import { TelegramClient } from '@mtcute/core/client.js'
|
||||
|
||||
import { getApiParams } from '../utils.ts'
|
||||
|
||||
Deno.test('2. calling methods', { sanitizeResources: false }, async (t) => {
|
||||
const tg = new TelegramClient(getApiParams('dc2.session'))
|
||||
|
||||
await tg.connect()
|
||||
|
||||
await t.step('getUsers(@BotFather)', async () => {
|
||||
const [user] = await tg.getUsers('botfather')
|
||||
|
||||
assertEquals(user?.isBot, true)
|
||||
assertEquals(user?.displayName, 'BotFather')
|
||||
})
|
||||
|
||||
await t.step('getUsers(@BotFather) - cached', async () => {
|
||||
const [user] = await tg.getUsers('botfather')
|
||||
|
||||
assertEquals(user?.isBot, true)
|
||||
assertEquals(user?.displayName, 'BotFather')
|
||||
})
|
||||
|
||||
await t.step('getHistory(777000)', async () => {
|
||||
const history = await tg.getHistory(777000, { limit: 5 })
|
||||
|
||||
assertEquals(history[0].chat.chatType, 'private')
|
||||
assertEquals(history[0].chat.id, 777000)
|
||||
assertEquals(history[0].chat.firstName, 'Telegram')
|
||||
})
|
||||
|
||||
await t.step('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')
|
||||
|
||||
assertEquals(res.isSelf, true)
|
||||
assertNotEquals(oldSelf.bio, newSelf.bio)
|
||||
assertEquals(newSelf.bio, bio)
|
||||
})
|
||||
|
||||
await tg.close()
|
||||
})
|
170
e2e/deno/tests/03.files.ts
Normal file
170
e2e/deno/tests/03.files.ts
Normal file
|
@ -0,0 +1,170 @@
|
|||
import { assertEquals } from 'https://deno.land/std@0.223.0/assert/mod.ts'
|
||||
import { createHash } from 'node:crypto'
|
||||
|
||||
import { FileDownloadLocation, Thumbnail } from '@mtcute/core'
|
||||
import { TelegramClient } from '@mtcute/core/client.js'
|
||||
import { sleep } from '@mtcute/core/utils.js'
|
||||
|
||||
import { getApiParams } from '../utils.ts'
|
||||
|
||||
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')
|
||||
}
|
||||
|
||||
Deno.test('3. working with files', { sanitizeResources: false }, async (t) => {
|
||||
// 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
|
||||
//
|
||||
// ...except we can't under deno because it's not implemented
|
||||
// https://github.com/denoland/deno/issues/19882
|
||||
// this.retries(2)
|
||||
|
||||
await t.step('same-dc', async (t) => {
|
||||
const tg = new TelegramClient(getApiParams('dc2.session'))
|
||||
|
||||
await tg.connect()
|
||||
|
||||
await t.step('should download pfp thumbs', async () => {
|
||||
const chat = await tg.getChat(CINNAMOROLL_PFP_CHAT)
|
||||
if (!chat.photo) throw new Error('Chat has no photo')
|
||||
|
||||
assertEquals(await downloadAsSha256(tg, chat.photo.big), CINNAMOROLL_PFP_THUMB_SHA256)
|
||||
})
|
||||
|
||||
await t.step('should download animated pfps', async () => {
|
||||
const chat = await tg.getFullChat(CINNAMOROLL_PFP_CHAT)
|
||||
const thumb = chat.fullPhoto?.getThumbnail(Thumbnail.THUMB_VIDEO_PROFILE)
|
||||
if (!thumb) throw new Error('Chat has no animated pfp')
|
||||
|
||||
assertEquals(await downloadAsSha256(tg, thumb), CINNAMOROLL_PFP_SHA256)
|
||||
})
|
||||
|
||||
await t.step('should download photos', async () => {
|
||||
const msg = await tg.getMessageByLink(UWU_MSG)
|
||||
|
||||
if (msg?.media?.type !== 'photo') {
|
||||
throw new Error('Message not found or not a photo')
|
||||
}
|
||||
|
||||
assertEquals(await downloadAsSha256(tg, msg.media), UWU_SHA256)
|
||||
})
|
||||
|
||||
await t.step('should download documents', async () => {
|
||||
const msg = await tg.getMessageByLink(SHREK_MSG)
|
||||
|
||||
if (msg?.media?.type !== 'document') {
|
||||
throw new Error('Message not found or not a document')
|
||||
}
|
||||
|
||||
assertEquals(await downloadAsSha256(tg, msg.media), SHREK_SHA256)
|
||||
})
|
||||
|
||||
await t.step('should cancel downloads', async () => {
|
||||
const msg = await tg.getMessageByLink(LARGE_MSG)
|
||||
|
||||
if (msg?.media?.type !== 'document') {
|
||||
throw new Error('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
|
||||
|
||||
assertEquals(downloaded, downloadedBefore, 'nothing should be downloaded after abort')
|
||||
})
|
||||
|
||||
await tg.close()
|
||||
})
|
||||
|
||||
await t.step('cross-dc', async (t) => {
|
||||
const tg = new TelegramClient(getApiParams('dc1.session'))
|
||||
|
||||
await tg.connect()
|
||||
|
||||
await t.step('should download pfp thumbs', async () => {
|
||||
const chat = await tg.getChat(CINNAMOROLL_PFP_CHAT)
|
||||
if (!chat.photo) throw new Error('Chat has no photo')
|
||||
|
||||
assertEquals(await downloadAsSha256(tg, chat.photo.big), CINNAMOROLL_PFP_THUMB_SHA256)
|
||||
})
|
||||
|
||||
await t.step('should download animated pfps', async () => {
|
||||
const chat = await tg.getFullChat(CINNAMOROLL_PFP_CHAT)
|
||||
const thumb = chat.fullPhoto?.getThumbnail(Thumbnail.THUMB_VIDEO_PROFILE)
|
||||
if (!thumb) throw new Error('Chat has no animated pfp')
|
||||
|
||||
assertEquals(await downloadAsSha256(tg, thumb), CINNAMOROLL_PFP_SHA256)
|
||||
})
|
||||
|
||||
await t.step('should download photos', async () => {
|
||||
const msg = await tg.getMessageByLink(UWU_MSG)
|
||||
|
||||
if (msg?.media?.type !== 'photo') {
|
||||
throw new Error('Message not found or not a photo')
|
||||
}
|
||||
|
||||
assertEquals(await downloadAsSha256(tg, msg.media), UWU_SHA256)
|
||||
})
|
||||
|
||||
await t.step('should download documents', async () => {
|
||||
const msg = await tg.getMessageByLink(SHREK_MSG)
|
||||
|
||||
if (msg?.media?.type !== 'document') {
|
||||
throw new Error('Message not found or not a document')
|
||||
}
|
||||
|
||||
assertEquals(await downloadAsSha256(tg, msg.media), SHREK_SHA256)
|
||||
})
|
||||
|
||||
await tg.close()
|
||||
})
|
||||
})
|
47
e2e/deno/tests/04.updates.ts
Normal file
47
e2e/deno/tests/04.updates.ts
Normal file
|
@ -0,0 +1,47 @@
|
|||
import { assertEquals, assertNotEquals } from 'https://deno.land/std@0.223.0/assert/mod.ts'
|
||||
|
||||
import { Message } from '@mtcute/core'
|
||||
import { TelegramClient } from '@mtcute/core/client.js'
|
||||
|
||||
import { getApiParams, waitFor } from '../utils.ts'
|
||||
|
||||
Deno.test('4. handling updates', { sanitizeResources: false }, async (t) => {
|
||||
const tg1 = new TelegramClient(getApiParams('dc1.session'))
|
||||
tg1.log.prefix = '[tg1] '
|
||||
const tg2 = new TelegramClient(getApiParams('dc2.session'))
|
||||
tg2.log.prefix = '[tg2] '
|
||||
|
||||
await tg1.connect()
|
||||
await tg1.startUpdatesLoop()
|
||||
await tg2.connect()
|
||||
|
||||
await t.step('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)
|
||||
|
||||
assertEquals(sentMsg.text, messageText)
|
||||
assertEquals(sentMsg.chat.id, tg1User!.id)
|
||||
|
||||
await waitFor(() => {
|
||||
assertNotEquals(
|
||||
tg1Messages.find((msg) => msg.text === messageText),
|
||||
undefined,
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
await tg1.close()
|
||||
await tg2.close()
|
||||
})
|
79
e2e/deno/tests/05.worker.ts
Normal file
79
e2e/deno/tests/05.worker.ts
Normal file
|
@ -0,0 +1,79 @@
|
|||
import { assertEquals, assertGreater, assertInstanceOf } from 'https://deno.land/std@0.223.0/assert/mod.ts'
|
||||
|
||||
import { TelegramClient } from '@mtcute/core/client.js'
|
||||
import { Message, TelegramWorkerPort, tl } from '@mtcute/deno'
|
||||
|
||||
import { getApiParams, waitFor } from '../utils.ts'
|
||||
import type { CustomMethods } from './_worker.ts'
|
||||
|
||||
Deno.test('5. worker', { sanitizeResources: false }, async (t) => {
|
||||
const worker = new Worker(new URL('_worker.ts', import.meta.url), {
|
||||
type: 'module',
|
||||
})
|
||||
|
||||
const port = new TelegramWorkerPort<CustomMethods>({
|
||||
worker,
|
||||
})
|
||||
const portClient = new TelegramClient({ client: port })
|
||||
|
||||
await t.step('should make api calls', async function () {
|
||||
const res = await port.call({ _: 'help.getConfig' })
|
||||
|
||||
assertEquals(res._, 'config')
|
||||
})
|
||||
|
||||
await t.step('should call custom methods', async function () {
|
||||
const hello = await port.invokeCustom('hello')
|
||||
assertEquals(hello, 'world')
|
||||
|
||||
const sum = await port.invokeCustom('sum', 2, 3)
|
||||
assertEquals(sum, 5)
|
||||
})
|
||||
|
||||
await t.step('should throw errors', async function () {
|
||||
try {
|
||||
await port.call({ _: 'test.useConfigSimple' })
|
||||
throw new Error('should have thrown')
|
||||
} catch (e) {
|
||||
assertInstanceOf(e, tl.RpcError)
|
||||
}
|
||||
})
|
||||
|
||||
await t.step('should receive updates', async function () {
|
||||
const client2 = new TelegramClient(getApiParams('dc2.session'))
|
||||
|
||||
try {
|
||||
await client2.connect()
|
||||
await port.startUpdatesLoop()
|
||||
|
||||
const me = await portClient.getMe()
|
||||
let username = me.username
|
||||
|
||||
if (!username) {
|
||||
username = `mtcute_e2e_${Math.random().toString(36).slice(2, 8)}`
|
||||
await portClient.setMyUsername(username)
|
||||
}
|
||||
|
||||
const msgs: Message[] = []
|
||||
portClient.on('new_message', (msg) => {
|
||||
msgs.push(msg)
|
||||
})
|
||||
|
||||
const testText = `test ${Math.random()}`
|
||||
await client2.sendText(username, testText)
|
||||
|
||||
await waitFor(() => {
|
||||
assertGreater(msgs.length, 0)
|
||||
assertEquals(msgs[0].text, testText)
|
||||
})
|
||||
} catch (e) {
|
||||
await client2.close()
|
||||
throw e
|
||||
}
|
||||
|
||||
await client2.close()
|
||||
})
|
||||
|
||||
await port.close()
|
||||
worker.terminate()
|
||||
})
|
18
e2e/deno/tests/_worker.ts
Normal file
18
e2e/deno/tests/_worker.ts
Normal file
|
@ -0,0 +1,18 @@
|
|||
import { WorkerCustomMethods } from '@mtcute/core/worker.js'
|
||||
import { BaseTelegramClient, TelegramWorker } from '@mtcute/deno'
|
||||
|
||||
import { getApiParams } from '../utils.ts'
|
||||
|
||||
const customMethods = {
|
||||
hello: async () => 'world',
|
||||
sum: async (a: number, b: number) => a + b,
|
||||
} as const satisfies WorkerCustomMethods
|
||||
export type CustomMethods = typeof customMethods
|
||||
|
||||
const client = new BaseTelegramClient(getApiParams('dc1.session'))
|
||||
|
||||
// eslint-disable-next-line no-new
|
||||
new TelegramWorker({
|
||||
client,
|
||||
customMethods,
|
||||
})
|
|
@ -1,22 +1,22 @@
|
|||
import { MaybePromise, MemoryStorage } from '@mtcute/core'
|
||||
import { setPlatform } from '@mtcute/core/platform.js'
|
||||
import { LogManager, sleep } from '@mtcute/core/utils.js'
|
||||
import { DenoCryptoProvider, DenoPlatform, TcpTransport } from '@mtcute/deno'
|
||||
import { DenoCryptoProvider, DenoPlatform, SqliteStorage, TcpTransport } from '@mtcute/deno'
|
||||
|
||||
export const getApiParams = (storage?: string) => {
|
||||
if (storage) throw new Error('unsupported yet')
|
||||
|
||||
if (!Deno.env.has('API_ID') || !Deno.env.has('API_HASH')) {
|
||||
throw new Error('API_ID and API_HASH env variables must be set')
|
||||
}
|
||||
|
||||
Deno.mkdirSync('.sessions', { recursive: true })
|
||||
|
||||
setPlatform(new DenoPlatform())
|
||||
|
||||
return {
|
||||
apiId: parseInt(Deno.env.get('API_ID')!),
|
||||
apiHash: Deno.env.get('API_HASH')!,
|
||||
testMode: true,
|
||||
storage: new MemoryStorage(),
|
||||
storage: storage ? new SqliteStorage(`.sessions/${storage}`) : new MemoryStorage(),
|
||||
logLevel: LogManager.VERBOSE,
|
||||
transport: () => new TcpTransport(),
|
||||
crypto: new DenoCryptoProvider(),
|
||||
|
|
|
@ -6,7 +6,7 @@ export function beforeExit(fn: () => void): () => void {
|
|||
if (!registered) {
|
||||
registered = true
|
||||
|
||||
window.addEventListener('unload', () => {
|
||||
globalThis.addEventListener('unload', () => {
|
||||
for (const callback of callbacks) {
|
||||
callback()
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue