refactor: remove concurrency limit from Telegram upload queue and enhance media group handling
This commit is contained in:
+26
-7
@@ -1,8 +1,27 @@
|
||||
// @ts-nocheck
|
||||
import { afterEach, beforeEach, describe, expect, it, mock, spyOn } from 'bun:test';
|
||||
import { afterEach, beforeAll, beforeEach, describe, expect, it, mock, spyOn } from 'bun:test';
|
||||
import { config } from '../src/env';
|
||||
import logger from '../src/utils/logger';
|
||||
|
||||
let realPhotoBuffer: Buffer;
|
||||
|
||||
beforeAll(async () => {
|
||||
try {
|
||||
const res = await fetch(
|
||||
'https://upload.wikimedia.org/wikipedia/commons/4/47/PNG_transparency_demonstration_1.png',
|
||||
);
|
||||
if (!res.ok) throw new Error('Wikimedia download failed');
|
||||
const arrayBuffer = await res.arrayBuffer();
|
||||
realPhotoBuffer = Buffer.from(arrayBuffer);
|
||||
} catch {
|
||||
// Fallback 1x1px JPEG
|
||||
realPhotoBuffer = Buffer.from(
|
||||
'ffd8ffe000104a46494600010101006000600000ffdb004300080606070605080707070909080a0c140d0c0b0b0c1912130f141d1a1f1e1d1a1c1c20242e2720222c231c1c2837292c30313434341f27393d38323c2e333432ffc0000b080001000101011100ffc4001f0000010501010110000000000000000000000102030405060708ffda000c03010002110311003f00a0ffd9',
|
||||
'hex',
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
// Mock Telegraf and fetch
|
||||
mock.module('telegraf', () => {
|
||||
return {
|
||||
@@ -66,8 +85,8 @@ describe('Telegram API Utilities', () => {
|
||||
|
||||
describe('forwardToStorage', () => {
|
||||
it('should forward photo to storage chat and return file details', async () => {
|
||||
const chunk = Buffer.from('fake photo data');
|
||||
const fileName = 'test_photo.jpg';
|
||||
const chunk = realPhotoBuffer;
|
||||
const fileName = 'test_photo.png';
|
||||
const result = await forwardToStorage(chunk, fileName, 'photo');
|
||||
|
||||
expect(result).toEqual({
|
||||
@@ -104,8 +123,8 @@ describe('Telegram API Utilities', () => {
|
||||
const bot = getBot();
|
||||
bot.telegram.sendPhoto = mock(() => Promise.reject(new Error('Telegram send failed')));
|
||||
|
||||
const chunk = Buffer.from('fake photo data');
|
||||
const fileName = 'test_photo.jpg';
|
||||
const chunk = realPhotoBuffer;
|
||||
const fileName = 'test_photo.png';
|
||||
|
||||
await expect(forwardToStorage(chunk, fileName, 'photo')).rejects.toThrow(
|
||||
'Telegram send failed',
|
||||
@@ -130,8 +149,8 @@ describe('Telegram API Utilities', () => {
|
||||
});
|
||||
});
|
||||
|
||||
const chunk = Buffer.from('fake photo data');
|
||||
const fileName = 'test_photo.jpg';
|
||||
const chunk = realPhotoBuffer;
|
||||
const fileName = 'test_photo.png';
|
||||
|
||||
const startTime = Date.now();
|
||||
const result = await forwardToStorage(chunk, fileName, 'photo');
|
||||
|
||||
@@ -2,10 +2,9 @@ import { describe, expect, it } from 'bun:test';
|
||||
import { enqueueUpload } from '../src/utils/telegramQueue';
|
||||
|
||||
describe('Telegram Queue', () => {
|
||||
it('should process tasks in order and limit concurrency', async () => {
|
||||
it('should process tasks in parallel without limit', async () => {
|
||||
let activeTasks = 0;
|
||||
let maxActiveTasks = 0;
|
||||
const executionOrder: number[] = [];
|
||||
|
||||
const createTask = (id: number, delayMs: number) => {
|
||||
return async () => {
|
||||
@@ -16,24 +15,22 @@ describe('Telegram Queue', () => {
|
||||
|
||||
await new Promise((resolve) => setTimeout(resolve, delayMs));
|
||||
|
||||
executionOrder.push(id);
|
||||
activeTasks--;
|
||||
return id;
|
||||
};
|
||||
};
|
||||
|
||||
// Enqueue 4 tasks with delays
|
||||
const promises = [
|
||||
enqueueUpload(createTask(1, 50)),
|
||||
enqueueUpload(createTask(2, 20)),
|
||||
enqueueUpload(createTask(3, 10)),
|
||||
enqueueUpload(createTask(4, 5)),
|
||||
enqueueUpload(createTask(2, 50)),
|
||||
enqueueUpload(createTask(3, 50)),
|
||||
enqueueUpload(createTask(4, 50)),
|
||||
];
|
||||
|
||||
const results = await Promise.all(promises);
|
||||
|
||||
expect(results).toEqual([1, 2, 3, 4]);
|
||||
// Concurrency limit is 2, so maximum active tasks at any time should be <= 2
|
||||
expect(maxActiveTasks).toBeLessThanOrEqual(2);
|
||||
// Concurrency limit is removed, so active tasks should be able to reach 4 (fully parallel)
|
||||
expect(maxActiveTasks).toBe(4);
|
||||
});
|
||||
});
|
||||
|
||||
+27
-8
@@ -1,5 +1,24 @@
|
||||
// @ts-nocheck
|
||||
import { afterAll, beforeEach, describe, expect, it, mock } from 'bun:test';
|
||||
import { afterAll, beforeAll, beforeEach, describe, expect, it, mock } from 'bun:test';
|
||||
|
||||
let realPhotoBuffer: Buffer;
|
||||
|
||||
beforeAll(async () => {
|
||||
try {
|
||||
const res = await fetch(
|
||||
'https://upload.wikimedia.org/wikipedia/commons/4/47/PNG_transparency_demonstration_1.png',
|
||||
);
|
||||
if (!res.ok) throw new Error('Wikimedia download failed');
|
||||
const arrayBuffer = await res.arrayBuffer();
|
||||
realPhotoBuffer = Buffer.from(arrayBuffer);
|
||||
} catch {
|
||||
// Fallback 1x1px JPEG
|
||||
realPhotoBuffer = Buffer.from(
|
||||
'ffd8ffe000104a46494600010101006000600000ffdb004300080606070605080707070909080a0c140d0c0b0b0c1912130f141d1a1f1e1d1a1c1c20242e2720222c231c1c2837292c30313434341f27393d38323c2e333432ffc0000b080001000101011100ffc4001f0000010501010110000000000000000000000102030405060708ffda000c03010002110311003f00a0ffd9',
|
||||
'hex',
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
// Mock db
|
||||
let mockSelectResult: any[] = [];
|
||||
@@ -96,8 +115,8 @@ describe('Upload Route Handler', () => {
|
||||
'content-type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
file: Buffer.from('hello world').toString('base64'),
|
||||
fileName: 'test.txt',
|
||||
file: realPhotoBuffer.toString('base64'),
|
||||
fileName: 'test.png',
|
||||
}),
|
||||
});
|
||||
|
||||
@@ -108,8 +127,8 @@ describe('Upload Route Handler', () => {
|
||||
expect(body.public_id).toBe('mocked-nanoid-id');
|
||||
expect(body.telegram_file_id).toBe('tg-file-id-123');
|
||||
expect(body.telegram_file_unique_id).toBe('tg-unique-id-abc');
|
||||
expect(body.file_name).toBe('test.txt');
|
||||
expect(body.file_type).toBe('document');
|
||||
expect(body.file_name).toBe('test.png');
|
||||
expect(body.file_type).toBe('photo');
|
||||
});
|
||||
|
||||
it('should reject JSON upload without file key', async () => {
|
||||
@@ -131,8 +150,8 @@ describe('Upload Route Handler', () => {
|
||||
|
||||
it('should process multipart upload successfully', async () => {
|
||||
const formData = new FormData();
|
||||
const fileBlob = new Blob([Buffer.from('multipart hello')], { type: 'text/plain' });
|
||||
formData.append('file', fileBlob, 'test_multi.txt');
|
||||
const fileBlob = new Blob([realPhotoBuffer], { type: 'image/png' });
|
||||
formData.append('file', fileBlob, 'test_multi.png');
|
||||
|
||||
const req = new Request('http://localhost:3000/api/upload', {
|
||||
method: 'POST',
|
||||
@@ -143,7 +162,7 @@ describe('Upload Route Handler', () => {
|
||||
expect(res.status).toBe(200);
|
||||
const body = await res.json();
|
||||
expect(body.public_id).toBe('mocked-nanoid-id');
|
||||
expect(body.file_name).toBe('test_multi.txt');
|
||||
expect(body.file_name).toBe('test_multi.png');
|
||||
});
|
||||
|
||||
it('should deduplicate multipart upload if hash exists', async () => {
|
||||
|
||||
Reference in New Issue
Block a user