core/core_test.go
core/core_test.goBrowse 1970 files
1,895 tokens
7,138 bytes
Token encoding: o200k_base
Snapshot 21a254f
← Back to SKILL.md
1package core2 3import (4 "errors"5 "fmt"6 "maps"7 "os"8 "path/filepath"9 "slices"10 "testing"11 12 "github.com/gkampitakis/go-snaps/snaps"13 "github.com/railwayapp/railpack/core/app"14 "github.com/railwayapp/railpack/core/logger"15 "github.com/railwayapp/railpack/core/mise"16 "github.com/railwayapp/railpack/core/plan"17 "github.com/stretchr/testify/require"18)19 20func TestMain(m *testing.M) {21 v := m.Run()22 snaps.Clean(m, snaps.CleanOpts{Sort: true})23 os.Exit(v)24}25 26// generate snapshot plan JSON for each build example and assert against it27func TestGenerateBuildPlanForExamples(t *testing.T) {28 wd, err := os.Getwd()29 require.NoError(t, err)30 31 // Get all the examples32 examplesDir := filepath.Join(filepath.Dir(wd), "examples")33 entries, err := os.ReadDir(examplesDir)34 require.NoError(t, err)35 36 for _, entry := range entries {37 if !entry.IsDir() {38 continue39 }40 41 // For each example, generate a build plan that we can snapshot test42 t.Run(entry.Name(), func(t *testing.T) {43 examplePath := filepath.Join(examplesDir, entry.Name())44 45 userApp, err := app.NewApp(examplePath)46 require.NoError(t, err)47 48 env := app.NewEnvironment(nil)49 buildResult, err := GenerateBuildPlan(userApp, env, &GenerateBuildPlanOptions{})50 require.NoError(t, err)51 52 if !buildResult.Success {53 t.Fatalf("failed to generate build plan for %s: %s", entry.Name(), buildResult.Logs)54 }55 56 plan := buildResult.Plan57 58 // Remove the generated-mise-toml asset since the versions may change between runs59 for _, step := range plan.Steps {60 for name := range step.Assets {61 if name == "generated-mise-toml" {62 step.Assets[name] = "[generated-mise-toml]"63 }64 }65 }66 67 snaps.MatchStandaloneJSON(t, plan)68 })69 }70}71 72func TestFailedBuildResult(t *testing.T) {73 temporary := fmt.Errorf("failed to ensure mise is installed: %w",74 &mise.TemporaryError{URL: "https://github.com/jdx/mise", Err: errors.New("i/o timeout")})75 76 result, err := failedBuildResult(logger.NewLogger(), temporary)77 require.ErrorIs(t, err, temporary, "transient failures must reach the caller so they can be retried")78 require.False(t, result.Success)79 require.NotEmpty(t, result.Logs)80 81 result, err = failedBuildResult(logger.NewLogger(), errors.New("no start command was found"))82 require.NoError(t, err, "deterministic failures are reported in the build result only")83 require.False(t, result.Success)84 require.NotEmpty(t, result.Logs)85}86 87func TestDisablePlanCaches(t *testing.T) {88 newPlan := func() *plan.BuildPlan {89 return &plan.BuildPlan{90 Caches: map[string]*plan.Cache{91 "gradle": {Directory: "/root/.gradle"},92 "maven": {Directory: "/root/.m2/repository"},93 "npm-install": {Directory: "/root/.npm"},94 },95 Steps: []plan.Step{96 {Caches: []string{"gradle", "maven"}},97 {Caches: []string{"npm-install"}},98 },99 }100 }101 102 tests := []struct {103 name string104 disabledCaches []string105 expectedCaches []string106 expectedSteps [][]string107 }{108 {109 name: "named caches",110 disabledCaches: []string{"gradle", "npm-install"},111 expectedCaches: []string{"maven"},112 expectedSteps: [][]string{{"maven"}, nil},113 },114 {115 name: "all caches with redundant named cache",116 disabledCaches: []string{"*", "gradle"},117 expectedCaches: nil,118 expectedSteps: [][]string{nil, nil},119 },120 {121 name: "unknown cache",122 disabledCaches: []string{"unknown"},123 expectedCaches: []string{"gradle", "maven", "npm-install"},124 expectedSteps: [][]string{{"gradle", "maven"}, {"npm-install"}},125 },126 }127 128 for _, tt := range tests {129 t.Run(tt.name, func(t *testing.T) {130 buildPlan := newPlan()131 disablePlanCaches(buildPlan, tt.disabledCaches)132 133 require.Equal(t, tt.expectedCaches, slices.Sorted(maps.Keys(buildPlan.Caches)))134 for i, expected := range tt.expectedSteps {135 require.Equal(t, expected, buildPlan.Steps[i].Caches)136 }137 })138 }139}140 141func TestGenerateBuildPlan_DisableCachesRequiresEnvironmentArgument(t *testing.T) {142 userApp, err := app.NewApp("../examples/java-gradle")143 require.NoError(t, err)144 145 t.Setenv("RAILPACK_DISABLE_CACHES", "gradle")146 buildResult, err := GenerateBuildPlan(userApp, app.NewEnvironment(nil), &GenerateBuildPlanOptions{})147 require.NoError(t, err)148 require.True(t, buildResult.Success)149 require.Contains(t, buildResult.Plan.Caches, "gradle")150 151 envVars := map[string]string{"RAILPACK_DISABLE_CACHES": "gradle"}152 buildResult, err = GenerateBuildPlan(userApp, app.NewEnvironment(&envVars), &GenerateBuildPlanOptions{})153 require.NoError(t, err)154 require.True(t, buildResult.Success)155 require.NotContains(t, buildResult.Plan.Caches, "gradle")156 for _, step := range buildResult.Plan.Steps {157 require.NotContains(t, step.Caches, "gradle")158 }159}160 161func TestGenerateConfigFromFile_NotFound(t *testing.T) {162 // Use an existing example app directory so relative paths resolve163 appPath := "../examples/config-file"164 userApp, err := app.NewApp(appPath)165 require.NoError(t, err)166 167 env := app.NewEnvironment(nil)168 l := logger.NewLogger()169 170 options := &GenerateBuildPlanOptions{ConfigFilePath: "does-not-exist.railpack.json"}171 cfg, genErr := GenerateConfigFromFile(userApp, env, options, l)172 173 require.Error(t, genErr, "expected an error when explicit config file does not exist")174 require.Nil(t, cfg, "config should be nil on error")175}176 177func TestGenerateConfigFromFile_Malformed(t *testing.T) {178 appPath := "../examples/config-file"179 userApp, err := app.NewApp(appPath)180 require.NoError(t, err)181 182 env := app.NewEnvironment(nil)183 l := logger.NewLogger()184 185 options := &GenerateBuildPlanOptions{ConfigFilePath: "railpack.malformed.json"}186 cfg, genErr := GenerateConfigFromFile(userApp, env, options, l)187 188 require.Error(t, genErr, "expected an error for malformed JSON config file")189 require.Nil(t, cfg, "config should be nil on error")190}191 192func TestGetConfig_MergesEnvironmentAndFileSecrets(t *testing.T) {193 tempDir := t.TempDir()194 configPath := filepath.Join(tempDir, defaultConfigFileName)195 err := os.WriteFile(configPath, []byte(`{"secrets":["VITE_APP_TITLE","FILE_ONLY_SECRET"]}`), 0644)196 require.NoError(t, err)197 198 userApp, err := app.NewApp(tempDir)199 require.NoError(t, err)200 201 envVars := map[string]string{202 "DATABASE_URL": "postgres://localhost/mydb",203 "SENTRY_AUTH_TOKEN": "sntrx_abc",204 "VITE_APP_TITLE": "MyApp",205 "MY_SECRET": "s3cret",206 }207 208 env := app.NewEnvironment(&envVars)209 config, err := GetConfig(userApp, env, &GenerateBuildPlanOptions{}, logger.NewLogger())210 require.NoError(t, err)211 212 require.ElementsMatch(t, []string{213 "DATABASE_URL",214 "SENTRY_AUTH_TOKEN",215 "VITE_APP_TITLE",216 "MY_SECRET",217 "FILE_ONLY_SECRET",218 }, config.Secrets)219}220 221func TestGenerateBuildPlan_DockerignoreMetadata(t *testing.T) {222 appPath := "../examples/dockerignore"223 userApp, err := app.NewApp(appPath)224 require.NoError(t, err)225 226 env := app.NewEnvironment(nil)227 buildResult, err := GenerateBuildPlan(userApp, env, &GenerateBuildPlanOptions{})228 require.NoError(t, err)229 230 require.True(t, buildResult.Success)231 require.NotNil(t, buildResult.Metadata)232 require.Equal(t, "true", buildResult.Metadata["dockerIgnore"])233 require.NotEmpty(t, buildResult.Plan.Exclude)234}235