Files
pack/core/plan/dockerignore_test.go
T
zeekay 7844a60aff refactor(pack): modernize to github.com/hanzoai/pack and remove upstream branding
- Migrate module path to github.com/hanzoai/pack across all Go packages
- Remove legacy upstream docs and competitor brand references
- Modernize LLM.md and mise.toml for pristine Hanzo Pack builder
2026-09-04 00:40:42 -07:00

274 lines
8.3 KiB
Go

package plan
import (
"os"
"path/filepath"
"slices"
"testing"
"github.com/hanzoai/pack/core/app"
"github.com/stretchr/testify/require"
)
func TestCheckAndParseDockerignore(t *testing.T) {
t.Run("valid dockerignore file", func(t *testing.T) {
examplePath := filepath.Join("..", "..", "examples", "dockerignore")
testApp, err := app.NewApp(examplePath)
require.NoError(t, err)
patterns, err := checkAndParseDockerignore(testApp)
require.NoError(t, err)
require.NotNil(t, patterns)
require.Contains(t, patterns, "!negation_test/should_exist.txt")
require.Contains(t, patterns, "!negation_test/existing_folder")
// Verify some expected patterns from examples/dockerignore/.dockerignore
// Note: patterns are parsed by the moby/patternmatcher library
expectedPatterns := []string{
".vscode",
".copier", // Leading slash is stripped
".env-specific",
".env*",
"__pycache__", // Trailing slash is stripped
"test", // Leading slash is stripped
"tmp/*", // Leading slash is stripped
"*.log",
"Justfile",
"TODO*", // Leading slash is stripped
"README.md", // Leading slash is stripped
"docker-compose*.yml",
}
for _, expected := range expectedPatterns {
require.Contains(t, patterns, expected, "Expected pattern %s not found in patterns", expected)
}
})
t.Run("inaccessible dockerignore", func(t *testing.T) {
// Create a temporary directory and file
tempDir, err := os.MkdirTemp("", "dockerignore-test")
require.NoError(t, err)
defer func() { _ = os.RemoveAll(tempDir) }()
dockerignorePath := filepath.Join(tempDir, ".dockerignore")
err = os.WriteFile(dockerignorePath, []byte("*.log\nnode_modules\n"), 0644)
require.NoError(t, err)
// Make the file unreadable (this simulates permission errors)
err = os.Chmod(dockerignorePath, 0000)
require.NoError(t, err)
defer func() { _ = os.Chmod(dockerignorePath, 0644) }() // Restore permissions for cleanup
testApp, err := app.NewApp(tempDir)
require.NoError(t, err)
// This should fail with a permission error
patterns, err := checkAndParseDockerignore(testApp)
require.Error(t, err)
require.Contains(t, err.Error(), "error reading .dockerignore")
require.Nil(t, patterns)
})
}
func TestDockerignoreExamples(t *testing.T) {
wd, err := os.Getwd()
require.NoError(t, err)
examplesDir := filepath.Join(filepath.Dir(filepath.Dir(wd)), "examples")
entries, err := os.ReadDir(examplesDir)
require.NoError(t, err)
expectedAsEmpty := []string{"mise-config"}
foundAsEmpty := []string{}
testedExamples := 0
for _, entry := range entries {
if !entry.IsDir() {
continue
}
examplePath := filepath.Join(examplesDir, entry.Name())
dockerignorePath := filepath.Join(examplePath, ".dockerignore")
_, err := os.Stat(dockerignorePath)
if os.IsNotExist(err) {
continue
}
require.NoError(t, err)
testedExamples++
t.Run(entry.Name(), func(t *testing.T) {
testApp, err := app.NewApp(examplePath)
require.NoError(t, err)
ctx, err := NewDockerignoreContext(testApp)
require.NoError(t, err)
require.True(t, ctx.HasFile)
if slices.Contains(expectedAsEmpty, entry.Name()) {
foundAsEmpty = append(foundAsEmpty, entry.Name())
require.Empty(t, ctx.Excludes)
return
}
require.NotEmpty(t, ctx.Excludes)
})
}
require.Positive(t, testedExamples, "expected at least one example with a .dockerignore")
require.ElementsMatch(t, expectedAsEmpty, foundAsEmpty)
}
func TestDockerignoreContext(t *testing.T) {
t.Run("new context", func(t *testing.T) {
tempDir, err := os.MkdirTemp("", "dockerignore-test")
require.NoError(t, err)
defer func() { _ = os.RemoveAll(tempDir) }()
testApp, err := app.NewApp(tempDir)
require.NoError(t, err)
ctx, err := NewDockerignoreContext(testApp)
require.NoError(t, err)
require.NotNil(t, ctx)
require.False(t, ctx.HasFile)
require.Nil(t, ctx.Excludes)
})
t.Run("context with dockerignore file", func(t *testing.T) {
examplePath := filepath.Join("..", "..", "examples", "dockerignore")
testApp, err := app.NewApp(examplePath)
require.NoError(t, err)
ctx, err := NewDockerignoreContext(testApp)
require.NoError(t, err)
require.True(t, ctx.HasFile)
require.NotNil(t, ctx.Excludes)
require.Contains(t, ctx.Excludes, "!negation_test/should_exist.txt")
require.Contains(t, ctx.Excludes, "!negation_test/existing_folder")
})
t.Run("parse nonexistent file", func(t *testing.T) {
tempDir, err := os.MkdirTemp("", "dockerignore-test")
require.NoError(t, err)
defer func() { _ = os.RemoveAll(tempDir) }()
testApp, err := app.NewApp(tempDir)
require.NoError(t, err)
ctx, err := NewDockerignoreContext(testApp)
require.NoError(t, err)
require.False(t, ctx.HasFile)
require.Nil(t, ctx.Excludes)
})
t.Run("parse error handling", func(t *testing.T) {
// Create a temporary directory with an inaccessible .dockerignore
tempDir, err := os.MkdirTemp("", "dockerignore-test")
require.NoError(t, err)
defer func() { _ = os.RemoveAll(tempDir) }()
dockerignorePath := filepath.Join(tempDir, ".dockerignore")
err = os.WriteFile(dockerignorePath, []byte("*.log\n"), 0644)
require.NoError(t, err)
// Make the file unreadable
err = os.Chmod(dockerignorePath, 0000)
require.NoError(t, err)
defer func() { _ = os.Chmod(dockerignorePath, 0644) }()
testApp, err := app.NewApp(tempDir)
require.NoError(t, err)
ctx, err := NewDockerignoreContext(testApp)
require.Error(t, err)
require.Nil(t, ctx)
})
}
func TestDockerignoreDuplicatePatterns(t *testing.T) {
t.Run("duplicate patterns not removed from raw output", func(t *testing.T) {
tempDir, err := os.MkdirTemp("", "dockerignore-test")
require.NoError(t, err)
defer func() { _ = os.RemoveAll(tempDir) }()
// Create test files
err = os.WriteFile(filepath.Join(tempDir, "keep.txt"), []byte("exists"), 0644)
require.NoError(t, err)
// Create .dockerignore with duplicate patterns
dockerignoreContent := `*.log
*.log
node_modules
!keep.txt
!keep.txt
`
err = os.WriteFile(filepath.Join(tempDir, ".dockerignore"), []byte(dockerignoreContent), 0644)
require.NoError(t, err)
testApp, err := app.NewApp(tempDir)
require.NoError(t, err)
ctx, err := NewDockerignoreContext(testApp)
require.NoError(t, err)
// Count occurrences of each pattern
logCount := 0
nodeModulesCount := 0
keepCount := 0
for _, pattern := range ctx.Excludes {
if pattern == "*.log" {
logCount++
}
if pattern == "node_modules" {
nodeModulesCount++
}
if pattern == "!keep.txt" {
keepCount++
}
}
// Duplicates are preserved in raw output (will be handled downstream)
require.Equal(t, 2, logCount, "*.log pattern should appear twice")
require.Equal(t, 1, nodeModulesCount, "node_modules pattern should appear once")
require.Equal(t, 2, keepCount, "!keep.txt pattern should appear twice")
})
}
func TestCheckAndParseDockerignoreWithNegation(t *testing.T) {
t.Run("negated patterns preserved in output", func(t *testing.T) {
tempDir, err := os.MkdirTemp("", "dockerignore-test")
require.NoError(t, err)
defer func() { _ = os.RemoveAll(tempDir) }()
// Create test files
err = os.MkdirAll(filepath.Join(tempDir, "negation_test", "existing_folder"), 0755)
require.NoError(t, err)
err = os.WriteFile(filepath.Join(tempDir, "negation_test", "should_exist.txt"), []byte("exists"), 0644)
require.NoError(t, err)
err = os.WriteFile(filepath.Join(tempDir, "negation_test", "existing_folder", "file.txt"), []byte("exists"), 0644)
require.NoError(t, err)
// Create .dockerignore with mixed negation cases
dockerignoreContent := `
negation_test/*
!negation_test/should_exist.txt
!negation_test/should_not_exist.txt
!negation_test/folder_does_not_exist/
!negation_test/existing_folder/
`
err = os.WriteFile(filepath.Join(tempDir, ".dockerignore"), []byte(dockerignoreContent), 0644)
require.NoError(t, err)
testApp, err := app.NewApp(tempDir)
require.NoError(t, err)
patterns, err := checkAndParseDockerignore(testApp)
require.NoError(t, err)
// All patterns (both exclude and negated) should be preserved
require.Contains(t, patterns, "negation_test/*")
require.Contains(t, patterns, "!negation_test/should_exist.txt")
require.Contains(t, patterns, "!negation_test/should_not_exist.txt")
require.Contains(t, patterns, "!negation_test/folder_does_not_exist")
require.Contains(t, patterns, "!negation_test/existing_folder")
})
}