diff --git a/README.md b/README.md index 63a32cc..440e9eb 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Dotconfig [![Go Reference](https://pkg.go.dev/badge/github.com/DeanPDX/dotconfig.svg)](https://pkg.go.dev/github.com/DeanPDX/dotconfig) [![Go Report Card](https://goreportcard.com/badge/github.com/DeanPDX/dotconfig)](https://goreportcard.com/report/github.com/DeanPDX/dotconfig) ![Codecov](https://img.shields.io/codecov/c/github/DeanPDX/dotconfig) +# Dotconfig [![Go Reference](https://pkg.go.dev/badge/github.com/DeanPDX/dotconfig.svg)](https://pkg.go.dev/github.com/DeanPDX/dotconfig) [![Go Report Card](https://goreportcard.com/badge/github.com/DeanPDX/dotconfig)](https://goreportcard.com/report/github.com/DeanPDX/dotconfig) [![Codecov](https://img.shields.io/codecov/c/github/DeanPDX/dotconfig)](https://app.codecov.io/gh/DeanPDX/dotconfig/) This package aims to simplify configuration from environment variables. In local development, we can supply a `.env` file with key/value pairs. When deployed, values come from a secret manager. This is similar to [joho/godotenv](https://github.com/joho/godotenv) but the aim here is to not only read the `.env` file but use reflection to produce a config struct. We also support optional/required fields in the struct and default values. diff --git a/dotconfig_test.go b/dotconfig_test.go index a548450..43650be 100644 --- a/dotconfig_test.go +++ b/dotconfig_test.go @@ -191,3 +191,26 @@ func TestSingleError(t *testing.T) { t.Errorf("Expecting exactly 1 error") } } + +func TestMustBeStruct(t *testing.T) { + _, err := dotconfig.FromFileName[string](".env") + // Just make sure we get exactly 1 error. + errs := dotconfig.Errors(err) + if len(errs) != 1 { + t.Fatalf("Expecting exactly 1 error") + } + if !errors.Is(errs[0], dotconfig.ErrConfigMustBeStruct) { + t.Errorf("Expecting invalid type error. Got: %v", errs[0]) + } +} + +type empty struct{} + +func TestFileIO(t *testing.T) { + // Just to get us to 100% I am doing this to + // hit the deferred file.Close() + _, err := dotconfig.FromFileName[empty]("go.mod") + if err != nil { + t.Fatal(err) + } +} diff --git a/joinerror_test.go b/joinerror_test.go new file mode 100644 index 0000000..b28ce80 --- /dev/null +++ b/joinerror_test.go @@ -0,0 +1,60 @@ +package dotconfig + +import ( + "errors" + "strings" + "testing" +) + +func TestErrorsReturnsErr(t *testing.T) { + // Make sure calling Errors on an error just returns that in collection + err := errors.New("test error") + errs := Errors(err) + if len(errs) != 1 { + t.Fatalf("Expected 1 error. Got %v", len(errs)) + } +} + +func TestErrorsReturnsNil(t *testing.T) { + // Make sure calling Errors on an error just returns that in collection + errs := Errors(nil) + if errs != nil { + t.Fatal("Expected nil slice") + } +} + +type empty struct{} + +type required struct { + MyInt int `env:"MY_INT,required"` +} + +type doubleRequired struct { + MyInt int `env:"MY_INT,required"` + MySecond int `env:"MY_SECOND, required"` +} + +func TestErrorsStringer(t *testing.T) { + // Make sure calling Errors on an error just returns that in collection + _, err := FromReader[empty](strings.NewReader("")) + if err != nil { + t.Fatal("Expected nil slice") + } + // Single error should return in common error format + _, err = FromReader[required](strings.NewReader("")) + want := "key not present in ENV: MY_INT" + if err.Error() != want { + t.Fatalf("Expected %v. Got %v.", want, err.Error()) + } + _, err = FromReader[doubleRequired](strings.NewReader("")) + want = `multiple errors: +- key not present in ENV: MY_INT +- key not present in ENV: MY_SECOND` + if err.Error() != want { + t.Fatalf("Expected %v. Got %v.", want, err.Error()) + } + errs := joinError{} + if errs.Error() != "" { + t.Fatalf("Expected empty string. Got: %v", errs.Error()) + } +}