From 4cdaaa115542daa7be09e516eb15689cdccb4e57 Mon Sep 17 00:00:00 2001 From: Masayoshi Mizutani Date: Sat, 22 Jun 2024 10:32:55 +0900 Subject: [PATCH] Add tests for override and document --- README.md | 25 +++++++++ go.mod | 4 +- go.sum | 8 +-- pkg/usecase/common.go | 10 ++++ pkg/usecase/exec_test.go | 90 ++++++++++++++++++++++++++++++ pkg/usecase/testdata/basic.env | 1 + pkg/usecase/testdata/override1.env | 1 + pkg/usecase/testdata/override2.env | 1 + pkg/usecase/usecase_test.go | 2 +- 9 files changed, 135 insertions(+), 7 deletions(-) create mode 100644 pkg/usecase/testdata/basic.env create mode 100644 pkg/usecase/testdata/override1.env create mode 100644 pkg/usecase/testdata/override2.env diff --git a/README.md b/README.md index 277b303..4fbfded 100644 --- a/README.md +++ b/README.md @@ -88,6 +88,31 @@ You can specify arguments to specify loading environment in same manner with exe ## Advanced Usage +### Overriding environment variable + +`zenv` can override environment variable by multiple `-e` option. + +```sh +$ cat .env +POSTGRES_DB=your_local_db +POSTGRES_USER=test_user +$ cat .env.local +POSTGRES_DB=your_local_dev_db +$ zenv -e .env -e .env.local psql +# Access to your_local_dev_db with test_user +``` + +The priority for loading environment variables is as follows: first, the `-e` option, followed by additional `-e` options, and finally, the arguments of the `zenv` command. + +```sh +$ cat .env1 +COLOR=blue +$ cat .env2 +COLOR=orange +$ zenv -e .env1 -e .env2 COLOR=red echo %COLOR +red +``` + ### Generate random secure value `secret generate` subcommand can generate random value like token and save to KeyChain. diff --git a/go.mod b/go.mod index cbe2fe7..1a4b075 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,7 @@ require ( github.com/google/uuid v1.3.0 github.com/keybase/go-keychain v0.0.0-20221221221913-9be78f6c498b github.com/m-mizutani/goerr v0.1.8 - github.com/m-mizutani/gt v0.0.4-0.20230223020823-2e7042cd92a6 + github.com/m-mizutani/gt v0.0.10 github.com/mattn/go-shellwords v1.0.12 github.com/rs/zerolog v1.29.0 github.com/urfave/cli/v2 v2.24.4 @@ -15,7 +15,7 @@ require ( require ( github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect - github.com/google/go-cmp v0.5.9 // indirect + github.com/google/go-cmp v0.6.0 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.17 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect diff --git a/go.sum b/go.sum index 329ffe4..4273db8 100644 --- a/go.sum +++ b/go.sum @@ -4,16 +4,16 @@ github.com/coreos/go-systemd/v22 v22.3.3-0.20220203105225-a9a7ef127534/go.mod h1 github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= -github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= -github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/keybase/go-keychain v0.0.0-20221221221913-9be78f6c498b h1:k2ZvAPXrDB1Q7fGRdUane+T08K+UaL96qH47Setr/7k= github.com/keybase/go-keychain v0.0.0-20221221221913-9be78f6c498b/go.mod h1:TXh6wFVZNh4iuqbymzy4r3obmj8hTPDlLNnoKNIbvJE= github.com/m-mizutani/goerr v0.1.8 h1:6UtsMmOkJsaYNtAsMNLvWIteZPl1NOxpKFYK5m65vpQ= github.com/m-mizutani/goerr v0.1.8/go.mod h1:fQkXuu06q+oLlp4FkbiTFzI/N/+WAK/Mz1W5kPZ6yzs= -github.com/m-mizutani/gt v0.0.4-0.20230223020823-2e7042cd92a6 h1:aB3qT7U3VGuzOyoz/6QbwE7DxbrFWQBLlbOYwjSSBss= -github.com/m-mizutani/gt v0.0.4-0.20230223020823-2e7042cd92a6/go.mod h1:0MPYSfGBLmYjTduzADVmIqD58ELQ5IfBFiK/f0FmB3k= +github.com/m-mizutani/gt v0.0.10 h1:gJsRcZ0R0kcVAGeahwDAVBCDCwOA/tFw3N1/kh3DnAY= +github.com/m-mizutani/gt v0.0.10/go.mod h1:0MPYSfGBLmYjTduzADVmIqD58ELQ5IfBFiK/f0FmB3k= github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= diff --git a/pkg/usecase/common.go b/pkg/usecase/common.go index 83f41e7..367df74 100644 --- a/pkg/usecase/common.go +++ b/pkg/usecase/common.go @@ -124,6 +124,16 @@ func (x *Usecase) parseArgs(args types.Arguments) (types.Arguments, []*model.Env } } + // Remove duplicated env vars. The last one is used. + unique := make(map[types.EnvKey]*model.EnvVar) + for _, v := range envVars { + unique[v.Key] = v + } + envVars = make([]*model.EnvVar, 0, len(unique)) + for _, v := range unique { + envVars = append(envVars, v) + } + assigned := make([]*model.EnvVar, len(envVars)) for i, v := range envVars { newVar := *v diff --git a/pkg/usecase/exec_test.go b/pkg/usecase/exec_test.go index 5f6b18c..bedd6df 100644 --- a/pkg/usecase/exec_test.go +++ b/pkg/usecase/exec_test.go @@ -2,6 +2,8 @@ package usecase_test import ( "fmt" + "os" + "sort" "testing" "github.com/m-mizutani/gt" @@ -82,6 +84,9 @@ func TestDotEnv(t *testing.T) { gt.Array(t, args). Equal([]types.Argument{"this", "test"}) + sort.Slice(vars, func(i, j int) bool { + return vars[i].Key < vars[j].Key + }) gt.Array(t, vars).Equal([]*model.EnvVar{ { Key: "COLOR", @@ -163,3 +168,88 @@ func TestReplacement(t *testing.T) { })) }) } + +func TestOverride(t *testing.T) { + type testCase struct { + inputFiles []types.FilePath + envVars []*model.EnvVar + } + + runTest := func(tc testCase) func(t *testing.T) { + return func(t *testing.T) { + var called int + uc, mock := usecase.NewWithMock(usecase.WithConfig(&model.Config{ + DotEnvFiles: tc.inputFiles, + })) + mock.ReadFileMock = func(filename types.FilePath) ([]byte, error) { + return os.ReadFile(string(filename)) + } + + mock.ExecMock = func(vars []*model.EnvVar, args types.Arguments) error { + called++ + + sort.Slice(vars, func(i, j int) bool { + return vars[i].Key < vars[j].Key + }) + + gt.Array(t, vars).Equal(tc.envVars) + return nil + } + + gt.NoError(t, uc.Exec(&model.ExecInput{ + Args: types.Arguments{"this", "test"}, + })) + gt.Equal(t, called, 1) + } + } + + t.Run("no override env vars", runTest(testCase{ + inputFiles: []types.FilePath{"testdata/basic.env"}, + envVars: []*model.EnvVar{ + {Key: "COLOR", Value: "blue"}, + }, + })) + + t.Run("override env vars by one file", runTest(testCase{ + inputFiles: []types.FilePath{ + "testdata/basic.env", + "testdata/override1.env", + }, + envVars: []*model.EnvVar{ + {Key: "COLOR", Value: "orange"}, + }, + })) + + t.Run("override env vars by two files", runTest(testCase{ + inputFiles: []types.FilePath{ + "testdata/basic.env", + "testdata/override1.env", + "testdata/override2.env", + }, + envVars: []*model.EnvVar{ + {Key: "COLOR", Value: "red"}, + }, + })) + + t.Run("override env vars prioritize by order", runTest(testCase{ + inputFiles: []types.FilePath{ + "testdata/basic.env", + "testdata/override2.env", + "testdata/override1.env", + }, + envVars: []*model.EnvVar{ + {Key: "COLOR", Value: "orange"}, + }, + })) + + t.Run("override env vars prioritize by order (2)", runTest(testCase{ + inputFiles: []types.FilePath{ + "testdata/override2.env", + "testdata/override1.env", + "testdata/basic.env", + }, + envVars: []*model.EnvVar{ + {Key: "COLOR", Value: "blue"}, + }, + })) +} diff --git a/pkg/usecase/testdata/basic.env b/pkg/usecase/testdata/basic.env new file mode 100644 index 0000000..607c307 --- /dev/null +++ b/pkg/usecase/testdata/basic.env @@ -0,0 +1 @@ +COLOR=blue diff --git a/pkg/usecase/testdata/override1.env b/pkg/usecase/testdata/override1.env new file mode 100644 index 0000000..74fc3ee --- /dev/null +++ b/pkg/usecase/testdata/override1.env @@ -0,0 +1 @@ +COLOR=orange diff --git a/pkg/usecase/testdata/override2.env b/pkg/usecase/testdata/override2.env new file mode 100644 index 0000000..8c8a62e --- /dev/null +++ b/pkg/usecase/testdata/override2.env @@ -0,0 +1 @@ +COLOR=red diff --git a/pkg/usecase/usecase_test.go b/pkg/usecase/usecase_test.go index de579c1..bcce7ba 100644 --- a/pkg/usecase/usecase_test.go +++ b/pkg/usecase/usecase_test.go @@ -53,7 +53,7 @@ func TestGenerate(t *testing.T) { mock.PutKeyChainValuesMock = func(envVars []*model.EnvVar, namespace types.Namespace) error { gt.V(t, namespace).Equal("zenv.bridge") gt.A(t, envVars).Length(1). - Elem(0, func(t testing.TB, v *model.EnvVar) { + At(0, func(t testing.TB, v *model.EnvVar) { gt.Value(t, v.Key).Equal("SECRET") gt.N(t, len(v.Value)).Equal(24) })