package test import ( "bytes" "go/parser" "go/token" "os" "os/exec" "path/filepath" "strings" "testing" ) var variantUseCaseFiles = []string{ "ListVariants.go", "CreateVariant.go", "UpdateVariant.go", "SetVariantEnabled.go", "DeleteVariant.go", "PurgeVariant.go", "ServeVariant.go", "variantCache.go", "variantSettings.go", "variantSummary.go", "mediaRemover.go", "DeleteMedia.go", "MergeMedia.go", } var newVariantGoFiles = []string{ "internal/domain/image/variantDomain.go", "internal/domain/image/variantRepositoryInterface.go", "internal/domain/image/variantRequest.go", "internal/domain/image/animatedGif.go", "internal/infrastructure/imaging/variantEncoders.go", "internal/infrastructure/imaging/variantGeometry.go", "internal/infrastructure/imaging/variantRenderer.go", "internal/infrastructure/config/imageVariantSettings.go", "internal/infrastructure/persistence/models/imageVariantModel.go", "internal/infrastructure/persistence/models/imageVariantFileModel.go", "internal/infrastructure/persistence/mappers/imageVariantMapper.go", "internal/infrastructure/persistence/repositories/imageVariantRepository.go", "internal/infrastructure/persistence/repositories/imageVariantFileRepository.go", "api/handlers/imagevariant/imageVariantHandler.go", "api/handlers/staticimages/staticImagesHandler.go", } func importsOf(t *testing.T, relative string) []string { t.Helper() parsed, err := parser.ParseFile(token.NewFileSet(), "../"+relative, nil, parser.ImportsOnly) if err != nil { t.Fatalf("cannot parse %s: %v", relative, err) } imports := []string{} for _, spec := range parsed.Imports { imports = append(imports, strings.Trim(spec.Path.Value, `"`)) } return imports } func isStandardLibrary(path string) bool { return !strings.Contains(strings.Split(path, "/")[0], ".") } func TestImageVariantLayering(t *testing.T) { t.Run("internal/domain/image imports only the stdlib and other domain packages", func(t *testing.T) { files, _ := filepath.Glob("../internal/domain/image/*.go") for _, file := range files { for _, path := range importsOf(t, strings.TrimPrefix(filepath.ToSlash(file), "../")) { if !isStandardLibrary(path) && !strings.HasPrefix(path, "RenewCMS/internal/domain/") { t.Errorf("%s imports %s", file, path) } } } }) t.Run("internal/domain/audit still imports only the stdlib and domain packages", func(t *testing.T) { files, _ := filepath.Glob("../internal/domain/audit/*.go") for _, file := range files { for _, path := range importsOf(t, strings.TrimPrefix(filepath.ToSlash(file), "../")) { if !isStandardLibrary(path) && !strings.HasPrefix(path, "RenewCMS/internal/domain/") { t.Errorf("%s imports %s", file, path) } } } }) t.Run("The variant use cases import no persistence, mailer, models, gorm, net/http, chi or api package", func(t *testing.T) { forbidden := []string{"persistence", "mailer", "models", "gorm", "net/http", "go-chi", "RenewCMS/api"} for _, name := range variantUseCaseFiles { for _, path := range importsOf(t, "internal/infrastructure/useCases/"+name) { for _, fragment := range forbidden { if strings.Contains(path, fragment) { t.Errorf("%s imports %s", name, path) } } } } }) t.Run("The variant handlers import no persistence, mailer, models or gorm package", func(t *testing.T) { for _, file := range []string{"api/handlers/imagevariant/imageVariantHandler.go", "api/handlers/staticimages/staticImagesHandler.go"} { for _, path := range importsOf(t, file) { for _, fragment := range []string{"persistence", "mailer", "models", "gorm"} { if strings.Contains(path, fragment) { t.Errorf("%s imports %s", file, path) } } } } }) t.Run("The renderer is free of gorm and http", func(t *testing.T) { for _, name := range []string{"variantEncoders.go", "variantGeometry.go", "variantRenderer.go"} { for _, path := range importsOf(t, "internal/infrastructure/imaging/"+name) { if strings.Contains(path, "gorm") || strings.Contains(path, "net/http") { t.Errorf("%s imports %s", name, path) } } } }) t.Run("Only api/dependecyInjection.go constructs the variant repositories, the renderer and the use cases", func(t *testing.T) { constructors := []string{"NewVariantRepository(", "NewVariantFileRepository(", "NewVariantRenderer(", "NewListVariantsUseCase(", "NewCreateVariantUseCase(", "NewUpdateVariantUseCase(", "NewSetVariantEnabledUseCase(", "NewDeleteVariantUseCase(", "NewPurgeVariantUseCase(", "NewServeVariantUseCase("} for _, root := range []string{"../api", "../internal", "../cmd"} { _ = filepath.Walk(root, func(path string, info os.FileInfo, err error) error { if err != nil || info.IsDir() || !strings.HasSuffix(path, ".go") || filepath.Base(path) == "dependecyInjection.go" { return err } content, _ := os.ReadFile(path) for _, constructor := range constructors { if bytes.Contains(content, []byte(constructor)) && !definesConstructor(content, constructor) { t.Errorf("%s calls %s", path, constructor) } } return nil }) } }) t.Run("The new GORM models are registered in migrate.go", func(t *testing.T) { migrate := readRepositoryFile(t, "internal/infrastructure/persistence/database/migrate.go") if !strings.Contains(migrate, "&models.ImageVariant{}") || !strings.Contains(migrate, "&models.ImageVariantFile{}") { t.Error("a variant model is missing from AutoMigrate") } }) } func definesConstructor(content []byte, constructor string) bool { return bytes.Contains(content, []byte("func "+constructor)) } func TestImageVariantFilesHaveNoComments(t *testing.T) { for _, relative := range newVariantGoFiles { t.Run(relative, func(t *testing.T) { parsed, err := parser.ParseFile(token.NewFileSet(), "../"+relative, nil, parser.ParseComments) if err != nil { t.Fatal(err) } for _, group := range parsed.Comments { for _, comment := range group.List { if !strings.HasPrefix(comment.Text, "//go:") { t.Errorf("comment %q", comment.Text) } } } }) } } func TestImageVariantBuildStaysCGOFree(t *testing.T) { if testing.Short() { t.Skip("builds the module") } environment := append(os.Environ(), "CGO_ENABLED=0") t.Run("The module builds with CGO_ENABLED=0 and the nofrontend tag", func(t *testing.T) { build := exec.Command("go", "build", "-tags", "nofrontend", "./...") build.Dir, build.Env = "..", environment if output, err := build.CombinedOutput(); err != nil { t.Fatalf("build failed: %v\n%s", err, output) } }) t.Run("No dependency compiles cgo files", func(t *testing.T) { list := exec.Command("go", "list", "-tags", "nofrontend", "-deps", "-f", "{{if .CgoFiles}}{{.ImportPath}}{{end}}", "./...") list.Dir, list.Env = "..", environment output, err := list.Output() if err != nil { t.Fatalf("go list failed: %v", err) } if strings.TrimSpace(string(output)) != "" { t.Errorf("packages with cgo files: %s", output) } }) }