feat(database): add automatic migration on startup and enhance text analysis cache source

This commit is contained in:
MythEclipse
2026-05-31 23:01:38 +07:00
parent 96cd0cfc62
commit 7607282db2
13 changed files with 178 additions and 14 deletions
+16 -1
View File
@@ -39,9 +39,10 @@ describe("loadConfig", () => {
expect(config.AI_ANALYSIS_DEBOUNCE_MS).toBe(500);
expect(config.AI_ANALYSIS_RECOVERY_INTERVAL_MS).toBe(15000);
expect(config.AI_ANALYSIS_ERROR_COOLDOWN_MS).toBe(30000);
expect(config.AI_ANALYSIS_MAX_BATCH_SIZE).toBe(25);
expect(config.AI_ANALYSIS_MAX_BATCH_SIZE).toBe(200);
expect(config.AI_ANALYSIS_MAX_CONTEXT_TOKENS).toBe(8000);
expect(config.AI_ANALYSIS_CONTEXT_MESSAGE_LIMIT).toBe(20);
expect(config.AUTO_MIGRATE_ON_STARTUP).toBe(true);
});
it("coerces AI analysis tuning values", async () => {
@@ -107,4 +108,18 @@ describe("loadConfig", () => {
expect(config.TEXT_CHANNEL_ID).toBe("text-channel");
expect(config.EFFECTIVE_VOICE_GUILD_ID).toBe("voice-guild");
});
it("allows disabling startup migrations explicitly", async () => {
process.env = {
...originalEnv,
DISCORD_TOKEN: "token",
AUTO_MIGRATE_ON_STARTUP: "false",
NODE_ENV: "test",
};
const { loadConfig } = await import("../src/config");
const config = loadConfig(process.env);
expect(config.AUTO_MIGRATE_ON_STARTUP).toBe(false);
});
});
+77 -3
View File
@@ -1,8 +1,82 @@
import { describe, expect, it } from "vitest";
import { runMigrations } from "../../src/database/migrate";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
const migrateSpy = vi.fn();
const initializeDatabaseSpy = vi.fn();
const closeDatabaseSpy = vi.fn();
const withDatabaseClientSpy = vi.fn();
const lockQuerySpy = vi.fn();
const unlockQuerySpy = vi.fn();
const drizzleSpy = vi.fn();
vi.mock("drizzle-orm/node-postgres", () => ({
drizzle: drizzleSpy,
}));
vi.mock("drizzle-orm/node-postgres/migrator", () => ({
migrate: migrateSpy,
}));
vi.mock("../../src/config", () => ({
config: {
AUTO_MIGRATE_ON_STARTUP: true,
},
}));
vi.mock("../../src/database/drizzle", () => ({
initializeDatabase: initializeDatabaseSpy,
closeDatabase: closeDatabaseSpy,
withDatabaseClient: withDatabaseClientSpy,
}));
describe("runMigrations", () => {
it("exports the PostgreSQL migration runner", () => {
beforeEach(() => {
migrateSpy.mockResolvedValue(undefined);
initializeDatabaseSpy.mockResolvedValue({} as never);
closeDatabaseSpy.mockResolvedValue(undefined);
drizzleSpy.mockReturnValue({} as never);
withDatabaseClientSpy.mockImplementation(
async (callback: () => Promise<unknown>) =>
callback({
query: vi.fn().mockImplementation((sql: string) => {
if (sql.includes("pg_advisory_lock")) {
return lockQuerySpy(sql);
}
if (sql.includes("pg_advisory_unlock")) {
return unlockQuerySpy(sql);
}
return Promise.resolve({});
}),
} as never),
);
});
afterEach(() => {
vi.clearAllMocks();
});
it("exports the PostgreSQL migration runner", async () => {
const { runMigrations } = await import("../../src/database/migrate");
expect(runMigrations).toBeTypeOf("function");
});
it("uses a session lock around the migration batch", async () => {
const { runMigrations } = await import("../../src/database/migrate");
await runMigrations();
expect(initializeDatabaseSpy).toHaveBeenCalledTimes(1);
expect(withDatabaseClientSpy).toHaveBeenCalledTimes(1);
expect(migrateSpy).toHaveBeenCalledWith(
expect.any(Object),
expect.objectContaining({ migrationsFolder: "./drizzle/migrations" }),
);
expect(lockQuerySpy).toHaveBeenCalledWith(
"SELECT pg_advisory_lock($1, $2)",
);
expect(unlockQuerySpy).toHaveBeenCalledWith(
"SELECT pg_advisory_unlock($1, $2)",
);
expect(closeDatabaseSpy).toHaveBeenCalledTimes(1);
});
});