Files
homarr/packages/db/test/mysql-migration.spec.ts
T
Thomas Camlong 4acd6548b5 fix(db): store assistant messages as mediumtext on mysql
MySQL caps `text` at 65,535 bytes. A single base64 image attachment is
allowed up to ~1.34MB by the chat route, so appending such a message fails
with ER_DATA_TOO_LONG under the default strict mode. SQLite and PostgreSQL
are unaffected because their text types are effectively unbounded.

`mediumtext` holds 16MB, comfortably above the 2MB request cap.

The migration keeps the parenthesised default expression that every other
text default in this folder uses, because MySQL rejects a literal default
on TEXT columns.
2026-08-07 17:29:55 +02:00

59 lines
2.2 KiB
TypeScript

import path from "path";
import { MySqlContainer } from "@testcontainers/mysql";
import { eq } from "drizzle-orm";
import { drizzle } from "drizzle-orm/mysql2";
import { migrate } from "drizzle-orm/mysql2/migrator";
import mysql from "mysql2";
import { describe, expect, test } from "vitest";
import { DB_CASING } from "@homarr/core/infrastructure/db/constants";
import * as mysqlSchema from "../schema/mysql";
describe("Mysql Migration", () => {
test("should add all tables and keys specified in migration files", async () => {
const mysqlContainer = await new MySqlContainer("mysql:latest").start();
const connection = mysql.createConnection({
host: mysqlContainer.getHost(),
database: mysqlContainer.getDatabase(),
port: mysqlContainer.getPort(),
user: mysqlContainer.getUsername(),
password: mysqlContainer.getUserPassword(),
});
const database = drizzle(connection, {
schema: mysqlSchema,
mode: "default",
casing: DB_CASING,
});
// Run migrations and check if it works
await migrate(database, {
migrationsFolder: path.join(__dirname, "..", "migrations", "mysql"),
});
// Check if users table exists
await database.query.users.findMany();
// A single base64 image attachment already exceeds the 64KB that `text` holds on MySQL, so
// `assistant_message.content` has to be `mediumtext`. Everything below 64KB passes either way.
const largeContent = "x".repeat(200_000);
await database.insert(mysqlSchema.users).values({ id: "size-check-user", name: "size-check" });
await database.insert(mysqlSchema.assistantThreads).values({ id: "size-check-thread", userId: "size-check-user" });
await database.insert(mysqlSchema.assistantMessages).values({
id: "size-check-message",
threadId: "size-check-thread",
parentId: null,
content: largeContent,
});
const [storedMessage] = await database.query.assistantMessages.findMany({
where: eq(mysqlSchema.assistantMessages.id, "size-check-message"),
});
expect(storedMessage?.content).toHaveLength(largeContent.length);
connection.end();
await mysqlContainer.stop();
}, 120_000);
});