...

Source file src/edge-infra.dev/pkg/sds/lib/etcd/server/embed/member_test.go

Documentation: edge-infra.dev/pkg/sds/lib/etcd/server/embed

     1  package embed
     2  
     3  import (
     4  	"strings"
     5  	"testing"
     6  
     7  	"github.com/spf13/afero"
     8  	"github.com/stretchr/testify/assert"
     9  	"github.com/stretchr/testify/require"
    10  	"go.etcd.io/etcd/server/v3/embed"
    11  )
    12  
    13  func TestNewMember(t *testing.T) {
    14  	testCases := map[string]struct {
    15  		input       *Config
    16  		expectError bool
    17  	}{
    18  		"Success": {
    19  			input: &Config{
    20  				Name: "test-new-member-name",
    21  			},
    22  			expectError: false,
    23  		},
    24  		"Failure_NoName": {
    25  			input:       &Config{},
    26  			expectError: true,
    27  		},
    28  	}
    29  
    30  	for name, tc := range testCases {
    31  		t.Run(name, func(t *testing.T) {
    32  			_, err := NewMember(tc.input)
    33  			if tc.expectError {
    34  				assert.Error(t, err)
    35  				return
    36  			}
    37  			assert.NoError(t, err)
    38  		})
    39  	}
    40  }
    41  
    42  func TestInitialize(t *testing.T) {
    43  	memberName := "test-initialize-name"
    44  	logLevel := "warn"
    45  	dataDir := strings.Join([]string{"default.etcd", memberName}, ".")
    46  
    47  	cfg := &Config{
    48  		Name:     memberName,
    49  		LogLevel: logLevel,
    50  	}
    51  	m, err := NewMember(cfg)
    52  	require.NoError(t, err)
    53  
    54  	m.initialize()
    55  
    56  	assert.Equal(t, memberName, m.config.Name)
    57  	assert.Equal(t, logLevel, m.config.embed.LogLevel)
    58  	assert.Equal(t, dataDir, m.config.embed.Dir)
    59  }
    60  
    61  func TestPrepareNew(t *testing.T) {
    62  	memberName := "test-prepare-new-name"
    63  	dataDir := strings.Join([]string{"default.etcd", memberName}, ".")
    64  
    65  	cfg := &Config{
    66  		Name: memberName,
    67  	}
    68  	m, err := NewMember(cfg)
    69  	require.NoError(t, err)
    70  	m.initialize()
    71  
    72  	fs := afero.NewOsFs()
    73  	require.NoError(t, fs.MkdirAll(dataDir, 0600))
    74  
    75  	_, err = afero.ReadDir(fs, dataDir)
    76  	require.NoError(t, err)
    77  
    78  	require.NoError(t, m.prepareNew())
    79  
    80  	_, err = afero.ReadDir(fs, dataDir)
    81  	assert.Error(t, err)
    82  
    83  	assert.NotNil(t, m.config.embed.ListenPeerUrls[0].Host)
    84  	assert.NotNil(t, m.config.embed.ListenClientUrls[0].Host)
    85  	assert.NotNil(t, m.config.embed.AdvertisePeerUrls[0].Host)
    86  	assert.NotNil(t, m.config.embed.AdvertiseClientUrls[0].Host)
    87  }
    88  
    89  func TestPreStartStateFlow(t *testing.T) {
    90  	testCases := map[string]struct {
    91  		fn               func(m *Member) error
    92  		expectedEndState string
    93  	}{
    94  		"Prepare_PreStart": {
    95  			fn: func(m *Member) error {
    96  				return m.prepare()
    97  			},
    98  			expectedEndState: preStart,
    99  		},
   100  		"Start_PreStart": {
   101  			fn: func(m *Member) error {
   102  				var errCh chan error
   103  				return m.start(errCh)
   104  			},
   105  			expectedEndState: running,
   106  		},
   107  		"Stop_PreStart": {
   108  			fn: func(m *Member) error {
   109  				return m.stop()
   110  			},
   111  			expectedEndState: preStart,
   112  		},
   113  		"Close_PreStart": {
   114  			fn: func(m *Member) error {
   115  				return m.close()
   116  			},
   117  			expectedEndState: preStart,
   118  		},
   119  	}
   120  
   121  	for name, tc := range testCases {
   122  		t.Run(name, func(t *testing.T) {
   123  			cfg := &Config{
   124  				Name: strings.Join([]string{"test-prepare", name}, "-"),
   125  			}
   126  			m, err := NewMember(cfg)
   127  			require.NoError(t, err)
   128  			require.NoError(t, m.prepare())
   129  			m.config.embed.InitialCluster = strings.Join([]string{m.config.Name, m.config.embed.AdvertisePeerUrls[0].String()}, "=")
   130  			m.etcd = &embed.Etcd{}
   131  			m.state = &preStartState{}
   132  
   133  			require.NoError(t, tc.fn(m))
   134  
   135  			assert.Equal(t, tc.expectedEndState, m.state.String())
   136  		})
   137  	}
   138  }
   139  
   140  func TestRunningStateFlow(t *testing.T) {
   141  	testCases := map[string]struct {
   142  		fn               func(m *Member) error
   143  		expectedEndState string
   144  	}{
   145  		"Prepare_Running": {
   146  			fn: func(m *Member) error {
   147  				return m.prepare()
   148  			},
   149  			expectedEndState: running,
   150  		},
   151  		"Start_Running": {
   152  			fn: func(m *Member) error {
   153  				var errCh chan error
   154  				return m.start(errCh)
   155  			},
   156  			expectedEndState: running,
   157  		},
   158  		"Stop_Running": {
   159  			fn: func(m *Member) error {
   160  				return m.stop()
   161  			},
   162  			expectedEndState: stopped,
   163  		},
   164  		"Close_Running": {
   165  			fn: func(m *Member) error {
   166  				return m.close()
   167  			},
   168  			expectedEndState: terminated,
   169  		},
   170  	}
   171  
   172  	for name, tc := range testCases {
   173  		t.Run(name, func(t *testing.T) {
   174  			cfg := &Config{
   175  				Name: strings.Join([]string{"test-prepare", name}, "-"),
   176  			}
   177  			m, err := NewMember(cfg)
   178  			require.NoError(t, err)
   179  			require.NoError(t, m.prepare())
   180  			m.config.embed.InitialCluster = strings.Join([]string{m.config.Name, m.config.embed.AdvertisePeerUrls[0].String()}, "=")
   181  			m.etcd = &embed.Etcd{}
   182  			var errCh chan error
   183  			require.NoError(t, m.start(errCh))
   184  			m.state = &runningState{}
   185  
   186  			require.NoError(t, tc.fn(m))
   187  
   188  			assert.Equal(t, tc.expectedEndState, m.state.String())
   189  		})
   190  	}
   191  }
   192  
   193  func TestStoppedStateFlow(t *testing.T) { //nolint:dupl
   194  	testCases := map[string]struct {
   195  		fn               func(m *Member) error
   196  		expectedEndState string
   197  	}{
   198  		"Prepare_Stopped": {
   199  			fn: func(m *Member) error {
   200  				return m.prepare()
   201  			},
   202  			expectedEndState: stopped,
   203  		},
   204  		"Start_Stopped": {
   205  			fn: func(m *Member) error {
   206  				var errCh chan error
   207  				return m.start(errCh)
   208  			},
   209  			expectedEndState: running,
   210  		},
   211  		"Stop_Stopped": {
   212  			fn: func(m *Member) error {
   213  				return m.stop()
   214  			},
   215  			expectedEndState: stopped,
   216  		},
   217  		"Close_Stopped": {
   218  			fn: func(m *Member) error {
   219  				return m.close()
   220  			},
   221  			expectedEndState: terminated,
   222  		},
   223  	}
   224  
   225  	for name, tc := range testCases {
   226  		t.Run(name, func(t *testing.T) {
   227  			m := createMember(t, name)
   228  
   229  			var errCh chan error
   230  			require.NoError(t, m.start(errCh))
   231  			require.NoError(t, m.stop())
   232  
   233  			m.state = &stoppedState{}
   234  
   235  			require.NoError(t, tc.fn(m))
   236  
   237  			assert.Equal(t, tc.expectedEndState, m.state.String())
   238  		})
   239  	}
   240  }
   241  
   242  func TestTerminatedStateFlow(t *testing.T) { //nolint:dupl
   243  	testCases := map[string]struct {
   244  		fn               func(m *Member) error
   245  		expectedEndState string
   246  	}{
   247  		"Prepare_Terminated": {
   248  			fn: func(m *Member) error {
   249  				return m.prepare()
   250  			},
   251  			expectedEndState: terminated,
   252  		},
   253  		"Start_Terminated": {
   254  			fn: func(m *Member) error {
   255  				var errCh chan error
   256  				return m.start(errCh)
   257  			},
   258  			expectedEndState: running,
   259  		},
   260  		"Stop_Terminated": {
   261  			fn: func(m *Member) error {
   262  				return m.stop()
   263  			},
   264  			expectedEndState: terminated,
   265  		},
   266  		"Close_Terminated": {
   267  			fn: func(m *Member) error {
   268  				return m.close()
   269  			},
   270  			expectedEndState: terminated,
   271  		},
   272  	}
   273  
   274  	for name, tc := range testCases {
   275  		t.Run(name, func(t *testing.T) {
   276  			m := createMember(t, name)
   277  
   278  			var errCh chan error
   279  			require.NoError(t, m.start(errCh))
   280  			require.NoError(t, m.close())
   281  			m.state = &terminatedState{}
   282  
   283  			require.NoError(t, tc.fn(m))
   284  
   285  			assert.Equal(t, tc.expectedEndState, m.state.String())
   286  		})
   287  	}
   288  }
   289  
   290  func createMember(t *testing.T, name string) *Member {
   291  	cfg := &Config{
   292  		Name: strings.Join([]string{"test-prepare", name}, "-"),
   293  	}
   294  	m, err := NewMember(cfg)
   295  	require.NoError(t, err)
   296  	require.NoError(t, m.prepare())
   297  
   298  	m.config.embed.InitialCluster = strings.Join([]string{m.config.Name, m.config.embed.AdvertisePeerUrls[0].String()}, "=")
   299  	m.etcd = &embed.Etcd{}
   300  
   301  	return m
   302  }
   303  

View as plain text