All checks were successful
CI / Test (1.24) (push) Successful in -27m13s
CI / Test (1.25) (push) Successful in -27m5s
CI / Build (push) Successful in -27m16s
CI / Lint (push) Successful in -27m0s
Integration Tests / Integration Tests (push) Successful in -27m14s
Release / Build and Release (push) Successful in -25m52s
* Implement directory reading for DBML files. * Merge schemas and tables from multiple files. * Add tests for multi-file loading and merging behavior. * Enhance file discovery and sorting logic.
804 lines
21 KiB
Go
804 lines
21 KiB
Go
package dbml
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
|
|
"git.warky.dev/wdevs/relspecgo/pkg/models"
|
|
"git.warky.dev/wdevs/relspecgo/pkg/readers"
|
|
)
|
|
|
|
func TestReader_ReadDatabase_Simple(t *testing.T) {
|
|
opts := &readers.ReaderOptions{
|
|
FilePath: filepath.Join("..", "..", "..", "tests", "assets", "dbml", "simple.dbml"),
|
|
}
|
|
|
|
reader := NewReader(opts)
|
|
db, err := reader.ReadDatabase()
|
|
if err != nil {
|
|
t.Fatalf("ReadDatabase() error = %v", err)
|
|
}
|
|
|
|
if db == nil {
|
|
t.Fatal("ReadDatabase() returned nil database")
|
|
}
|
|
|
|
if len(db.Schemas) == 0 {
|
|
t.Fatal("Expected at least one schema")
|
|
}
|
|
|
|
schema := db.Schemas[0]
|
|
if schema.Name != "public" {
|
|
t.Errorf("Expected schema name 'public', got '%s'", schema.Name)
|
|
}
|
|
|
|
if len(schema.Tables) != 1 {
|
|
t.Fatalf("Expected 1 table, got %d", len(schema.Tables))
|
|
}
|
|
|
|
table := schema.Tables[0]
|
|
if table.Name != "users" {
|
|
t.Errorf("Expected table name 'users', got '%s'", table.Name)
|
|
}
|
|
|
|
if len(table.Columns) != 4 {
|
|
t.Errorf("Expected 4 columns, got %d", len(table.Columns))
|
|
}
|
|
|
|
// Verify id column
|
|
idCol, exists := table.Columns["id"]
|
|
if !exists {
|
|
t.Fatal("Column 'id' not found")
|
|
}
|
|
if !idCol.IsPrimaryKey {
|
|
t.Error("Column 'id' should be primary key")
|
|
}
|
|
if !idCol.AutoIncrement {
|
|
t.Error("Column 'id' should be auto-increment")
|
|
}
|
|
if !idCol.NotNull {
|
|
t.Error("Column 'id' should be not null")
|
|
}
|
|
if idCol.Type != "bigint" {
|
|
t.Errorf("Expected id type 'bigint', got '%s'", idCol.Type)
|
|
}
|
|
|
|
// Verify email column
|
|
emailCol, exists := table.Columns["email"]
|
|
if !exists {
|
|
t.Fatal("Column 'email' not found")
|
|
}
|
|
if !emailCol.NotNull {
|
|
t.Error("Column 'email' should be not null")
|
|
}
|
|
if emailCol.Type != "varchar(255)" {
|
|
t.Errorf("Expected email type 'varchar(255)', got '%s'", emailCol.Type)
|
|
}
|
|
|
|
// Verify default value
|
|
createdCol, exists := table.Columns["created_at"]
|
|
if !exists {
|
|
t.Fatal("Column 'created_at' not found")
|
|
}
|
|
if createdCol.Default == nil {
|
|
t.Error("Expected default value for created_at")
|
|
}
|
|
}
|
|
|
|
func TestReader_ReadDatabase_Complex(t *testing.T) {
|
|
opts := &readers.ReaderOptions{
|
|
FilePath: filepath.Join("..", "..", "..", "tests", "assets", "dbml", "complex.dbml"),
|
|
}
|
|
|
|
reader := NewReader(opts)
|
|
db, err := reader.ReadDatabase()
|
|
if err != nil {
|
|
t.Fatalf("ReadDatabase() error = %v", err)
|
|
}
|
|
|
|
if db == nil {
|
|
t.Fatal("ReadDatabase() returned nil database")
|
|
}
|
|
|
|
// Verify multiple schemas
|
|
if len(db.Schemas) != 2 {
|
|
t.Fatalf("Expected 2 schemas, got %d", len(db.Schemas))
|
|
}
|
|
|
|
// Find public schema
|
|
var publicSchema *models.Schema
|
|
var adminSchema *models.Schema
|
|
for _, schema := range db.Schemas {
|
|
if schema.Name == "public" {
|
|
publicSchema = schema
|
|
} else if schema.Name == "admin" {
|
|
adminSchema = schema
|
|
}
|
|
}
|
|
|
|
if publicSchema == nil {
|
|
t.Fatal("Public schema not found")
|
|
}
|
|
if adminSchema == nil {
|
|
t.Fatal("Admin schema not found")
|
|
}
|
|
|
|
// Verify public schema has 3 tables
|
|
if len(publicSchema.Tables) != 3 {
|
|
t.Errorf("Expected 3 tables in public schema, got %d", len(publicSchema.Tables))
|
|
}
|
|
|
|
// Find tables
|
|
var usersTable, postsTable, commentsTable *models.Table
|
|
for _, table := range publicSchema.Tables {
|
|
switch table.Name {
|
|
case "users":
|
|
usersTable = table
|
|
case "posts":
|
|
postsTable = table
|
|
case "comments":
|
|
commentsTable = table
|
|
}
|
|
}
|
|
|
|
if usersTable == nil {
|
|
t.Fatal("Users table not found")
|
|
}
|
|
if postsTable == nil {
|
|
t.Fatal("Posts table not found")
|
|
}
|
|
if commentsTable == nil {
|
|
t.Fatal("Comments table not found")
|
|
}
|
|
|
|
// Verify users table has indexes
|
|
if len(usersTable.Indexes) != 2 {
|
|
t.Errorf("Expected 2 indexes on users table, got %d", len(usersTable.Indexes))
|
|
}
|
|
|
|
// Verify named index
|
|
emailIdx, exists := usersTable.Indexes["idx_users_email"]
|
|
if !exists {
|
|
t.Error("Index 'idx_users_email' not found")
|
|
} else {
|
|
if !emailIdx.Unique {
|
|
t.Error("Email index should be unique")
|
|
}
|
|
if len(emailIdx.Columns) != 1 || emailIdx.Columns[0] != "email" {
|
|
t.Error("Email index should have 'email' column")
|
|
}
|
|
}
|
|
|
|
// Verify table description (DBML Note field maps to Description)
|
|
// Note: The description is only set if the DBML reader properly parses the Note
|
|
if usersTable.Description == "" {
|
|
t.Log("Warning: users table description is empty (Note field may not be parsed)")
|
|
}
|
|
|
|
// Verify posts table columns
|
|
if len(postsTable.Columns) != 9 {
|
|
t.Errorf("Expected 9 columns in posts table, got %d", len(postsTable.Columns))
|
|
}
|
|
|
|
// Verify slug column is unique
|
|
slugCol, exists := postsTable.Columns["slug"]
|
|
if !exists {
|
|
t.Fatal("Column 'slug' not found in posts table")
|
|
}
|
|
if !slugCol.NotNull {
|
|
t.Error("Slug column should be not null")
|
|
}
|
|
|
|
// Verify default values
|
|
publishedCol, exists := postsTable.Columns["published"]
|
|
if !exists {
|
|
t.Fatal("Column 'published' not found")
|
|
}
|
|
if publishedCol.Default != "false" {
|
|
t.Errorf("Expected published default 'false', got '%v'", publishedCol.Default)
|
|
}
|
|
|
|
viewCountCol, exists := postsTable.Columns["view_count"]
|
|
if !exists {
|
|
t.Fatal("Column 'view_count' not found")
|
|
}
|
|
if viewCountCol.Default != "0" {
|
|
t.Errorf("Expected view_count default '0', got '%v'", viewCountCol.Default)
|
|
}
|
|
|
|
// Verify posts indexes
|
|
if len(postsTable.Indexes) != 3 {
|
|
t.Errorf("Expected 3 indexes on posts table, got %d", len(postsTable.Indexes))
|
|
}
|
|
|
|
// Verify btree index type
|
|
found := false
|
|
for _, idx := range postsTable.Indexes {
|
|
if idx.Type == "btree" {
|
|
found = true
|
|
break
|
|
}
|
|
}
|
|
if !found {
|
|
t.Error("Expected at least one btree index on posts table")
|
|
}
|
|
|
|
// Verify foreign key constraints
|
|
// Note: Constraint names are generated by the reader
|
|
if len(postsTable.Constraints) == 0 {
|
|
t.Log("Warning: No constraints found on posts table - Ref statements may not be parsed correctly")
|
|
t.Skip("Skipping constraint verification as none were found")
|
|
}
|
|
|
|
// Find any foreign key constraint (name may vary)
|
|
var fkPostsUser *models.Constraint
|
|
for _, c := range postsTable.Constraints {
|
|
if c.Type == models.ForeignKeyConstraint && c.ReferencedTable == "users" {
|
|
fkPostsUser = c
|
|
break
|
|
}
|
|
}
|
|
|
|
if fkPostsUser == nil {
|
|
t.Fatal("Foreign key to users table not found")
|
|
}
|
|
if fkPostsUser.Type != models.ForeignKeyConstraint {
|
|
t.Error("Expected foreign key constraint type")
|
|
}
|
|
if fkPostsUser.ReferencedTable != "users" {
|
|
t.Errorf("Expected referenced table 'users', got '%s'", fkPostsUser.ReferencedTable)
|
|
}
|
|
if fkPostsUser.OnDelete != "CASCADE" {
|
|
t.Errorf("Expected ON DELETE CASCADE, got '%s'", fkPostsUser.OnDelete)
|
|
}
|
|
if fkPostsUser.OnUpdate != "CASCADE" {
|
|
t.Errorf("Expected ON UPDATE CASCADE, got '%s'", fkPostsUser.OnUpdate)
|
|
}
|
|
if len(fkPostsUser.Columns) != 1 || fkPostsUser.Columns[0] != "user_id" {
|
|
t.Error("Expected FK column 'user_id'")
|
|
}
|
|
if len(fkPostsUser.ReferencedColumns) != 1 || fkPostsUser.ReferencedColumns[0] != "id" {
|
|
t.Error("Expected FK referenced column 'id'")
|
|
}
|
|
|
|
// Verify comments table constraints
|
|
if len(commentsTable.Constraints) == 0 {
|
|
t.Log("Warning: No constraints found on comments table")
|
|
t.Skip("Skipping constraint verification as none were found")
|
|
}
|
|
|
|
// Find foreign keys (names may vary)
|
|
var fkCommentsPost, fkCommentsUser *models.Constraint
|
|
for _, c := range commentsTable.Constraints {
|
|
if c.Type == models.ForeignKeyConstraint {
|
|
if c.ReferencedTable == "posts" {
|
|
fkCommentsPost = c
|
|
} else if c.ReferencedTable == "users" {
|
|
fkCommentsUser = c
|
|
}
|
|
}
|
|
}
|
|
|
|
// Check foreign key to posts with CASCADE
|
|
if fkCommentsPost == nil {
|
|
t.Error("Foreign key to posts table not found")
|
|
} else if fkCommentsPost.OnDelete != "CASCADE" {
|
|
t.Errorf("Expected ON DELETE CASCADE for comments->posts FK, got '%s'", fkCommentsPost.OnDelete)
|
|
}
|
|
|
|
// Check foreign key to users with SET NULL
|
|
if fkCommentsUser == nil {
|
|
t.Error("Foreign key to users table not found")
|
|
} else if fkCommentsUser.OnDelete != "SET NULL" {
|
|
t.Errorf("Expected ON DELETE SET NULL for comments->users FK, got '%s'", fkCommentsUser.OnDelete)
|
|
}
|
|
|
|
// Verify admin schema
|
|
if len(adminSchema.Tables) != 1 {
|
|
t.Errorf("Expected 1 table in admin schema, got %d", len(adminSchema.Tables))
|
|
}
|
|
|
|
auditTable := adminSchema.Tables[0]
|
|
if auditTable.Name != "audit_logs" {
|
|
t.Errorf("Expected table name 'audit_logs', got '%s'", auditTable.Name)
|
|
}
|
|
if auditTable.Schema != "admin" {
|
|
t.Errorf("Expected table schema 'admin', got '%s'", auditTable.Schema)
|
|
}
|
|
|
|
// Verify cross-schema foreign key
|
|
if len(auditTable.Constraints) == 0 {
|
|
t.Log("Warning: No constraints found on audit_logs table")
|
|
t.Skip("Skipping constraint verification as none were found")
|
|
}
|
|
|
|
// Find foreign key to users (name may vary)
|
|
var fkAuditUser *models.Constraint
|
|
for _, c := range auditTable.Constraints {
|
|
if c.Type == models.ForeignKeyConstraint && c.ReferencedTable == "users" {
|
|
fkAuditUser = c
|
|
break
|
|
}
|
|
}
|
|
|
|
if fkAuditUser == nil {
|
|
t.Fatal("Foreign key to users table not found")
|
|
}
|
|
if fkAuditUser.ReferencedSchema != "public" {
|
|
t.Errorf("Expected referenced schema 'public', got '%s'", fkAuditUser.ReferencedSchema)
|
|
}
|
|
if fkAuditUser.ReferencedTable != "users" {
|
|
t.Errorf("Expected referenced table 'users', got '%s'", fkAuditUser.ReferencedTable)
|
|
}
|
|
}
|
|
|
|
func TestReader_ReadDatabase_Minimal(t *testing.T) {
|
|
opts := &readers.ReaderOptions{
|
|
FilePath: filepath.Join("..", "..", "..", "tests", "assets", "dbml", "minimal.dbml"),
|
|
}
|
|
|
|
reader := NewReader(opts)
|
|
db, err := reader.ReadDatabase()
|
|
if err != nil {
|
|
t.Fatalf("ReadDatabase() error = %v", err)
|
|
}
|
|
|
|
if len(db.Schemas) == 0 {
|
|
t.Fatal("Expected at least one schema")
|
|
}
|
|
|
|
schema := db.Schemas[0]
|
|
if len(schema.Tables) != 1 {
|
|
t.Fatalf("Expected 1 table, got %d", len(schema.Tables))
|
|
}
|
|
|
|
table := schema.Tables[0]
|
|
if table.Name != "users" {
|
|
t.Errorf("Expected table name 'users', got '%s'", table.Name)
|
|
}
|
|
|
|
if len(table.Columns) != 1 {
|
|
t.Errorf("Expected 1 column, got %d", len(table.Columns))
|
|
}
|
|
|
|
idCol, exists := table.Columns["id"]
|
|
if !exists {
|
|
t.Fatal("Column 'id' not found")
|
|
}
|
|
if !idCol.IsPrimaryKey {
|
|
t.Error("Column 'id' should be primary key")
|
|
}
|
|
}
|
|
|
|
func TestReader_ReadSchema(t *testing.T) {
|
|
opts := &readers.ReaderOptions{
|
|
FilePath: filepath.Join("..", "..", "..", "tests", "assets", "dbml", "simple.dbml"),
|
|
}
|
|
|
|
reader := NewReader(opts)
|
|
schema, err := reader.ReadSchema()
|
|
if err != nil {
|
|
t.Fatalf("ReadSchema() error = %v", err)
|
|
}
|
|
|
|
if schema == nil {
|
|
t.Fatal("ReadSchema() returned nil schema")
|
|
}
|
|
|
|
if schema.Name != "public" {
|
|
t.Errorf("Expected schema name 'public', got '%s'", schema.Name)
|
|
}
|
|
|
|
if len(schema.Tables) != 1 {
|
|
t.Errorf("Expected 1 table, got %d", len(schema.Tables))
|
|
}
|
|
}
|
|
|
|
func TestReader_ReadTable(t *testing.T) {
|
|
opts := &readers.ReaderOptions{
|
|
FilePath: filepath.Join("..", "..", "..", "tests", "assets", "dbml", "simple.dbml"),
|
|
}
|
|
|
|
reader := NewReader(opts)
|
|
table, err := reader.ReadTable()
|
|
if err != nil {
|
|
t.Fatalf("ReadTable() error = %v", err)
|
|
}
|
|
|
|
if table == nil {
|
|
t.Fatal("ReadTable() returned nil table")
|
|
}
|
|
|
|
if table.Name != "users" {
|
|
t.Errorf("Expected table name 'users', got '%s'", table.Name)
|
|
}
|
|
|
|
if len(table.Columns) != 4 {
|
|
t.Errorf("Expected 4 columns, got %d", len(table.Columns))
|
|
}
|
|
}
|
|
|
|
func TestReader_ReadDatabase_InvalidPath(t *testing.T) {
|
|
opts := &readers.ReaderOptions{
|
|
FilePath: "/nonexistent/file.dbml",
|
|
}
|
|
|
|
reader := NewReader(opts)
|
|
_, err := reader.ReadDatabase()
|
|
if err == nil {
|
|
t.Error("Expected error for invalid file path")
|
|
}
|
|
}
|
|
|
|
func TestReader_ReadDatabase_EmptyPath(t *testing.T) {
|
|
opts := &readers.ReaderOptions{
|
|
FilePath: "",
|
|
}
|
|
|
|
reader := NewReader(opts)
|
|
_, err := reader.ReadDatabase()
|
|
if err == nil {
|
|
t.Error("Expected error for empty file path")
|
|
}
|
|
}
|
|
|
|
func TestReader_ReadDatabase_WithMetadata(t *testing.T) {
|
|
opts := &readers.ReaderOptions{
|
|
FilePath: filepath.Join("..", "..", "..", "tests", "assets", "dbml", "simple.dbml"),
|
|
Metadata: map[string]interface{}{
|
|
"name": "custom_db_name",
|
|
},
|
|
}
|
|
|
|
reader := NewReader(opts)
|
|
db, err := reader.ReadDatabase()
|
|
if err != nil {
|
|
t.Fatalf("ReadDatabase() error = %v", err)
|
|
}
|
|
|
|
if db.Name != "custom_db_name" {
|
|
t.Errorf("Expected database name 'custom_db_name', got '%s'", db.Name)
|
|
}
|
|
}
|
|
|
|
func TestGetPrimaryKey(t *testing.T) {
|
|
opts := &readers.ReaderOptions{
|
|
FilePath: filepath.Join("..", "..", "..", "tests", "assets", "dbml", "simple.dbml"),
|
|
}
|
|
|
|
reader := NewReader(opts)
|
|
table, err := reader.ReadTable()
|
|
if err != nil {
|
|
t.Fatalf("ReadTable() error = %v", err)
|
|
}
|
|
|
|
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) {
|
|
opts := &readers.ReaderOptions{
|
|
FilePath: filepath.Join("..", "..", "..", "tests", "assets", "dbml", "complex.dbml"),
|
|
}
|
|
|
|
reader := NewReader(opts)
|
|
db, err := reader.ReadDatabase()
|
|
if err != nil {
|
|
t.Fatalf("ReadDatabase() error = %v", err)
|
|
}
|
|
|
|
// Find posts table
|
|
var postsTable *models.Table
|
|
for _, schema := range db.Schemas {
|
|
for _, table := range schema.Tables {
|
|
if table.Name == "posts" {
|
|
postsTable = table
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
if postsTable == nil {
|
|
t.Fatal("Posts table not found")
|
|
}
|
|
|
|
fks := postsTable.GetForeignKeys()
|
|
if len(fks) == 0 {
|
|
t.Skip("No foreign keys found - Ref statements may not be parsed correctly")
|
|
}
|
|
|
|
if fks[0].Type != models.ForeignKeyConstraint {
|
|
t.Error("Expected foreign key constraint type")
|
|
}
|
|
}
|
|
|
|
// Tests for multi-file directory loading
|
|
|
|
func TestReadDirectory_MultipleFiles(t *testing.T) {
|
|
opts := &readers.ReaderOptions{
|
|
FilePath: filepath.Join("..", "..", "..", "tests", "assets", "dbml", "multifile"),
|
|
}
|
|
|
|
reader := NewReader(opts)
|
|
db, err := reader.ReadDatabase()
|
|
if err != nil {
|
|
t.Fatalf("ReadDatabase() error = %v", err)
|
|
}
|
|
|
|
if db == nil {
|
|
t.Fatal("ReadDatabase() returned nil database")
|
|
}
|
|
|
|
// Should have public schema
|
|
if len(db.Schemas) == 0 {
|
|
t.Fatal("Expected at least one schema")
|
|
}
|
|
|
|
var publicSchema *models.Schema
|
|
for _, schema := range db.Schemas {
|
|
if schema.Name == "public" {
|
|
publicSchema = schema
|
|
break
|
|
}
|
|
}
|
|
|
|
if publicSchema == nil {
|
|
t.Fatal("Public schema not found")
|
|
}
|
|
|
|
// Should have 3 tables: users, posts, comments
|
|
if len(publicSchema.Tables) != 3 {
|
|
t.Fatalf("Expected 3 tables, got %d", len(publicSchema.Tables))
|
|
}
|
|
|
|
// Find tables
|
|
var usersTable, postsTable, commentsTable *models.Table
|
|
for _, table := range publicSchema.Tables {
|
|
switch table.Name {
|
|
case "users":
|
|
usersTable = table
|
|
case "posts":
|
|
postsTable = table
|
|
case "comments":
|
|
commentsTable = table
|
|
}
|
|
}
|
|
|
|
if usersTable == nil {
|
|
t.Fatal("Users table not found")
|
|
}
|
|
if postsTable == nil {
|
|
t.Fatal("Posts table not found")
|
|
}
|
|
if commentsTable == nil {
|
|
t.Fatal("Comments table not found")
|
|
}
|
|
|
|
// Verify users table has merged columns from 1_users.dbml and 3_add_columns.dbml
|
|
expectedUserColumns := []string{"id", "email", "name", "created_at"}
|
|
if len(usersTable.Columns) != len(expectedUserColumns) {
|
|
t.Errorf("Expected %d columns in users table, got %d", len(expectedUserColumns), len(usersTable.Columns))
|
|
}
|
|
|
|
for _, colName := range expectedUserColumns {
|
|
if _, exists := usersTable.Columns[colName]; !exists {
|
|
t.Errorf("Expected column '%s' in users table", colName)
|
|
}
|
|
}
|
|
|
|
// Verify posts table columns
|
|
expectedPostColumns := []string{"id", "user_id", "title", "content", "created_at"}
|
|
for _, colName := range expectedPostColumns {
|
|
if _, exists := postsTable.Columns[colName]; !exists {
|
|
t.Errorf("Expected column '%s' in posts table", colName)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestReadDirectory_TableMerging(t *testing.T) {
|
|
opts := &readers.ReaderOptions{
|
|
FilePath: filepath.Join("..", "..", "..", "tests", "assets", "dbml", "multifile"),
|
|
}
|
|
|
|
reader := NewReader(opts)
|
|
db, err := reader.ReadDatabase()
|
|
if err != nil {
|
|
t.Fatalf("ReadDatabase() error = %v", err)
|
|
}
|
|
|
|
// Find users table
|
|
var usersTable *models.Table
|
|
for _, schema := range db.Schemas {
|
|
for _, table := range schema.Tables {
|
|
if table.Name == "users" && schema.Name == "public" {
|
|
usersTable = table
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
if usersTable == nil {
|
|
t.Fatal("Users table not found")
|
|
}
|
|
|
|
// Verify columns from file 1 (id, email)
|
|
if _, exists := usersTable.Columns["id"]; !exists {
|
|
t.Error("Column 'id' from 1_users.dbml not found")
|
|
}
|
|
if _, exists := usersTable.Columns["email"]; !exists {
|
|
t.Error("Column 'email' from 1_users.dbml not found")
|
|
}
|
|
|
|
// Verify columns from file 3 (name, created_at)
|
|
if _, exists := usersTable.Columns["name"]; !exists {
|
|
t.Error("Column 'name' from 3_add_columns.dbml not found")
|
|
}
|
|
if _, exists := usersTable.Columns["created_at"]; !exists {
|
|
t.Error("Column 'created_at' from 3_add_columns.dbml not found")
|
|
}
|
|
|
|
// Verify column properties from file 1
|
|
emailCol := usersTable.Columns["email"]
|
|
if !emailCol.NotNull {
|
|
t.Error("Email column should be not null (from 1_users.dbml)")
|
|
}
|
|
if emailCol.Type != "varchar(255)" {
|
|
t.Errorf("Expected email type 'varchar(255)', got '%s'", emailCol.Type)
|
|
}
|
|
}
|
|
|
|
func TestReadDirectory_CommentedRefsLast(t *testing.T) {
|
|
// This test verifies that files with commented refs are processed last
|
|
// by checking that the file discovery returns them in the correct order
|
|
dirPath := filepath.Join("..", "..", "..", "tests", "assets", "dbml", "multifile")
|
|
|
|
opts := &readers.ReaderOptions{
|
|
FilePath: dirPath,
|
|
}
|
|
|
|
reader := NewReader(opts)
|
|
files, err := reader.discoverDBMLFiles(dirPath)
|
|
if err != nil {
|
|
t.Fatalf("discoverDBMLFiles() error = %v", err)
|
|
}
|
|
|
|
if len(files) < 2 {
|
|
t.Skip("Not enough files to test ordering")
|
|
}
|
|
|
|
// Check that 9_refs.dbml (which has commented refs) comes last
|
|
lastFile := filepath.Base(files[len(files)-1])
|
|
if lastFile != "9_refs.dbml" {
|
|
t.Errorf("Expected last file to be '9_refs.dbml' (has commented refs), got '%s'", lastFile)
|
|
}
|
|
|
|
// Check that numbered files without commented refs come first
|
|
firstFile := filepath.Base(files[0])
|
|
if firstFile != "1_users.dbml" {
|
|
t.Errorf("Expected first file to be '1_users.dbml', got '%s'", firstFile)
|
|
}
|
|
}
|
|
|
|
func TestReadDirectory_EmptyDirectory(t *testing.T) {
|
|
// Create a temporary empty directory
|
|
tmpDir := filepath.Join("..", "..", "..", "tests", "assets", "dbml", "empty_test_dir")
|
|
err := os.MkdirAll(tmpDir, 0755)
|
|
if err != nil {
|
|
t.Fatalf("Failed to create temp directory: %v", err)
|
|
}
|
|
defer os.RemoveAll(tmpDir)
|
|
|
|
opts := &readers.ReaderOptions{
|
|
FilePath: tmpDir,
|
|
}
|
|
|
|
reader := NewReader(opts)
|
|
db, err := reader.ReadDatabase()
|
|
if err != nil {
|
|
t.Fatalf("ReadDatabase() should not error on empty directory, got: %v", err)
|
|
}
|
|
|
|
if db == nil {
|
|
t.Fatal("ReadDatabase() returned nil database")
|
|
}
|
|
|
|
// Empty directory should return empty database
|
|
if len(db.Schemas) != 0 {
|
|
t.Errorf("Expected 0 schemas for empty directory, got %d", len(db.Schemas))
|
|
}
|
|
}
|
|
|
|
func TestReadDatabase_BackwardCompat(t *testing.T) {
|
|
// Test that single file loading still works
|
|
opts := &readers.ReaderOptions{
|
|
FilePath: filepath.Join("..", "..", "..", "tests", "assets", "dbml", "simple.dbml"),
|
|
}
|
|
|
|
reader := NewReader(opts)
|
|
db, err := reader.ReadDatabase()
|
|
if err != nil {
|
|
t.Fatalf("ReadDatabase() error = %v", err)
|
|
}
|
|
|
|
if db == nil {
|
|
t.Fatal("ReadDatabase() returned nil database")
|
|
}
|
|
|
|
if len(db.Schemas) == 0 {
|
|
t.Fatal("Expected at least one schema")
|
|
}
|
|
|
|
schema := db.Schemas[0]
|
|
if len(schema.Tables) != 1 {
|
|
t.Fatalf("Expected 1 table, got %d", len(schema.Tables))
|
|
}
|
|
|
|
table := schema.Tables[0]
|
|
if table.Name != "users" {
|
|
t.Errorf("Expected table name 'users', got '%s'", table.Name)
|
|
}
|
|
}
|
|
|
|
func TestParseFilePrefix(t *testing.T) {
|
|
tests := []struct {
|
|
filename string
|
|
wantPrefix int
|
|
wantHas bool
|
|
}{
|
|
{"1_schema.dbml", 1, true},
|
|
{"2_tables.dbml", 2, true},
|
|
{"10_relationships.dbml", 10, true},
|
|
{"99_data.dbml", 99, true},
|
|
{"schema.dbml", 0, false},
|
|
{"tables_no_prefix.dbml", 0, false},
|
|
{"/path/to/1_file.dbml", 1, true},
|
|
{"/path/to/file.dbml", 0, false},
|
|
{"1-file.dbml", 1, true},
|
|
{"2-another.dbml", 2, true},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.filename, func(t *testing.T) {
|
|
gotPrefix, gotHas := parseFilePrefix(tt.filename)
|
|
if gotPrefix != tt.wantPrefix {
|
|
t.Errorf("parseFilePrefix(%s) prefix = %d, want %d", tt.filename, gotPrefix, tt.wantPrefix)
|
|
}
|
|
if gotHas != tt.wantHas {
|
|
t.Errorf("parseFilePrefix(%s) hasPrefix = %v, want %v", tt.filename, gotHas, tt.wantHas)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestHasCommentedRefs(t *testing.T) {
|
|
// Test with the actual multifile test fixtures
|
|
tests := []struct {
|
|
filename string
|
|
wantHas bool
|
|
}{
|
|
{filepath.Join("..", "..", "..", "tests", "assets", "dbml", "multifile", "1_users.dbml"), false},
|
|
{filepath.Join("..", "..", "..", "tests", "assets", "dbml", "multifile", "2_posts.dbml"), false},
|
|
{filepath.Join("..", "..", "..", "tests", "assets", "dbml", "multifile", "3_add_columns.dbml"), false},
|
|
{filepath.Join("..", "..", "..", "tests", "assets", "dbml", "multifile", "9_refs.dbml"), true},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(filepath.Base(tt.filename), func(t *testing.T) {
|
|
gotHas, err := hasCommentedRefs(tt.filename)
|
|
if err != nil {
|
|
t.Fatalf("hasCommentedRefs() error = %v", err)
|
|
}
|
|
if gotHas != tt.wantHas {
|
|
t.Errorf("hasCommentedRefs(%s) = %v, want %v", filepath.Base(tt.filename), gotHas, tt.wantHas)
|
|
}
|
|
})
|
|
}
|
|
}
|