This commit is contained in:
Eden Kirin
2025-10-31 19:04:40 +01:00
parent ca10b01fb0
commit 4e4827d640
12 changed files with 2612 additions and 10 deletions

View File

@ -0,0 +1,299 @@
package main
import (
"os"
"path/filepath"
"testing"
"github.com/entity-maker/entity-maker/internal/database"
"github.com/entity-maker/entity-maker/internal/generator"
)
// Note: The cmd/entity-maker package has low test coverage by design.
// The run() function is integration code that orchestrates calls to:
// - Prompt functions (requires user input)
// - Database connections (requires live PostgreSQL)
// - File system operations (requires specific paths)
//
// All the business logic is tested in the internal/* packages where
// coverage is high (91-97%). The tests below validate the file generation
// workflow in isolation without needing the full integration environment.
// TestPackageCompiles validates that the package compiles correctly
func TestPackageCompiles(t *testing.T) {
// This test ensures the main package compiles correctly
// The main() function itself is hard to test as it calls os.Exit()
// and depends on external resources (database, user input, file system)
// If this test runs, the package compiled successfully
t.Log("Main package compiled successfully")
}
// TestFileGeneration tests the file generation logic in isolation
func TestFileGeneration(t *testing.T) {
// Create a temporary directory for testing
tmpDir := t.TempDir()
moduleName := "test_entity"
moduleDir := filepath.Join(tmpDir, moduleName)
// Create the module directory
if err := os.MkdirAll(moduleDir, 0755); err != nil {
t.Fatalf("Failed to create module directory: %v", err)
}
// Create a simple test context
tableInfo := &database.TableInfo{
Schema: "public",
TableName: "test_table",
Columns: []database.Column{
{Name: "id", DataType: "integer", IsPrimaryKey: true, IsNullable: false},
{Name: "name", DataType: "varchar", IsNullable: false},
},
ForeignKeys: []database.ForeignKey{},
EnumTypes: map[string]database.EnumType{},
}
ctx := generator.NewContext(tableInfo, "")
// Define files to generate (same as in main.go)
files := map[string]func(*generator.Context) (string, error){
"table.py": generator.GenerateTable,
"model.py": generator.GenerateModel,
"filter.py": generator.GenerateFilter,
"load_options.py": generator.GenerateLoadOptions,
"repository.py": generator.GenerateRepository,
"manager.py": generator.GenerateManager,
"factory.py": generator.GenerateFactory,
"mapper.py": generator.GenerateMapper,
"__init__.py": generator.GenerateInit,
}
// Generate and write each file
for filename, genFunc := range files {
content, err := genFunc(ctx)
if err != nil {
t.Errorf("Failed to generate %s: %v", filename, err)
continue
}
filePath := filepath.Join(moduleDir, filename)
if err := os.WriteFile(filePath, []byte(content), 0644); err != nil {
t.Errorf("Failed to write %s: %v", filename, err)
continue
}
// Verify file was created
if _, err := os.Stat(filePath); os.IsNotExist(err) {
t.Errorf("File %s was not created", filename)
}
// Verify file has content
if len(content) == 0 && filename != "__init__.py" {
t.Errorf("File %s has no content", filename)
}
}
// Verify all expected files exist
expectedFiles := []string{
"table.py", "model.py", "filter.py", "load_options.py",
"repository.py", "manager.py", "factory.py", "mapper.py", "__init__.py",
}
for _, filename := range expectedFiles {
filePath := filepath.Join(moduleDir, filename)
if _, err := os.Stat(filePath); os.IsNotExist(err) {
t.Errorf("Expected file %s does not exist", filename)
}
}
}
// TestFileGenerationWithEnums tests file generation when enum types are present
func TestFileGenerationWithEnums(t *testing.T) {
tmpDir := t.TempDir()
moduleName := "test_entity"
moduleDir := filepath.Join(tmpDir, moduleName)
if err := os.MkdirAll(moduleDir, 0755); err != nil {
t.Fatalf("Failed to create module directory: %v", err)
}
// Create test context with enums
tableInfo := &database.TableInfo{
Schema: "public",
TableName: "test_table",
Columns: []database.Column{
{Name: "id", DataType: "integer", IsPrimaryKey: true, IsNullable: false},
{Name: "status", DataType: "USER-DEFINED", UdtName: "status_enum", IsNullable: false},
},
ForeignKeys: []database.ForeignKey{},
EnumTypes: map[string]database.EnumType{
"status_enum": {
TypeName: "status_enum",
Values: []string{"active", "inactive", "pending"},
},
},
}
ctx := generator.NewContext(tableInfo, "")
files := map[string]func(*generator.Context) (string, error){
"table.py": generator.GenerateTable,
"model.py": generator.GenerateModel,
"filter.py": generator.GenerateFilter,
"load_options.py": generator.GenerateLoadOptions,
"repository.py": generator.GenerateRepository,
"manager.py": generator.GenerateManager,
"factory.py": generator.GenerateFactory,
"mapper.py": generator.GenerateMapper,
"__init__.py": generator.GenerateInit,
}
// Add enum.py since we have enum types
if len(tableInfo.EnumTypes) > 0 {
files["enum.py"] = generator.GenerateEnum
}
// Generate all files
for filename, genFunc := range files {
content, err := genFunc(ctx)
if err != nil {
t.Errorf("Failed to generate %s: %v", filename, err)
continue
}
filePath := filepath.Join(moduleDir, filename)
if err := os.WriteFile(filePath, []byte(content), 0644); err != nil {
t.Errorf("Failed to write %s: %v", filename, err)
}
}
// Verify enum.py was created
enumPath := filepath.Join(moduleDir, "enum.py")
if _, err := os.Stat(enumPath); os.IsNotExist(err) {
t.Error("enum.py was not created when enum types are present")
}
}
// TestModuleDirectoryCreation tests directory creation logic
func TestModuleDirectoryCreation(t *testing.T) {
tmpDir := t.TempDir()
tests := []struct {
name string
moduleName string
expectErr bool
}{
{
name: "simple module name",
moduleName: "user",
expectErr: false,
},
{
name: "nested module name",
moduleName: filepath.Join("nested", "module"),
expectErr: false,
},
{
name: "module with underscore",
moduleName: "user_account",
expectErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
moduleDir := filepath.Join(tmpDir, tt.moduleName)
err := os.MkdirAll(moduleDir, 0755)
if (err != nil) != tt.expectErr {
t.Errorf("MkdirAll() error = %v, expectErr %v", err, tt.expectErr)
}
if !tt.expectErr {
// Verify directory exists
info, err := os.Stat(moduleDir)
if err != nil {
t.Errorf("Directory should exist: %v", err)
}
if !info.IsDir() {
t.Error("Path should be a directory")
}
}
})
}
}
// TestGeneratedFilePermissions tests that generated files have correct permissions
func TestGeneratedFilePermissions(t *testing.T) {
tmpDir := t.TempDir()
testFile := filepath.Join(tmpDir, "test.py")
content := "# Test content\n"
if err := os.WriteFile(testFile, []byte(content), 0644); err != nil {
t.Fatalf("Failed to write test file: %v", err)
}
info, err := os.Stat(testFile)
if err != nil {
t.Fatalf("Failed to stat test file: %v", err)
}
mode := info.Mode()
// Check that owner can read and write
if mode&0600 != 0600 {
t.Error("File should be readable and writable by owner")
}
// Check that file is not executable
if mode&0111 != 0 {
t.Error("Python files should not be executable")
}
}
// TestGeneratedFilesAreNonEmpty tests that generated files have content
func TestGeneratedFilesAreNonEmpty(t *testing.T) {
tableInfo := &database.TableInfo{
Schema: "public",
TableName: "users",
Columns: []database.Column{
{Name: "id", DataType: "integer", IsPrimaryKey: true, IsNullable: false},
{Name: "name", DataType: "varchar", IsNullable: false},
{Name: "email", DataType: "varchar", IsNullable: true},
},
ForeignKeys: []database.ForeignKey{},
EnumTypes: map[string]database.EnumType{},
}
ctx := generator.NewContext(tableInfo, "")
generators := map[string]func(*generator.Context) (string, error){
"table": generator.GenerateTable,
"model": generator.GenerateModel,
"filter": generator.GenerateFilter,
"load_options": generator.GenerateLoadOptions,
"repository": generator.GenerateRepository,
"manager": generator.GenerateManager,
"factory": generator.GenerateFactory,
"mapper": generator.GenerateMapper,
}
for name, genFunc := range generators {
t.Run(name, func(t *testing.T) {
content, err := genFunc(ctx)
if err != nil {
t.Fatalf("Failed to generate %s: %v", name, err)
}
if len(content) == 0 {
t.Errorf("Generated %s content should not be empty", name)
}
// Check that content has at least some basic Python structure
if name != "mapper" { // mapper is just a snippet
if len(content) < 10 {
t.Errorf("Generated %s content seems too short: %d bytes", name, len(content))
}
}
})
}
}