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) {
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) {
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