package sqlexec import ( "testing" "git.warky.dev/wdevs/relspecgo/pkg/models" "git.warky.dev/wdevs/relspecgo/pkg/writers" ) func TestNewWriter(t *testing.T) { opts := &writers.WriterOptions{ Metadata: map[string]any{ "connection_string": "postgres://localhost/test", }, } writer := NewWriter(opts) if writer == nil { t.Fatal("Expected non-nil writer") } if writer.options != opts { t.Error("Writer options not set correctly") } } func TestWriter_WriteDatabase_NilDatabase(t *testing.T) { writer := NewWriter(&writers.WriterOptions{ Metadata: map[string]any{ "connection_string": "postgres://localhost/test", }, }) err := writer.WriteDatabase(nil) if err == nil { t.Error("Expected error for nil database, got nil") } } func TestWriter_WriteDatabase_MissingConnectionString(t *testing.T) { writer := NewWriter(&writers.WriterOptions{ Metadata: map[string]any{}, }) db := &models.Database{ Name: "test", Schemas: []*models.Schema{ { Name: "public", Scripts: []*models.Script{ {Name: "test", SQL: "SELECT 1;"}, }, }, }, } err := writer.WriteDatabase(db) if err == nil { t.Error("Expected error for missing connection_string, got nil") } } func TestWriter_WriteSchema_NilSchema(t *testing.T) { writer := NewWriter(&writers.WriterOptions{ Metadata: map[string]any{ "connection_string": "postgres://localhost/test", }, }) err := writer.WriteSchema(nil) if err == nil { t.Error("Expected error for nil schema, got nil") } } func TestWriter_WriteSchema_MissingConnectionString(t *testing.T) { writer := NewWriter(&writers.WriterOptions{ Metadata: map[string]any{}, }) schema := &models.Schema{ Name: "public", Scripts: []*models.Script{ {Name: "test", SQL: "SELECT 1;"}, }, } err := writer.WriteSchema(schema) if err == nil { t.Error("Expected error for missing connection_string, got nil") } } func TestWriter_WriteTable(t *testing.T) { writer := NewWriter(&writers.WriterOptions{}) err := writer.WriteTable(&models.Table{}) if err == nil { t.Error("Expected error for WriteTable (not supported), got nil") } } // TestScriptSorting verifies that scripts are sorted correctly by Priority, Sequence, then Name func TestScriptSorting(t *testing.T) { scripts := []*models.Script{ {Name: "z_script1", Priority: 2, Sequence: 1, SQL: "SELECT 1;"}, {Name: "script2", Priority: 1, Sequence: 3, SQL: "SELECT 2;"}, {Name: "a_script3", Priority: 1, Sequence: 1, SQL: "SELECT 3;"}, {Name: "b_script4", Priority: 1, Sequence: 1, SQL: "SELECT 4;"}, {Name: "script5", Priority: 3, Sequence: 1, SQL: "SELECT 5;"}, {Name: "script6", Priority: 2, Sequence: 2, SQL: "SELECT 6;"}, } // Create a copy and sort it using the same logic as executeScripts sortedScripts := make([]*models.Script, len(scripts)) copy(sortedScripts, scripts) // Sort by Priority, Sequence, then Name (matching executeScripts logic) for i := 0; i < len(sortedScripts)-1; i++ { for j := i + 1; j < len(sortedScripts); j++ { si, sj := sortedScripts[i], sortedScripts[j] // Compare by priority first if si.Priority > sj.Priority { sortedScripts[i], sortedScripts[j] = sortedScripts[j], sortedScripts[i] } else if si.Priority == sj.Priority { // If same priority, compare by sequence if si.Sequence > sj.Sequence { sortedScripts[i], sortedScripts[j] = sortedScripts[j], sortedScripts[i] } else if si.Sequence == sj.Sequence { // If same sequence, compare by name if si.Name > sj.Name { sortedScripts[i], sortedScripts[j] = sortedScripts[j], sortedScripts[i] } } } } } // Expected order after sorting (Priority -> Sequence -> Name) expectedOrder := []string{ "a_script3", // Priority 1, Sequence 1, Name a_script3 "b_script4", // Priority 1, Sequence 1, Name b_script4 "script2", // Priority 1, Sequence 3 "z_script1", // Priority 2, Sequence 1 "script6", // Priority 2, Sequence 2 "script5", // Priority 3, Sequence 1 } for i, expected := range expectedOrder { if sortedScripts[i].Name != expected { t.Errorf("Position %d: expected %s, got %s", i, expected, sortedScripts[i].Name) } } // Verify priorities are ascending for i := 0; i < len(sortedScripts)-1; i++ { if sortedScripts[i].Priority > sortedScripts[i+1].Priority { t.Errorf("Priority not ascending at position %d: %d > %d", i, sortedScripts[i].Priority, sortedScripts[i+1].Priority) } // Within same priority, sequences should be ascending if sortedScripts[i].Priority == sortedScripts[i+1].Priority && sortedScripts[i].Sequence > sortedScripts[i+1].Sequence { t.Errorf("Sequence not ascending at position %d with same priority %d: %d > %d", i, sortedScripts[i].Priority, sortedScripts[i].Sequence, sortedScripts[i+1].Sequence) } // Within same priority and sequence, names should be ascending if sortedScripts[i].Priority == sortedScripts[i+1].Priority && sortedScripts[i].Sequence == sortedScripts[i+1].Sequence && sortedScripts[i].Name > sortedScripts[i+1].Name { t.Errorf("Name not ascending at position %d with same priority/sequence: %s > %s", i, sortedScripts[i].Name, sortedScripts[i+1].Name) } } } func TestWriter_WriteSchema_EmptyScripts(t *testing.T) { // This test verifies that writing an empty script list doesn't cause errors // even without a database connection (should return early) writer := NewWriter(&writers.WriterOptions{ Metadata: map[string]any{ "connection_string": "postgres://invalid/test", }, }) schema := &models.Schema{ Name: "public", Scripts: []*models.Script{}, } // Note: This will try to connect even with empty scripts // In a real scenario, the executeScripts function returns early for empty scripts // but the connection is made before that. This test documents the behavior. err := writer.WriteSchema(schema) // We expect a connection error since we're using an invalid connection string if err == nil { t.Error("Expected connection error, got nil") } } // NOTE: Integration tests for actual database execution should be added separately // Those tests would require: // 1. A running PostgreSQL instance // 2. Test database setup/teardown // 3. Verification of actual script execution // 4. Testing error handling during execution // 5. Testing transaction behavior if added // // Example integration test structure: // func TestWriter_Integration_ExecuteScripts(t *testing.T) { // if testing.Short() { // t.Skip("Skipping integration test") // } // // Setup test database // // Create test scripts // // Execute scripts // // Verify results // // Cleanup // }