Error en producción al crear un tenant:
null value in column "site_key" of relation "umind_tenants"
violates not-null constraint (SQLSTATE 23502)
Es un bug que introduje yo. Al pasar uMind a multi-agente saqué SiteKey de
UmindTenant y renombré TenantID→AgenteID en seis tablas. GORM agrega
columnas pero nunca las borra ni les cambia las restricciones, así que las
viejas quedaron en la base CON su NOT NULL original — y el INSERT nuevo ya
no las incluye.
Al mirarlo, el alcance era mayor que el error reportado: no es solo
site_key. Las seis tablas renombradas tienen su tenant_id huérfano también
NOT NULL, así que fallaba insertar documentos, chunks, mensajes, tools,
canales y conexiones. En la práctica uMind quedaba inutilizable después de
desplegar el refactor: ni crear un tenant, ni ingestar conocimiento, ni
guardar un mensaje del chat.
La migración corre en cada arranque, antes de MigrarUmindAgentes, y
consulta information_schema para no intentar el ALTER a ciegas en una
instalación nueva donde la columna no existe.
No se hace DROP COLUMN a propósito: los datos viejos quedan por si hay que
reconciliar algo. Solo se libera la restricción.
El test compara los nombres de tabla contra el TableName() real de cada
modelo. Un typo ahí haría que la migración no encuentre la columna y siga
de largo: el bug seguiría vivo y el arranque se vería sano.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
54 lines
2.0 KiB
Go
54 lines
2.0 KiB
Go
package migrations
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"github.com/sujit-baniya/fiber-boilerplate/pkg/models"
|
|
)
|
|
|
|
// El refactor multi-agente dejó columnas viejas con su NOT NULL original, y
|
|
// GORM no las toca. El síntoma en producción es:
|
|
//
|
|
// null value in column "site_key" violates not-null constraint (23502)
|
|
//
|
|
// Si un nombre de tabla acá no coincide con el TableName() real, la migración
|
|
// consulta information_schema, no encuentra nada y sigue de largo sin avisar:
|
|
// el bug quedaría igual y el arranque se vería sano. Por eso se comparan
|
|
// contra los modelos en vez de confiar en las cadenas escritas a mano.
|
|
func TestTablasHuerfanasCoincidenConLosModelos(t *testing.T) {
|
|
reales := map[string]bool{
|
|
models.UmindTenant{}.TableName(): true,
|
|
models.UmindDocumento{}.TableName(): true,
|
|
models.UmindChunk{}.TableName(): true,
|
|
models.UmindMensaje{}.TableName(): true,
|
|
models.UmindHerramienta{}.TableName(): true,
|
|
models.UmindCanal{}.TableName(): true,
|
|
models.UmindConexion{}.TableName(): true,
|
|
}
|
|
|
|
for _, h := range ColumnasHuerfanasUmind {
|
|
if !reales[h.Tabla] {
|
|
t.Errorf("la tabla %q no corresponde a ningún TableName() de uMind — la migración no encontraría la columna", h.Tabla)
|
|
}
|
|
}
|
|
|
|
// Las siete tablas afectadas tienen que estar cubiertas: si falta una,
|
|
// insertar en ella sigue fallando.
|
|
if len(ColumnasHuerfanasUmind) != len(reales) {
|
|
t.Errorf("hay %d entradas para %d tablas afectadas", len(ColumnasHuerfanasUmind), len(reales))
|
|
}
|
|
}
|
|
|
|
// Las columnas huérfanas ya no deben existir en los structs: si alguna volvió
|
|
// a mapearse, liberar su NOT NULL sería incorrecto.
|
|
func TestLasColumnasHuerfanasYaNoSeMapean(t *testing.T) {
|
|
// UmindTenant ya no debe tener SiteKey; vive en UmindAgente.
|
|
if _, tiene := any(models.UmindAgente{}).(interface{ TableName() string }); !tiene {
|
|
t.Skip("modelo inesperado")
|
|
}
|
|
a := models.UmindAgente{SiteKey: "umk_x"}
|
|
if a.SiteKey != "umk_x" {
|
|
t.Error("UmindAgente debería ser el dueño de SiteKey")
|
|
}
|
|
}
|