Skip to content

Env Vars Starting with USER not loading #319

Closed
@jhuggart

Description

Describe the bug
Env Vars Starting with USER not loading

To Reproduce

package config

import (
	"errors"
	"fmt"
	"github.com/knadh/koanf/providers/env"
	"github.com/knadh/koanf/v2"
	"os"
	"reflect"
	"strings"
	"testing"
)

type Config struct {
	loader *koanf.Koanf
}

var (
	ErrFailedToLoadFromEnv       = errors.New("failed to load from env")
	ErrFailedToUnmarshalToStruct = errors.New("failed to unmarshal to struct")
)

const (
	emptyPrefix = ""
	emptyDelim  = ""
	emptyPath   = ""
)

// New constructs a new config receiver
func New() *Config {
	c := &Config{
		loader: koanf.New(emptyDelim),
	}

	return c
}

// Hydrate takes a pointer to config and hydrates it
func (c *Config) Hydrate(cfgStruct any) error {
	// Load environment variables and merge into the loaded config.
	// emptyPrefix and emptyDelim. There is no env prefix and we do not need a delimiter
	if err := c.loader.Load(env.Provider(emptyPrefix, emptyDelim, func(s string) string {
		return strings.ToLower(s)
	}), nil); err != nil {
		return fmt.Errorf("%w: %v", ErrFailedToLoadFromEnv, err)
	}

	// Unmarshal to `config` tags using "FlatPaths" -> ie, don't bother with nesting config
	if err := c.loader.UnmarshalWithConf(emptyPath, cfgStruct, koanf.UnmarshalConf{
		Tag:       "config",
		FlatPaths: true,
	}); err != nil {
		return fmt.Errorf("%w: %v", ErrFailedToUnmarshalToStruct, err)
	}

	return nil
}

type TestConfig struct {
	ValueHere     string `config:"value_here"`
	UserValueHere string `config:"user_value_here"`
}

func TestHydrate(test *testing.T) {
	cases := []struct {
		name           string
		input          any
		envVals        map[string]string
		expectedOutput any
		expectedError  error
	}{
		{
			name:  "env values set correctly",
			input: TestConfig{},
			envVals: map[string]string{
				"USER_VALUE_HERE": "hi",
				"VALUE_HERE":      "hello",
			},
			expectedOutput: TestConfig{
				UserValueHere: "hi",
				ValueHere:     "hello",
			},
		},
	}

	for _, c := range cases {
		test.Run(c.name, func(t *testing.T) {
			for k, v := range c.envVals {
				if err := os.Setenv(k, v); err != nil {
					t.Fatal("failed to set env var", err)
				}
				defer os.Unsetenv(k)
			}

			conf := New()

			var toHydrate TestConfig
			if err := conf.Hydrate(&toHydrate); !errors.Is(err, c.expectedError) {
				t.Fatalf("mismatched errors - expected %v, got %v", c.expectedError, err)
			}

			if !reflect.DeepEqual(toHydrate, c.expectedOutput) {
				t.Fatalf("mismatched results - expected %v, got %v", c.expectedOutput, toHydrate)
			}
		})
	}
}

Expected behavior
Both env vars are mapped to the input struct

Please provide the following information):

  • OS: mac
  • Koanf Version v2.1.1

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions