Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 5e6f03e412 | |||
| 1dcbc79387 | |||
| 59c4a5ebf8 | |||
| 091e1913ee | |||
| 0e6e94797c |
11
.github/workflows/release.yml
vendored
11
.github/workflows/release.yml
vendored
@@ -25,6 +25,7 @@ jobs:
|
|||||||
id: get_version
|
id: get_version
|
||||||
run: |
|
run: |
|
||||||
echo "VERSION=${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT
|
echo "VERSION=${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT
|
||||||
|
echo "BUILD_DATE=$(date -u '+%Y-%m-%d %H:%M:%S UTC')" >> $GITHUB_OUTPUT
|
||||||
echo "Version: ${GITHUB_REF#refs/tags/}"
|
echo "Version: ${GITHUB_REF#refs/tags/}"
|
||||||
|
|
||||||
- name: Build binaries for multiple platforms
|
- name: Build binaries for multiple platforms
|
||||||
@@ -32,19 +33,19 @@ jobs:
|
|||||||
mkdir -p dist
|
mkdir -p dist
|
||||||
|
|
||||||
# Linux AMD64
|
# Linux AMD64
|
||||||
GOOS=linux GOARCH=amd64 go build -o dist/relspec-linux-amd64 -ldflags "-X main.version=${{ steps.get_version.outputs.VERSION }}" ./cmd/relspec
|
GOOS=linux GOARCH=amd64 go build -o dist/relspec-linux-amd64 -ldflags "-X 'main.version=${{ steps.get_version.outputs.VERSION }}' -X 'main.buildDate=${{ steps.get_version.outputs.BUILD_DATE }}'" ./cmd/relspec
|
||||||
|
|
||||||
# Linux ARM64
|
# Linux ARM64
|
||||||
GOOS=linux GOARCH=arm64 go build -o dist/relspec-linux-arm64 -ldflags "-X main.version=${{ steps.get_version.outputs.VERSION }}" ./cmd/relspec
|
GOOS=linux GOARCH=arm64 go build -o dist/relspec-linux-arm64 -ldflags "-X 'main.version=${{ steps.get_version.outputs.VERSION }}' -X 'main.buildDate=${{ steps.get_version.outputs.BUILD_DATE }}'" ./cmd/relspec
|
||||||
|
|
||||||
# macOS AMD64
|
# macOS AMD64
|
||||||
GOOS=darwin GOARCH=amd64 go build -o dist/relspec-darwin-amd64 -ldflags "-X main.version=${{ steps.get_version.outputs.VERSION }}" ./cmd/relspec
|
GOOS=darwin GOARCH=amd64 go build -o dist/relspec-darwin-amd64 -ldflags "-X 'main.version=${{ steps.get_version.outputs.VERSION }}' -X 'main.buildDate=${{ steps.get_version.outputs.BUILD_DATE }}'" ./cmd/relspec
|
||||||
|
|
||||||
# macOS ARM64 (Apple Silicon)
|
# macOS ARM64 (Apple Silicon)
|
||||||
GOOS=darwin GOARCH=arm64 go build -o dist/relspec-darwin-arm64 -ldflags "-X main.version=${{ steps.get_version.outputs.VERSION }}" ./cmd/relspec
|
GOOS=darwin GOARCH=arm64 go build -o dist/relspec-darwin-arm64 -ldflags "-X 'main.version=${{ steps.get_version.outputs.VERSION }}' -X 'main.buildDate=${{ steps.get_version.outputs.BUILD_DATE }}'" ./cmd/relspec
|
||||||
|
|
||||||
# Windows AMD64
|
# Windows AMD64
|
||||||
GOOS=windows GOARCH=amd64 go build -o dist/relspec-windows-amd64.exe -ldflags "-X main.version=${{ steps.get_version.outputs.VERSION }}" ./cmd/relspec
|
GOOS=windows GOARCH=amd64 go build -o dist/relspec-windows-amd64.exe -ldflags "-X 'main.version=${{ steps.get_version.outputs.VERSION }}' -X 'main.buildDate=${{ steps.get_version.outputs.BUILD_DATE }}'" ./cmd/relspec
|
||||||
|
|
||||||
# Create checksums
|
# Create checksums
|
||||||
cd dist
|
cd dist
|
||||||
|
|||||||
13
Makefile
13
Makefile
@@ -14,6 +14,11 @@ GOGET=$(GOCMD) get
|
|||||||
GOMOD=$(GOCMD) mod
|
GOMOD=$(GOCMD) mod
|
||||||
GOCLEAN=$(GOCMD) clean
|
GOCLEAN=$(GOCMD) clean
|
||||||
|
|
||||||
|
# Version information
|
||||||
|
VERSION := $(shell git describe --tags --always --dirty 2>/dev/null || echo "dev")
|
||||||
|
BUILD_DATE := $(shell date -u +"%Y-%m-%d %H:%M:%S UTC")
|
||||||
|
LDFLAGS := -X 'main.version=$(VERSION)' -X 'main.buildDate=$(BUILD_DATE)'
|
||||||
|
|
||||||
# Auto-detect container runtime (Docker or Podman)
|
# Auto-detect container runtime (Docker or Podman)
|
||||||
CONTAINER_RUNTIME := $(shell \
|
CONTAINER_RUNTIME := $(shell \
|
||||||
if command -v podman > /dev/null 2>&1; then \
|
if command -v podman > /dev/null 2>&1; then \
|
||||||
@@ -37,9 +42,9 @@ COMPOSE_CMD := $(shell \
|
|||||||
all: lint test build ## Run linting, tests, and build
|
all: lint test build ## Run linting, tests, and build
|
||||||
|
|
||||||
build: deps ## Build the binary
|
build: deps ## Build the binary
|
||||||
@echo "Building $(BINARY_NAME)..."
|
@echo "Building $(BINARY_NAME) $(VERSION)..."
|
||||||
@mkdir -p $(BUILD_DIR)
|
@mkdir -p $(BUILD_DIR)
|
||||||
$(GOBUILD) -o $(BUILD_DIR)/$(BINARY_NAME) ./cmd/relspec
|
$(GOBUILD) -ldflags "$(LDFLAGS)" -o $(BUILD_DIR)/$(BINARY_NAME) ./cmd/relspec
|
||||||
@echo "Build complete: $(BUILD_DIR)/$(BINARY_NAME)"
|
@echo "Build complete: $(BUILD_DIR)/$(BINARY_NAME)"
|
||||||
|
|
||||||
test: test-unit ## Run all unit tests (alias for test-unit)
|
test: test-unit ## Run all unit tests (alias for test-unit)
|
||||||
@@ -91,8 +96,8 @@ clean: ## Clean build artifacts
|
|||||||
@echo "Clean complete"
|
@echo "Clean complete"
|
||||||
|
|
||||||
install: ## Install the binary to $GOPATH/bin
|
install: ## Install the binary to $GOPATH/bin
|
||||||
@echo "Installing $(BINARY_NAME)..."
|
@echo "Installing $(BINARY_NAME) $(VERSION)..."
|
||||||
$(GOCMD) install ./cmd/relspec
|
$(GOCMD) install -ldflags "$(LDFLAGS)" ./cmd/relspec
|
||||||
@echo "Install complete"
|
@echo "Install complete"
|
||||||
|
|
||||||
deps: ## Download dependencies
|
deps: ## Download dependencies
|
||||||
|
|||||||
@@ -1,9 +1,49 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
"runtime/debug"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// Version information, set via ldflags during build
|
||||||
|
version = "dev"
|
||||||
|
buildDate = "unknown"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
// If version wasn't set via ldflags, try to get it from build info
|
||||||
|
if version == "dev" {
|
||||||
|
if info, ok := debug.ReadBuildInfo(); ok {
|
||||||
|
// Try to get version from VCS
|
||||||
|
var vcsRevision, vcsTime string
|
||||||
|
for _, setting := range info.Settings {
|
||||||
|
switch setting.Key {
|
||||||
|
case "vcs.revision":
|
||||||
|
if len(setting.Value) >= 7 {
|
||||||
|
vcsRevision = setting.Value[:7]
|
||||||
|
}
|
||||||
|
case "vcs.time":
|
||||||
|
vcsTime = setting.Value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if vcsRevision != "" {
|
||||||
|
version = vcsRevision
|
||||||
|
}
|
||||||
|
|
||||||
|
if vcsTime != "" {
|
||||||
|
if t, err := time.Parse(time.RFC3339, vcsTime); err == nil {
|
||||||
|
buildDate = t.UTC().Format("2006-01-02 15:04:05 UTC")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var rootCmd = &cobra.Command{
|
var rootCmd = &cobra.Command{
|
||||||
Use: "relspec",
|
Use: "relspec",
|
||||||
Short: "RelSpec - Database schema conversion and analysis tool",
|
Short: "RelSpec - Database schema conversion and analysis tool",
|
||||||
@@ -13,6 +53,9 @@ bidirectional conversion between various database schema formats.
|
|||||||
It reads database schemas from multiple sources (live databases, DBML,
|
It reads database schemas from multiple sources (live databases, DBML,
|
||||||
DCTX, DrawDB, etc.) and writes them to various formats (GORM, Bun,
|
DCTX, DrawDB, etc.) and writes them to various formats (GORM, Bun,
|
||||||
JSON, YAML, SQL, etc.).`,
|
JSON, YAML, SQL, etc.).`,
|
||||||
|
PersistentPreRun: func(cmd *cobra.Command, args []string) {
|
||||||
|
fmt.Printf("RelSpec %s (built: %s)\n\n", version, buildDate)
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@@ -24,4 +67,5 @@ func init() {
|
|||||||
rootCmd.AddCommand(editCmd)
|
rootCmd.AddCommand(editCmd)
|
||||||
rootCmd.AddCommand(mergeCmd)
|
rootCmd.AddCommand(mergeCmd)
|
||||||
rootCmd.AddCommand(splitCmd)
|
rootCmd.AddCommand(splitCmd)
|
||||||
|
rootCmd.AddCommand(versionCmd)
|
||||||
}
|
}
|
||||||
|
|||||||
16
cmd/relspec/version.go
Normal file
16
cmd/relspec/version.go
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
var versionCmd = &cobra.Command{
|
||||||
|
Use: "version",
|
||||||
|
Short: "Print version information",
|
||||||
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
|
fmt.Printf("RelSpec %s\n", version)
|
||||||
|
fmt.Printf("Built: %s\n", buildDate)
|
||||||
|
},
|
||||||
|
}
|
||||||
@@ -231,14 +231,13 @@ func (r *Reader) queryColumns(schemaName string) (map[string]map[string]*models.
|
|||||||
}
|
}
|
||||||
|
|
||||||
column := models.InitColumn(columnName, tableName, schema)
|
column := models.InitColumn(columnName, tableName, schema)
|
||||||
column.Type = r.mapDataType(dataType, udtName)
|
|
||||||
column.NotNull = (isNullable == "NO")
|
|
||||||
column.Sequence = uint(ordinalPosition)
|
|
||||||
|
|
||||||
|
// Check if this is a serial type (has nextval default)
|
||||||
|
hasNextval := false
|
||||||
if columnDefault != nil {
|
if columnDefault != nil {
|
||||||
// Parse default value - remove nextval for sequences
|
|
||||||
defaultVal := *columnDefault
|
defaultVal := *columnDefault
|
||||||
if strings.HasPrefix(defaultVal, "nextval") {
|
if strings.HasPrefix(defaultVal, "nextval") {
|
||||||
|
hasNextval = true
|
||||||
column.AutoIncrement = true
|
column.AutoIncrement = true
|
||||||
column.Default = defaultVal
|
column.Default = defaultVal
|
||||||
} else {
|
} else {
|
||||||
@@ -246,6 +245,11 @@ func (r *Reader) queryColumns(schemaName string) (map[string]map[string]*models.
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Map data type, preserving serial types when detected
|
||||||
|
column.Type = r.mapDataType(dataType, udtName, hasNextval)
|
||||||
|
column.NotNull = (isNullable == "NO")
|
||||||
|
column.Sequence = uint(ordinalPosition)
|
||||||
|
|
||||||
if description != nil {
|
if description != nil {
|
||||||
column.Description = *description
|
column.Description = *description
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package pgsql
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/jackc/pgx/v5"
|
"github.com/jackc/pgx/v5"
|
||||||
|
|
||||||
@@ -259,33 +260,46 @@ func (r *Reader) close() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// mapDataType maps PostgreSQL data types to canonical types
|
// mapDataType maps PostgreSQL data types to canonical types
|
||||||
func (r *Reader) mapDataType(pgType, udtName string) string {
|
func (r *Reader) mapDataType(pgType, udtName string, hasNextval bool) string {
|
||||||
|
// If the column has a nextval default, it's likely a serial type
|
||||||
|
// Map to the appropriate serial type instead of the base integer type
|
||||||
|
if hasNextval {
|
||||||
|
switch strings.ToLower(pgType) {
|
||||||
|
case "integer", "int", "int4":
|
||||||
|
return "serial"
|
||||||
|
case "bigint", "int8":
|
||||||
|
return "bigserial"
|
||||||
|
case "smallint", "int2":
|
||||||
|
return "smallserial"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Map common PostgreSQL types
|
// Map common PostgreSQL types
|
||||||
typeMap := map[string]string{
|
typeMap := map[string]string{
|
||||||
"integer": "int",
|
"integer": "integer",
|
||||||
"bigint": "int64",
|
"bigint": "bigint",
|
||||||
"smallint": "int16",
|
"smallint": "smallint",
|
||||||
"int": "int",
|
"int": "integer",
|
||||||
"int2": "int16",
|
"int2": "smallint",
|
||||||
"int4": "int",
|
"int4": "integer",
|
||||||
"int8": "int64",
|
"int8": "bigint",
|
||||||
"serial": "int",
|
"serial": "serial",
|
||||||
"bigserial": "int64",
|
"bigserial": "bigserial",
|
||||||
"smallserial": "int16",
|
"smallserial": "smallserial",
|
||||||
"numeric": "decimal",
|
"numeric": "numeric",
|
||||||
"decimal": "decimal",
|
"decimal": "decimal",
|
||||||
"real": "float32",
|
"real": "real",
|
||||||
"double precision": "float64",
|
"double precision": "double precision",
|
||||||
"float4": "float32",
|
"float4": "real",
|
||||||
"float8": "float64",
|
"float8": "double precision",
|
||||||
"money": "decimal",
|
"money": "money",
|
||||||
"character varying": "string",
|
"character varying": "varchar",
|
||||||
"varchar": "string",
|
"varchar": "varchar",
|
||||||
"character": "string",
|
"character": "char",
|
||||||
"char": "string",
|
"char": "char",
|
||||||
"text": "string",
|
"text": "text",
|
||||||
"boolean": "bool",
|
"boolean": "boolean",
|
||||||
"bool": "bool",
|
"bool": "boolean",
|
||||||
"date": "date",
|
"date": "date",
|
||||||
"time": "time",
|
"time": "time",
|
||||||
"time without time zone": "time",
|
"time without time zone": "time",
|
||||||
|
|||||||
@@ -177,20 +177,20 @@ func TestMapDataType(t *testing.T) {
|
|||||||
udtName string
|
udtName string
|
||||||
expected string
|
expected string
|
||||||
}{
|
}{
|
||||||
{"integer", "int4", "int"},
|
{"integer", "int4", "integer"},
|
||||||
{"bigint", "int8", "int64"},
|
{"bigint", "int8", "bigint"},
|
||||||
{"smallint", "int2", "int16"},
|
{"smallint", "int2", "smallint"},
|
||||||
{"character varying", "varchar", "string"},
|
{"character varying", "varchar", "varchar"},
|
||||||
{"text", "text", "string"},
|
{"text", "text", "text"},
|
||||||
{"boolean", "bool", "bool"},
|
{"boolean", "bool", "boolean"},
|
||||||
{"timestamp without time zone", "timestamp", "timestamp"},
|
{"timestamp without time zone", "timestamp", "timestamp"},
|
||||||
{"timestamp with time zone", "timestamptz", "timestamptz"},
|
{"timestamp with time zone", "timestamptz", "timestamptz"},
|
||||||
{"json", "json", "json"},
|
{"json", "json", "json"},
|
||||||
{"jsonb", "jsonb", "jsonb"},
|
{"jsonb", "jsonb", "jsonb"},
|
||||||
{"uuid", "uuid", "uuid"},
|
{"uuid", "uuid", "uuid"},
|
||||||
{"numeric", "numeric", "decimal"},
|
{"numeric", "numeric", "numeric"},
|
||||||
{"real", "float4", "float32"},
|
{"real", "float4", "real"},
|
||||||
{"double precision", "float8", "float64"},
|
{"double precision", "float8", "double precision"},
|
||||||
{"date", "date", "date"},
|
{"date", "date", "date"},
|
||||||
{"time without time zone", "time", "time"},
|
{"time without time zone", "time", "time"},
|
||||||
{"bytea", "bytea", "bytea"},
|
{"bytea", "bytea", "bytea"},
|
||||||
@@ -199,12 +199,31 @@ func TestMapDataType(t *testing.T) {
|
|||||||
|
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.pgType, func(t *testing.T) {
|
t.Run(tt.pgType, func(t *testing.T) {
|
||||||
result := reader.mapDataType(tt.pgType, tt.udtName)
|
result := reader.mapDataType(tt.pgType, tt.udtName, false)
|
||||||
if result != tt.expected {
|
if result != tt.expected {
|
||||||
t.Errorf("mapDataType(%s, %s) = %s, expected %s", tt.pgType, tt.udtName, result, tt.expected)
|
t.Errorf("mapDataType(%s, %s) = %s, expected %s", tt.pgType, tt.udtName, result, tt.expected)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Test serial type detection with hasNextval=true
|
||||||
|
serialTests := []struct {
|
||||||
|
pgType string
|
||||||
|
expected string
|
||||||
|
}{
|
||||||
|
{"integer", "serial"},
|
||||||
|
{"bigint", "bigserial"},
|
||||||
|
{"smallint", "smallserial"},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range serialTests {
|
||||||
|
t.Run(tt.pgType+"_with_nextval", func(t *testing.T) {
|
||||||
|
result := reader.mapDataType(tt.pgType, "", true)
|
||||||
|
if result != tt.expected {
|
||||||
|
t.Errorf("mapDataType(%s, '', true) = %s, expected %s", tt.pgType, result, tt.expected)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestParseIndexDefinition(t *testing.T) {
|
func TestParseIndexDefinition(t *testing.T) {
|
||||||
|
|||||||
@@ -62,6 +62,17 @@ func (tm *TypeMapper) isSimpleType(sqlType string) bool {
|
|||||||
return simpleTypes[sqlType]
|
return simpleTypes[sqlType]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// isSerialType checks if a SQL type is a serial type (auto-incrementing)
|
||||||
|
func (tm *TypeMapper) isSerialType(sqlType string) bool {
|
||||||
|
baseType := tm.extractBaseType(sqlType)
|
||||||
|
serialTypes := map[string]bool{
|
||||||
|
"serial": true,
|
||||||
|
"bigserial": true,
|
||||||
|
"smallserial": true,
|
||||||
|
}
|
||||||
|
return serialTypes[baseType]
|
||||||
|
}
|
||||||
|
|
||||||
// baseGoType returns the base Go type for a SQL type (not null, simple types only)
|
// baseGoType returns the base Go type for a SQL type (not null, simple types only)
|
||||||
func (tm *TypeMapper) baseGoType(sqlType string) string {
|
func (tm *TypeMapper) baseGoType(sqlType string) string {
|
||||||
typeMap := map[string]string{
|
typeMap := map[string]string{
|
||||||
@@ -190,6 +201,11 @@ func (tm *TypeMapper) BuildBunTag(column *models.Column, table *models.Table) st
|
|||||||
parts = append(parts, "pk")
|
parts = append(parts, "pk")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Auto increment (for serial types or explicit auto_increment)
|
||||||
|
if column.AutoIncrement || tm.isSerialType(column.Type) {
|
||||||
|
parts = append(parts, "autoincrement")
|
||||||
|
}
|
||||||
|
|
||||||
// Default value
|
// Default value
|
||||||
if column.Default != nil {
|
if column.Default != nil {
|
||||||
// Sanitize default value to remove backticks
|
// Sanitize default value to remove backticks
|
||||||
@@ -251,7 +267,15 @@ func (tm *TypeMapper) BuildRelationshipTag(constraint *models.Constraint, relTyp
|
|||||||
if len(constraint.Columns) > 0 && len(constraint.ReferencedColumns) > 0 {
|
if len(constraint.Columns) > 0 && len(constraint.ReferencedColumns) > 0 {
|
||||||
localCol := constraint.Columns[0]
|
localCol := constraint.Columns[0]
|
||||||
foreignCol := constraint.ReferencedColumns[0]
|
foreignCol := constraint.ReferencedColumns[0]
|
||||||
parts = append(parts, fmt.Sprintf("join:%s=%s", localCol, foreignCol))
|
|
||||||
|
// For has-many relationships, swap the columns
|
||||||
|
// has-one: join:fk_in_this_table=pk_in_other_table
|
||||||
|
// has-many: join:pk_in_this_table=fk_in_other_table
|
||||||
|
if relType == "has-many" {
|
||||||
|
parts = append(parts, fmt.Sprintf("join:%s=%s", foreignCol, localCol))
|
||||||
|
} else {
|
||||||
|
parts = append(parts, fmt.Sprintf("join:%s=%s", localCol, foreignCol))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return strings.Join(parts, ",")
|
return strings.Join(parts, ",")
|
||||||
|
|||||||
@@ -90,8 +90,8 @@ func TestWriter_WriteTable(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Verify Bun-specific elements
|
// Verify Bun-specific elements
|
||||||
if !strings.Contains(generated, "bun:\"id,type:bigint,pk,") {
|
if !strings.Contains(generated, "bun:\"id,type:bigint,pk,autoincrement,") {
|
||||||
t.Errorf("Missing Bun-style primary key tag")
|
t.Errorf("Missing Bun-style primary key tag with autoincrement")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -308,14 +308,20 @@ func TestWriter_MultipleReferencesToSameTable(t *testing.T) {
|
|||||||
filepointerStr := string(filepointerContent)
|
filepointerStr := string(filepointerContent)
|
||||||
|
|
||||||
// Should have two different has-many relationships with unique names
|
// Should have two different has-many relationships with unique names
|
||||||
hasManyExpectations := []string{
|
hasManyExpectations := []struct {
|
||||||
"RelRIDFilepointerRequestOrgAPIEvents", // Has many via rid_filepointer_request
|
fieldName string
|
||||||
"RelRIDFilepointerResponseOrgAPIEvents", // Has many via rid_filepointer_response
|
tag string
|
||||||
|
}{
|
||||||
|
{"RelRIDFilepointerRequestOrgAPIEvents", "join:id_filepointer=rid_filepointer_request"}, // Has many via rid_filepointer_request
|
||||||
|
{"RelRIDFilepointerResponseOrgAPIEvents", "join:id_filepointer=rid_filepointer_response"}, // Has many via rid_filepointer_response
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, exp := range hasManyExpectations {
|
for _, exp := range hasManyExpectations {
|
||||||
if !strings.Contains(filepointerStr, exp) {
|
if !strings.Contains(filepointerStr, exp.fieldName) {
|
||||||
t.Errorf("Missing has-many relationship field: %s\nGenerated:\n%s", exp, filepointerStr)
|
t.Errorf("Missing has-many relationship field: %s\nGenerated:\n%s", exp.fieldName, filepointerStr)
|
||||||
|
}
|
||||||
|
if !strings.Contains(filepointerStr, exp.tag) {
|
||||||
|
t.Errorf("Missing has-many relationship join tag: %s\nGenerated:\n%s", exp.tag, filepointerStr)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -618,6 +624,37 @@ func TestTypeMapper_BuildBunTag(t *testing.T) {
|
|||||||
},
|
},
|
||||||
want: []string{"status,", "type:text,", "default:active,"},
|
want: []string{"status,", "type:text,", "default:active,"},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "auto increment with AutoIncrement flag",
|
||||||
|
column: &models.Column{
|
||||||
|
Name: "id",
|
||||||
|
Type: "bigint",
|
||||||
|
NotNull: true,
|
||||||
|
IsPrimaryKey: true,
|
||||||
|
AutoIncrement: true,
|
||||||
|
},
|
||||||
|
want: []string{"id,", "type:bigint,", "pk,", "autoincrement,"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "serial type (auto-increment)",
|
||||||
|
column: &models.Column{
|
||||||
|
Name: "id",
|
||||||
|
Type: "serial",
|
||||||
|
NotNull: true,
|
||||||
|
IsPrimaryKey: true,
|
||||||
|
},
|
||||||
|
want: []string{"id,", "type:serial,", "pk,", "autoincrement,"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "bigserial type (auto-increment)",
|
||||||
|
column: &models.Column{
|
||||||
|
Name: "id",
|
||||||
|
Type: "bigserial",
|
||||||
|
NotNull: true,
|
||||||
|
IsPrimaryKey: true,
|
||||||
|
},
|
||||||
|
want: []string{"id,", "type:bigserial,", "pk,", "autoincrement,"},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
|
|||||||
Reference in New Issue
Block a user