package json import ( "encoding/json" "os" "path/filepath" "testing" "git.warky.dev/wdevs/relspecgo/pkg/models" "git.warky.dev/wdevs/relspecgo/pkg/writers" ) func TestWriter_WriteTable(t *testing.T) { table := models.InitTable("users", "public") table.Description = "User accounts table" idCol := models.InitColumn("id", "users", "public") idCol.Type = "bigint" idCol.IsPrimaryKey = true idCol.AutoIncrement = true idCol.NotNull = true table.Columns["id"] = idCol emailCol := models.InitColumn("email", "users", "public") emailCol.Type = "varchar" emailCol.Length = 255 emailCol.NotNull = true emailCol.Comment = "User email address" table.Columns["email"] = emailCol tmpDir := t.TempDir() outputPath := filepath.Join(tmpDir, "test.json") opts := &writers.WriterOptions{ OutputPath: outputPath, } writer := NewWriter(opts) err := writer.WriteTable(table) if err != nil { t.Fatalf("WriteTable() error = %v", err) } content, err := os.ReadFile(outputPath) if err != nil { t.Fatalf("Failed to read output file: %v", err) } var result models.Table if err := json.Unmarshal(content, &result); err != nil { t.Fatalf("Failed to unmarshal JSON: %v", err) } if result.Name != "users" { t.Errorf("Expected table name 'users', got '%s'", result.Name) } if result.Description != "User accounts table" { t.Errorf("Expected description 'User accounts table', got '%s'", result.Description) } if len(result.Columns) != 2 { t.Errorf("Expected 2 columns, got %d", len(result.Columns)) } // Verify id column idColResult, exists := result.Columns["id"] if !exists { t.Fatal("Column 'id' not found") } if !idColResult.IsPrimaryKey { t.Error("Column 'id' should be primary key") } if !idColResult.AutoIncrement { t.Error("Column 'id' should be auto-increment") } // Verify email column emailColResult, exists := result.Columns["email"] if !exists { t.Fatal("Column 'email' not found") } if emailColResult.Length != 255 { t.Errorf("Expected email length 255, got %d", emailColResult.Length) } if emailColResult.Comment != "User email address" { t.Errorf("Expected email comment 'User email address', got '%s'", emailColResult.Comment) } } func TestWriter_WriteDatabase_WithRelationships(t *testing.T) { db := models.InitDatabase("test_db") db.Description = "Test database" db.DatabaseType = models.PostgresqlDatabaseType schema := models.InitSchema("public") // Create users table usersTable := models.InitTable("users", "public") idCol := models.InitColumn("id", "users", "public") idCol.Type = "bigint" idCol.IsPrimaryKey = true idCol.AutoIncrement = true idCol.NotNull = true usersTable.Columns["id"] = idCol emailCol := models.InitColumn("email", "users", "public") emailCol.Type = "varchar" emailCol.Length = 255 emailCol.NotNull = true usersTable.Columns["email"] = emailCol // Add index emailIdx := models.InitIndex("idx_users_email", "users", "public") emailIdx.Columns = []string{"email"} emailIdx.Unique = true emailIdx.Type = "btree" emailIdx.Table = "users" emailIdx.Schema = "public" usersTable.Indexes["idx_users_email"] = emailIdx // Create posts table postsTable := models.InitTable("posts", "public") postIdCol := models.InitColumn("id", "posts", "public") postIdCol.Type = "bigint" postIdCol.IsPrimaryKey = true postIdCol.NotNull = true postsTable.Columns["id"] = postIdCol userIdCol := models.InitColumn("user_id", "posts", "public") userIdCol.Type = "bigint" userIdCol.NotNull = true postsTable.Columns["user_id"] = userIdCol titleCol := models.InitColumn("title", "posts", "public") titleCol.Type = "varchar" titleCol.Length = 200 titleCol.NotNull = true postsTable.Columns["title"] = titleCol // Add foreign key constraint fk := models.InitConstraint("fk_posts_user", models.ForeignKeyConstraint) fk.Table = "posts" fk.Schema = "public" fk.Columns = []string{"user_id"} fk.ReferencedTable = "users" fk.ReferencedSchema = "public" fk.ReferencedColumns = []string{"id"} fk.OnDelete = "CASCADE" fk.OnUpdate = "CASCADE" postsTable.Constraints["fk_posts_user"] = fk schema.Tables = append(schema.Tables, usersTable, postsTable) db.Schemas = append(db.Schemas, schema) tmpDir := t.TempDir() outputPath := filepath.Join(tmpDir, "test.json") opts := &writers.WriterOptions{ OutputPath: outputPath, } writer := NewWriter(opts) err := writer.WriteDatabase(db) if err != nil { t.Fatalf("WriteDatabase() error = %v", err) } content, err := os.ReadFile(outputPath) if err != nil { t.Fatalf("Failed to read output file: %v", err) } var result models.Database if err := json.Unmarshal(content, &result); err != nil { t.Fatalf("Failed to unmarshal JSON: %v", err) } if result.Name != "test_db" { t.Errorf("Expected database name 'test_db', got '%s'", result.Name) } if result.DatabaseType != models.PostgresqlDatabaseType { t.Errorf("Expected database type 'pgsql', got '%s'", result.DatabaseType) } if len(result.Schemas) != 1 { t.Fatalf("Expected 1 schema, got %d", len(result.Schemas)) } resultSchema := result.Schemas[0] if len(resultSchema.Tables) != 2 { t.Fatalf("Expected 2 tables, got %d", len(resultSchema.Tables)) } // Find posts table and verify foreign key var postsTableResult *models.Table for _, table := range resultSchema.Tables { if table.Name == "posts" { postsTableResult = table break } } if postsTableResult == nil { t.Fatal("Posts table not found") } if len(postsTableResult.Constraints) != 1 { t.Errorf("Expected 1 constraint, got %d", len(postsTableResult.Constraints)) } fkResult, exists := postsTableResult.Constraints["fk_posts_user"] if !exists { t.Fatal("Foreign key 'fk_posts_user' not found") } if fkResult.Type != models.ForeignKeyConstraint { t.Error("Expected foreign key constraint type") } if fkResult.ReferencedTable != "users" { t.Errorf("Expected referenced table 'users', got '%s'", fkResult.ReferencedTable) } if fkResult.OnDelete != "CASCADE" { t.Errorf("Expected ON DELETE CASCADE, got '%s'", fkResult.OnDelete) } if fkResult.OnUpdate != "CASCADE" { t.Errorf("Expected ON UPDATE CASCADE, got '%s'", fkResult.OnUpdate) } // Verify index var usersTableResult *models.Table for _, table := range resultSchema.Tables { if table.Name == "users" { usersTableResult = table break } } if usersTableResult == nil { t.Fatal("Users table not found") } if len(usersTableResult.Indexes) != 1 { t.Errorf("Expected 1 index, got %d", len(usersTableResult.Indexes)) } idxResult, exists := usersTableResult.Indexes["idx_users_email"] if !exists { t.Fatal("Index 'idx_users_email' not found") } if !idxResult.Unique { t.Error("Email index should be unique") } if idxResult.Type != "btree" { t.Errorf("Expected index type 'btree', got '%s'", idxResult.Type) } } func TestWriter_WriteSchema(t *testing.T) { schema := models.InitSchema("public") schema.Description = "Public schema" table := models.InitTable("users", "public") idCol := models.InitColumn("id", "users", "public") idCol.Type = "bigint" idCol.IsPrimaryKey = true table.Columns["id"] = idCol usernameCol := models.InitColumn("username", "users", "public") usernameCol.Type = "varchar" usernameCol.Length = 50 usernameCol.NotNull = true table.Columns["username"] = usernameCol schema.Tables = append(schema.Tables, table) tmpDir := t.TempDir() outputPath := filepath.Join(tmpDir, "test.json") opts := &writers.WriterOptions{ OutputPath: outputPath, } writer := NewWriter(opts) err := writer.WriteSchema(schema) if err != nil { t.Fatalf("WriteSchema() error = %v", err) } content, err := os.ReadFile(outputPath) if err != nil { t.Fatalf("Failed to read output file: %v", err) } var result models.Schema if err := json.Unmarshal(content, &result); err != nil { t.Fatalf("Failed to unmarshal JSON: %v", err) } if result.Name != "public" { t.Errorf("Expected schema name 'public', got '%s'", result.Name) } if result.Description != "Public schema" { t.Errorf("Expected description 'Public schema', got '%s'", result.Description) } if len(result.Tables) != 1 { t.Errorf("Expected 1 table, got %d", len(result.Tables)) } } func TestWriter_WriteTable_EmptyPath(t *testing.T) { table := models.InitTable("users", "public") idCol := models.InitColumn("id", "users", "public") idCol.Type = "bigint" table.Columns["id"] = idCol // When OutputPath is empty, it should print to stdout (not error) opts := &writers.WriterOptions{ OutputPath: "", } writer := NewWriter(opts) err := writer.WriteTable(table) if err != nil { t.Fatalf("WriteTable() with empty path should not error, got: %v", err) } } func TestWriter_WriteDatabase_WithDefaults(t *testing.T) { db := models.InitDatabase("test_db") schema := models.InitSchema("public") table := models.InitTable("products", "public") idCol := models.InitColumn("id", "products", "public") idCol.Type = "bigint" idCol.IsPrimaryKey = true table.Columns["id"] = idCol isActiveCol := models.InitColumn("is_active", "products", "public") isActiveCol.Type = "boolean" isActiveCol.Default = "true" table.Columns["is_active"] = isActiveCol createdCol := models.InitColumn("created_at", "products", "public") createdCol.Type = "timestamp" createdCol.Default = "CURRENT_TIMESTAMP" table.Columns["created_at"] = createdCol schema.Tables = append(schema.Tables, table) db.Schemas = append(db.Schemas, schema) tmpDir := t.TempDir() outputPath := filepath.Join(tmpDir, "test.json") opts := &writers.WriterOptions{ OutputPath: outputPath, } writer := NewWriter(opts) err := writer.WriteDatabase(db) if err != nil { t.Fatalf("WriteDatabase() error = %v", err) } content, err := os.ReadFile(outputPath) if err != nil { t.Fatalf("Failed to read output file: %v", err) } var result models.Database if err := json.Unmarshal(content, &result); err != nil { t.Fatalf("Failed to unmarshal JSON: %v", err) } tableResult := result.Schemas[0].Tables[0] // Verify default values isActiveColResult, exists := tableResult.Columns["is_active"] if !exists { t.Fatal("Column 'is_active' not found") } if isActiveColResult.Default != "true" { t.Errorf("Expected is_active default 'true', got '%v'", isActiveColResult.Default) } createdColResult, exists := tableResult.Columns["created_at"] if !exists { t.Fatal("Column 'created_at' not found") } if createdColResult.Default != "CURRENT_TIMESTAMP" { t.Errorf("Expected created_at default 'CURRENT_TIMESTAMP', got '%v'", createdColResult.Default) } } func TestGetPrimaryKey(t *testing.T) { table := models.InitTable("users", "public") idCol := models.InitColumn("id", "users", "public") idCol.Type = "bigint" idCol.IsPrimaryKey = true table.Columns["id"] = idCol emailCol := models.InitColumn("email", "users", "public") emailCol.Type = "varchar" table.Columns["email"] = emailCol pk := table.GetPrimaryKey() if pk == nil { t.Fatal("Expected primary key, got nil") } if pk.Name != "id" { t.Errorf("Expected primary key name 'id', got '%s'", pk.Name) } } func TestGetForeignKeys(t *testing.T) { table := models.InitTable("posts", "public") idCol := models.InitColumn("id", "posts", "public") idCol.Type = "bigint" idCol.IsPrimaryKey = true table.Columns["id"] = idCol userIdCol := models.InitColumn("user_id", "posts", "public") userIdCol.Type = "bigint" table.Columns["user_id"] = userIdCol // Add foreign key constraint fk := models.InitConstraint("fk_posts_user", models.ForeignKeyConstraint) fk.Columns = []string{"user_id"} fk.ReferencedTable = "users" fk.ReferencedColumns = []string{"id"} table.Constraints["fk_posts_user"] = fk fks := table.GetForeignKeys() if len(fks) != 1 { t.Errorf("Expected 1 foreign key, got %d", len(fks)) } if fks[0].Type != models.ForeignKeyConstraint { t.Error("Expected foreign key constraint type") } }