218 lines
5.9 KiB
Go
218 lines
5.9 KiB
Go
package pgsql
|
|
|
|
import (
|
|
"bytes"
|
|
"strings"
|
|
"testing"
|
|
|
|
"git.warky.dev/wdevs/relspecgo/pkg/models"
|
|
"git.warky.dev/wdevs/relspecgo/pkg/writers"
|
|
)
|
|
|
|
func TestWriteMigration_NewTable(t *testing.T) {
|
|
// Current database (empty)
|
|
current := models.InitDatabase("testdb")
|
|
currentSchema := models.InitSchema("public")
|
|
current.Schemas = append(current.Schemas, currentSchema)
|
|
|
|
// Model database (with new table)
|
|
model := models.InitDatabase("testdb")
|
|
modelSchema := models.InitSchema("public")
|
|
|
|
table := models.InitTable("users", "public")
|
|
idCol := models.InitColumn("id", "users", "public")
|
|
idCol.Type = "integer"
|
|
idCol.NotNull = true
|
|
table.Columns["id"] = idCol
|
|
|
|
nameCol := models.InitColumn("name", "users", "public")
|
|
nameCol.Type = "text"
|
|
table.Columns["name"] = nameCol
|
|
|
|
modelSchema.Tables = append(modelSchema.Tables, table)
|
|
model.Schemas = append(model.Schemas, modelSchema)
|
|
|
|
// Generate migration
|
|
var buf bytes.Buffer
|
|
writer, err := NewMigrationWriter(&writers.WriterOptions{})
|
|
if err != nil {
|
|
t.Fatalf("Failed to create writer: %v", err)
|
|
}
|
|
writer.writer = &buf
|
|
|
|
err = writer.WriteMigration(model, current)
|
|
if err != nil {
|
|
t.Fatalf("WriteMigration failed: %v", err)
|
|
}
|
|
|
|
output := buf.String()
|
|
t.Logf("Generated migration:\n%s", output)
|
|
|
|
// Verify CREATE TABLE is present
|
|
if !strings.Contains(output, "CREATE TABLE") {
|
|
t.Error("Migration missing CREATE TABLE statement")
|
|
}
|
|
if !strings.Contains(output, "users") {
|
|
t.Error("Migration missing table name 'users'")
|
|
}
|
|
}
|
|
|
|
func TestWriteMigration_WithAudit(t *testing.T) {
|
|
// Current database (empty)
|
|
current := models.InitDatabase("testdb")
|
|
currentSchema := models.InitSchema("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"
|
|
idCol.IsPrimaryKey = true
|
|
table.Columns["id"] = idCol
|
|
|
|
nameCol := models.InitColumn("name", "users", "public")
|
|
nameCol.Type = "text"
|
|
table.Columns["name"] = nameCol
|
|
|
|
passwordCol := models.InitColumn("password", "users", "public")
|
|
passwordCol.Type = "text"
|
|
table.Columns["password"] = passwordCol
|
|
|
|
modelSchema.Tables = append(modelSchema.Tables, table)
|
|
model.Schemas = append(model.Schemas, modelSchema)
|
|
|
|
// Configure audit
|
|
auditConfig := NewAuditConfig()
|
|
auditConfig.AuditSchema = "public"
|
|
tableConfig := auditConfig.EnableTableAudit("public", "users")
|
|
tableConfig.EncryptedColumns = []string{"password"}
|
|
|
|
// Generate migration with audit
|
|
var buf bytes.Buffer
|
|
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)
|
|
if err != nil {
|
|
t.Fatalf("WriteMigration failed: %v", err)
|
|
}
|
|
|
|
output := buf.String()
|
|
t.Logf("Generated migration with audit:\n%s", output)
|
|
|
|
// Verify audit tables
|
|
if !strings.Contains(output, "CREATE TABLE IF NOT EXISTS public.atevent") {
|
|
t.Error("Migration missing atevent table")
|
|
}
|
|
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 TestTemplateExecutor_CreateTable(t *testing.T) {
|
|
executor, err := NewTemplateExecutor()
|
|
if err != nil {
|
|
t.Fatalf("Failed to create executor: %v", err)
|
|
}
|
|
|
|
data := CreateTableData{
|
|
SchemaName: "public",
|
|
TableName: "test_table",
|
|
Columns: []ColumnData{
|
|
{Name: "id", Type: "integer", NotNull: true},
|
|
{Name: "name", Type: "text", Default: "'unknown'"},
|
|
},
|
|
}
|
|
|
|
sql, err := executor.ExecuteCreateTable(data)
|
|
if err != nil {
|
|
t.Fatalf("Failed to execute template: %v", err)
|
|
}
|
|
|
|
t.Logf("Generated SQL:\n%s", sql)
|
|
|
|
if !strings.Contains(sql, "CREATE TABLE IF NOT EXISTS public.test_table") {
|
|
t.Error("SQL missing CREATE TABLE statement")
|
|
}
|
|
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 TestTemplateExecutor_AuditFunction(t *testing.T) {
|
|
executor, err := NewTemplateExecutor()
|
|
if err != nil {
|
|
t.Fatalf("Failed to create executor: %v", err)
|
|
}
|
|
|
|
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"},
|
|
},
|
|
}
|
|
|
|
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")
|
|
}
|
|
}
|