From 6a6c993a856684d955cfde09f1f30a792cbe58e0 Mon Sep 17 00:00:00 2001 From: Mesut GENEZ Date: Sat, 28 Dec 2024 00:28:13 +0300 Subject: [PATCH] tests --- tests/benchmark/benchmark_test.go | 184 +++++++++++++++++++++++++++ tests/integration/stream_test.go | 200 ++++++++++++++++++++++++++++++ 2 files changed, 384 insertions(+) create mode 100644 tests/benchmark/benchmark_test.go create mode 100644 tests/integration/stream_test.go diff --git a/tests/benchmark/benchmark_test.go b/tests/benchmark/benchmark_test.go new file mode 100644 index 0000000..1b9d72e --- /dev/null +++ b/tests/benchmark/benchmark_test.go @@ -0,0 +1,184 @@ +package benchmark + +import ( + "testing" + + "github.com/mstgnz/sqlmapper/mysql" + "github.com/mstgnz/sqlmapper/postgres" + "github.com/mstgnz/sqlmapper/sqlite" +) + +var complexMySQLSchema = ` +CREATE TABLE users ( + id INT AUTO_INCREMENT PRIMARY KEY, + name VARCHAR(100) NOT NULL, + email VARCHAR(255) UNIQUE, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP +); + +CREATE TABLE categories ( + id INT AUTO_INCREMENT PRIMARY KEY, + name VARCHAR(100) NOT NULL, + description TEXT, + parent_id INT, + FOREIGN KEY (parent_id) REFERENCES categories(id) +); + +CREATE TABLE products ( + id INT AUTO_INCREMENT PRIMARY KEY, + category_id INT NOT NULL, + name VARCHAR(200) NOT NULL, + description TEXT, + price DECIMAL(10,2) NOT NULL, + stock INT DEFAULT 0, + status ENUM('active', 'inactive', 'deleted') DEFAULT 'active', + metadata JSON, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + FOREIGN KEY (category_id) REFERENCES categories(id) +); + +CREATE TABLE orders ( + id INT AUTO_INCREMENT PRIMARY KEY, + user_id INT NOT NULL, + total_amount DECIMAL(12,2) NOT NULL, + status VARCHAR(50) DEFAULT 'pending', + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + FOREIGN KEY (user_id) REFERENCES users(id) +); + +CREATE TABLE order_items ( + id INT AUTO_INCREMENT PRIMARY KEY, + order_id INT NOT NULL, + product_id INT NOT NULL, + quantity INT NOT NULL, + price DECIMAL(10,2) NOT NULL, + FOREIGN KEY (order_id) REFERENCES orders(id), + FOREIGN KEY (product_id) REFERENCES products(id) +); + +CREATE INDEX idx_user_email ON users(email); +CREATE INDEX idx_category_parent ON categories(parent_id); +CREATE INDEX idx_product_category ON products(category_id); +CREATE INDEX idx_product_status ON products(status); +CREATE FULLTEXT INDEX idx_product_search ON products(name, description); + +CREATE VIEW active_products AS +SELECT p.*, c.name as category_name +FROM products p +JOIN categories c ON p.category_id = c.id +WHERE p.status = 'active'; + +DELIMITER // +CREATE TRIGGER update_product_timestamp +BEFORE UPDATE ON products +FOR EACH ROW +BEGIN + SET NEW.updated_at = CURRENT_TIMESTAMP; +END// +DELIMITER ; +` + +func BenchmarkMySQLToPostgreSQL(b *testing.B) { + mysqlParser := mysql.NewMySQL() + pgParser := postgres.NewPostgreSQL() + + b.ResetTimer() + for i := 0; i < b.N; i++ { + schema, err := mysqlParser.Parse(complexMySQLSchema) + if err != nil { + b.Fatal(err) + } + + _, err = pgParser.Generate(schema) + if err != nil { + b.Fatal(err) + } + } +} + +func BenchmarkMySQLToSQLite(b *testing.B) { + mysqlParser := mysql.NewMySQL() + sqliteParser := sqlite.NewSQLite() + + b.ResetTimer() + for i := 0; i < b.N; i++ { + schema, err := mysqlParser.Parse(complexMySQLSchema) + if err != nil { + b.Fatal(err) + } + + _, err = sqliteParser.Generate(schema) + if err != nil { + b.Fatal(err) + } + } +} + +func BenchmarkPostgreSQLToMySQL(b *testing.B) { + pgParser := postgres.NewPostgreSQL() + mysqlParser := mysql.NewMySQL() + + // Convert MySQL schema to PostgreSQL first + schema, err := mysql.NewMySQL().Parse(complexMySQLSchema) + if err != nil { + b.Fatal(err) + } + + pgSQL, err := pgParser.Generate(schema) + if err != nil { + b.Fatal(err) + } + + b.ResetTimer() + for i := 0; i < b.N; i++ { + schema, err := pgParser.Parse(pgSQL) + if err != nil { + b.Fatal(err) + } + + _, err = mysqlParser.Generate(schema) + if err != nil { + b.Fatal(err) + } + } +} + +func BenchmarkSQLiteToMySQL(b *testing.B) { + sqliteParser := sqlite.NewSQLite() + mysqlParser := mysql.NewMySQL() + + // Use a simpler schema for SQLite + simpleSQLiteSchema := ` +CREATE TABLE users ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + name TEXT NOT NULL, + email TEXT UNIQUE, + created_at TEXT DEFAULT CURRENT_TIMESTAMP +); + +CREATE TABLE products ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + name TEXT NOT NULL, + price REAL NOT NULL, + stock INTEGER DEFAULT 0 +); + +CREATE VIEW active_products AS +SELECT * FROM products WHERE stock > 0; +` + + b.ResetTimer() + for i := 0; i < b.N; i++ { + schema, err := sqliteParser.Parse(simpleSQLiteSchema) + if err != nil { + b.Fatal(err) + } + + _, err = mysqlParser.Generate(schema) + if err != nil { + b.Fatal(err) + } + } +} diff --git a/tests/integration/stream_test.go b/tests/integration/stream_test.go new file mode 100644 index 0000000..5d91657 --- /dev/null +++ b/tests/integration/stream_test.go @@ -0,0 +1,200 @@ +package integration + +import ( + "bytes" + "strings" + "testing" + + "github.com/mstgnz/sqlmapper" + "github.com/mstgnz/sqlmapper/mysql" + "github.com/mstgnz/sqlmapper/postgres" + "github.com/stretchr/testify/assert" +) + +func TestMySQLStreamParser(t *testing.T) { + parser := mysql.NewMySQLStreamParser() + + // Test input + input := ` +CREATE TABLE users ( + id INT AUTO_INCREMENT PRIMARY KEY, + name VARCHAR(255) NOT NULL, + email VARCHAR(255) UNIQUE, + status ENUM('active', 'inactive') DEFAULT 'active', + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP +); + +CREATE VIEW active_users AS +SELECT * FROM users WHERE status = 'active'; + +CREATE FUNCTION get_user_status(user_id INT) +RETURNS VARCHAR(50) +BEGIN + DECLARE status VARCHAR(50); + SELECT status INTO status FROM users WHERE id = user_id; + RETURN status; +END; + +CREATE TRIGGER before_user_update +BEFORE UPDATE ON users +FOR EACH ROW +BEGIN + SET NEW.updated_at = CURRENT_TIMESTAMP; +END; +` + + // Test parsing + var objects []sqlmapper.SchemaObject + err := parser.ParseStream(strings.NewReader(input), func(obj sqlmapper.SchemaObject) error { + objects = append(objects, obj) + return nil + }) + + assert.NoError(t, err) + assert.Equal(t, 4, len(objects)) + + // Test table object + assert.Equal(t, sqlmapper.TableObject, objects[0].Type) + table := objects[0].Data.(*sqlmapper.Table) + assert.Equal(t, "users", table.Name) + assert.Equal(t, 5, len(table.Columns)) + + // Test view object + assert.Equal(t, sqlmapper.ViewObject, objects[1].Type) + view := objects[1].Data.(*sqlmapper.View) + assert.Equal(t, "active_users", view.Name) + + // Test function object + assert.Equal(t, sqlmapper.FunctionObject, objects[2].Type) + function := objects[2].Data.(*sqlmapper.Function) + assert.Equal(t, "get_user_status", function.Name) + + // Test trigger object + assert.Equal(t, sqlmapper.TriggerObject, objects[3].Type) + trigger := objects[3].Data.(*sqlmapper.Trigger) + assert.Equal(t, "before_user_update", trigger.Name) + + // Test generation + var output bytes.Buffer + schema := &sqlmapper.Schema{ + Tables: []sqlmapper.Table{*table}, + Views: []sqlmapper.View{*view}, + Functions: []sqlmapper.Function{*function}, + Triggers: []sqlmapper.Trigger{*trigger}, + } + + err = parser.GenerateStream(schema, &output) + assert.NoError(t, err) + assert.Contains(t, output.String(), "CREATE TABLE users") + assert.Contains(t, output.String(), "CREATE VIEW active_users") + assert.Contains(t, output.String(), "CREATE FUNCTION get_user_status") + assert.Contains(t, output.String(), "CREATE TRIGGER before_user_update") +} + +func TestPostgreSQLStreamParser(t *testing.T) { + parser := postgres.NewPostgreSQLStreamParser() + + // Test input + input := ` +CREATE TYPE user_status AS ENUM ('active', 'inactive'); + +CREATE TABLE users ( + id SERIAL PRIMARY KEY, + name VARCHAR(255) NOT NULL, + email VARCHAR(255) UNIQUE, + status user_status DEFAULT 'active', + created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP +); + +CREATE INDEX idx_users_email ON users USING btree (email); + +CREATE MATERIALIZED VIEW active_users AS +SELECT * FROM users WHERE status = 'active' +WITH DATA; + +CREATE FUNCTION get_user_status(user_id INTEGER) +RETURNS user_status +LANGUAGE plpgsql +AS $$ +DECLARE + status user_status; +BEGIN + SELECT status INTO status FROM users WHERE id = user_id; + RETURN status; +END; +$$; + +CREATE TRIGGER before_user_update + BEFORE UPDATE ON users + FOR EACH ROW + EXECUTE FUNCTION update_timestamp(); + +GRANT SELECT, INSERT ON users TO app_user; +` + + // Test parsing + var objects []sqlmapper.SchemaObject + err := parser.ParseStream(strings.NewReader(input), func(obj sqlmapper.SchemaObject) error { + objects = append(objects, obj) + return nil + }) + + assert.NoError(t, err) + assert.Equal(t, 7, len(objects)) + + // Test type object + assert.Equal(t, sqlmapper.TypeObject, objects[0].Type) + typ := objects[0].Data.(*sqlmapper.Type) + assert.Equal(t, "user_status", typ.Name) + + // Test table object + assert.Equal(t, sqlmapper.TableObject, objects[1].Type) + table := objects[1].Data.(*sqlmapper.Table) + assert.Equal(t, "users", table.Name) + assert.Equal(t, 5, len(table.Columns)) + + // Test index object + assert.Equal(t, sqlmapper.IndexObject, objects[2].Type) + index := objects[2].Data.(*sqlmapper.Index) + assert.Equal(t, "idx_users_email", index.Name) + + // Test view object + assert.Equal(t, sqlmapper.ViewObject, objects[3].Type) + view := objects[3].Data.(*sqlmapper.View) + assert.Equal(t, "active_users", view.Name) + assert.True(t, view.IsMaterialized) + + // Test function object + assert.Equal(t, sqlmapper.FunctionObject, objects[4].Type) + function := objects[4].Data.(*sqlmapper.Function) + assert.Equal(t, "get_user_status", function.Name) + + // Test trigger object + assert.Equal(t, sqlmapper.TriggerObject, objects[5].Type) + trigger := objects[5].Data.(*sqlmapper.Trigger) + assert.Equal(t, "before_user_update", trigger.Name) + + // Test permission object + assert.Equal(t, sqlmapper.PermissionObject, objects[6].Type) + permission := objects[6].Data.(*sqlmapper.Permission) + assert.Equal(t, "GRANT", permission.Type) + + // Test generation + var output bytes.Buffer + schema := &sqlmapper.Schema{ + Types: []sqlmapper.Type{*typ}, + Tables: []sqlmapper.Table{*table}, + Views: []sqlmapper.View{*view}, + Functions: []sqlmapper.Function{*function}, + Triggers: []sqlmapper.Trigger{*trigger}, + Permissions: []sqlmapper.Permission{*permission}, + } + + err = parser.GenerateStream(schema, &output) + assert.NoError(t, err) + assert.Contains(t, output.String(), "CREATE TYPE user_status") + assert.Contains(t, output.String(), "CREATE TABLE users") + assert.Contains(t, output.String(), "CREATE MATERIALIZED VIEW active_users") + assert.Contains(t, output.String(), "CREATE FUNCTION get_user_status") + assert.Contains(t, output.String(), "CREATE TRIGGER before_user_update") +}