github.com/ddev/ddev@v1.23.2-0.20240519125000-d824ffe36ff3/pkg/config/state/state_manager_test.go (about)

     1  package state_test
     2  
     3  import (
     4  	"path/filepath"
     5  	"testing"
     6  
     7  	"github.com/ddev/ddev/pkg/config/state/storage/yaml"
     8  	"github.com/ddev/ddev/pkg/config/state/types"
     9  	"github.com/stretchr/testify/suite"
    10  )
    11  
    12  func TestStateTestSuite(t *testing.T) {
    13  	suite.Run(t, new(StateTestSuite))
    14  }
    15  
    16  type TestStateSubEntry struct {
    17  	BoolField1   bool    `yaml:"bool_field1,omitempty"`
    18  	IntField1    int     `yaml:"int_field1,omitempty"`
    19  	UIntField1   uint    `yaml:"uint_field1,omitempty"`
    20  	FloatField1  float32 `yaml:"float_field1,omitempty"`
    21  	StringField1 string  `yaml:"string_field1,omitempty"`
    22  }
    23  
    24  type TestStateEntry struct {
    25  	BoolField1   bool              `yaml:"bool_field1,omitempty"`
    26  	BoolField2   bool              `yaml:"bool_field2,omitempty"`
    27  	IntField1    int               `yaml:"int_field1,omitempty"`
    28  	IntField2    int16             `yaml:"int_field2,omitempty"`
    29  	UIntField1   uint              `yaml:"uint_field1,omitempty"`
    30  	UIntField2   uint16            `yaml:"uint_field2,omitempty"`
    31  	FloatField1  float32           `yaml:"float_field1,omitempty"`
    32  	FloatField2  float64           `yaml:"float_field2,omitempty"`
    33  	StringField1 string            `yaml:"string_field1,omitempty"`
    34  	StringField2 string            `yaml:"string_field2,omitempty"`
    35  	Sub          TestStateSubEntry `yaml:"sub,omitempty"`
    36  }
    37  
    38  type StateTestSuite struct {
    39  	suite.Suite
    40  }
    41  
    42  func (suite *StateTestSuite) TestGetReturnsCorrectState() {
    43  	var tests = []struct {
    44  		Key      types.StateEntryKey
    45  		Expected TestStateEntry
    46  	}{
    47  		// key1 to 3 are available in the test data.
    48  		{
    49  			Key: "key1",
    50  			Expected: TestStateEntry{
    51  				BoolField1:   true,
    52  				BoolField2:   false,
    53  				IntField1:    11,
    54  				IntField2:    12,
    55  				UIntField1:   112,
    56  				UIntField2:   123,
    57  				FloatField1:  1.1,
    58  				FloatField2:  1.2,
    59  				StringField1: "string11",
    60  				StringField2: "string12",
    61  			},
    62  		},
    63  		{
    64  			Key: "key2",
    65  			Expected: TestStateEntry{
    66  				BoolField1:   false,
    67  				BoolField2:   true,
    68  				IntField1:    21,
    69  				IntField2:    22,
    70  				UIntField1:   212,
    71  				UIntField2:   223,
    72  				FloatField1:  2.1,
    73  				FloatField2:  2.2,
    74  				StringField1: "string21",
    75  				StringField2: "string22",
    76  			},
    77  		},
    78  		{
    79  			Key: "key3",
    80  			Expected: TestStateEntry{
    81  				BoolField1:   true,
    82  				BoolField2:   true,
    83  				IntField1:    31,
    84  				IntField2:    32,
    85  				UIntField1:   312,
    86  				UIntField2:   323,
    87  				FloatField1:  3.1,
    88  				FloatField2:  3.2,
    89  				StringField1: "string31",
    90  				StringField2: "string32",
    91  			},
    92  		},
    93  		// Tests if the initialization works properly.
    94  		{
    95  			Key:      "dummy",
    96  			Expected: TestStateEntry{},
    97  		},
    98  	}
    99  
   100  	// Use the state file from the test data.
   101  	state := yaml.NewState(filepath.Join("testdata", "state.yml"))
   102  
   103  	// Variable must be initialized before the for loop to make the last test
   104  	// working which needs values to be set.
   105  	stateEntry := TestStateEntry{}
   106  
   107  	for _, tt := range tests {
   108  		suite.Run(tt.Key, func() {
   109  			suite.NoError(state.Get(tt.Key, &stateEntry))
   110  			suite.EqualValues(tt.Expected, stateEntry)
   111  		})
   112  	}
   113  }
   114  
   115  func (suite *StateTestSuite) TestSetUpdatesState() {
   116  	// Use a non existing state file from the temp dir.
   117  	state := yaml.NewState(filepath.Join(suite.T().TempDir(), "state.yml"))
   118  
   119  	stateEntryOut := TestStateEntry{
   120  		BoolField1:   true,
   121  		BoolField2:   false,
   122  		FloatField1:  12.34,
   123  		FloatField2:  23.45,
   124  		IntField1:    12,
   125  		IntField2:    23,
   126  		UIntField1:   123,
   127  		UIntField2:   234,
   128  		StringField1: "string1",
   129  		StringField2: "string2",
   130  		Sub: TestStateSubEntry{
   131  			BoolField1:   true,
   132  			IntField1:    1,
   133  			UIntField1:   2,
   134  			FloatField1:  3.1,
   135  			StringField1: "string1",
   136  		},
   137  	}
   138  	stateEntryIn := TestStateEntry{}
   139  
   140  	suite.NoError(state.Set("test_state_entry", stateEntryOut))
   141  	suite.NoError(state.Get("test_state_entry", &stateEntryIn))
   142  	suite.EqualValues(stateEntryOut, stateEntryIn)
   143  }
   144  
   145  func (suite *StateTestSuite) TestSetPreservesExistingStates() {
   146  	var tests = []struct {
   147  		Key   types.StateEntryKey
   148  		Entry TestStateEntry
   149  	}{
   150  		{
   151  			Key: "key1",
   152  			Entry: TestStateEntry{
   153  				BoolField1:   true,
   154  				BoolField2:   false,
   155  				IntField1:    11,
   156  				IntField2:    12,
   157  				UIntField1:   112,
   158  				UIntField2:   123,
   159  				FloatField1:  1.1,
   160  				FloatField2:  1.2,
   161  				StringField1: "string11",
   162  				StringField2: "string12",
   163  			},
   164  		},
   165  		{
   166  			Key: "key2",
   167  			Entry: TestStateEntry{
   168  				BoolField1:   false,
   169  				BoolField2:   true,
   170  				IntField1:    21,
   171  				IntField2:    22,
   172  				UIntField1:   212,
   173  				UIntField2:   223,
   174  				FloatField1:  2.1,
   175  				FloatField2:  2.2,
   176  				StringField1: "string21",
   177  				StringField2: "string22",
   178  			},
   179  		},
   180  		{
   181  			Key: "key3",
   182  			Entry: TestStateEntry{
   183  				BoolField1:   true,
   184  				BoolField2:   true,
   185  				IntField1:    31,
   186  				IntField2:    32,
   187  				UIntField1:   312,
   188  				UIntField2:   323,
   189  				FloatField1:  3.1,
   190  				FloatField2:  3.2,
   191  				StringField1: "string31",
   192  				StringField2: "string32",
   193  			},
   194  		},
   195  	}
   196  
   197  	// Use a non existing state file from the temp dir.
   198  	state := yaml.NewState(filepath.Join(suite.T().TempDir(), "state.yml"))
   199  
   200  	for _, tt := range tests {
   201  		suite.NoError(state.Set(tt.Key, tt.Entry))
   202  	}
   203  
   204  	// Save and reload states.
   205  	suite.NoError(state.Save())
   206  	suite.NoError(state.Load())
   207  
   208  	// Add test state.
   209  	testState := TestStateEntry{
   210  		BoolField1: true,
   211  		BoolField2: true,
   212  	}
   213  	suite.NoError(state.Set("test", testState))
   214  
   215  	// Verify previous states.
   216  	stateEntry := TestStateEntry{}
   217  
   218  	for _, tt := range tests {
   219  		suite.Run(tt.Key, func() {
   220  			suite.NoError(state.Get(tt.Key, &stateEntry))
   221  			suite.EqualValues(tt.Entry, stateEntry)
   222  		})
   223  	}
   224  
   225  	// Verify test state.
   226  	suite.NoError(state.Get("test", &stateEntry))
   227  	suite.EqualValues(testState, stateEntry)
   228  }
   229  
   230  func (suite *StateTestSuite) TestGetExpectsPointer() {
   231  	// Use a non existing state file from the temp dir.
   232  	state := yaml.NewState(filepath.Join(suite.T().TempDir(), "state.yml"))
   233  
   234  	stateEntry := TestStateEntry{}
   235  
   236  	suite.ErrorContains(state.Get("test_state_entry", stateEntry), "stateEntry must be a pointer")
   237  }
   238  
   239  func (suite *StateTestSuite) TestSetExpectsNonPointer() {
   240  	// Use a non existing state file from the temp dir.
   241  	state := yaml.NewState(filepath.Join(suite.T().TempDir(), "state.yml"))
   242  
   243  	stateEntry := TestStateEntry{}
   244  
   245  	suite.ErrorContains(state.Set("test_state_entry", &stateEntry), "stateEntry must not be a pointer")
   246  }
   247  
   248  func (suite *StateTestSuite) TestLoadedIsProperlySet() {
   249  	var tests = []struct {
   250  		Name string
   251  		File string
   252  	}{
   253  		{
   254  			Name: "StateExists",
   255  			File: filepath.Join("testdata", "state.yml"),
   256  		},
   257  		{
   258  			Name: "StateNotExists",
   259  			File: filepath.Join(suite.T().TempDir(), "state.yml"),
   260  		},
   261  	}
   262  
   263  	for _, tt := range tests {
   264  		suite.Run(tt.Name, func() {
   265  			state := yaml.NewState(tt.File)
   266  
   267  			suite.False(state.Loaded())
   268  			suite.NoError(state.Load())
   269  			suite.True(state.Loaded())
   270  			suite.NoError(state.Save())
   271  			suite.True(state.Loaded())
   272  		})
   273  	}
   274  }
   275  
   276  func (suite *StateTestSuite) TestChangedIsProperlySet() {
   277  	state := yaml.NewState(filepath.Join(suite.T().TempDir(), "state.yml"))
   278  
   279  	suite.False(state.Changed())
   280  	suite.NoError(state.Load())
   281  	suite.False(state.Changed())
   282  	suite.NoError(state.Set("test_state_entry", TestStateEntry{}))
   283  	suite.True(state.Changed())
   284  	suite.NoError(state.Save())
   285  	suite.False(state.Changed())
   286  }