feat: migrate entire codebase to TypeScript

This commit is contained in:
MythEclipse
2026-05-18 07:27:06 +07:00
parent 9c5a855f7d
commit 3f4e697733
22 changed files with 212 additions and 150 deletions
@@ -1,3 +1,4 @@
// @ts-nocheck
import { describe, it, expect, mock, beforeEach, afterAll } from "bun:test";
const mockServe = mock((options) => {
@@ -15,24 +16,24 @@ const mockStartBot = mock(() => Promise.resolve({
stop: mock()
}));
mock.module("../src/bot.js", () => ({
mock.module("../src/bot", () => ({
startBot: mockStartBot
}));
mock.module("../src/routes/upload.js", () => ({
mock.module("../src/routes/upload", () => ({
handleUpload: mock()
}));
mock.module("../src/routes/files.js", () => ({
mock.module("../src/routes/files", () => ({
handleFileRedirect: mock(),
handleFileInfo: mock()
}));
mock.module("../src/routes/health.js", () => ({
mock.module("../src/routes/health", () => ({
handleHealth: mock()
}));
mock.module("../src/utils/rateLimit.js", () => ({
mock.module("../src/utils/rateLimit", () => ({
cleanupRateLimitCache: mock()
}));
@@ -47,7 +48,7 @@ describe("Bootstrap Server", () => {
});
it("should bootstrap the application successfully", async () => {
await import("../src/index.js");
await import("../src/index");
expect(mockServe).toHaveBeenCalled();
expect(mockStartBot).toHaveBeenCalled();
+8 -7
View File
@@ -1,5 +1,6 @@
// @ts-nocheck
import { describe, it, expect, mock, spyOn, beforeEach, afterAll } from "bun:test";
import logger from "../src/utils/logger.js";
import logger from "../src/utils/logger";
// Mock environment
process.env.BOT_TOKEN = "8605908810:AAFpUzlIBktfd_7wpEj7zMJob2CFxvG-ZGY";
@@ -30,7 +31,7 @@ mock.module("telegraf", () => {
const mockInsert = mock(() => ({
values: mock(() => Promise.resolve())
}));
mock.module("../src/db/index.js", () => ({
mock.module("../src/db/index", () => ({
db: {
insert: mockInsert
},
@@ -43,7 +44,7 @@ const mockForwardToStorage = mock(() => Promise.resolve({
telegramFileUniqueId: "stored_unique_id",
storageMessageId: 9999
}));
mock.module("../src/utils/telegram.js", () => ({
mock.module("../src/utils/telegram", () => ({
forwardToStorage: mockForwardToStorage
}));
@@ -63,7 +64,7 @@ describe("Telegram Bot Handler", () => {
});
it("should initialize and launch the bot", async () => {
const { startBot } = await import("../src/bot.js");
const { startBot } = await import("../src/bot");
const bot = await startBot();
expect(bot).toBeDefined();
@@ -77,7 +78,7 @@ describe("Telegram Bot Handler", () => {
});
it("should handle /start command", async () => {
const { startBot } = await import("../src/bot.js");
const { startBot } = await import("../src/bot");
await startBot();
const startHandler = mockCommand.mock.calls.find(call => call[0] === "start")[1];
@@ -91,7 +92,7 @@ describe("Telegram Bot Handler", () => {
});
it("should process document uploads and save to db", async () => {
const { startBot } = await import("../src/bot.js");
const { startBot } = await import("../src/bot");
await startBot();
const fileHandler = mockOn.mock.calls[0][1];
@@ -120,7 +121,7 @@ describe("Telegram Bot Handler", () => {
});
it("should reject uploads exceeding max size limit", async () => {
const { startBot } = await import("../src/bot.js");
const { startBot } = await import("../src/bot");
await startBot();
const fileHandler = mockOn.mock.calls[0][1];
+3 -2
View File
@@ -1,6 +1,7 @@
// @ts-nocheck
import { describe, it, expect } from "bun:test";
import { db, files } from "../src/db/index.js";
import { files as schemaFiles } from "../src/db/schema.js";
import { db, files } from "../src/db/index";
import { files as schemaFiles } from "../src/db/schema";
describe("Database Layer", () => {
it("should export db instance", () => {
+2 -1
View File
@@ -1,5 +1,6 @@
// @ts-nocheck
import { describe, it, expect, beforeAll } from "bun:test";
import { config } from "../src/env.js";
import { config } from "../src/env";
describe("Environment Variables Validation", () => {
it("config should have all required fields", () => {
+2 -1
View File
@@ -1,5 +1,6 @@
// @ts-nocheck
import { describe, it, expect } from "bun:test";
import { getFileType, checkFileSize, extractFileName, extractMimeType } from "../src/utils/file.js";
import { getFileType, checkFileSize, extractFileName, extractMimeType } from "../src/utils/file";
describe("File Utilities", () => {
describe("getFileType", () => {
+20 -12
View File
@@ -1,3 +1,4 @@
// @ts-nocheck
import { describe, it, expect, mock, beforeEach, afterAll } from "bun:test";
// Mock database layer
@@ -9,7 +10,7 @@ const mockSelect = mock(() => ({
}))
}));
mock.module("../src/db/index.js", () => ({
mock.module("../src/db/index", () => ({
db: {
select: mockSelect
},
@@ -22,9 +23,9 @@ mock.module("../src/db/index.js", () => ({
// Mock telegram utils
const mockGetFile = mock(() => Promise.resolve({ file_path: "photos/file_0.jpg" }));
mock.module("../src/utils/telegram.js", () => ({
mock.module("../src/utils/telegram", () => ({
getBot: () => ({
api: {
telegram: {
getFile: mockGetFile
}
})
@@ -32,7 +33,7 @@ mock.module("../src/utils/telegram.js", () => ({
// Mock rateLimit
const mockCheckRateLimit = mock(() => true);
mock.module("../src/utils/rateLimit.js", () => ({
mock.module("../src/utils/rateLimit", () => ({
checkRateLimit: mockCheckRateLimit
}));
@@ -47,7 +48,7 @@ describe("File Route Handlers", () => {
// Set up mock token
process.env.BOT_TOKEN = "123456:ABC-DEF";
const filesRoute = await import("../src/routes/files.js");
const filesRoute = await import("../src/routes/files");
handleFileRedirect = filesRoute.handleFileRedirect;
handleFileInfo = filesRoute.handleFileInfo;
});
@@ -56,8 +57,9 @@ describe("File Route Handlers", () => {
it("should return 429 if rate limit is exceeded", async () => {
mockCheckRateLimit.mockImplementationOnce(() => false);
const req = new Request("http://localhost:3000/f/test-id");
req.params = { public_id: "test-id" };
const res = await handleFileRedirect(req, { params: { public_id: "test-id" } });
const res = await handleFileRedirect(req);
expect(res.status).toBe(429);
const body = await res.json();
expect(body.error).toBe("Rate limit exceeded");
@@ -73,7 +75,8 @@ describe("File Route Handlers", () => {
}));
const req = new Request("http://localhost:3000/f/missing-id");
const res = await handleFileRedirect(req, { params: { public_id: "missing-id" } });
req.params = { public_id: "missing-id" };
const res = await handleFileRedirect(req);
expect(res.status).toBe(404);
const body = await res.json();
expect(body.error).toBe("File not found");
@@ -94,7 +97,8 @@ describe("File Route Handlers", () => {
}));
const req = new Request("http://localhost:3000/f/test-id");
const res = await handleFileRedirect(req, { params: { public_id: "test-id" } });
req.params = { public_id: "test-id" };
const res = await handleFileRedirect(req);
expect(res.status).toBe(302);
expect(res.headers.get("Location")).toBe("https://api.telegram.org/file/bot123456:ABC-DEF/photos/file_0.jpg");
});
@@ -105,7 +109,8 @@ describe("File Route Handlers", () => {
});
const req = new Request("http://localhost:3000/f/test-id");
const res = await handleFileRedirect(req, { params: { public_id: "test-id" } });
req.params = { public_id: "test-id" };
const res = await handleFileRedirect(req);
expect(res.status).toBe(500);
const body = await res.json();
expect(body.error).toBe("Server error");
@@ -123,7 +128,8 @@ describe("File Route Handlers", () => {
}));
const req = new Request("http://localhost:3000/file/missing-id/info");
const res = await handleFileInfo(req, { params: { public_id: "missing-id" } });
req.params = { public_id: "missing-id" };
const res = await handleFileInfo(req);
expect(res.status).toBe(404);
const body = await res.json();
expect(body.error).toBe("File not found");
@@ -149,7 +155,8 @@ describe("File Route Handlers", () => {
}));
const req = new Request("http://localhost:3000/file/test-id/info");
const res = await handleFileInfo(req, { params: { public_id: "test-id" } });
req.params = { public_id: "test-id" };
const res = await handleFileInfo(req);
expect(res.status).toBe(200);
const body = await res.json();
expect(body).toEqual({
@@ -169,7 +176,8 @@ describe("File Route Handlers", () => {
});
const req = new Request("http://localhost:3000/file/test-id/info");
const res = await handleFileInfo(req, { params: { public_id: "test-id" } });
req.params = { public_id: "test-id" };
const res = await handleFileInfo(req);
expect(res.status).toBe(500);
const body = await res.json();
expect(body.error).toBe("Server error");
+3 -2
View File
@@ -1,9 +1,10 @@
// @ts-nocheck
import { describe, it, expect, mock, beforeEach } from "bun:test";
// Mock database layer
const mockExecute = mock(() => Promise.resolve());
mock.module("../src/db/index.js", () => ({
mock.module("../src/db/index", () => ({
db: {
execute: mockExecute
}
@@ -14,7 +15,7 @@ describe("Health Route Handler", () => {
beforeEach(async () => {
mockExecute.mockClear();
const healthRoute = await import("../src/routes/health.js");
const healthRoute = await import("../src/routes/health");
handleHealth = healthRoute.handleHealth;
});
@@ -1,6 +1,7 @@
// @ts-nocheck
import { describe, it, expect, beforeEach, afterEach, mock, spyOn } from "bun:test";
import { checkRateLimit, cleanupRateLimitCache } from "../src/utils/rateLimit.js";
import logger from "../src/utils/logger.js";
import { checkRateLimit, cleanupRateLimitCache } from "../src/utils/rateLimit";
import logger from "../src/utils/logger";
// Spy on logger.warn
const warnSpy = spyOn(logger, "warn");
@@ -1,6 +1,7 @@
// @ts-nocheck
import { describe, it, expect, beforeEach, afterEach, mock, spyOn } from "bun:test";
import logger from "../src/utils/logger.js";
import { config } from "../src/env.js";
import logger from "../src/utils/logger";
import { config } from "../src/env";
// Mock Telegraf and fetch
mock.module("telegraf", () => {
@@ -8,7 +9,7 @@ mock.module("telegraf", () => {
Telegraf: class {
constructor(token) {
this.token = token;
this.api = {
this.telegram = {
sendPhoto: mock(() => Promise.resolve({
message_id: 12345,
photo: [
@@ -34,7 +35,7 @@ describe("Telegram API Utilities", () => {
global.fetch = mock(() => Promise.resolve(new Response(JSON.stringify({ ok: true }))));
// Import dynamically so mocking is applied first
const telegramUtils = await import("../src/utils/telegram.js");
const telegramUtils = await import("../src/utils/telegram");
forwardToStorage = telegramUtils.forwardToStorage;
getFileInfo = telegramUtils.getFileInfo;
getBot = telegramUtils.getBot;
@@ -48,7 +49,7 @@ describe("Telegram API Utilities", () => {
it("should return the telegraf bot instance", () => {
const bot = getBot();
expect(bot).toBeDefined();
expect(bot.api).toBeDefined();
expect(bot.telegram).toBeDefined();
});
});
@@ -71,7 +72,7 @@ describe("Telegram API Utilities", () => {
it("should handle error when forwarding fails", async () => {
const bot = getBot();
bot.api.sendPhoto = mock(() => Promise.reject(new Error("Telegram send failed")));
bot.telegram.sendPhoto = mock(() => Promise.reject(new Error("Telegram send failed")));
const chunk = Buffer.from("fake photo data");
const fileName = "test_photo.jpg";
+5 -4
View File
@@ -1,3 +1,4 @@
// @ts-nocheck
import { describe, it, expect, mock, beforeEach, afterAll } from "bun:test";
// Mock db
@@ -5,7 +6,7 @@ const mockInsert = mock(() => ({
values: mock(() => Promise.resolve())
}));
mock.module("../src/db/index.js", () => ({
mock.module("../src/db/index", () => ({
db: {
insert: mockInsert
},
@@ -18,14 +19,14 @@ mock.module("nanoid", () => ({
}));
// Mock telegram utils
mock.module("../src/utils/telegram.js", () => ({
mock.module("../src/utils/telegram", () => ({
forwardToStorage: mock(() => Promise.resolve({
telegramFileId: "tg-file-id-123",
telegramFileUniqueId: "tg-unique-id-abc",
storageMessageId: 98765
})),
getBot: () => ({
api: {
telegram: {
getFile: mock(() => Promise.resolve({
file_id: "tg-file-id-123",
file_size: 1000,
@@ -40,7 +41,7 @@ describe("Upload Route Handler", () => {
beforeEach(async () => {
mockInsert.mockClear();
const uploadRoute = await import("../src/routes/upload.js");
const uploadRoute = await import("../src/routes/upload");
handleUpload = uploadRoute.handleUpload;
});