Tests
This commit is contained in:
299
cmd/entity-maker/main_test.go
Normal file
299
cmd/entity-maker/main_test.go
Normal 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))
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user