test: fixed tests

This commit is contained in:
alina 🌸 2024-09-19 01:31:42 +03:00
parent a7ae3735c6
commit 8b4f4984a1
Signed by: teidesu
SSH key fingerprint: SHA256:uNeCpw6aTSU4aIObXLvHfLkDa82HWH9EiOj9AXOIRpI
14 changed files with 75 additions and 551 deletions

View file

@ -1,15 +1,6 @@
import { expect } from 'vitest'
import { typed } from '@fuman/utils'
import { setPlatform } from '../../packages/core/src/platform.js'
// @ts-expect-error no .env here
if (import.meta.env.TEST_ENV === 'browser' || import.meta.env.TEST_ENV === 'deno') {
setPlatform(new (await import('../../packages/web/src/platform.js')).WebPlatform())
} else {
setPlatform(new (await import('../../packages/node/src/common-internals-node/platform.js')).NodePlatform())
}
// consider Buffers equal to Uint8Arrays
expect.addEqualityTesters([
function (a, b) {

View file

@ -8,7 +8,7 @@ describe('BaseTelegramClient', () => {
const session = await client.exportSession()
expect(session).toMatchInlineSnapshot(
'"AwQAAAAXAQIADjE0OS4xNTQuMTY3LjUwALsBAAAXAQICDzE0OS4xNTQuMTY3LjIyMrsBAAD-AAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"',
`"AwQAAAAXAgIADjE0OS4xNTQuMTY3LjUwALsBAAAXAgICDzE0OS4xNTQuMTY3LjIyMrsBAAD-AAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"`,
)
})
})

View file

@ -1,224 +0,0 @@
// todo: move to fuman
// import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
// import { StubTelegramTransport, createStub, defaultTestCryptoProvider } from '@mtcute/test'
// import { LogManager, timers } from '../utils/index.js'
// import type { PersistentConnectionParams } from './persistent-connection.js'
// import { PersistentConnection } from './persistent-connection.js'
// import { defaultReconnectionStrategy } from './reconnection.js'
// class FakePersistentConnection extends PersistentConnection {
// constructor(params: PersistentConnectionParams) {
// const log = new LogManager()
// log.level = 0
// super(params, log)
// }
// onConnected() {
// this.onConnectionUsable()
// }
// onError() {}
// onMessage() {}
// }
// describe('PersistentConnection', () => {
// beforeEach(() => void vi.useFakeTimers())
// afterEach(() => void vi.useRealTimers())
// const create = async (params?: Partial<PersistentConnectionParams>) => {
// return new FakePersistentConnection({
// crypto: await defaultTestCryptoProvider(),
// transportFactory: () => new StubTelegramTransport({}),
// dc: createStub('dcOption'),
// testMode: false,
// reconnectionStrategy: defaultReconnectionStrategy,
// ...params,
// })
// }
// it('should set up listeners on transport', async () => {
// const transportFactory = vi.fn().mockImplementation(() => {
// const transport = new StubTelegramTransport({})
// vi.spyOn(transport, 'on')
// return transport
// })
// await create({ transportFactory })
// const transport = transportFactory.mock.results[0].value as StubTelegramTransport
// expect(transport.on).toHaveBeenCalledWith('ready', expect.any(Function))
// expect(transport.on).toHaveBeenCalledWith('message', expect.any(Function))
// expect(transport.on).toHaveBeenCalledWith('error', expect.any(Function))
// expect(transport.on).toHaveBeenCalledWith('close', expect.any(Function))
// })
// it('should properly reset old transport', async () => {
// const transportFactory = vi.fn().mockImplementation(() => {
// const transport = new StubTelegramTransport({})
// vi.spyOn(transport, 'close')
// return transport
// })
// const pc = await create({ transportFactory })
// const transport = transportFactory.mock.results[0].value as StubTelegramTransport
// pc.changeTransport(transportFactory)
// expect(transport.close).toHaveBeenCalledOnce()
// })
// it('should buffer unsent packages', async () => {
// const transportFactory = vi.fn().mockImplementation(() => {
// const transport = new StubTelegramTransport({})
// const transportConnect = transport.connect
// vi.spyOn(transport, 'connect').mockImplementation((dc, test) => {
// timers.setTimeout(() => {
// transportConnect.call(transport, dc, test)
// }, 100)
// })
// vi.spyOn(transport, 'send')
// return transport
// })
// const pc = await create({ transportFactory })
// const transport = transportFactory.mock.results[0].value as StubTelegramTransport
// const data1 = new Uint8Array([1, 2, 3])
// const data2 = new Uint8Array([4, 5, 6])
// await pc.send(data1)
// await pc.send(data2)
// expect(transport.send).toHaveBeenCalledTimes(0)
// await vi.advanceTimersByTimeAsync(150)
// expect(transport.send).toHaveBeenCalledTimes(2)
// expect(transport.send).toHaveBeenCalledWith(data1)
// expect(transport.send).toHaveBeenCalledWith(data2)
// })
// it('should reconnect on close', async () => {
// const reconnectionStrategy = vi.fn().mockImplementation(() => 1000)
// const transportFactory = vi.fn().mockImplementation(() => new StubTelegramTransport({}))
// const pc = await create({
// reconnectionStrategy,
// transportFactory,
// })
// const transport = transportFactory.mock.results[0].value as StubTelegramTransport
// pc.connect()
// await vi.waitFor(() => expect(pc.isConnected).toBe(true))
// transport.close()
// expect(reconnectionStrategy).toHaveBeenCalledOnce()
// expect(pc.isConnected).toBe(false)
// await vi.advanceTimersByTimeAsync(1000)
// expect(pc.isConnected).toBe(true)
// })
// describe('inactivity timeout', () => {
// it('should disconnect on inactivity (passed in constructor)', async () => {
// const pc = await create({
// inactivityTimeout: 1000,
// })
// pc.connect()
// await vi.waitFor(() => expect(pc.isConnected).toBe(true))
// vi.advanceTimersByTime(1000)
// await vi.waitFor(() => expect(pc.isConnected).toBe(false))
// })
// it('should disconnect on inactivity (set up with setInactivityTimeout)', async () => {
// const pc = await create()
// pc.connect()
// pc.setInactivityTimeout(1000)
// await vi.waitFor(() => expect(pc.isConnected).toBe(true))
// vi.advanceTimersByTime(1000)
// await vi.waitFor(() => expect(pc.isConnected).toBe(false))
// })
// it('should not disconnect on inactivity if disabled', async () => {
// const pc = await create({
// inactivityTimeout: 1000,
// })
// pc.connect()
// pc.setInactivityTimeout(undefined)
// await vi.waitFor(() => expect(pc.isConnected).toBe(true))
// vi.advanceTimersByTime(1000)
// await vi.waitFor(() => expect(pc.isConnected).toBe(true))
// })
// it('should reconnect after inactivity before sending', async () => {
// const transportFactory = vi.fn().mockImplementation(() => {
// const transport = new StubTelegramTransport({})
// vi.spyOn(transport, 'connect')
// vi.spyOn(transport, 'send')
// return transport
// })
// const pc = await create({
// inactivityTimeout: 1000,
// transportFactory,
// })
// const transport = transportFactory.mock.results[0].value as StubTelegramTransport
// pc.connect()
// vi.advanceTimersByTime(1000)
// await vi.waitFor(() => expect(pc.isConnected).toBe(false))
// vi.mocked(transport.connect).mockClear()
// await pc.send(new Uint8Array([1, 2, 3]))
// expect(transport.connect).toHaveBeenCalledOnce()
// expect(transport.send).toHaveBeenCalledOnce()
// })
// it('should propagate errors', async () => {
// const transportFactory = vi.fn().mockImplementation(() => new StubTelegramTransport({}))
// const pc = await create({ transportFactory })
// const transport = transportFactory.mock.results[0].value as StubTelegramTransport
// pc.connect()
// await vi.waitFor(() => expect(pc.isConnected).toBe(true))
// const onErrorSpy = vi.spyOn(pc, 'onError')
// transport.emit('error', new Error('test error'))
// expect(onErrorSpy).toHaveBeenCalledOnce()
// })
// })
// })

View file

@ -68,7 +68,7 @@ export abstract class PersistentConnection extends EventEmitter {
connect: (dc) => {
this._updateLogPrefix()
this.log.debug('connecting to %j', dc)
return params.transport.connect(dc, params.testMode)
return params.transport.connect(dc)
},
onOpen: this._onOpen.bind(this),
onClose: this._onClose.bind(this),
@ -159,7 +159,7 @@ export abstract class PersistentConnection extends EventEmitter {
this._codec = transport.packetCodec(this.params.dc)
this._codec.setup?.(this.params.crypto, this.log)
await this._fuman.changeTransport(dc => transport.connect(dc, this.params.testMode))
await this._fuman.changeTransport(transport.connect.bind(transport))
this._fuman.connect(this.params.dc)
}

View file

@ -22,7 +22,7 @@ export interface ITelegramConnection extends IConnection<any, any> {
export interface TelegramTransport {
setup?(crypto: ICryptoProvider, log: Logger): void
connect: (dc: BasicDcOption, testMode: boolean) => Promise<ITelegramConnection>
connect: (dc: BasicDcOption) => Promise<ITelegramConnection>
packetCodec: (dc: BasicDcOption) => IPacketCodec
}

View file

@ -50,7 +50,7 @@ export class BaseTelegramClient extends BaseTelegramClientBase {
super({
// eslint-disable-next-line
crypto: nativeCrypto ? new nativeCrypto() : new NodeCryptoProvider(),
transport: TcpTransport,
transport: new TcpTransport(),
platform: new NodePlatform(),
...opts,
storage:

View file

@ -1,158 +0,0 @@
// import type { Socket } from 'node:net'
// import type { MockedObject } from 'vitest'
// import { describe, expect, it, vi } from 'vitest'
// import { TransportState } from '@mtcute/core'
// import { getPlatform } from '@mtcute/core/platform.js'
// import { LogManager, defaultProductionDc } from '@mtcute/core/utils.js'
// todo: move to fuman
// if (import.meta.env.TEST_ENV === 'node') {
// vi.doMock('net', () => ({
// connect: vi.fn().mockImplementation((port: number, ip: string, cb: () => void) => {
// cb()
// return {
// on: vi.fn(),
// write: vi.fn().mockImplementation((data: Uint8Array, cb: () => void) => {
// cb()
// }),
// end: vi.fn(),
// removeAllListeners: vi.fn(),
// destroy: vi.fn(),
// }
// }),
// }))
// const net = await import('node:net')
// const connect = vi.mocked(net.connect)
// const { TcpTransport } = await import('./tcp.js')
// const { defaultTestCryptoProvider, u8HexDecode } = await import('@mtcute/test')
// describe('TcpTransport', () => {
// const getLastSocket = () => {
// return connect.mock.results[connect.mock.results.length - 1].value as MockedObject<Socket>
// }
// const create = async () => {
// const transport = new TcpTransport()
// const logger = new LogManager()
// logger.level = 0
// transport.setup(await defaultTestCryptoProvider(), logger)
// return transport
// }
// it('should initiate a tcp connection to the given dc', async () => {
// const t = await create()
// t.connect(defaultProductionDc.main, false)
// expect(connect).toHaveBeenCalledOnce()
// expect(connect).toHaveBeenCalledWith(
// defaultProductionDc.main.port,
// defaultProductionDc.main.ipAddress,
// expect.any(Function),
// )
// await vi.waitFor(() => expect(t.state()).toEqual(TransportState.Ready))
// })
// it('should set up event handlers', async () => {
// const t = await create()
// t.connect(defaultProductionDc.main, false)
// const socket = getLastSocket()
// expect(socket.on).toHaveBeenCalledTimes(3)
// expect(socket.on).toHaveBeenCalledWith('data', expect.any(Function))
// expect(socket.on).toHaveBeenCalledWith('error', expect.any(Function))
// expect(socket.on).toHaveBeenCalledWith('close', expect.any(Function))
// })
// it('should write packet codec tag once connected', async () => {
// const t = await create()
// t.connect(defaultProductionDc.main, false)
// const socket = getLastSocket()
// await vi.waitFor(() =>
// expect(socket.write).toHaveBeenCalledWith(
// u8HexDecode('eeeeeeee'), // intermediate
// expect.any(Function),
// ),
// )
// })
// it('should write to the underlying socket', async () => {
// const t = await create()
// t.connect(defaultProductionDc.main, false)
// await vi.waitFor(() => expect(t.state()).toEqual(TransportState.Ready))
// await t.send(getPlatform().hexDecode('00010203040506070809'))
// const socket = getLastSocket()
// expect(socket.write).toHaveBeenCalledWith(u8HexDecode('0a00000000010203040506070809'), expect.any(Function))
// })
// it('should correctly close', async () => {
// const t = await create()
// t.connect(defaultProductionDc.main, false)
// await vi.waitFor(() => expect(t.state()).toEqual(TransportState.Ready))
// t.close()
// const socket = getLastSocket()
// expect(socket.removeAllListeners).toHaveBeenCalledOnce()
// expect(socket.destroy).toHaveBeenCalledOnce()
// })
// it('should feed data to the packet codec', async () => {
// const t = await create()
// const codec = t._packetCodec
// const spyFeed = vi.spyOn(codec, 'feed')
// t.connect(defaultProductionDc.main, false)
// await vi.waitFor(() => expect(t.state()).toEqual(TransportState.Ready))
// const socket = getLastSocket()
// const onDataCall = socket.on.mock.calls.find(c => (c as string[])[0] === 'data') as unknown as [
// string,
// (data: Uint8Array) => void,
// ]
// onDataCall[1](u8HexDecode('00010203040506070809'))
// expect(spyFeed).toHaveBeenCalledWith(u8HexDecode('00010203040506070809'))
// })
// it('should propagate errors', async () => {
// const t = await create()
// const spyEmit = vi.fn()
// t.on('error', spyEmit)
// t.connect(defaultProductionDc.main, false)
// await vi.waitFor(() => expect(t.state()).toEqual(TransportState.Ready))
// const socket = getLastSocket()
// const onErrorCall = socket.on.mock.calls.find(c => (c as string[])[0] === 'error') as unknown as [
// string,
// (error: Error) => void,
// ]
// onErrorCall[1](new Error('test error'))
// expect(spyEmit).toHaveBeenCalledWith(new Error('test error'))
// })
// })
// } else {
// describe.skip('TcpTransport', () => {})
// }

View file

@ -28,7 +28,8 @@
},
"dependencies": {
"long": "5.2.3",
"@fuman/utils": "workspace:^"
"@fuman/utils": "workspace:^",
"@fuman/net": "workspace:^"
},
"devDependencies": {
"@mtcute/tl-utils": "workspace:^"

View file

@ -17,25 +17,22 @@ describe('client stub', () => {
})
})
// for some reason, this test fails in browser. todo: investigate
if (import.meta.env.TEST_ENV !== 'browser') {
it('should correctly decrypt intercepted raw messages', async () => {
const log: string[] = []
it('should correctly decrypt intercepted raw messages', async () => {
const log: string[] = []
const client = new StubTelegramClient()
const client = new StubTelegramClient()
client.onRawMessage((msg) => {
log.push(`message ctor=${hex.encode(msg.subarray(0, 4))}`)
client.close().catch(() => {})
})
await client.with(async () => {
await client.call({ _: 'help.getConfig' }).catch(() => {}) // ignore "client closed" error
expect(log).toEqual([
'message ctor=dcf8f173', // msg_container
])
})
client.onRawMessage((msg) => {
log.push(`message ctor=${hex.encode(msg.subarray(0, 4))}`)
client.close().catch(() => {})
})
}
await client.with(async () => {
await client.call({ _: 'help.getConfig' }).catch(() => {}) // ignore "client closed" error
expect(log).toEqual([
'message ctor=dcf8f173', // msg_container
])
})
})
})

View file

@ -1,13 +1,13 @@
import type { MaybePromise, MustEqual, RpcCallOptions } from '@mtcute/core'
import { IntermediatePacketCodec, tl } from '@mtcute/core'
import { tl } from '@mtcute/core'
import type { BaseTelegramClientOptions } from '@mtcute/core/client.js'
import { BaseTelegramClient } from '@mtcute/core/client.js'
import { defaultCryptoProvider, defaultPlatform } from './platform.js'
import { StubMemoryTelegramStorage } from './storage.js'
// import { StubTelegramTransport } from './transport.js'
import type { InputResponder } from './types.js'
import { markedIdToPeer } from './utils.js'
import { StubTelegramTransport } from './transport.js'
interface MessageBox {
pts: number
@ -26,36 +26,28 @@ export class StubTelegramClient extends BaseTelegramClient {
super({
apiId: 0,
apiHash: '',
logLevel: 5,
logLevel: 9,
storage,
disableUpdates: true,
transport: {
connect: () => {
// const transport = new StubTelegramTransport({
// onMessage: (data) => {
// if (!this._onRawMessage) {
// if (this._responders.size) {
// this.emitError(new Error('Unexpected outgoing message'))
// }
transport: new StubTelegramTransport({
onMessage: (data, dcId) => {
if (!this._onRawMessage) {
if (this._responders.size) {
this.emitError(new Error('Unexpected outgoing message'))
}
// return
// }
return
}
// const dcId = transport._currentDc!.id
// const key = storage.authKeys.get(dcId)
const key = storage.authKeys.get(dcId)
// if (key) {
// this._onRawMessage(storage.decryptOutgoingMessage(transport._crypto, data, dcId))
// }
// },
// })
if (key) {
return this._onRawMessage(storage.decryptOutgoingMessage(this.crypto, data, dcId))
}
// return transport
// todo: fuman
throw new Error('not implemented')
this._onRawMessage?.(data)
},
packetCodec: () => new IntermediatePacketCodec(),
},
}),
crypto: defaultCryptoProvider,
platform: defaultPlatform,
...params,

View file

@ -1,45 +0,0 @@
// todo: fuman
// import { describe, expect, it, vi } from 'vitest'
// import { MemoryStorage } from '@mtcute/core'
// import { BaseTelegramClient } from '@mtcute/core/client.js'
// import { defaultCryptoProvider } from './platform.js'
// import { createStub } from './stub.js'
// import { StubTelegramTransport } from './transport.js'
// describe('transport stub', () => {
// it('should correctly intercept calls', async () => {
// const log: string[] = []
// const client = new BaseTelegramClient({
// apiId: 0,
// apiHash: '',
// logLevel: 0,
// defaultDcs: {
// main: createStub('dcOption', { ipAddress: '1.2.3.4', port: 1234 }),
// media: createStub('dcOption', { ipAddress: '1.2.3.4', port: 5678 }),
// },
// storage: new MemoryStorage(),
// crypto: defaultCryptoProvider,
// transport: () =>
// new StubTelegramTransport({
// onConnect: (dc, testMode) => {
// log.push(`connect ${dc.ipAddress}:${dc.port} test=${testMode}`)
// client.close().catch(() => {})
// },
// onMessage(msg) {
// log.push(`message size=${msg.length}`)
// },
// }),
// })
// client.connect().catch(() => {}) // ignore "client closed" error
// await vi.waitFor(() =>
// expect(log).toEqual([
// 'message size=40', // req_pq_multi
// 'connect 1.2.3.4:1234 test=false',
// ]),
// )
// })
// })

View file

@ -1,68 +1,35 @@
// todo: implement in fuman
// import EventEmitter from 'node:events'
import { type IPacketCodec, type ITelegramConnection, IntermediatePacketCodec, type TelegramTransport } from '@mtcute/core'
import type { BasicDcOption } from '@mtcute/core/utils.js'
import { FakeConnection } from '@fuman/net'
// import type { ITelegramTransport } from '@mtcute/core'
// import { TransportState } from '@mtcute/core'
// import type { ICryptoProvider, Logger } from '@mtcute/core/utils.js'
// import type { tl } from '@mtcute/tl'
export class StubTelegramTransport implements TelegramTransport {
constructor(
readonly params: {
packetCodec?: () => IPacketCodec
onConnect?: (dc: BasicDcOption) => void
onClose?: () => void
onMessage?: (msg: Uint8Array, dcId: number) => void
},
) {}
// export class StubTelegramTransport extends EventEmitter implements ITelegramConnection {
// constructor(
// readonly params: {
// getMtproxyInfo?: () => tl.RawInputClientProxy
// onConnect?: (dc: tl.RawDcOption, testMode: boolean) => void
// onClose?: () => void
// onMessage?: (msg: Uint8Array) => void
// },
// ) {
// super()
private _dcId = 0
async connect(dc: BasicDcOption): Promise<ITelegramConnection> {
this.params.onConnect?.(dc)
this._dcId = dc.id
return new FakeConnection<BasicDcOption>(dc)
}
// if (params.getMtproxyInfo) {
// (this as unknown as ITelegramTransport).getMtproxyInfo = params.getMtproxyInfo
// }
// }
packetCodec(): IPacketCodec {
const inner = this.params.packetCodec?.() ?? new IntermediatePacketCodec()
// _state: TransportState = TransportState.Idle
// _currentDc: tl.RawDcOption | null = null
// _crypto!: ICryptoProvider
// _log!: Logger
// write(data: Uint8Array): void {
// this.emit('message', data)
// }
// setup(crypto: ICryptoProvider, log: Logger): void {
// this._crypto = crypto
// this._log = log
// }
// state(): TransportState {
// return this._state
// }
// currentDc(): tl.RawDcOption | null {
// return this._currentDc
// }
// connect(dc: tl.RawDcOption, testMode: boolean): void {
// this._currentDc = dc
// this._state = TransportState.Ready
// this.emit('ready')
// this._log.debug('stubbing connection to %s:%d', dc.ipAddress, dc.port)
// this.params.onConnect?.(dc, testMode)
// }
// close(): void {
// this._currentDc = null
// this._state = TransportState.Idle
// this.emit('close')
// this._log.debug('stub connection closed')
// this.params.onClose?.()
// }
// async send(data: Uint8Array): Promise<void> {
// this.params.onMessage?.(data)
// }
// }
return {
decode: (reader, eof) => inner.decode(reader, eof),
reset: () => inner.reset(),
tag: () => inner.tag(),
encode: (message, into) => {
this.params.onMessage?.(message, this._dcId)
return inner.encode(message, into)
},
}
}
}

View file

@ -54,8 +54,8 @@ export class WebSocketTransport implements TelegramTransport {
this._WebSocket = ws
}
async connect(dc: BasicDcOption, testMode: boolean): Promise<ITelegramConnection> {
const url = `wss://${this._subdomains[dc.id]}.${this._baseDomain}/apiws${testMode ? '_test' : ''}`
async connect(dc: BasicDcOption): Promise<ITelegramConnection> {
const url = `wss://${this._subdomains[dc.id]}.${this._baseDomain}/apiws${dc.testMode ? '_test' : ''}`
return connectWs({
url,

View file

@ -355,6 +355,9 @@ importers:
packages/test:
dependencies:
'@fuman/net':
specifier: workspace:^
version: link:../../private/fuman/packages/net
'@fuman/utils':
specifier: workspace:^
version: link:../../private/fuman/packages/utils