fix: correct import ordering and update tests for drizzle-orm migration

This commit is contained in:
MythEclipse
2026-05-14 15:47:03 +07:00
parent 50d4517079
commit b600dad011
10 changed files with 87 additions and 140 deletions
+7 -4
View File
@@ -1,6 +1,6 @@
import { drizzle as drizzlePostgres } from "drizzle-orm/node-postgres";
import { drizzle as drizzleSqlite } from "drizzle-orm/better-sqlite3";
import Database from "better-sqlite3";
import { drizzle as drizzleSqlite } from "drizzle-orm/better-sqlite3";
import { drizzle as drizzlePostgres } from "drizzle-orm/node-postgres";
import { Pool } from "pg";
import { config } from "../config";
import { createChildLogger } from "../logger";
@@ -8,7 +8,10 @@ import * as schema from "./schema";
const logger = createChildLogger("drizzle");
let db: ReturnType<typeof drizzlePostgres> | ReturnType<typeof drizzleSqlite> | null = null;
let db:
| ReturnType<typeof drizzlePostgres>
| ReturnType<typeof drizzleSqlite>
| null = null;
/**
* Initialize the database connection based on DATABASE_TYPE config
@@ -51,7 +54,7 @@ export async function initializeDatabase() {
export function getDatabase() {
if (db === null) {
throw new Error(
"Database not initialized. Call initializeDatabase() first."
"Database not initialized. Call initializeDatabase() first.",
);
}
return db;
+11 -17
View File
@@ -1,18 +1,18 @@
import {
bigint as pgBigint,
foreignKey as pgForeignKey,
index as pgIndex,
integer as pgInteger,
real as pgReal,
pgTable,
text as pgText,
integer as pgInteger,
bigint as pgBigint,
real as pgReal,
index as pgIndex,
foreignKey as pgForeignKey,
} from "drizzle-orm/pg-core";
import {
sqliteTable,
text as sqliteText,
index as sqliteIndex,
integer as sqliteInteger,
real as sqliteReal,
index as sqliteIndex,
sqliteTable,
text as sqliteText,
} from "drizzle-orm/sqlite-core";
import { config } from "../config";
@@ -257,14 +257,10 @@ export const sqliteUIStateTable = sqliteTable("ui_state", {
// ========================================
export const muxerJobsTable =
config.DATABASE_TYPE === "postgres"
? pgMuxerJobsTable
: sqliteMuxerJobsTable;
config.DATABASE_TYPE === "postgres" ? pgMuxerJobsTable : sqliteMuxerJobsTable;
export const messagesTable =
config.DATABASE_TYPE === "postgres"
? pgMessagesTable
: sqliteMessagesTable;
config.DATABASE_TYPE === "postgres" ? pgMessagesTable : sqliteMessagesTable;
export const attachmentsTable =
config.DATABASE_TYPE === "postgres"
@@ -272,9 +268,7 @@ export const attachmentsTable =
: sqliteAttachmentsTable;
export const uiStateTable =
config.DATABASE_TYPE === "postgres"
? pgUIStateTable
: sqliteUIStateTable;
config.DATABASE_TYPE === "postgres" ? pgUIStateTable : sqliteUIStateTable;
// Export table types for use in queries
export type MuxerJob = typeof muxerJobsTable.$inferSelect;
+1 -1
View File
@@ -4,7 +4,7 @@ import "@snazzah/davey";
import "dotenv/config";
import { Client } from "discord.js-selfbot-v13";
import { config } from "./config";
import { initializeDatabase, closeDatabase } from "./database/drizzle";
import { closeDatabase, initializeDatabase } from "./database/drizzle";
import { createChildLogger } from "./logger";
import { startPendingAIAnalysisWorker } from "./moderation/aiAnalyzer";
import { syncBacklogMessages } from "./moderation/backlogSync";
+2 -6
View File
@@ -245,9 +245,7 @@ Satu JSON object per pesan dalam array.`,
return { results, raw: response };
}
async function analyzeAndStoreBatch(
messages: MessageRecord[],
): Promise<void> {
async function analyzeAndStoreBatch(messages: MessageRecord[]): Promise<void> {
if (messages.length === 0) return;
const analyzableMessages = messages.filter(
@@ -359,9 +357,7 @@ async function drainQueue(): Promise<void> {
}
}
export function queueMessageAnalysis(
messageId: string,
): void {
export function queueMessageAnalysis(messageId: string): void {
if (!config.AI_ANALYSIS_ENABLED) return;
logger.debug({ messageId }, "Queueing AI analysis");
queuedMessageIds.add(messageId);
+1 -3
View File
@@ -40,9 +40,7 @@ async function syncChannelMessages(
return synced;
}
export async function syncBacklogMessages(
client: Client,
): Promise<void> {
export async function syncBacklogMessages(client: Client): Promise<void> {
if (!config.MONITOR_GUILD_ID) {
logger.warn("MONITOR_GUILD_ID not configured, skipping backlog sync");
return;
+3 -5
View File
@@ -1,9 +1,9 @@
import type { Client, Message } from "discord.js-selfbot-v13";
import { eq } from "drizzle-orm";
import { config } from "../config";
import { createChildLogger } from "../logger";
import { getDatabase } from "../database/drizzle";
import { messagesTable } from "../database/schema";
import { eq } from "drizzle-orm";
import { createChildLogger } from "../logger";
import { queueMessageAnalysis } from "./aiAnalyzer";
import {
getDisplayContent,
@@ -94,9 +94,7 @@ export async function captureMessage(
);
}
export function registerMessageCapture(
client: Client,
): void {
export function registerMessageCapture(client: Client): void {
client.on("messageCreate", async (message) => {
if (!message.guildId || message.guildId !== config.MONITOR_GUILD_ID) return;
if (message.author?.bot) return;
+3 -5
View File
@@ -1,14 +1,12 @@
import { and, asc, desc, eq, isNull, or } from "drizzle-orm";
import { getDatabase } from "../database/drizzle";
import { messagesTable, attachmentsTable } from "../database/schema";
import { eq, or, desc, asc, and, isNull } from "drizzle-orm";
import { attachmentsTable, messagesTable } from "../database/schema";
import { createChildLogger } from "../logger";
import type { AttachmentRecord, MessageRecord } from "./types";
const logger = createChildLogger("message-store");
export async function insertMessage(
message: MessageRecord,
): Promise<void> {
export async function insertMessage(message: MessageRecord): Promise<void> {
try {
const db = getDatabase() as any;
await db.insert(messagesTable).values(message).onConflictDoNothing();
+13 -8
View File
@@ -1,6 +1,9 @@
import { getDatabase as getDrizzleDatabase, initializeDatabase } from "./database/drizzle";
import { and, asc, eq, lt, sql } from "drizzle-orm";
import {
getDatabase as getDrizzleDatabase,
initializeDatabase,
} from "./database/drizzle";
import { muxerJobsTable, uiStateTable } from "./database/schema";
import { eq, asc, lt, and, sql } from "drizzle-orm";
import { createChildLogger } from "./logger";
const logger = createChildLogger("muxer-queue");
@@ -224,9 +227,10 @@ export async function cleanupCompletedJobs(
),
);
const deletedCount = typeof result === "object" && "rowsAffected" in result
? result.rowsAffected
: 0;
const deletedCount =
typeof result === "object" && "rowsAffected" in result
? result.rowsAffected
: 0;
logger.info({ deletedCount }, "Cleaned up completed jobs");
@@ -258,9 +262,10 @@ export async function getJobStats(): Promise<{
};
for (const row of rows) {
const count = typeof row.count === "object" && "count" in row.count
? (row.count as any).count
: Number(row.count);
const count =
typeof row.count === "object" && "count" in row.count
? (row.count as any).count
: Number(row.count);
if (row.status === "pending") stats.pending = count;
else if (row.status === "processing") stats.processing = count;
else if (row.status === "completed") stats.completed = count;
+6 -2
View File
@@ -5,6 +5,7 @@ import http from "http";
import path from "path";
import * as prism from "prism-media";
import { WebSocketServer } from "ws";
import { getDatabase } from "./database/drizzle";
import { AppError } from "./errors";
import { createChildLogger, logger } from "./logger";
import { getMetrics, uptimeGauge } from "./metrics";
@@ -18,7 +19,6 @@ import {
getPersistedValue,
setPersistedValue,
} from "./muxer-queue";
import { getDatabase } from "./database/drizzle";
import { discordPlayer } from "./player";
import type { VoiceController } from "./voiceController";
@@ -296,7 +296,11 @@ export async function startWebserver(
count: attachments.length,
});
} else {
const messages = await getMessagesByChannel(channel, limitNum, offsetNum);
const messages = await getMessagesByChannel(
channel,
limitNum,
offsetNum,
);
res.json({
type: "text",
data: messages,
+40 -89
View File
@@ -3,10 +3,9 @@ import { afterAll, beforeAll, describe, expect, it, vi } from "vitest";
const originalEnv = process.env;
describe("PostgreSQL Connection", () => {
let skipPostgresTests = false;
describe("Drizzle ORM Database", () => {
let config: any;
let postgres: any;
let drizzle: any;
let logger: any;
beforeAll(async () => {
@@ -23,118 +22,70 @@ describe("PostgreSQL Connection", () => {
// Import after environment is set
const configModule = await import("../src/config");
const postgresModule = await import("../src/database/postgres");
const drizzleModule = await import("../src/database/drizzle");
const loggerModule = await import("../src/logger");
config = configModule.config;
postgres = postgresModule;
drizzle = drizzleModule;
logger = loggerModule.createChildLogger("database.test");
if (config.DATABASE_TYPE !== "postgres") {
skipPostgresTests = true;
logger.info("Skipping PostgreSQL tests (DATABASE_TYPE != postgres)");
}
logger.info(`Testing with DATABASE_TYPE: ${config.DATABASE_TYPE}`);
});
afterAll(async () => {
if (config && config.DATABASE_TYPE === "postgres") {
try {
await postgres.closePool();
} catch (error) {
if (logger) {
logger.error(
{ error: error instanceof Error ? error.message : String(error) },
"Error closing pool in afterAll",
);
}
try {
await drizzle.closeDatabase();
} catch (error) {
if (logger) {
logger.error(
{ error: error instanceof Error ? error.message : String(error) },
"Error closing database in afterAll",
);
}
}
process.env = originalEnv;
});
it("should initialize connection pool", async () => {
if (skipPostgresTests) {
logger.info("Skipping test: DATABASE_TYPE is not postgres");
return;
}
it("should initialize database connection", async () => {
const db = await drizzle.initializeDatabase();
const pool = postgres.getPool();
expect(pool).toBeDefined();
expect(pool).toHaveProperty("connect");
expect(pool).toHaveProperty("query");
expect(pool).toHaveProperty("end");
expect(db).toBeDefined();
expect(db).toHaveProperty("query");
expect(db).toHaveProperty("select");
});
it("should execute query", async () => {
if (skipPostgresTests) {
logger.info("Skipping test: DATABASE_TYPE is not postgres");
return;
}
it("should return same instance on subsequent calls", async () => {
const db1 = await drizzle.initializeDatabase();
const db2 = await drizzle.initializeDatabase();
const result = await postgres.query("SELECT 1 as num");
expect(result).toBeDefined();
expect(result.rows).toBeDefined();
expect(result.rows.length).toBeGreaterThan(0);
expect(result.rows[0]).toHaveProperty("num");
expect(result.rows[0].num).toBe(1);
expect(db1).toBe(db2);
});
it("should handle connection errors gracefully", async () => {
if (skipPostgresTests) {
logger.info("Skipping test: DATABASE_TYPE is not postgres");
return;
}
it("should get database instance", async () => {
await drizzle.initializeDatabase();
const db = drizzle.getDatabase();
// Test that invalid queries throw errors appropriately
try {
await postgres.query("SELECT * FROM nonexistent_table_xyz");
// If we get here, the test should fail
expect.fail("Expected query to throw an error");
} catch (error) {
// Expected behavior: query should throw an error for invalid table
expect(error).toBeDefined();
expect(error instanceof Error).toBe(true);
}
expect(db).toBeDefined();
expect(db).toHaveProperty("query");
});
it("should acquire and release client from pool", async () => {
if (skipPostgresTests) {
logger.info("Skipping test: DATABASE_TYPE is not postgres");
return;
}
it("should throw error if database not initialized", async () => {
// Reset the database state
vi.resetModules();
const client = await postgres.getClient();
const drizzleModule = await import("../src/database/drizzle");
expect(client).toBeDefined();
expect(client).toHaveProperty("query");
expect(client).toHaveProperty("release");
// Execute a simple query with the client
const result = await client.query("SELECT 1 as num");
expect(result.rows[0].num).toBe(1);
// Release the client back to the pool
client.release();
expect(() => {
drizzleModule.getDatabase();
}).toThrow("Database not initialized");
});
it("should build config from DATABASE_URL", () => {
if (skipPostgresTests) {
logger.info("Skipping test: DATABASE_TYPE is not postgres");
return;
}
it("should close database connection", async () => {
await drizzle.initializeDatabase();
await drizzle.closeDatabase();
// Test buildConfig function with a sample DATABASE_URL
const pgConfig = postgres.buildConfig();
expect(pgConfig).toBeDefined();
expect(pgConfig).toHaveProperty("host");
expect(pgConfig).toHaveProperty("port");
expect(pgConfig).toHaveProperty("min");
expect(pgConfig).toHaveProperty("max");
expect(pgConfig.port).toBeGreaterThan(0);
expect(pgConfig.min).toBeGreaterThan(0);
expect(pgConfig.max).toBeGreaterThanOrEqual(pgConfig.min);
expect(() => {
drizzle.getDatabase();
}).toThrow("Database not initialized");
});
});