package config import ( "os" "path/filepath" "strings" "testing" "github.com/dokku/dokku/plugins/common" . "github.com/onsi/gomega" ) var ( testAppName = "test-app-1" dokkuRoot = common.MustGetEnv("DOKKU_ROOT") dokkuLibRoot = common.MustGetEnv("DOKKU_LIB_ROOT") testAppRoot = filepath.Join(dokkuRoot, testAppName) testAppDir = filepath.Join(dokkuLibRoot, "config", testAppName) globalConfigFile = filepath.Join(dokkuLibRoot, "config", "--global", "ENV") ) func setupTests() (err error) { if err := os.Setenv("PLUGIN_PATH", "/var/lib/dokku/plugins"); err != nil { return err } if err := os.Setenv("PLUGIN_ENABLED_PATH", "/var/lib/dokku/plugins/enabled"); err != nil { return err } return os.Setenv("PLUGIN_CORE_AVAILABLE_PATH", "/var/lib/dokku/core-plugins/available") } func setupTestApp() (err error) { Expect(os.MkdirAll(testAppRoot, 0766)).To(Succeed()) Expect(os.MkdirAll(testAppDir, 0766)).To(Succeed()) b := []byte("export testKey=TESTING\n") if err = os.WriteFile(strings.Join([]string{testAppDir, "/ENV"}, ""), b, 0644); err != nil { return } Expect(os.MkdirAll(filepath.Dir(globalConfigFile), 0766)).To(Succeed()) b = []byte("export testKey=GLOBAL_TESTING\nexport globalKey=GLOBAL_VALUE") if err = os.WriteFile(globalConfigFile, b, 0644); err != nil { return } return } func teardownTestApp() { os.RemoveAll(testAppRoot) os.RemoveAll(testAppDir) } func TestConfigGetWithDefault(t *testing.T) { RegisterTestingT(t) Expect(setupTests()).To(Succeed()) Expect(setupTestApp()).To(Succeed()) Expect(GetWithDefault(testAppName, "unknownKey", "UNKNOWN")).To(Equal("UNKNOWN")) Expect(GetWithDefault(testAppName, "testKey", "testKey")).To(Equal("TESTING")) Expect(GetWithDefault(testAppName+"-nonexistent", "testKey", "default")).To(Equal("default")) teardownTestApp() } func TestConfigGet(t *testing.T) { RegisterTestingT(t) Expect(setupTests()).To(Succeed()) Expect(setupTestApp()).To(Succeed()) defer teardownTestApp() expectValue(testAppName, "testKey", "TESTING") expectValue("", "testKey", "GLOBAL_TESTING") expectNoValue(testAppName, "testKey2") expectNoValue("", "testKey2") } func TestConfigSetMany(t *testing.T) { RegisterTestingT(t) Expect(setupTests()).To(Succeed()) Expect(setupTestApp()).To(Succeed()) defer teardownTestApp() expectValue(testAppName, "testKey", "TESTING") vals := []string{"testKey=updated", "testKey2=new"} Expect(CommandSet(testAppName, vals, false, true, false)).To(Succeed()) expectValue(testAppName, "testKey", "updated") expectValue(testAppName, "testKey2", "new") vals = []string{"testKey=updated_global", "testKey2=new_global"} Expect(CommandSet("", vals, true, true, false)).To(Succeed()) expectValue("", "testKey", "updated_global") expectValue("", "testKey2", "new_global") expectValue("", "globalKey", "GLOBAL_VALUE") expectValue(testAppName, "testKey", "updated") expectValue(testAppName, "testKey2", "new") Expect(CommandSet(testAppName+"does_not_exist", vals, false, true, false)).ToNot(Succeed()) } func TestConfigUnsetAll(t *testing.T) { RegisterTestingT(t) Expect(setupTests()).To(Succeed()) Expect(setupTestApp()).To(Succeed()) defer teardownTestApp() expectValue(testAppName, "testKey", "TESTING") expectValue("", "testKey", "GLOBAL_TESTING") Expect(CommandClear(testAppName, false, true)).To(Succeed()) expectNoValue(testAppName, "testKey") expectNoValue(testAppName, "noKey") expectNoValue(testAppName, "globalKey") Expect(CommandClear(testAppName+"does-not-exist", false, true)).ToNot(Succeed()) } func TestConfigUnsetMany(t *testing.T) { RegisterTestingT(t) Expect(setupTests()).To(Succeed()) Expect(setupTestApp()).To(Succeed()) defer teardownTestApp() expectValue(testAppName, "testKey", "TESTING") expectValue("", "testKey", "GLOBAL_TESTING") keys := []string{"testKey", "noKey"} Expect(CommandUnset(testAppName, keys, false, true)).To(Succeed()) expectNoValue(testAppName, "testKey") expectValue("", "testKey", "GLOBAL_TESTING") Expect(CommandUnset(testAppName, keys, false, true)).To(Succeed()) expectNoValue(testAppName, "testKey") expectNoValue(testAppName, "globalKey") Expect(CommandUnset(testAppName+"does-not-exist", keys, false, true)).ToNot(Succeed()) } func TestConfigImport(t *testing.T) { RegisterTestingT(t) Expect(setupTests()).To(Succeed()) Expect(setupTestApp()).To(Succeed()) defer teardownTestApp() tempFile, err := os.CreateTemp("", "test-config-import-*.env") Expect(err).To(Succeed()) defer os.Remove(tempFile.Name()) content := ` testKey=TESTING-updated1 testKey2=TESTING-updated2 ` _, err = tempFile.WriteString(content) Expect(err).To(Succeed()) tempFile.Close() Expect(CommandImport(testAppName, false, false, true, "envfile", tempFile.Name())).To(Succeed()) expectValue(testAppName, "testKey", "TESTING-updated1") expectValue(testAppName, "testKey2", "TESTING-updated2") env, err := LoadAppEnv(testAppName) Expect(err).To(Succeed()) env.Set("testKey", "TESTING-original1") env.Set("testKey2", "TESTING-original2") env.Set("testKey3", "TESTING-original3") env.Write() Expect(CommandImport(testAppName, false, false, true, "envfile", tempFile.Name())).To(Succeed()) expectValue(testAppName, "testKey", "TESTING-updated1") expectValue(testAppName, "testKey2", "TESTING-updated2") expectValue(testAppName, "testKey3", "TESTING-original3") Expect(CommandImport(testAppName, false, true, true, "envfile", tempFile.Name())).To(Succeed()) expectValue(testAppName, "testKey", "TESTING-updated1") expectValue(testAppName, "testKey2", "TESTING-updated2") expectNoValue(testAppName, "testKey3") } func TestConfigImportJSON(t *testing.T) { RegisterTestingT(t) Expect(setupTests()).To(Succeed()) Expect(setupTestApp()).To(Succeed()) defer teardownTestApp() tempFile, err := os.CreateTemp("", "test-config-import-*.json") Expect(err).To(Succeed()) defer os.Remove(tempFile.Name()) content := `{"testKey": "TESTING-updated1", "testKey2": "TESTING-updated2"}` _, err = tempFile.WriteString(content) Expect(err).To(Succeed()) tempFile.Close() Expect(CommandImport(testAppName, false, false, true, "json", tempFile.Name())).To(Succeed()) expectValue(testAppName, "testKey", "TESTING-updated1") expectValue(testAppName, "testKey2", "TESTING-updated2") env, err := LoadAppEnv(testAppName) Expect(err).To(Succeed()) env.Set("testKey", "TESTING-original1") env.Set("testKey2", "TESTING-original2") env.Set("testKey3", "TESTING-original3") env.Write() Expect(CommandImport(testAppName, false, false, true, "json", tempFile.Name())).To(Succeed()) expectValue(testAppName, "testKey", "TESTING-updated1") expectValue(testAppName, "testKey2", "TESTING-updated2") expectValue(testAppName, "testKey3", "TESTING-original3") Expect(CommandImport(testAppName, false, true, true, "json", tempFile.Name())).To(Succeed()) expectValue(testAppName, "testKey", "TESTING-updated1") expectValue(testAppName, "testKey2", "TESTING-updated2") expectNoValue(testAppName, "testKey3") } func TestEnvironmentLoading(t *testing.T) { RegisterTestingT(t) Expect(setupTests()).To(Succeed()) Expect(setupTestApp()).To(Succeed()) defer teardownTestApp() env, err := LoadMergedAppEnv(testAppName) Expect(err).To(Succeed()) v, _ := env.Get("testKey") Expect(v).To(Equal("TESTING")) v, _ = env.Get("globalKey") Expect(v).To(Equal("GLOBAL_VALUE")) Expect(env.Write()).ToNot(Succeed()) env, err = LoadAppEnv(testAppName) env.Set("testKey", "TESTING-updated") env.Set("testKey2", "TESTING-'\n'-updated") env.Write() expectValue(testAppName, "testKey", "TESTING-updated") expectValue(testAppName, "testKey2", "TESTING-'\n'-updated") expectValue("", "testKey", "GLOBAL_TESTING") Expect(err).To(Succeed()) } func TestInvalidKeys(t *testing.T) { RegisterTestingT(t) Expect(setupTests()).To(Succeed()) Expect(setupTestApp()).To(Succeed()) defer teardownTestApp() invalidKeys := []string{"0invalidKey", "invalid:key", "invalid=Key", "!invalidKey"} for _, key := range invalidKeys { Expect(SetMany(testAppName, map[string]string{key: "value"}, false, false)).NotTo(Succeed()) Expect(UnsetMany(testAppName, []string{key}, false)).NotTo(Succeed()) value, ok := Get(testAppName, key) Expect(ok).To(Equal(false)) Expect(value).To(Equal("")) value2 := GetWithDefault(testAppName, key, "default") Expect(value2).To(Equal("default")) } } func TestInvalidEnvOnDisk(t *testing.T) { RegisterTestingT(t) Expect(setupTests()).To(Succeed()) Expect(setupTestApp()).To(Succeed()) defer teardownTestApp() appConfigFile := strings.Join([]string{testAppDir, "/ENV"}, "") b := []byte("export --invalid-key=TESTING\nexport valid_key=value\n") if err := os.WriteFile(appConfigFile, b, 0644); err != nil { return } env, err := LoadAppEnv(testAppName) Expect(err).NotTo(HaveOccurred()) _, ok := env.Get("--invalid-key") Expect(ok).To(Equal(false)) value, ok := env.Get("valid_key") Expect(ok).To(Equal(true)) Expect(value).To(Equal("value")) //LoadAppEnv eliminates it from the file content, err := os.ReadFile(appConfigFile) Expect(err).NotTo(HaveOccurred()) Expect(strings.Contains(string(content), "--invalid-key")).To(BeFalse()) } func expectValue(appName string, key string, expected string) { v, ok := Get(appName, key) Expect(ok).To(Equal(true)) Expect(v).To(Equal(expected)) } func expectNoValue(appName string, key string) { _, ok := Get(appName, key) Expect(ok).To(Equal(false)) }