sql writer
This commit is contained in:
@@ -34,10 +34,13 @@ func TestWriteMigration_NewTable(t *testing.T) {
|
||||
|
||||
// Generate migration
|
||||
var buf bytes.Buffer
|
||||
writer := NewMigrationWriter(&writers.WriterOptions{})
|
||||
writer, err := NewMigrationWriter(&writers.WriterOptions{})
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create writer: %v", err)
|
||||
}
|
||||
writer.writer = &buf
|
||||
|
||||
err := writer.WriteMigration(model, current)
|
||||
err = writer.WriteMigration(model, current)
|
||||
if err != nil {
|
||||
t.Fatalf("WriteMigration failed: %v", err)
|
||||
}
|
||||
@@ -54,234 +57,161 @@ func TestWriteMigration_NewTable(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestWriteMigration_AddColumn(t *testing.T) {
|
||||
// Current database (with table but missing column)
|
||||
func TestWriteMigration_WithAudit(t *testing.T) {
|
||||
// Current database (empty)
|
||||
current := models.InitDatabase("testdb")
|
||||
currentSchema := models.InitSchema("public")
|
||||
currentTable := models.InitTable("users", "public")
|
||||
current.Schemas = append(current.Schemas, currentSchema)
|
||||
|
||||
// Model database (with table to audit)
|
||||
model := models.InitDatabase("testdb")
|
||||
modelSchema := models.InitSchema("public")
|
||||
|
||||
table := models.InitTable("users", "public")
|
||||
|
||||
idCol := models.InitColumn("id", "users", "public")
|
||||
idCol.Type = "integer"
|
||||
currentTable.Columns["id"] = idCol
|
||||
idCol.IsPrimaryKey = true
|
||||
table.Columns["id"] = idCol
|
||||
|
||||
currentSchema.Tables = append(currentSchema.Tables, currentTable)
|
||||
current.Schemas = append(current.Schemas, currentSchema)
|
||||
nameCol := models.InitColumn("name", "users", "public")
|
||||
nameCol.Type = "text"
|
||||
table.Columns["name"] = nameCol
|
||||
|
||||
// Model database (with additional column)
|
||||
model := models.InitDatabase("testdb")
|
||||
modelSchema := models.InitSchema("public")
|
||||
modelTable := models.InitTable("users", "public")
|
||||
passwordCol := models.InitColumn("password", "users", "public")
|
||||
passwordCol.Type = "text"
|
||||
table.Columns["password"] = passwordCol
|
||||
|
||||
idCol2 := models.InitColumn("id", "users", "public")
|
||||
idCol2.Type = "integer"
|
||||
modelTable.Columns["id"] = idCol2
|
||||
|
||||
emailCol := models.InitColumn("email", "users", "public")
|
||||
emailCol.Type = "text"
|
||||
modelTable.Columns["email"] = emailCol
|
||||
|
||||
modelSchema.Tables = append(modelSchema.Tables, modelTable)
|
||||
modelSchema.Tables = append(modelSchema.Tables, table)
|
||||
model.Schemas = append(model.Schemas, modelSchema)
|
||||
|
||||
// Generate migration
|
||||
// Configure audit
|
||||
auditConfig := NewAuditConfig()
|
||||
auditConfig.AuditSchema = "public"
|
||||
tableConfig := auditConfig.EnableTableAudit("public", "users")
|
||||
tableConfig.EncryptedColumns = []string{"password"}
|
||||
|
||||
// Generate migration with audit
|
||||
var buf bytes.Buffer
|
||||
writer := NewMigrationWriter(&writers.WriterOptions{})
|
||||
options := &writers.WriterOptions{
|
||||
Metadata: map[string]interface{}{
|
||||
"audit_config": auditConfig,
|
||||
},
|
||||
}
|
||||
writer, err := NewMigrationWriter(options)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create writer: %v", err)
|
||||
}
|
||||
writer.writer = &buf
|
||||
|
||||
err := writer.WriteMigration(model, current)
|
||||
err = writer.WriteMigration(model, current)
|
||||
if err != nil {
|
||||
t.Fatalf("WriteMigration failed: %v", err)
|
||||
}
|
||||
|
||||
output := buf.String()
|
||||
t.Logf("Generated migration:\n%s", output)
|
||||
t.Logf("Generated migration with audit:\n%s", output)
|
||||
|
||||
// Verify ADD COLUMN is present
|
||||
if !strings.Contains(output, "ADD COLUMN") {
|
||||
t.Error("Migration missing ADD COLUMN statement")
|
||||
// Verify audit tables
|
||||
if !strings.Contains(output, "CREATE TABLE IF NOT EXISTS public.atevent") {
|
||||
t.Error("Migration missing atevent table")
|
||||
}
|
||||
if !strings.Contains(output, "email") {
|
||||
t.Error("Migration missing column name 'email'")
|
||||
if !strings.Contains(output, "CREATE TABLE IF NOT EXISTS public.atdetail") {
|
||||
t.Error("Migration missing atdetail table")
|
||||
}
|
||||
|
||||
// Verify audit function
|
||||
if !strings.Contains(output, "CREATE OR REPLACE FUNCTION public.ft_audit_users()") {
|
||||
t.Error("Migration missing audit function")
|
||||
}
|
||||
|
||||
// Verify audit trigger
|
||||
if !strings.Contains(output, "CREATE TRIGGER t_audit_users") {
|
||||
t.Error("Migration missing audit trigger")
|
||||
}
|
||||
|
||||
// Verify encrypted column handling
|
||||
if !strings.Contains(output, "'****************'") {
|
||||
t.Error("Migration missing encrypted column handling")
|
||||
}
|
||||
}
|
||||
|
||||
func TestWriteMigration_ChangeColumnType(t *testing.T) {
|
||||
// Current database (with integer column)
|
||||
current := models.InitDatabase("testdb")
|
||||
currentSchema := models.InitSchema("public")
|
||||
currentTable := models.InitTable("users", "public")
|
||||
|
||||
idCol := models.InitColumn("id", "users", "public")
|
||||
idCol.Type = "integer"
|
||||
currentTable.Columns["id"] = idCol
|
||||
|
||||
currentSchema.Tables = append(currentSchema.Tables, currentTable)
|
||||
current.Schemas = append(current.Schemas, currentSchema)
|
||||
|
||||
// Model database (changed to bigint)
|
||||
model := models.InitDatabase("testdb")
|
||||
modelSchema := models.InitSchema("public")
|
||||
modelTable := models.InitTable("users", "public")
|
||||
|
||||
idCol2 := models.InitColumn("id", "users", "public")
|
||||
idCol2.Type = "bigint"
|
||||
modelTable.Columns["id"] = idCol2
|
||||
|
||||
modelSchema.Tables = append(modelSchema.Tables, modelTable)
|
||||
model.Schemas = append(model.Schemas, modelSchema)
|
||||
|
||||
// Generate migration
|
||||
var buf bytes.Buffer
|
||||
writer := NewMigrationWriter(&writers.WriterOptions{})
|
||||
writer.writer = &buf
|
||||
|
||||
err := writer.WriteMigration(model, current)
|
||||
func TestTemplateExecutor_CreateTable(t *testing.T) {
|
||||
executor, err := NewTemplateExecutor()
|
||||
if err != nil {
|
||||
t.Fatalf("WriteMigration failed: %v", err)
|
||||
t.Fatalf("Failed to create executor: %v", err)
|
||||
}
|
||||
|
||||
output := buf.String()
|
||||
t.Logf("Generated migration:\n%s", output)
|
||||
|
||||
// Verify ALTER COLUMN TYPE is present
|
||||
if !strings.Contains(output, "ALTER COLUMN") {
|
||||
t.Error("Migration missing ALTER COLUMN statement")
|
||||
data := CreateTableData{
|
||||
SchemaName: "public",
|
||||
TableName: "test_table",
|
||||
Columns: []ColumnData{
|
||||
{Name: "id", Type: "integer", NotNull: true},
|
||||
{Name: "name", Type: "text", Default: "'unknown'"},
|
||||
},
|
||||
}
|
||||
if !strings.Contains(output, "TYPE bigint") {
|
||||
t.Error("Migration missing TYPE bigint")
|
||||
}
|
||||
}
|
||||
|
||||
func TestWriteMigration_AddForeignKey(t *testing.T) {
|
||||
// Current database (two tables, no relationship)
|
||||
current := models.InitDatabase("testdb")
|
||||
currentSchema := models.InitSchema("public")
|
||||
|
||||
usersTable := models.InitTable("users", "public")
|
||||
idCol := models.InitColumn("id", "users", "public")
|
||||
idCol.Type = "integer"
|
||||
usersTable.Columns["id"] = idCol
|
||||
|
||||
postsTable := models.InitTable("posts", "public")
|
||||
postIdCol := models.InitColumn("id", "posts", "public")
|
||||
postIdCol.Type = "integer"
|
||||
postsTable.Columns["id"] = postIdCol
|
||||
|
||||
userIdCol := models.InitColumn("user_id", "posts", "public")
|
||||
userIdCol.Type = "integer"
|
||||
postsTable.Columns["user_id"] = userIdCol
|
||||
|
||||
currentSchema.Tables = append(currentSchema.Tables, usersTable, postsTable)
|
||||
current.Schemas = append(current.Schemas, currentSchema)
|
||||
|
||||
// Model database (with foreign key)
|
||||
model := models.InitDatabase("testdb")
|
||||
modelSchema := models.InitSchema("public")
|
||||
|
||||
modelUsersTable := models.InitTable("users", "public")
|
||||
modelIdCol := models.InitColumn("id", "users", "public")
|
||||
modelIdCol.Type = "integer"
|
||||
modelUsersTable.Columns["id"] = modelIdCol
|
||||
|
||||
modelPostsTable := models.InitTable("posts", "public")
|
||||
modelPostIdCol := models.InitColumn("id", "posts", "public")
|
||||
modelPostIdCol.Type = "integer"
|
||||
modelPostsTable.Columns["id"] = modelPostIdCol
|
||||
|
||||
modelUserIdCol := models.InitColumn("user_id", "posts", "public")
|
||||
modelUserIdCol.Type = "integer"
|
||||
modelPostsTable.Columns["user_id"] = modelUserIdCol
|
||||
|
||||
// Add foreign key constraint
|
||||
fkConstraint := &models.Constraint{
|
||||
Name: "fk_posts_users",
|
||||
Type: models.ForeignKeyConstraint,
|
||||
Columns: []string{"user_id"},
|
||||
ReferencedTable: "users",
|
||||
ReferencedSchema: "public",
|
||||
ReferencedColumns: []string{"id"},
|
||||
OnDelete: "CASCADE",
|
||||
OnUpdate: "CASCADE",
|
||||
}
|
||||
modelPostsTable.Constraints["fk_posts_users"] = fkConstraint
|
||||
|
||||
modelSchema.Tables = append(modelSchema.Tables, modelUsersTable, modelPostsTable)
|
||||
model.Schemas = append(model.Schemas, modelSchema)
|
||||
|
||||
// Generate migration
|
||||
var buf bytes.Buffer
|
||||
writer := NewMigrationWriter(&writers.WriterOptions{})
|
||||
writer.writer = &buf
|
||||
|
||||
err := writer.WriteMigration(model, current)
|
||||
sql, err := executor.ExecuteCreateTable(data)
|
||||
if err != nil {
|
||||
t.Fatalf("WriteMigration failed: %v", err)
|
||||
t.Fatalf("Failed to execute template: %v", err)
|
||||
}
|
||||
|
||||
output := buf.String()
|
||||
t.Logf("Generated migration:\n%s", output)
|
||||
t.Logf("Generated SQL:\n%s", sql)
|
||||
|
||||
// Verify FOREIGN KEY is present
|
||||
if !strings.Contains(output, "FOREIGN KEY") {
|
||||
t.Error("Migration missing FOREIGN KEY statement")
|
||||
if !strings.Contains(sql, "CREATE TABLE IF NOT EXISTS public.test_table") {
|
||||
t.Error("SQL missing CREATE TABLE statement")
|
||||
}
|
||||
if !strings.Contains(output, "ON DELETE CASCADE") {
|
||||
t.Error("Migration missing ON DELETE CASCADE")
|
||||
if !strings.Contains(sql, "id integer NOT NULL") {
|
||||
t.Error("SQL missing id column definition")
|
||||
}
|
||||
if !strings.Contains(sql, "name text DEFAULT 'unknown'") {
|
||||
t.Error("SQL missing name column definition")
|
||||
}
|
||||
}
|
||||
|
||||
func TestWriteMigration_AddIndex(t *testing.T) {
|
||||
// Current database (table without index)
|
||||
current := models.InitDatabase("testdb")
|
||||
currentSchema := models.InitSchema("public")
|
||||
currentTable := models.InitTable("users", "public")
|
||||
|
||||
emailCol := models.InitColumn("email", "users", "public")
|
||||
emailCol.Type = "text"
|
||||
currentTable.Columns["email"] = emailCol
|
||||
|
||||
currentSchema.Tables = append(currentSchema.Tables, currentTable)
|
||||
current.Schemas = append(current.Schemas, currentSchema)
|
||||
|
||||
// Model database (with unique index)
|
||||
model := models.InitDatabase("testdb")
|
||||
modelSchema := models.InitSchema("public")
|
||||
modelTable := models.InitTable("users", "public")
|
||||
|
||||
modelEmailCol := models.InitColumn("email", "users", "public")
|
||||
modelEmailCol.Type = "text"
|
||||
modelTable.Columns["email"] = modelEmailCol
|
||||
|
||||
// Add unique index
|
||||
index := &models.Index{
|
||||
Name: "uk_users_email",
|
||||
Unique: true,
|
||||
Columns: []string{"email"},
|
||||
Type: "btree",
|
||||
}
|
||||
modelTable.Indexes["uk_users_email"] = index
|
||||
|
||||
modelSchema.Tables = append(modelSchema.Tables, modelTable)
|
||||
model.Schemas = append(model.Schemas, modelSchema)
|
||||
|
||||
// Generate migration
|
||||
var buf bytes.Buffer
|
||||
writer := NewMigrationWriter(&writers.WriterOptions{})
|
||||
writer.writer = &buf
|
||||
|
||||
err := writer.WriteMigration(model, current)
|
||||
func TestTemplateExecutor_AuditFunction(t *testing.T) {
|
||||
executor, err := NewTemplateExecutor()
|
||||
if err != nil {
|
||||
t.Fatalf("WriteMigration failed: %v", err)
|
||||
t.Fatalf("Failed to create executor: %v", err)
|
||||
}
|
||||
|
||||
output := buf.String()
|
||||
t.Logf("Generated migration:\n%s", output)
|
||||
|
||||
// Verify CREATE UNIQUE INDEX is present
|
||||
if !strings.Contains(output, "CREATE UNIQUE INDEX") {
|
||||
t.Error("Migration missing CREATE UNIQUE INDEX statement")
|
||||
data := AuditFunctionData{
|
||||
SchemaName: "public",
|
||||
FunctionName: "ft_audit_users",
|
||||
TableName: "users",
|
||||
TablePrefix: "NULL",
|
||||
PrimaryKey: "id",
|
||||
AuditSchema: "public",
|
||||
UserFunction: "current_user",
|
||||
AuditInsert: true,
|
||||
AuditUpdate: true,
|
||||
AuditDelete: true,
|
||||
UpdateCondition: "old.name IS DISTINCT FROM new.name",
|
||||
UpdateColumns: []AuditColumnData{
|
||||
{Name: "name", OldValue: "old.name::text", NewValue: "new.name::text"},
|
||||
},
|
||||
DeleteColumns: []AuditColumnData{
|
||||
{Name: "name", OldValue: "old.name::text"},
|
||||
},
|
||||
}
|
||||
if !strings.Contains(output, "uk_users_email") {
|
||||
t.Error("Migration missing index name")
|
||||
|
||||
sql, err := executor.ExecuteAuditFunction(data)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to execute template: %v", err)
|
||||
}
|
||||
|
||||
t.Logf("Generated SQL:\n%s", sql)
|
||||
|
||||
if !strings.Contains(sql, "CREATE OR REPLACE FUNCTION public.ft_audit_users()") {
|
||||
t.Error("SQL missing function definition")
|
||||
}
|
||||
if !strings.Contains(sql, "IF TG_OP = 'INSERT'") {
|
||||
t.Error("SQL missing INSERT handling")
|
||||
}
|
||||
if !strings.Contains(sql, "ELSIF TG_OP = 'UPDATE'") {
|
||||
t.Error("SQL missing UPDATE handling")
|
||||
}
|
||||
if !strings.Contains(sql, "ELSIF TG_OP = 'DELETE'") {
|
||||
t.Error("SQL missing DELETE handling")
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user