github.com/drud/ddev@v1.21.5-alpha1.0.20230226034409-94fcc4b94453/pkg/ddevapp/config_merge_test.go (about)

     1  package ddevapp_test
     2  
     3  import (
     4  	"github.com/drud/ddev/pkg/ddevapp"
     5  	"github.com/drud/ddev/pkg/fileutil"
     6  	"github.com/drud/ddev/pkg/testcommon"
     7  	asrt "github.com/stretchr/testify/assert"
     8  	"github.com/stretchr/testify/require"
     9  	"os"
    10  	"path/filepath"
    11  	"sort"
    12  	"testing"
    13  )
    14  
    15  // TestConfigMerge takes a variety of test expectations and checks to see if they work out
    16  func TestConfigMerge(t *testing.T) {
    17  	assert := asrt.New(t)
    18  
    19  	// Each item here has a set of config.*.yaml to be composed, and a config.yaml that should reflect the result
    20  	for _, c := range []string{"envtest", "fulloverridetest", "hookstest", "overridetest", "scalartest"} {
    21  		composedApp := SetupTestTempDir(t, filepath.Join(c, "components"))
    22  		expectedApp := SetupTestTempDir(t, filepath.Join(c, "expected"))
    23  		composedApp.Name = ""
    24  		composedApp.AppRoot = ""
    25  		composedApp.ConfigPath = ""
    26  		expectedApp.Name = ""
    27  		expectedApp.AppRoot = ""
    28  		expectedApp.ConfigPath = ""
    29  		// We don't know in advance the ordering of the WebEnvironment for testing purposes,
    30  		// and it doesn't matter in reality, so sort both for testing before comparison
    31  		sort.Strings(expectedApp.WebEnvironment)
    32  		sort.Strings(composedApp.WebEnvironment)
    33  
    34  		assert.Equal(expectedApp, composedApp, "%s failed", c)
    35  	}
    36  }
    37  
    38  // TestConfigMergeStringList verifies that scalar string merges update w/o clobber
    39  func TestConfigMergeStringList(t *testing.T) {
    40  	assert := asrt.New(t)
    41  	app := SetupTestTempDir(t, "")
    42  
    43  	// test matches allowing for delete syntax (a prefixed !)
    44  	assertSimpleMatch := func(expected bool, match string, setting []string) {
    45  		test := assert.True
    46  		if !expected {
    47  			test = assert.False
    48  		}
    49  
    50  		for _, val := range setting {
    51  			if val == match {
    52  				test(true, match)
    53  				return
    54  			}
    55  		}
    56  		test(false, match)
    57  	}
    58  	// no clobber of old setting
    59  	assertSimpleMatch(true, "somename", app.AdditionalHostnames)
    60  	// successful merge
    61  	assertSimpleMatch(true, "somename-new", app.AdditionalHostnames)
    62  }
    63  
    64  // TestConfigMergeEnvItems verifies that config overrides web_environment
    65  // and do not destroy (but may update) config.yaml stuff
    66  func TestConfigMergeEnvItems(t *testing.T) {
    67  	assert := asrt.New(t)
    68  	app := SetupTestTempDir(t, "")
    69  
    70  	// check the config file w/o overrides
    71  	noOverridesApp, err := ddevapp.NewApp(app.AppRoot, false)
    72  	require.NoError(t, err)
    73  	assert.IsType([]string{}, noOverridesApp.WebEnvironment)
    74  
    75  	// The app loaded without overrides should get the original values expected here
    76  	for _, v := range []string{`LARRY=l`, `MOE=m`, `CURLEY=c`} {
    77  		assert.Contains(noOverridesApp.WebEnvironment, v, "the app without overrides should have had %v but it didn't, webEnvironment=%v", v, noOverridesApp.WebEnvironment)
    78  	}
    79  
    80  	// With overrides we should have different values with the config.override.yaml values added
    81  	withOverridesApp, err := ddevapp.NewApp(app.AppRoot, true)
    82  	require.NoError(t, err)
    83  
    84  	for _, v := range []string{`LARRY=lz`, `MOE=mz`, `CURLEY=c`, `SHEMP=s`} {
    85  		assert.Contains(withOverridesApp.WebEnvironment, v, "the app without overrides should have had %v but it didn't, webEnvironment=%v", v, noOverridesApp.WebEnvironment)
    86  	}
    87  
    88  }
    89  
    90  // TestConfigHooksMerge makes sure that hooks get merged with additional config.*.yaml
    91  func TestConfigHooksMerge(t *testing.T) {
    92  	app := SetupTestTempDir(t, "")
    93  
    94  	// some helpers
    95  	getHookTasks := func(hooks map[string][]ddevapp.YAMLTask, hook string) []ddevapp.YAMLTask {
    96  		tasks, ok := hooks[hook]
    97  		if !ok {
    98  			return nil
    99  		}
   100  		return tasks
   101  	}
   102  
   103  	// the fields we want are private!
   104  	hasTask := func(hooks map[string][]ddevapp.YAMLTask, hook, taskKey, desc string) bool {
   105  		tasks := getHookTasks(hooks, hook)
   106  		if tasks == nil {
   107  			return false
   108  		}
   109  		found := false
   110  		for _, task := range tasks {
   111  			taskDesc := ""
   112  			taskInterface, ok := task[taskKey]
   113  			if !ok {
   114  				// we guessed the key wrong
   115  				continue
   116  			}
   117  
   118  			taskDesc, ok = taskInterface.(string)
   119  			if !ok {
   120  				// we expected the command to be a string, but WTF?
   121  				continue
   122  			}
   123  
   124  			//t.Logf("key %s as %s", taskKey, taskDesc)
   125  
   126  			if taskDesc == desc {
   127  				found = true
   128  			}
   129  
   130  		}
   131  		return found
   132  	}
   133  
   134  	assertTask := func(expected bool, hook, taskKey, desc string) {
   135  		tasks := getHookTasks(app.Hooks, hook)
   136  		if tasks == nil {
   137  			if expected {
   138  				t.Errorf("did not found tasks for %s", hook)
   139  			} else {
   140  				return
   141  			}
   142  		} else {
   143  			found := hasTask(app.Hooks, hook, taskKey, desc)
   144  			if found != expected {
   145  				msg := "Expected "
   146  				if !expected {
   147  					msg = "Did not expect "
   148  				}
   149  				t.Errorf("%s Hook %s with %s", msg, hook, desc)
   150  			}
   151  		}
   152  	}
   153  
   154  	assertTask(true, "post-start", "exec", "simple random expression")
   155  	assertTask(true, "post-start", "exec-host", "simple host command")
   156  	assertTask(true, "post-start", "exec", "simple web command")
   157  	assertTask(true, "post-import-db", "exec", "drush uli")
   158  
   159  }
   160  
   161  // SetupTestTempDir creates the test directory and related objects.
   162  func SetupTestTempDir(t *testing.T, subDir string) *ddevapp.DdevApp {
   163  	assert := asrt.New(t)
   164  
   165  	projDir, err := filepath.Abs(testcommon.CreateTmpDir(t.Name()))
   166  	require.NoError(t, err)
   167  
   168  	testConfig := filepath.Join("./testdata/", t.Name(), subDir, "/.ddev")
   169  	err = fileutil.CopyDir(testConfig, filepath.Join(projDir, ".ddev"))
   170  	require.NoError(t, err)
   171  
   172  	app, err := ddevapp.NewApp(projDir, true)
   173  	require.NoError(t, err)
   174  
   175  	t.Cleanup(func() {
   176  		err = os.RemoveAll(projDir)
   177  		assert.NoError(err)
   178  	})
   179  
   180  	return app
   181  }
   182  
   183  // TestEnvToUniqueEnv tests EnvToUniqueEnv
   184  func TestEnvToUniqueEnv(t *testing.T) {
   185  	assert := asrt.New(t)
   186  
   187  	testBedSources := [][]string{
   188  		{"ONE=one", "ONE=two", "ONE=three", "TWO=two", "TWO=three", "TWO=four"},
   189  	}
   190  
   191  	testBedExpectations := [][]string{
   192  		{"ONE=three", "TWO=four"},
   193  	}
   194  
   195  	for i := 0; i < len(testBedSources); i++ {
   196  		res := ddevapp.EnvToUniqueEnv(&testBedSources[i])
   197  		sort.Strings(res)
   198  		assert.Equal(testBedExpectations[i], res)
   199  	}
   200  }