mirror of
https://github.com/hanzoai/pack.git
synced 2026-09-30 23:43:13 +00:00
- 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
158 lines
4.6 KiB
Go
158 lines
4.6 KiB
Go
package core
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
|
|
"github.com/gkampitakis/go-snaps/snaps"
|
|
"github.com/hanzoai/pack/core/app"
|
|
"github.com/hanzoai/pack/core/logger"
|
|
"github.com/hanzoai/pack/core/mise"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func TestMain(m *testing.M) {
|
|
v := m.Run()
|
|
snaps.Clean(m, snaps.CleanOpts{Sort: true})
|
|
os.Exit(v)
|
|
}
|
|
|
|
// generate snapshot plan JSON for each build example and assert against it
|
|
func TestGenerateBuildPlanForExamples(t *testing.T) {
|
|
wd, err := os.Getwd()
|
|
require.NoError(t, err)
|
|
|
|
// Get all the examples
|
|
examplesDir := filepath.Join(filepath.Dir(wd), "examples")
|
|
entries, err := os.ReadDir(examplesDir)
|
|
require.NoError(t, err)
|
|
|
|
for _, entry := range entries {
|
|
if !entry.IsDir() {
|
|
continue
|
|
}
|
|
|
|
// For each example, generate a build plan that we can snapshot test
|
|
t.Run(entry.Name(), func(t *testing.T) {
|
|
examplePath := filepath.Join(examplesDir, entry.Name())
|
|
|
|
userApp, err := app.NewApp(examplePath)
|
|
require.NoError(t, err)
|
|
|
|
env := app.NewEnvironment(nil)
|
|
buildResult, err := GenerateBuildPlan(userApp, env, &GenerateBuildPlanOptions{})
|
|
require.NoError(t, err)
|
|
|
|
if !buildResult.Success {
|
|
t.Fatalf("failed to generate build plan for %s: %s", entry.Name(), buildResult.Logs)
|
|
}
|
|
|
|
plan := buildResult.Plan
|
|
|
|
// Remove the generated-mise-toml asset since the versions may change between runs
|
|
for _, step := range plan.Steps {
|
|
for name := range step.Assets {
|
|
if name == "generated-mise-toml" {
|
|
step.Assets[name] = "[generated-mise-toml]"
|
|
}
|
|
}
|
|
}
|
|
|
|
snaps.MatchStandaloneJSON(t, plan)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestFailedBuildResult(t *testing.T) {
|
|
temporary := fmt.Errorf("failed to ensure mise is installed: %w",
|
|
&mise.TemporaryError{URL: "https://github.com/jdx/mise", Err: errors.New("i/o timeout")})
|
|
|
|
result, err := failedBuildResult(logger.NewLogger(), temporary)
|
|
require.ErrorIs(t, err, temporary, "transient failures must reach the caller so they can be retried")
|
|
require.False(t, result.Success)
|
|
require.NotEmpty(t, result.Logs)
|
|
|
|
result, err = failedBuildResult(logger.NewLogger(), errors.New("no start command was found"))
|
|
require.NoError(t, err, "deterministic failures are reported in the build result only")
|
|
require.False(t, result.Success)
|
|
require.NotEmpty(t, result.Logs)
|
|
}
|
|
|
|
func TestGenerateConfigFromFile_NotFound(t *testing.T) {
|
|
// Use an existing example app directory so relative paths resolve
|
|
appPath := "../examples/config-file"
|
|
userApp, err := app.NewApp(appPath)
|
|
require.NoError(t, err)
|
|
|
|
env := app.NewEnvironment(nil)
|
|
l := logger.NewLogger()
|
|
|
|
options := &GenerateBuildPlanOptions{ConfigFilePath: "does-not-exist.railpack.json"}
|
|
cfg, genErr := GenerateConfigFromFile(userApp, env, options, l)
|
|
|
|
require.Error(t, genErr, "expected an error when explicit config file does not exist")
|
|
require.Nil(t, cfg, "config should be nil on error")
|
|
}
|
|
|
|
func TestGenerateConfigFromFile_Malformed(t *testing.T) {
|
|
appPath := "../examples/config-file"
|
|
userApp, err := app.NewApp(appPath)
|
|
require.NoError(t, err)
|
|
|
|
env := app.NewEnvironment(nil)
|
|
l := logger.NewLogger()
|
|
|
|
options := &GenerateBuildPlanOptions{ConfigFilePath: "railpack.malformed.json"}
|
|
cfg, genErr := GenerateConfigFromFile(userApp, env, options, l)
|
|
|
|
require.Error(t, genErr, "expected an error for malformed JSON config file")
|
|
require.Nil(t, cfg, "config should be nil on error")
|
|
}
|
|
|
|
func TestGetConfig_MergesEnvironmentAndFileSecrets(t *testing.T) {
|
|
tempDir := t.TempDir()
|
|
configPath := filepath.Join(tempDir, defaultConfigFileName)
|
|
err := os.WriteFile(configPath, []byte(`{"secrets":["VITE_APP_TITLE","FILE_ONLY_SECRET"]}`), 0644)
|
|
require.NoError(t, err)
|
|
|
|
userApp, err := app.NewApp(tempDir)
|
|
require.NoError(t, err)
|
|
|
|
envVars := map[string]string{
|
|
"DATABASE_URL": "postgres://localhost/mydb",
|
|
"SENTRY_AUTH_TOKEN": "sntrx_abc",
|
|
"VITE_APP_TITLE": "MyApp",
|
|
"MY_SECRET": "s3cret",
|
|
}
|
|
|
|
env := app.NewEnvironment(&envVars)
|
|
config, err := GetConfig(userApp, env, &GenerateBuildPlanOptions{}, logger.NewLogger())
|
|
require.NoError(t, err)
|
|
|
|
require.ElementsMatch(t, []string{
|
|
"DATABASE_URL",
|
|
"SENTRY_AUTH_TOKEN",
|
|
"VITE_APP_TITLE",
|
|
"MY_SECRET",
|
|
"FILE_ONLY_SECRET",
|
|
}, config.Secrets)
|
|
}
|
|
|
|
func TestGenerateBuildPlan_DockerignoreMetadata(t *testing.T) {
|
|
appPath := "../examples/dockerignore"
|
|
userApp, err := app.NewApp(appPath)
|
|
require.NoError(t, err)
|
|
|
|
env := app.NewEnvironment(nil)
|
|
buildResult, err := GenerateBuildPlan(userApp, env, &GenerateBuildPlanOptions{})
|
|
require.NoError(t, err)
|
|
|
|
require.True(t, buildResult.Success)
|
|
require.NotNil(t, buildResult.Metadata)
|
|
require.Equal(t, "true", buildResult.Metadata["dockerIgnore"])
|
|
require.NotEmpty(t, buildResult.Plan.Exclude)
|
|
}
|