Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,4 @@ go.work.sum
.DS_Store
*.log
.vscode/settings.json
coverage.txt
157 changes: 157 additions & 0 deletions config_direct_field_tracking_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
package modular

import (
"strings"
"testing"

"github.com/GoCodeAlone/modular/feeders"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"
)

// TestDirectFeederFieldTracking tests field tracking when calling feeder.Feed() directly
func TestDirectFeederFieldTracking(t *testing.T) {
tests := []struct {
name string
envVars map[string]string
}{
{
name: "basic environment variable tracking",
envVars: map[string]string{
"APP_NAME": "Test App",
"APP_DEBUG": "true",
},
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// Set up environment variables
for key, value := range tt.envVars {
t.Setenv(key, value)
}

// Create logger that captures debug output
mockLogger := new(MockLogger)
mockLogger.On("Debug", mock.Anything, mock.Anything).Return()

// Create field tracker
tracker := NewDefaultFieldTracker()
tracker.SetLogger(mockLogger)

// Create test configuration struct
type TestConfig struct {
App struct {
Name string `env:"APP_NAME"`
Debug bool `env:"APP_DEBUG"`
}
}

config := &TestConfig{}

// Create environment feeder with field tracking
envFeeder := feeders.NewEnvFeeder()
envFeeder.SetVerboseDebug(true, mockLogger)

// Set up field tracking bridge
bridge := NewFieldTrackingBridge(tracker)
envFeeder.SetFieldTracker(bridge)

// Feed configuration directly
err := envFeeder.Feed(config)
require.NoError(t, err)

// Verify that config values were actually set
assert.Equal(t, "Test App", config.App.Name)
assert.True(t, config.App.Debug)

// Verify that field populations were tracked
assert.NotEmpty(t, tracker.FieldPopulations, "Should have tracked field populations")

// Print tracked populations for debugging
t.Logf("Tracked %d field populations:", len(tracker.FieldPopulations))
for i, fp := range tracker.FieldPopulations {
t.Logf(" %d: %s -> %v (from %s:%s)", i, fp.FieldPath, fp.Value, fp.SourceType, fp.SourceKey)
}
})
}
}

// TestInstanceAwareDirectFieldTracking tests instance-aware field tracking with direct feeding
func TestInstanceAwareDirectFieldTracking(t *testing.T) {
// Set up environment variables for instance-aware tracking
envVars := map[string]string{
"DB_PRIMARY_DRIVER": "postgres",
"DB_PRIMARY_DSN": "postgres://localhost/primary",
"DB_SECONDARY_DRIVER": "mysql",
"DB_SECONDARY_DSN": "mysql://localhost/secondary",
}

for key, value := range envVars {
t.Setenv(key, value)
}

// Create logger that captures debug output
mockLogger := new(MockLogger)
mockLogger.On("Debug", mock.Anything, mock.Anything).Return()

// Create field tracker
tracker := NewDefaultFieldTracker()
tracker.SetLogger(mockLogger)

// Create test configuration structures
type ConnectionConfig struct {
Driver string `env:"DRIVER"`
DSN string `env:"DSN"`
}

// Test the primary connection first
primaryConfig := &ConnectionConfig{}

// Create instance-aware environment feeder
instanceAwareFeeder := feeders.NewInstanceAwareEnvFeeder(func(instanceKey string) string {
return "DB_" + strings.ToUpper(instanceKey) + "_"
})
instanceAwareFeeder.SetVerboseDebug(true, mockLogger)

// Set up field tracking bridge
bridge := NewFieldTrackingBridge(tracker)
instanceAwareFeeder.SetFieldTracker(bridge)

// Feed primary configuration
err := instanceAwareFeeder.FeedKey("primary", primaryConfig)
require.NoError(t, err)

// Verify that config values were actually set
assert.Equal(t, "postgres", primaryConfig.Driver)
assert.Equal(t, "postgres://localhost/primary", primaryConfig.DSN)

// Test secondary connection
secondaryConfig := &ConnectionConfig{}
err = instanceAwareFeeder.FeedKey("secondary", secondaryConfig)
require.NoError(t, err)

// Verify that config values were actually set
assert.Equal(t, "mysql", secondaryConfig.Driver)
assert.Equal(t, "mysql://localhost/secondary", secondaryConfig.DSN)

// Verify that field populations were tracked
assert.NotEmpty(t, tracker.FieldPopulations, "Should have tracked field populations")

// Print tracked populations for debugging
t.Logf("Tracked %d field populations:", len(tracker.FieldPopulations))
for i, fp := range tracker.FieldPopulations {
t.Logf(" %d: %s -> %v (from %s:%s, instance:%s)", i, fp.FieldPath, fp.Value, fp.SourceType, fp.SourceKey, fp.InstanceKey)
}

// Verify specific field populations
primaryDriverPop := tracker.GetFieldPopulation("Driver")
if primaryDriverPop != nil {
assert.Equal(t, "Driver", primaryDriverPop.FieldName)
assert.Equal(t, "env", primaryDriverPop.SourceType)
assert.Equal(t, "DB_PRIMARY_DRIVER", primaryDriverPop.SourceKey)
assert.Equal(t, "postgres", primaryDriverPop.Value)
assert.Equal(t, "primary", primaryDriverPop.InstanceKey)
}
}
11 changes: 6 additions & 5 deletions config_feeders.go
Original file line number Diff line number Diff line change
@@ -1,19 +1,20 @@
package modular

import (
"github.com/golobby/config/v3"

"github.com/GoCodeAlone/modular/feeders"
)

// Feeder defines the interface for configuration feeders that provide configuration data.
type Feeder interface {
// Feed gets a struct and feeds it using configuration data.
Feed(structure interface{}) error
}

// ConfigFeeders provides a default set of configuration feeders for common use cases
var ConfigFeeders = []Feeder{
feeders.NewEnvFeeder(),
}

// Feeder aliases
type Feeder = config.Feeder

// ComplexFeeder extends the basic Feeder interface with additional functionality for complex configuration scenarios
type ComplexFeeder interface {
Feeder
Expand Down
Loading