Files
migrate/sqlite_test.go
Colin Henry b4529e018f Restructure dialects and add PostgreSQL support
Architecture Changes:
- Split dialect implementations into separate files for better organization
- Move SQLite dialect from dialect.go to sqlite.go
- Keep only Dialect interface in dialect.go with comprehensive documentation
- Each dialect now in its own file following single responsibility principle

New Features:
- Add PostgreSQL dialect support (Postgres() function)
- PostgreSQL uses SERIAL PRIMARY KEY (auto-incrementing integer)
- PostgreSQL uses $1, $2 placeholders instead of ? for parameters
- PostgreSQL uses SELECT 1 for table existence check (more efficient)
- Both dialects implement proper SQL identifier quoting for security

Testing:
- Add comprehensive dialect-specific tests in sqlite_test.go
- Add comprehensive dialect-specific tests in postgres_test.go
- Test SQL generation for all dialect methods
- Test SQL injection protection via identifier escaping
- All tests pass (8 test functions, 10 subtests)

Documentation:
- Update README with PostgreSQL usage example
- Add "Supported Databases" section listing SQLite and PostgreSQL
- Improve code examples with proper imports and error handling
- Document how to implement Dialect interface for other databases

File Structure:
- dialect.go: Interface definition only (18 lines)
- sqlite.go: SQLite dialect implementation (39 lines)
- postgres.go: PostgreSQL dialect implementation (42 lines)
- sqlite_test.go: SQLite dialect tests (67 lines)
- postgres_test.go: PostgreSQL dialect tests (67 lines)

Security:
- Both dialects use quoteIdentifier() for SQL injection protection
- Identifiers are quoted and internal quotes are escaped
- Follows SQL standard quoting mechanism (double quotes for escaping)

This change maintains backward compatibility while adding PostgreSQL support
and improving code organization for future dialect additions.
2026-01-18 01:34:09 -08:00

68 lines
1.8 KiB
Go

package migrate
import (
"strings"
"testing"
)
func TestSqlite3Dialect(t *testing.T) {
sl3 := Sqlite3()
t.Run("CreateTable", func(t *testing.T) {
sql := sl3.CreateTable("dbversion")
if !strings.Contains(sql, "INTEGER PRIMARY KEY AUTOINCREMENT") {
t.Errorf("Expected INTEGER PRIMARY KEY AUTOINCREMENT, got: %s", sql)
}
if !strings.Contains(sql, `"dbversion"`) {
t.Errorf("Expected quoted table name, got: %s", sql)
}
})
t.Run("TableExists", func(t *testing.T) {
sql := sl3.TableExists("dbversion")
if !strings.Contains(sql, `"dbversion"`) {
t.Errorf("Expected quoted table name, got: %s", sql)
}
if !strings.Contains(sql, "SELECT *") {
t.Errorf("Expected SELECT *, got: %s", sql)
}
})
t.Run("CheckVersion", func(t *testing.T) {
sql := sl3.CheckVersion("dbversion")
if !strings.Contains(sql, "ORDER BY id DESC") {
t.Errorf("Expected ORDER BY id DESC, got: %s", sql)
}
if !strings.Contains(sql, "LIMIT 0, 1") {
t.Errorf("Expected LIMIT 0, 1, got: %s", sql)
}
})
t.Run("InsertVersion", func(t *testing.T) {
sql := sl3.InsertVersion("dbversion")
// SQLite uses ? placeholders
if !strings.Contains(sql, "VALUES (?,?)") {
t.Errorf("Expected SQLite placeholders (?,?), got: %s", sql)
}
if !strings.Contains(sql, `"dbversion"`) {
t.Errorf("Expected quoted table name, got: %s", sql)
}
})
t.Run("QuoteIdentifier", func(t *testing.T) {
sl3 := sqlite3{}
// Test normal identifier
quoted := sl3.quoteIdentifier("tablename")
if quoted != `"tablename"` {
t.Errorf("Expected quoted identifier, got: %s", quoted)
}
// Test identifier with quotes (SQL injection attempt)
quoted = sl3.quoteIdentifier(`table"; DROP TABLE users; --`)
if !strings.Contains(quoted, `""`) {
t.Errorf("Expected escaped quotes, got: %s", quoted)
}
})
}