195 lines
6.6 KiB
Go
195 lines
6.6 KiB
Go
package dctx
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
|
|
"git.warky.dev/wdevs/relspecgo/pkg/models"
|
|
"git.warky.dev/wdevs/relspecgo/pkg/readers"
|
|
dctxreader "git.warky.dev/wdevs/relspecgo/pkg/readers/dctx"
|
|
"git.warky.dev/wdevs/relspecgo/pkg/writers"
|
|
"github.com/stretchr/testify/assert"
|
|
)
|
|
|
|
func TestRoundTrip_WriteAndRead(t *testing.T) {
|
|
// 1. Create a sample schema with relationships
|
|
schema := models.InitSchema("public")
|
|
schema.Name = "TestDB"
|
|
|
|
// Table 1: users
|
|
usersTable := models.InitTable("users", "public")
|
|
usersTable.Comment = "Stores user information"
|
|
idCol := models.InitColumn("id", "users", "public")
|
|
idCol.Type = "serial"
|
|
idCol.IsPrimaryKey = true
|
|
idCol.NotNull = true
|
|
usersTable.Columns["id"] = idCol
|
|
nameCol := models.InitColumn("name", "users", "public")
|
|
nameCol.Type = "varchar"
|
|
nameCol.Length = 100
|
|
usersTable.Columns["name"] = nameCol
|
|
pkIndex := models.InitIndex("users_pkey", "users", "public")
|
|
pkIndex.Unique = true
|
|
pkIndex.Columns = []string{"id"}
|
|
usersTable.Indexes["users_pkey"] = pkIndex
|
|
|
|
pkConstraint := models.InitConstraint("users_pkey", models.PrimaryKeyConstraint)
|
|
pkConstraint.Table = "users"
|
|
pkConstraint.Schema = "public"
|
|
pkConstraint.Columns = []string{"id"}
|
|
usersTable.Constraints["users_pkey"] = pkConstraint
|
|
|
|
schema.Tables = append(schema.Tables, usersTable)
|
|
|
|
// Table 2: posts
|
|
postsTable := models.InitTable("posts", "public")
|
|
postsTable.Comment = "Stores blog posts"
|
|
postIDCol := models.InitColumn("id", "posts", "public")
|
|
postIDCol.Type = "serial"
|
|
postIDCol.IsPrimaryKey = true
|
|
postIDCol.NotNull = true
|
|
postsTable.Columns["id"] = postIDCol
|
|
titleCol := models.InitColumn("title", "posts", "public")
|
|
titleCol.Type = "varchar"
|
|
titleCol.Length = 255
|
|
postsTable.Columns["title"] = titleCol
|
|
userIDCol := models.InitColumn("user_id", "posts", "public")
|
|
userIDCol.Type = "integer"
|
|
postsTable.Columns["user_id"] = userIDCol
|
|
postsPKIndex := models.InitIndex("posts_pkey", "posts", "public")
|
|
postsPKIndex.Unique = true
|
|
postsPKIndex.Columns = []string{"id"}
|
|
postsTable.Indexes["posts_pkey"] = postsPKIndex
|
|
|
|
fkIndex := models.InitIndex("posts_user_id_idx", "posts", "public")
|
|
fkIndex.Columns = []string{"user_id"}
|
|
postsTable.Indexes["posts_user_id_idx"] = fkIndex
|
|
|
|
postsPKConstraint := models.InitConstraint("posts_pkey", models.PrimaryKeyConstraint)
|
|
postsPKConstraint.Table = "posts"
|
|
postsPKConstraint.Schema = "public"
|
|
postsPKConstraint.Columns = []string{"id"}
|
|
postsTable.Constraints["posts_pkey"] = postsPKConstraint
|
|
|
|
// Foreign key constraint
|
|
fkConstraint := models.InitConstraint("fk_posts_users", models.ForeignKeyConstraint)
|
|
fkConstraint.Table = "posts"
|
|
fkConstraint.Schema = "public"
|
|
fkConstraint.Columns = []string{"user_id"}
|
|
fkConstraint.ReferencedTable = "users"
|
|
fkConstraint.ReferencedSchema = "public"
|
|
fkConstraint.ReferencedColumns = []string{"id"}
|
|
fkConstraint.OnDelete = "CASCADE"
|
|
fkConstraint.OnUpdate = "NO ACTION"
|
|
postsTable.Constraints["fk_posts_users"] = fkConstraint
|
|
|
|
schema.Tables = append(schema.Tables, postsTable)
|
|
|
|
// Relation
|
|
relation := models.InitRelationship("posts_to_users", models.OneToMany)
|
|
relation.FromTable = "posts"
|
|
relation.FromSchema = "public"
|
|
relation.ToTable = "users"
|
|
relation.ToSchema = "public"
|
|
relation.ForeignKey = "fk_posts_users"
|
|
schema.Relations = append(schema.Relations, relation)
|
|
|
|
// 2. Write the schema to DCTX
|
|
outputPath := filepath.Join(t.TempDir(), "roundtrip.dctx")
|
|
writerOpts := &writers.WriterOptions{
|
|
OutputPath: outputPath,
|
|
}
|
|
writer := NewWriter(writerOpts)
|
|
|
|
err := writer.WriteSchema(schema)
|
|
assert.NoError(t, err)
|
|
|
|
// Verify file was created
|
|
_, err = os.Stat(outputPath)
|
|
assert.NoError(t, err, "Output file should exist")
|
|
|
|
// 3. Read the schema back from DCTX
|
|
readerOpts := &readers.ReaderOptions{
|
|
FilePath: outputPath,
|
|
}
|
|
reader := dctxreader.NewReader(readerOpts)
|
|
|
|
db, err := reader.ReadDatabase()
|
|
assert.NoError(t, err)
|
|
assert.NotNil(t, db)
|
|
|
|
// 4. Verify the schema was read correctly
|
|
assert.Len(t, db.Schemas, 1, "Should have one schema")
|
|
readSchema := db.Schemas[0]
|
|
|
|
// Verify tables
|
|
assert.Len(t, readSchema.Tables, 2, "Should have two tables")
|
|
|
|
// Find users and posts tables
|
|
var readUsersTable, readPostsTable *models.Table
|
|
for _, table := range readSchema.Tables {
|
|
switch table.Name {
|
|
case "users":
|
|
readUsersTable = table
|
|
case "posts":
|
|
readPostsTable = table
|
|
}
|
|
}
|
|
|
|
assert.NotNil(t, readUsersTable, "Users table should exist")
|
|
assert.NotNil(t, readPostsTable, "Posts table should exist")
|
|
|
|
// Verify columns
|
|
assert.Len(t, readUsersTable.Columns, 2, "Users table should have 2 columns")
|
|
assert.NotNil(t, readUsersTable.Columns["id"])
|
|
assert.NotNil(t, readUsersTable.Columns["name"])
|
|
|
|
assert.Len(t, readPostsTable.Columns, 3, "Posts table should have 3 columns")
|
|
assert.NotNil(t, readPostsTable.Columns["id"])
|
|
assert.NotNil(t, readPostsTable.Columns["title"])
|
|
assert.NotNil(t, readPostsTable.Columns["user_id"])
|
|
|
|
// Verify relationships were preserved
|
|
// The DCTX reader stores relationships on the foreign table (posts)
|
|
assert.NotEmpty(t, readPostsTable.Relationships, "Posts table should have relationships")
|
|
|
|
// Debug: print all relationships
|
|
t.Logf("Posts table has %d relationships:", len(readPostsTable.Relationships))
|
|
for name, rel := range readPostsTable.Relationships {
|
|
t.Logf(" - %s: from=%s to=%s fk=%s", name, rel.FromTable, rel.ToTable, rel.ForeignKey)
|
|
}
|
|
|
|
// Find the relationship - the reader creates it with FromTable as primary and ToTable as foreign
|
|
var postsToUsersRel *models.Relationship
|
|
for _, rel := range readPostsTable.Relationships {
|
|
// The relationship should have posts as ToTable (foreign) and users as FromTable (primary)
|
|
if rel.FromTable == "users" && rel.ToTable == "posts" {
|
|
postsToUsersRel = rel
|
|
break
|
|
}
|
|
}
|
|
|
|
assert.NotNil(t, postsToUsersRel, "Should have relationship from users to posts")
|
|
if postsToUsersRel != nil {
|
|
assert.Equal(t, "users", postsToUsersRel.FromTable, "Relationship should come from users (primary) table")
|
|
assert.Equal(t, "posts", postsToUsersRel.ToTable, "Relationship should point to posts (foreign) table")
|
|
assert.NotEmpty(t, postsToUsersRel.ForeignKey, "Relationship should have a foreign key")
|
|
}
|
|
|
|
// Verify foreign key constraint
|
|
fks := readPostsTable.GetForeignKeys()
|
|
assert.NotEmpty(t, fks, "Posts table should have foreign keys")
|
|
|
|
if len(fks) > 0 {
|
|
fk := fks[0]
|
|
assert.Equal(t, models.ForeignKeyConstraint, fk.Type)
|
|
assert.Contains(t, fk.Columns, "user_id")
|
|
assert.Equal(t, "users", fk.ReferencedTable)
|
|
assert.Contains(t, fk.ReferencedColumns, "id")
|
|
assert.Equal(t, "CASCADE", fk.OnDelete)
|
|
}
|
|
|
|
t.Logf("Round-trip test successful: wrote and read back %d tables with relationships", len(readSchema.Tables))
|
|
}
|