Commit fa3a90b4 authored by Addshore's avatar Addshore 🏄 Committed by Addshore
Browse files

Refactor dotenv stuff to internal/util with tests

This was prep work for maybe taking a look at T284227
parent 489dc8c8
......@@ -20,7 +20,7 @@ package cmd
import (
"fmt"
"gerrit.wikimedia.org/r/mediawiki/tools/cli/internal/env"
"gerrit.wikimedia.org/r/mediawiki/tools/cli/internal/util/dotenv"
"github.com/spf13/cobra"
)
......@@ -43,7 +43,7 @@ func EnvDelete(directory func() string) *cobra.Command {
Short: "Deletes an environment variable",
Args: cobra.MinimumNArgs(1),
Run: func(cmd *cobra.Command, args []string) {
env.DotFileForDirectory(directory()).Delete(args[0])
dotenv.FileForDirectory(directory()).Delete(args[0])
},
}
}
......@@ -55,7 +55,7 @@ func EnvSet(directory func() string) *cobra.Command {
Short: "Set an environment variable",
Args: cobra.MinimumNArgs(2),
Run: func(cmd *cobra.Command, args []string) {
env.DotFileForDirectory(directory()).Set(args[0], args[1])
dotenv.FileForDirectory(directory()).Set(args[0], args[1])
},
}
}
......@@ -67,7 +67,7 @@ func EnvGet(directory func() string) *cobra.Command {
Short: "Get an environment variable",
Args: cobra.MinimumNArgs(1),
Run: func(cmd *cobra.Command, args []string) {
fmt.Println(env.DotFileForDirectory(directory()).Get(args[0]))
fmt.Println(dotenv.FileForDirectory(directory()).Get(args[0]))
},
}
}
......@@ -78,7 +78,7 @@ func EnvList(directory func() string) *cobra.Command {
Use: "list",
Short: "List all environment variables",
Run: func(cmd *cobra.Command, args []string) {
for name, value := range env.DotFileForDirectory(directory()).List() {
for name, value := range dotenv.FileForDirectory(directory()).List() {
fmt.Println(name + "=" + value)
}
},
......@@ -91,7 +91,7 @@ func EnvWhere(directory func() string) *cobra.Command {
Use: "where",
Short: "Output the location of the .env file",
Run: func(cmd *cobra.Command, args []string) {
fmt.Println(env.DotFileForDirectory(directory()).Path())
fmt.Println(dotenv.FileForDirectory(directory()).Path())
},
}
}
......@@ -22,9 +22,9 @@ import (
"os"
"os/user"
"gerrit.wikimedia.org/r/mediawiki/tools/cli/internal/env"
"gerrit.wikimedia.org/r/mediawiki/tools/cli/internal/exec"
"gerrit.wikimedia.org/r/mediawiki/tools/cli/internal/mwdd/files"
"gerrit.wikimedia.org/r/mediawiki/tools/cli/internal/util/dotenv"
)
/*MWDD representation of a mwdd v2 setup*/
......@@ -78,8 +78,8 @@ func (m MWDD) DockerComposeProjectName() string {
}
/*Env ...*/
func (m MWDD) Env() env.DotFile {
return env.DotFileForDirectory(m.Directory())
func (m MWDD) Env() dotenv.File {
return dotenv.FileForDirectory(m.Directory())
}
/*EnsureReady ...*/
......
/*Package env for interacting with a .env file
/*Package dotenv for interacting with a .env file
Copyright © 2020 Addshore
......@@ -15,50 +15,50 @@ GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package env
package dotenv
import (
"log"
"os"
"path/filepath"
"strings"
"github.com/joho/godotenv"
)
/*DotFile representation of a .env file*/
type DotFile string
/*File location of the .env file to work on*/
type File string
/*DotFileForDirectory returns a dotFIle for the given directory*/
func DotFileForDirectory(directory string) DotFile {
return DotFile(directory + string(os.PathSeparator) + ".env")
/*FileForDirectory returns the File for the given directory*/
func FileForDirectory(directory string) File {
return File(directory + string(os.PathSeparator) + ".env")
}
/*Path the path of the .env file*/
func (f DotFile) Path() string {
func (f File) Path() string {
return string(f)
}
/*EnsureExists ensures that the .env file exists, creating an empty one if not*/
func (f DotFile) EnsureExists() {
/*EnsureExists ensures that the File exists, creating an empty one if not*/
func (f File) EnsureExists() {
if _, err := os.Stat(f.Path()); err != nil {
err := os.MkdirAll(strings.Replace(f.Path(), ".env", "", -1), 0700)
err := os.MkdirAll(strings.Replace(f.Path(), filepath.Base(f.Path()), "", -1), 0700)
if err != nil {
log.Fatal(err)
panic(err)
}
_, err = os.OpenFile(f.Path(), os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0600)
if err != nil {
log.Fatal(err)
panic(err)
}
}
}
func (f DotFile) read() map[string]string {
func (f File) read() map[string]string {
f.EnsureExists()
envMap, _ := godotenv.Read(f.Path())
return envMap
}
func (f DotFile) write(envMap map[string]string) {
func (f File) write(envMap map[string]string) {
// Override the regular gotdotenv Write method to avoid adding quotes
// https://github.com/joho/godotenv/issues/50#issuecomment-364873528
// https://github.com/moby/moby/issues/12997
......@@ -67,38 +67,38 @@ func (f DotFile) write(envMap map[string]string) {
}
/*Delete a value from the env*/
func (f DotFile) Delete(name string) {
func (f File) Delete(name string) {
envMap := f.read()
delete(envMap, name)
f.write(envMap)
}
/*Set a value in the env*/
func (f DotFile) Set(name string, value string) {
func (f File) Set(name string, value string) {
envMap := f.read()
envMap[name] = value
f.write(envMap)
}
/*Get a value from the env*/
func (f DotFile) Get(name string) string {
func (f File) Get(name string) string {
envMap := f.read()
return envMap[name]
}
/*Has a value in the env*/
func (f DotFile) Has(name string) bool {
func (f File) Has(name string) bool {
envMap := f.read()
_, ok := envMap[name]
return ok
}
/*Missing a value in the env*/
func (f DotFile) Missing(name string) bool {
func (f File) Missing(name string) bool {
return !f.Has(name)
}
/*List all values from the env*/
func (f DotFile) List() map[string]string {
func (f File) List() map[string]string {
return f.read()
}
/*Package dotenv for interacting with a .env file
Copyright © 2020 Addshore
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package dotenv
import (
"io/ioutil"
"math/rand"
"os"
"strings"
"testing"
"time"
)
func randomString() string {
// A bit of randomness so that we dont need to open a file for our non existant test
rand.Seed(time.Now().UnixNano())
chars := []rune("ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890")
var b strings.Builder
for i := 0; i < 10; i++ {
b.WriteRune(chars[rand.Intn(len(chars))])
}
return b.String()
}
func TestFile_Path(t *testing.T) {
tests := []struct {
name string
f File
want string
}{
{
name: "Path even if it doesnt exist",
f: File("/tmp/mwcli-test-dotenv-not-created"),
want: "/tmp/mwcli-test-dotenv-not-created",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := tt.f.Path(); got != tt.want {
t.Errorf("File.Path() = %v, want %v", got, tt.want)
}
})
}
}
func TestFile_EnsureExists(t *testing.T) {
emptyDotEnvPath := "/tmp/mwcli-test-dotenv-" + randomString()
emptyDotEnv, err := os.Create(emptyDotEnvPath)
if err != nil {
panic(err)
}
emptyDotEnv.Close()
tests := []struct {
name string
f File
}{
{
name: "EnsureExists creates file",
f: File("/tmp/mwcli-test-dotenv-" + randomString()),
},
{
name: "EnsureExists works with existing file",
f: File(emptyDotEnvPath),
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
tt.f.EnsureExists()
// Check it exists
_, err := os.Stat(tt.f.Path())
if err != nil {
t.Errorf("File.EnsureExists() failed to create file: %v", err)
}
// And is empty
b, err := ioutil.ReadFile(tt.f.Path())
if string(b) != "" {
t.Errorf("File.EnsureExists() failed to create empty file: %v", err)
}
})
}
}
func TestFile_GeneralIntegration(t *testing.T) {
envFile := File("/tmp/mwcli-test-dotenv-" + randomString())
envFile.EnsureExists()
type args struct {
name string
}
tests := []struct {
name string
f File
applyAndIsGood func(f File) bool
expectList map[string]string
}{
{
name: "Has no non existing value",
applyAndIsGood: func(f File) bool {
return f.Has("FOO") == false
},
},
{
name: "So it is missing",
applyAndIsGood: func(f File) bool {
return f.Missing("FOO") == true
},
},
{
name: "Set a single value, now has it",
applyAndIsGood: func(f File) bool {
f.Set("FOO", "AVALUE")
return f.Has("FOO") == true
},
},
{
name: "Set another, now we have 2",
applyAndIsGood: func(f File) bool {
f.Set("BAR", "BVALUE")
return f.Has("FOO") == true && f.Get("BAR") == "BVALUE"
},
},
{
name: "Set one that already exists to a new value, it changes",
applyAndIsGood: func(f File) bool {
f.Set("FOO", "CVALUE")
return f.Get("FOO") == "CVALUE"
},
},
{
name: "They appear in List too",
applyAndIsGood: func(f File) bool {
return f.List()["FOO"] == "CVALUE" && f.List()["BAR"] == "BVALUE" && len(f.List()) == 2
},
},
{
name: "Delete one, and we have 1",
applyAndIsGood: func(f File) bool {
f.Delete("BAR")
return f.Has("FOO") == true && f.Has("BAR") == false
},
},
{
name: "Delete the last one, and it is empty",
applyAndIsGood: func(f File) bool {
f.Delete("FOO")
return len(f.List()) == 0
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
isGood := tt.applyAndIsGood(envFile)
if !isGood {
t.Errorf("%q failed "+envFile.Path(), tt.name)
}
})
}
}
/*Package env for interacting with a .env file
/*Package dotenv for interacting with a .env file
Copyright © 2020 Addshore
......@@ -15,7 +15,7 @@ GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package env
package dotenv
import (
"fmt"
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment