package lift import ( "os" "path/filepath" "testing" "github.com/bazelbuild/rules_go/go/tools/bazel" "github.com/stretchr/testify/assert" "gopkg.in/yaml.v3" "edge-infra.dev/pkg/lib/cli/sh" ) var ( dummyInfraCfg = CapabilityConfig{ "fake/infra-package", ResourceMatcher{ APIGroups: []string{"fake.apigroup.u"}, }, } dummyRuntimeCfg = CapabilityConfig{ "prometheus", ResourceMatcher{ APIGroups: []string{"prometheus.real.apigroup.io"}, }, } dummyRuntimeCfg2 = CapabilityConfig{ "linkerd", ResourceMatcher{ APIGroups: []string{"linkerd.real.apigroup.io"}, }, } infraOnlyCfg = Config{ Infrastructure: dummyInfraCfg, } dummyParameters1 = []Parameter{ {"special_thing_i_must_parameterize", 64, "simply got to have it"}, {"config_isnt_my_forte", 32, "at least im honest"}, } dummyParameters2 = []Parameter{ {"foobie", 12, "foobie brothers"}, {"scooby", 48, "snacks"}, } ) func TestConfig_New(t *testing.T) { dir := bazel.TestTmpDir() defer os.RemoveAll(dir) cfg := &Config{Infrastructure: dummyInfraCfg} cfgpath := filepath.Join(dir, "warehouse.yaml") writeCfg(t, cfg, cfgpath) t.Run("Environment variables", func(t *testing.T) { whpath := "/test/path" t.Setenv("WAREHOUSE_PATH", whpath) cachepath := "test/cache" t.Setenv("WAREHOUSE_CACHE", cachepath) repo := "us-east1-docker.pkg.dev" t.Setenv("WAREHOUSE_REPO", repo) t.Setenv("WAREHOUSE_CONFIG", cfgpath) actual, err := NewConfig() assert.NoError(t, err) assert.Equal(t, "/test/path", actual.WAREHOUSEPATH) assert.Equal(t, "test/cache", actual.Cache) assert.Equal(t, "us-east1-docker.pkg.dev", actual.Repo) // Test that environment variables take precedence over config file values. cfg := &Config{ WAREHOUSEPATH: "/should/be/ignored", Cache: "should/be/ignored", Repo: "ignored.pkg.dev", Infrastructure: dummyInfraCfg, } writeCfg(t, cfg, cfgpath) actual, err = NewConfig() assert.NoError(t, err) assert.Equal(t, "/test/path", actual.WAREHOUSEPATH) assert.Equal(t, "test/cache", actual.Cache) assert.Equal(t, "us-east1-docker.pkg.dev", actual.Repo) }) t.Run("Cache falls back to HOME", func(t *testing.T) { writeCfg(t, &Config{ Infrastructure: dummyInfraCfg, WAREHOUSEPATH: "/empty", }, cfgpath) t.Setenv("WAREHOUSE_CONFIG", cfgpath) t.Setenv("HOME", "fake/home/for/fake/user") actual, err := NewConfig() assert.NoError(t, err) assert.Equal(t, "fake/home/for/fake/user/.cache/warehouse", actual.Cache) }) t.Run("WAREHOUSE_PATH falls back to repo root", func(t *testing.T) { repo := createTestGitRepo(t, dir) assert.NoError(t, os.Chdir(repo)) writeCfg(t, &Config{Infrastructure: dummyInfraCfg, Cache: "empty"}, cfgpath) t.Setenv("WAREHOUSE_CONFIG", cfgpath) actual, err := NewConfig() assert.NoError(t, err) assert.Equal(t, repo, actual.WAREHOUSEPATH) }) t.Run("Config file falls back to root of WAREHOUSE_PATH", func(t *testing.T) { cfgpath := filepath.Join(dir, ConfigFile) writeCfg(t, &Config{Cache: "empty", Infrastructure: dummyInfraCfg}, cfgpath) t.Setenv("WAREHOUSE_PATH", dir) _, err := NewConfig() // If the config file isn't located, we will error due to missing // infrastructure field assert.NoError(t, err) }) t.Run("Config file is loaded from inferred WAREHOUSE_PATH", func(t *testing.T) { repo := createTestGitRepo(t, dir) assert.NoError(t, os.Chdir(repo)) writeCfg(t, &Config{ WAREHOUSEPATH: repo, Infrastructure: dummyInfraCfg, Cache: "empty", }, filepath.Join(repo, ConfigFile)) actual, err := NewConfig() assert.NoError(t, err) assert.Equal(t, "empty", actual.Cache) }) } func TestConfig_Validate(t *testing.T) { tcs := map[string]struct { cfg Config err bool }{ "minimal": { cfg: Config{WAREHOUSEPATH: "/any", Infrastructure: dummyInfraCfg}, err: false, }, "empty": { cfg: Config{}, err: true, }, "no path": { cfg: Config{Infrastructure: dummyInfraCfg}, err: true, }, "relative path": { cfg: Config{WAREHOUSEPATH: "any", Infrastructure: dummyInfraCfg}, err: true, }, } for name, tc := range tcs { t.Run(name, func(t *testing.T) { actual := tc.cfg.Validate() if tc.err && actual == nil { t.Error("expected error") } if !tc.err && actual != nil { t.Error("unexpected error", actual) } }) } } func TestConfig_WithRuntime(t *testing.T) { tcs := map[string]struct { src Config input Runtime expected Config err bool }{ "empty input": { infraOnlyCfg, Runtime{}, infraOnlyCfg, false, }, "source is empty": { Config{}, Runtime{Capabilities: []CapabilityConfig{dummyRuntimeCfg}}, Config{Runtime: Runtime{Capabilities: []CapabilityConfig{dummyRuntimeCfg}}}, false, }, "source is not empty": { Config{Runtime: Runtime{Capabilities: []CapabilityConfig{dummyRuntimeCfg2}}}, Runtime{Capabilities: []CapabilityConfig{dummyRuntimeCfg}}, Config{Runtime: Runtime{Capabilities: []CapabilityConfig{dummyRuntimeCfg2, dummyRuntimeCfg}}}, false, }, "conflict": { Config{Runtime: Runtime{Capabilities: []CapabilityConfig{dummyRuntimeCfg}}}, Runtime{Capabilities: []CapabilityConfig{dummyRuntimeCfg}}, Config{}, true, }, } for name, tc := range tcs { t.Run(name, func(t *testing.T) { actual, err := tc.src.WithRuntime(tc.input) if err != nil && tc.err == false { t.Error("unexpected error", err) } if err == nil && tc.err == true { t.Error("expected error but got nil") } assert.Equal(t, tc.expected, actual) }) } } func TestConfig_WithParameters(t *testing.T) { tcs := map[string]struct { src Config input []Parameter expected Config err bool }{ "empty input": { infraOnlyCfg, nil, infraOnlyCfg, false, }, "source is empty": { Config{}, dummyParameters1, Config{Parameters: dummyParameters1}, false, }, "source is not empty": { Config{Parameters: dummyParameters2}, dummyParameters1, Config{Parameters: append(dummyParameters2, dummyParameters1...)}, false, }, "conflict": { Config{Parameters: dummyParameters1}, dummyParameters1, Config{}, true, }, } for name, tc := range tcs { t.Run(name, func(t *testing.T) { actual, err := tc.src.WithParameters(tc.input) if err != nil && tc.err == false { t.Error("unexpected error", err) } if err == nil && tc.err == true { t.Error("expected error but got nil") } assert.Equal(t, tc.expected, actual) }) } } func writeCfg(t *testing.T, cfg *Config, path string) { t.Helper() data, err := yaml.Marshal(cfg) assert.NoError(t, err) assert.NoError(t, os.WriteFile(path, data, 0644)) } func createTestGitRepo(t *testing.T, dir string) string { t.Helper() repo, err := os.MkdirTemp(dir, "git_test-*") assert.NoError(t, err) t.Log("test repo dir", repo) shell := sh.NewInDir(repo) for _, c := range []string{ "git init -q", "git config user.name \"testbot\"", "git config user.email \"testmail@testworld.com\"", "git checkout -b test", } { assert.NoError(t, shell.RunE(c)) } return repo }