package main // SPDX-License-Identifier: GPL-3.0-or-later import ( "fmt" "os" "path/filepath" "strings" "time" "github.com/magefile/mage/mg" "github.com/magefile/mage/sh" ) // Testing namespace provides comprehensive testing commands type Testing mg.Namespace // All runs all test suites with coverage func (Testing) All() error { mg.Deps(Testing.Setup) fmt.Println("Running comprehensive test suite...") // Set test environment variables env := map[string]string{ "CGO_ENABLED": "1", // Required for SQLite "GO_TEST_SHORT": "false", "TEST_TIMEOUT": "45m", } // Run all tests with coverage return sh.RunWith(env, "go", "test", "-v", "-timeout", "45m", "-race", "-coverprofile=coverage.out", "-coverpkg=./...", "./tests/...", ) } // API runs API endpoint tests func (Testing) API() error { mg.Deps(Testing.Setup) fmt.Println("Running API tests...") env := map[string]string{ "CGO_ENABLED": "1", } return sh.RunWith(env, "go", "test", "-v", "-timeout", "10m", "./tests/api/...", ) } // Performance runs load and performance tests func (Testing) Performance() error { mg.Deps(Testing.Setup) fmt.Println("Running performance tests...") env := map[string]string{ "CGO_ENABLED": "1", "TEST_TIMEOUT": "30m", "PERF_TEST_WORKERS": "10", "PERF_TEST_JOBS": "50", } return sh.RunWith(env, "go", "test", "-v", "-timeout", "30m", "-run", "TestLoadSuite", "./tests/performance/...", ) } // Integration runs end-to-end workflow tests func (Testing) Integration() error { mg.Deps(Testing.Setup) fmt.Println("Running integration tests...") env := map[string]string{ "CGO_ENABLED": "1", "TEST_TIMEOUT": "20m", } return sh.RunWith(env, "go", "test", "-v", "-timeout", "20m", "-run", "TestIntegrationSuite", "./tests/integration/...", ) } // Database runs database and migration tests func (Testing) Database() error { mg.Deps(Testing.Setup) fmt.Println("Running database tests...") env := map[string]string{ "CGO_ENABLED": "1", } return sh.RunWith(env, "go", "test", "-v", "-timeout", "10m", "-run", "TestDatabaseSuite", "./tests/database/...", ) } // Docker runs tests in containerized environment func (Testing) Docker() error { fmt.Println("Running tests in Docker environment...") // Start test environment if err := sh.Run("docker", "compose", "-f", "tests/docker/compose.test.yml", "up", "-d", "--build"); err != nil { return fmt.Errorf("failed to start test environment: %w", err) } // Wait for services to be ready fmt.Println("Waiting for test services to be ready...") time.Sleep(30 * time.Second) // Run tests err := sh.Run("docker", "compose", "-f", "tests/docker/compose.test.yml", "--profile", "test-runner", "up", "--abort-on-container-exit") // Cleanup regardless of test result cleanupErr := sh.Run("docker", "compose", "-f", "tests/docker/compose.test.yml", "down", "-v") if err != nil { return fmt.Errorf("tests failed: %w", err) } if cleanupErr != nil { fmt.Printf("Warning: cleanup failed: %v\n", cleanupErr) } return nil } // DockerPerf runs performance tests with multiple workers func (Testing) DockerPerf() error { fmt.Println("Running performance tests with multiple workers...") // Start performance test environment if err := sh.Run("docker", "compose", "-f", "tests/docker/compose.test.yml", "--profile", "performance", "up", "-d", "--build"); err != nil { return fmt.Errorf("failed to start performance test environment: %w", err) } // Wait for services fmt.Println("Waiting for performance test environment...") time.Sleep(45 * time.Second) // Run performance tests err := sh.Run("docker", "exec", "flamenco-test-manager", "go", "test", "-v", "-timeout", "30m", "./tests/performance/...") // Cleanup cleanupErr := sh.Run("docker", "compose", "-f", "tests/docker/compose.test.yml", "down", "-v") if err != nil { return fmt.Errorf("performance tests failed: %w", err) } if cleanupErr != nil { fmt.Printf("Warning: cleanup failed: %v\n", cleanupErr) } return nil } // Setup prepares the test environment func (Testing) Setup() error { fmt.Println("Setting up test environment...") // Create test directories testDirs := []string{ "./tmp/test-data", "./tmp/test-results", "./tmp/shared-storage", } for _, dir := range testDirs { if err := os.MkdirAll(dir, 0755); err != nil { return fmt.Errorf("failed to create test directory %s: %w", dir, err) } } // Download dependencies if err := sh.Run("go", "mod", "download"); err != nil { return fmt.Errorf("failed to download dependencies: %w", err) } // Verify test database migrations are available migrationsDir := "./internal/manager/persistence/migrations" if _, err := os.Stat(migrationsDir); os.IsNotExist(err) { return fmt.Errorf("migrations directory not found: %s", migrationsDir) } fmt.Println("Test environment setup complete") return nil } // Clean removes test artifacts and temporary files func (Testing) Clean() error { fmt.Println("Cleaning up test artifacts...") // Remove test files and directories cleanupPaths := []string{ "./tmp/test-*", "./coverage.out", "./coverage.html", "./test-results.json", "./cpu.prof", "./mem.prof", } for _, pattern := range cleanupPaths { matches, err := filepath.Glob(pattern) if err != nil { continue } for _, match := range matches { if err := os.RemoveAll(match); err != nil { fmt.Printf("Warning: failed to remove %s: %v\n", match, err) } } } // Stop and clean Docker test environment sh.Run("docker", "compose", "-f", "tests/docker/compose.test.yml", "down", "-v") fmt.Println("Test cleanup complete") return nil } // Coverage generates test coverage reports func (Testing) Coverage() error { mg.Deps(Testing.All) fmt.Println("Generating coverage reports...") // Generate HTML coverage report if err := sh.Run("go", "tool", "cover", "-html=coverage.out", "-o", "coverage.html"); err != nil { return fmt.Errorf("failed to generate HTML coverage report: %w", err) } // Print coverage summary if err := sh.Run("go", "tool", "cover", "-func=coverage.out"); err != nil { return fmt.Errorf("failed to display coverage summary: %w", err) } fmt.Println("Coverage reports generated:") fmt.Println(" - coverage.html (interactive)") fmt.Println(" - coverage.out (raw data)") return nil } // Bench runs performance benchmarks func (Testing) Bench() error { fmt.Println("Running performance benchmarks...") env := map[string]string{ "CGO_ENABLED": "1", } return sh.RunWith(env, "go", "test", "-bench=.", "-benchmem", "-run=^$", // Don't run regular tests "./tests/performance/...", ) } // Profile runs tests with profiling enabled func (Testing) Profile() error { fmt.Println("Running tests with profiling...") env := map[string]string{ "CGO_ENABLED": "1", } if err := sh.RunWith(env, "go", "test", "-v", "-cpuprofile=cpu.prof", "-memprofile=mem.prof", "-timeout", "20m", "./tests/performance/..."); err != nil { return err } fmt.Println("Profiling data generated:") fmt.Println(" - cpu.prof (CPU profile)") fmt.Println(" - mem.prof (memory profile)") fmt.Println("") fmt.Println("Analyze with:") fmt.Println(" go tool pprof cpu.prof") fmt.Println(" go tool pprof mem.prof") return nil } // Race runs tests with race detection func (Testing) Race() error { fmt.Println("Running tests with race detection...") env := map[string]string{ "CGO_ENABLED": "1", } return sh.RunWith(env, "go", "test", "-v", "-race", "-timeout", "30m", "./tests/...", ) } // Short runs fast tests only (skips slow integration tests) func (Testing) Short() error { fmt.Println("Running short test suite...") env := map[string]string{ "CGO_ENABLED": "1", "GO_TEST_SHORT": "true", } return sh.RunWith(env, "go", "test", "-v", "-short", "-timeout", "10m", "./tests/api/...", "./tests/database/...", ) } // Watch runs tests continuously when files change func (Testing) Watch() error { fmt.Println("Starting test watcher...") fmt.Println("This would require a file watcher implementation") fmt.Println("For now, use: go test ./tests/... -v -watch (with external tool)") return nil } // Validate checks test environment and dependencies func (Testing) Validate() error { fmt.Println("Validating test environment...") // Check Go version if err := sh.Run("go", "version"); err != nil { return fmt.Errorf("Go not available: %w", err) } // Check Docker availability if err := sh.Run("docker", "--version"); err != nil { fmt.Printf("Warning: Docker not available: %v\n", err) } // Check required directories requiredDirs := []string{ "./internal/manager/persistence/migrations", "./pkg/api", "./tests", } for _, dir := range requiredDirs { if _, err := os.Stat(dir); os.IsNotExist(err) { return fmt.Errorf("required directory missing: %s", dir) } } // Check test dependencies deps := []string{ "github.com/stretchr/testify", "github.com/pressly/goose/v3", "modernc.org/sqlite", } for _, dep := range deps { if err := sh.Run("go", "list", "-m", dep); err != nil { return fmt.Errorf("required dependency missing: %s", dep) } } fmt.Println("Test environment validation complete") return nil } // TestData sets up test data files func (Testing) TestData() error { fmt.Println("Setting up test data...") testDataDir := "./tmp/shared-storage" if err := os.MkdirAll(testDataDir, 0755); err != nil { return fmt.Errorf("failed to create test data directory: %w", err) } // Create subdirectories subdirs := []string{ "projects", "renders", "assets", "shaman-checkouts", } for _, subdir := range subdirs { dir := filepath.Join(testDataDir, subdir) if err := os.MkdirAll(dir, 0755); err != nil { return fmt.Errorf("failed to create %s: %w", dir, err) } } // Create simple test files testFiles := map[string]string{ "projects/test.blend": "# Dummy Blender file for testing", "projects/animation.blend": "# Animation test file", "assets/texture.png": "# Dummy texture file", } for filename, content := range testFiles { fullPath := filepath.Join(testDataDir, filename) if err := os.WriteFile(fullPath, []byte(content), 0644); err != nil { return fmt.Errorf("failed to create test file %s: %w", fullPath, err) } } fmt.Printf("Test data created in %s\n", testDataDir) return nil } // CI runs tests in CI/CD environment with proper reporting func (Testing) CI() error { mg.Deps(Testing.Setup, Testing.TestData) fmt.Println("Running tests in CI mode...") env := map[string]string{ "CGO_ENABLED": "1", "CI": "true", "GO_TEST_SHORT": "false", } // Run tests with JSON output for CI parsing if err := sh.RunWith(env, "go", "test", "-v", "-timeout", "45m", "-race", "-coverprofile=coverage.out", "-coverpkg=./...", "-json", "./tests/..."); err != nil { return fmt.Errorf("CI tests failed: %w", err) } // Generate coverage reports if err := (Testing{}).Coverage(); err != nil { fmt.Printf("Warning: failed to generate coverage reports: %v\n", err) } return nil } // Status shows the current test environment status func (Testing) Status() error { fmt.Println("Test Environment Status:") fmt.Println("=======================") // Check Go environment fmt.Println("\n### Go Environment ###") sh.Run("go", "version") sh.Run("go", "env", "GOROOT", "GOPATH", "CGO_ENABLED") // Check test directories fmt.Println("\n### Test Directories ###") testDirs := []string{ "./tests", "./tmp/test-data", "./tmp/shared-storage", } for _, dir := range testDirs { if stat, err := os.Stat(dir); err == nil { if stat.IsDir() { fmt.Printf("✓ %s (exists)\n", dir) } else { fmt.Printf("✗ %s (not a directory)\n", dir) } } else { fmt.Printf("✗ %s (missing)\n", dir) } } // Check Docker environment fmt.Println("\n### Docker Environment ###") if err := sh.Run("docker", "--version"); err != nil { fmt.Printf("✗ Docker not available: %v\n", err) } else { fmt.Println("✓ Docker available") // Check if test containers are running output, err := sh.Output("docker", "ps", "--filter", "name=flamenco-test", "--format", "{{.Names}}") if err == nil && output != "" { fmt.Println("Running test containers:") containers := strings.Split(strings.TrimSpace(output), "\n") for _, container := range containers { if container != "" { fmt.Printf(" - %s\n", container) } } } else { fmt.Println("No test containers running") } } // Check recent test artifacts fmt.Println("\n### Test Artifacts ###") artifacts := []string{ "coverage.out", "coverage.html", "test-results.json", "cpu.prof", "mem.prof", } for _, artifact := range artifacts { if stat, err := os.Stat(artifact); err == nil { fmt.Printf("✓ %s (modified: %s)\n", artifact, stat.ModTime().Format("2006-01-02 15:04:05")) } else { fmt.Printf("✗ %s (missing)\n", artifact) } } fmt.Println("\nUse 'mage test:setup' to initialize test environment") fmt.Println("Use 'mage test:all' to run comprehensive test suite") return nil }