mtcute/packages/tl-runtime/tests/binary-writer.spec.ts

213 lines
7.9 KiB
TypeScript
Raw Normal View History

/* eslint-disable @typescript-eslint/no-unsafe-call */
2021-04-08 12:19:38 +03:00
import { expect } from 'chai'
import { randomBytes } from 'crypto'
import Long from 'long'
import { describe, it } from 'mocha'
import { hexDecodeToBuffer, hexEncode } from '../src/encodings/hex.js'
import { TlBinaryWriter, TlSerializationCounter, TlWriterMap } from '../src/index.js'
describe('TlBinaryWriter', () => {
2023-09-24 01:32:22 +03:00
const testSingleMethod = (size: number, fn: (w: TlBinaryWriter) => void, map?: TlWriterMap): string => {
const w = TlBinaryWriter.alloc(map, size)
2021-04-08 12:19:38 +03:00
fn(w)
expect(w.pos).eq(size)
return hexEncode(w.uint8View)
2021-04-08 12:19:38 +03:00
}
it('should write int32', () => {
expect(testSingleMethod(4, (w) => w.int(0))).eq('00000000')
expect(testSingleMethod(4, (w) => w.int(1))).eq('01000000')
expect(testSingleMethod(4, (w) => w.int(67305985))).eq('01020304')
expect(testSingleMethod(4, (w) => w.int(-1))).eq('ffffffff')
2021-04-08 12:19:38 +03:00
})
it('should write uint32', () => {
expect(testSingleMethod(4, (w) => w.uint(0))).eq('00000000')
expect(testSingleMethod(4, (w) => w.uint(1))).eq('01000000')
expect(testSingleMethod(4, (w) => w.uint(67305985))).eq('01020304')
expect(testSingleMethod(4, (w) => w.uint(4294967295))).eq('ffffffff')
})
it('should write int53', () => {
expect(testSingleMethod(8, (w) => w.int53(0))).eq('0000000000000000')
expect(testSingleMethod(8, (w) => w.int53(1))).eq('0100000000000000')
2023-09-24 01:32:22 +03:00
expect(testSingleMethod(8, (w) => w.int53(67305985))).eq('0102030400000000')
expect(testSingleMethod(8, (w) => w.int53(281479271743489))).eq('0100010001000100')
expect(testSingleMethod(8, (w) => w.int53(-1))).eq('ffffffffffffffff')
2021-04-08 12:19:38 +03:00
})
it('should write long', () => {
2023-09-24 01:32:22 +03:00
expect(testSingleMethod(8, (w) => w.long(Long.NEG_ONE))).eq('ffffffffffffffff')
expect(testSingleMethod(8, (w) => w.long(Long.fromString('8671175386481439762')))).eq('1234567812345678')
expect(testSingleMethod(8, (w) => w.long(Long.fromString('-6700480189419895787')))).eq('15c415b5c41c03a3')
2021-04-08 12:19:38 +03:00
})
it('should write float', () => {
expect(testSingleMethod(4, (w) => w.float(1))).eq('0000803f')
expect(testSingleMethod(4, (w) => w.float(1.234))).eq('b6f39d3f')
expect(testSingleMethod(4, (w) => w.float(0.666))).eq('fa7e2a3f')
})
it('should write double', () => {
expect(testSingleMethod(8, (w) => w.double(1))).eq('000000000000f03f')
2023-09-24 01:32:22 +03:00
expect(testSingleMethod(8, (w) => w.double(10.5))).eq('0000000000002540')
2021-04-08 12:19:38 +03:00
expect(testSingleMethod(8, (w) => w.double(8.8))).eq('9a99999999992140')
})
it('should write raw bytes', () => {
expect(testSingleMethod(5, (w) => w.raw(new Uint8Array([4, 3, 5, 1, 1])))).eq('0403050101')
2021-04-08 12:19:38 +03:00
})
it('should write tg-encoded boolean', () => {
expect(testSingleMethod(4, (w) => w.boolean(false))).eq('379779bc')
expect(testSingleMethod(4, (w) => w.boolean(true))).eq('b5757299')
})
it('should write tg-encoded bytes', () => {
expect(testSingleMethod(4, (w) => w.bytes(new Uint8Array([1, 2, 3])))).eq('03010203')
expect(testSingleMethod(8, (w) => w.bytes(new Uint8Array([1, 2, 3, 4])))).eq('0401020304000000')
2021-04-08 12:19:38 +03:00
const random250bytes = randomBytes(250)
2023-09-24 01:32:22 +03:00
expect(testSingleMethod(252, (w) => w.bytes(random250bytes))).eq('fa' + random250bytes.toString('hex') + '00')
2021-04-08 12:19:38 +03:00
const random1000bytes = randomBytes(1000)
// eslint-disable-next-line no-restricted-globals
2021-04-08 12:19:38 +03:00
const buffer = Buffer.alloc(1004)
buffer[0] = 254
buffer.writeIntLE(1000, 1, 3)
buffer.set(random1000bytes, 4)
2023-09-24 01:32:22 +03:00
expect(testSingleMethod(1004, (w) => w.bytes(random1000bytes))).eq(buffer.toString('hex'))
2021-04-08 12:19:38 +03:00
})
const stubObjectsMap: TlWriterMap = {
deadbeef: function (w, obj) {
w.uint(0xdeadbeef)
w.int(obj.a)
w.object(obj.b)
2021-04-08 12:19:38 +03:00
},
facedead: function (w) {
w.uint(0xfacedead)
2021-04-08 12:19:38 +03:00
},
baadc0de: function (w, obj) {
w.uint(0xbaadc0de)
w.uint(obj.n)
2021-04-08 12:19:38 +03:00
},
bebf0c3d: function (w, obj) {
w.uint(0xbebf0c3d)
w.vector(w.int, obj.vec)
2021-04-08 12:19:38 +03:00
},
// eslint-disable-next-line
_staticSize: {} as any,
2021-04-08 12:19:38 +03:00
}
it('should write tg-encoded objects', () => {
const object1 = {
_: 'deadbeef',
a: 1,
b: { _: 'facedead' },
}
const object2 = {
_: 'baadc0de',
n: 2,
}
const length =
TlSerializationCounter.countNeededBytes(stubObjectsMap, object1) +
TlSerializationCounter.countNeededBytes(stubObjectsMap, object2)
2021-04-08 12:19:38 +03:00
expect(length).eq(20)
expect(
testSingleMethod(
length,
(w) => {
w.object(object1)
w.object(object2)
},
stubObjectsMap,
),
2021-04-08 12:19:38 +03:00
).eq('efbeadde01000000addecefadec0adba02000000')
})
it('should write tg-encoded vectors', () => {
const object1 = {
_: 'deadbeef',
a: 1,
b: { _: 'facedead' },
}
const object2 = {
_: 'baadc0de',
n: 2,
}
const object3 = {
_: 'bebf0c3d',
vec: [1, 2],
}
const length =
TlSerializationCounter.countNeededBytes(stubObjectsMap, object1) +
TlSerializationCounter.countNeededBytes(stubObjectsMap, object2) +
TlSerializationCounter.countNeededBytes(stubObjectsMap, object3) +
2021-04-08 12:19:38 +03:00
8 // because technically in tl vector can't be top-level, but whatever :shrug:
expect(length).eq(48)
expect(
testSingleMethod(
length,
(w) => {
w.vector(w.object, [object1, object2, object3])
},
stubObjectsMap,
),
2023-09-24 01:32:22 +03:00
).eq('15c4b51c03000000efbeadde01000000addecefadec0adba020000003d0cbfbe15c4b51c020000000100000002000000')
2021-04-08 12:19:38 +03:00
})
describe('examples from documentation', () => {
// https://core.telegram.org/mtproto/samples-auth_key#2-a-response-from-the-server-has-been-received-with-the-following-content
it('should be able to write resPQ', () => {
const expected =
'000000000000000001c8831ec97ae55140000000632416053e0549828cca27e966b301a48fece2fca5cf4d33f4a11ea877ba4aa5739073300817ed48941a08f98100000015c4b51c01000000216be86c022bb4c3'
const resPq = {
2021-04-08 12:19:38 +03:00
_: 'mt_resPQ',
nonce: hexDecodeToBuffer('3E0549828CCA27E966B301A48FECE2FC'),
serverNonce: hexDecodeToBuffer('A5CF4D33F4A11EA877BA4AA573907330'),
pq: hexDecodeToBuffer('17ED48941A08F981'),
2023-09-24 01:32:22 +03:00
serverPublicKeyFingerprints: [Long.fromString('c3b42b026ce86b21', 16)],
}
const map: TlWriterMap = {
mt_resPQ: function (w, obj) {
w.uint(85337187)
w.int128(obj.nonce)
w.int128(obj.serverNonce)
w.bytes(obj.pq)
w.vector(w.long, obj.serverPublicKeyFingerprints)
},
// eslint-disable-next-line
_staticSize: {} as any,
2021-04-08 12:19:38 +03:00
}
const length =
20 + // mtproto header
TlSerializationCounter.countNeededBytes(map, resPq)
2021-04-08 12:19:38 +03:00
expect(length).eq(expected.length / 2)
expect(
testSingleMethod(
length,
(w) => {
w.long(Long.ZERO) // authKeyId
w.long(Long.fromString('51E57AC91E83C801', true, 16)) // messageId
w.uint(64) // messageLength
w.object(resPq)
},
map,
),
2021-04-08 12:19:38 +03:00
).eq(expected)
})
})
})