]>
Commit | Line | Data |
---|---|---|
6988a057 JH |
1 | package hotline |
2 | ||
625b0580 JH |
3 | import ( |
4 | "bytes" | |
5 | "context" | |
6 | "fmt" | |
00d1ef67 JH |
7 | "github.com/davecgh/go-spew/spew" |
8 | "github.com/stretchr/testify/assert" | |
625b0580 JH |
9 | "go.uber.org/zap" |
10 | "go.uber.org/zap/zapcore" | |
11 | "net" | |
12 | "os" | |
625b0580 JH |
13 | "testing" |
14 | ) | |
15 | ||
16 | type testCase struct { | |
17 | name string // test case description | |
18 | account Account // Account struct for a user that will test transaction will execute under | |
19 | request *Transaction // transaction that will be sent by the client to the server | |
20 | setup func() // Optional test-specific setup required for the scenario | |
21 | teardown func() // Optional test-specific teardown for the scenario | |
22 | mockHandler map[int]*mockClientHandler | |
23 | } | |
24 | ||
25 | func (tt *testCase) Setup(srv *Server) error { | |
b25c4a19 | 26 | if err := srv.NewUser(tt.account.Login, tt.account.Name, string(negateString([]byte(tt.account.Password))), *tt.account.Access); err != nil { |
625b0580 JH |
27 | return err |
28 | } | |
29 | ||
30 | if tt.setup != nil { | |
31 | tt.setup() | |
32 | } | |
33 | ||
34 | return nil | |
35 | } | |
36 | ||
37 | func (tt *testCase) Teardown(srv *Server) error { | |
38 | if err := srv.DeleteUser(tt.account.Login); err != nil { | |
39 | return err | |
40 | } | |
41 | ||
42 | if tt.teardown != nil { | |
43 | tt.teardown() | |
44 | } | |
45 | ||
46 | return nil | |
47 | } | |
48 | ||
49 | func NewTestLogger() *zap.SugaredLogger { | |
50 | encoderCfg := zap.NewProductionEncoderConfig() | |
51 | encoderCfg.TimeKey = "timestamp" | |
52 | encoderCfg.EncodeTime = zapcore.ISO8601TimeEncoder | |
53 | ||
54 | core := zapcore.NewCore( | |
55 | zapcore.NewConsoleEncoder(encoderCfg), | |
56 | zapcore.Lock(os.Stdout), | |
57 | zap.DebugLevel, | |
58 | ) | |
59 | ||
60 | cores := []zapcore.Core{core} | |
61 | l := zap.New(zapcore.NewTee(cores...)) | |
62 | defer func() { _ = l.Sync() }() | |
63 | return l.Sugar() | |
64 | } | |
65 | ||
66 | func StartTestServer() (*Server, context.Context, context.CancelFunc) { | |
67 | ctx, cancelRoot := context.WithCancel(context.Background()) | |
68 | ||
92a7e455 JH |
69 | FS = OSFileStore{} |
70 | ||
625b0580 JH |
71 | srv, err := NewServer("test/config/", "localhost", 0, NewTestLogger()) |
72 | if err != nil { | |
73 | panic(err) | |
74 | } | |
75 | ||
76 | go func() { | |
77 | err := srv.ListenAndServe(ctx, cancelRoot) | |
78 | if err != nil { | |
79 | panic(err) | |
80 | } | |
81 | }() | |
82 | ||
83 | return srv, ctx, cancelRoot | |
84 | } | |
85 | ||
86 | func TestHandshake(t *testing.T) { | |
92a7e455 JH |
87 | mfs := MockFileStore{} |
88 | fh, _ := os.Open("./test/config/Agreement.txt") | |
89 | mfs.On("Open", "/test/config/Agreement.txt").Return(fh, nil) | |
90 | fh, _ = os.Open("./test/config/config.yaml") | |
91 | mfs.On("Open", "/test/config/config.yaml").Return(fh, nil) | |
92 | FS = mfs | |
93 | spew.Dump(mfs) | |
94 | ||
625b0580 JH |
95 | srv, _, cancelFunc := StartTestServer() |
96 | defer cancelFunc() | |
97 | ||
98 | port := srv.APIListener.Addr().(*net.TCPAddr).Port | |
99 | ||
100 | conn, err := net.Dial("tcp", fmt.Sprintf(":%v", port)) | |
101 | if err != nil { | |
102 | t.Fatal(err) | |
103 | } | |
104 | defer conn.Close() | |
105 | ||
106 | conn.Write([]byte{0x54, 0x52, 0x54, 0x50, 0x00, 0x01, 0x00, 0x00}) | |
107 | ||
108 | replyBuf := make([]byte, 8) | |
109 | _, _ = conn.Read(replyBuf) | |
110 | ||
111 | want := []byte{84, 82, 84, 80, 0, 0, 0, 0} | |
112 | if bytes.Compare(replyBuf, want) != 0 { | |
113 | t.Errorf("%q, want %q", replyBuf, want) | |
114 | } | |
115 | ||
116 | } | |
117 | ||
118 | //func TestLogin(t *testing.T) { | |
6988a057 | 119 | // |
625b0580 JH |
120 | // tests := []struct { |
121 | // name string | |
122 | // client *Client | |
123 | // }{ | |
6988a057 | 124 | // { |
625b0580 JH |
125 | // name: "when login is successful", |
126 | // client: NewClient("guest", NewTestLogger()), | |
6988a057 | 127 | // }, |
6988a057 | 128 | // } |
6988a057 JH |
129 | // for _, test := range tests { |
130 | // t.Run(test.name, func(t *testing.T) { | |
6988a057 | 131 | // |
6988a057 JH |
132 | // }) |
133 | // } | |
134 | //} | |
625b0580 | 135 | |
625b0580 JH |
136 | func TestNewUser(t *testing.T) { |
137 | srv, _, _ := StartTestServer() | |
138 | ||
139 | tests := []testCase{ | |
140 | //{ | |
141 | // name: "a valid new account", | |
142 | // mockHandler: func() mockClientHandler { | |
143 | // mh := mockClientHandler{} | |
144 | // mh.On("Handle", mock.AnythingOfType("*hotline.Client"), mock.MatchedBy(func(t *Transaction) bool { | |
145 | // println("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@") | |
146 | // spew.Dump(t.Type) | |
147 | // spew.Dump(bytes.Equal(t.Type, []byte{0x01, 0x5e})) | |
148 | // //if !bytes.Equal(t.GetField(fieldError).Data, []byte("You are not allowed to create new accounts.")) { | |
149 | // // return false | |
150 | // //} | |
151 | // return bytes.Equal(t.Type, []byte{0x01, 0x5e}, | |
152 | // ) | |
153 | // })).Return( | |
154 | // []Transaction{}, nil, | |
155 | // ) | |
156 | // | |
157 | // clientHandlers[tranNewUser] = mh | |
158 | // return mh | |
159 | // }(), | |
160 | // client: func() *Client { | |
161 | // c := NewClient("testUser", NewTestLogger()) | |
162 | // return c | |
163 | // }(), | |
164 | // teardown: func() { | |
165 | // _ = srv.DeleteUser("testUser") | |
166 | // }, | |
167 | // account: Account{ | |
168 | // Login: "test", | |
169 | // Name: "unnamed", | |
170 | // Password: "test", | |
171 | // Access: &[]byte{255, 255, 255, 255, 255, 255, 255, 255}, | |
172 | // }, | |
173 | // request: NewTransaction( | |
174 | // tranNewUser, nil, | |
175 | // NewField(fieldUserLogin, []byte(NegatedUserString([]byte("testUser")))), | |
176 | // NewField(fieldUserName, []byte("testUserName")), | |
177 | // NewField(fieldUserPassword, []byte(NegatedUserString([]byte("testPw")))), | |
178 | // NewField(fieldUserAccess, []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}), | |
179 | // ), | |
180 | // want: &Transaction{ | |
181 | // Fields: []Field{}, | |
182 | // }, | |
183 | //}, | |
184 | //{ | |
185 | // name: "a newUser request from a user without the required access", | |
186 | // mockHandler: func() *mockClientHandler { | |
187 | // mh := mockClientHandler{} | |
188 | // mh.On("Handle", mock.AnythingOfType("*hotline.Client"), mock.MatchedBy(func(t *Transaction) bool { | |
189 | // if !bytes.Equal(t.GetField(fieldError).Data, []byte("You are not allowed to create new accounts.")) { | |
190 | // return false | |
191 | // } | |
192 | // return bytes.Equal(t.Type, []byte{0x01, 0x5e}) | |
193 | // })).Return( | |
194 | // []Transaction{}, nil, | |
195 | // ) | |
196 | // return &mh | |
197 | // }(), | |
198 | // teardown: func() { | |
199 | // _ = srv.DeleteUser("testUser") | |
200 | // }, | |
201 | // account: Account{ | |
202 | // Login: "test", | |
203 | // Name: "unnamed", | |
204 | // Password: "test", | |
205 | // Access: &[]byte{0, 0, 0, 0, 0, 0, 0, 0}, | |
206 | // }, | |
207 | // request: NewTransaction( | |
208 | // tranNewUser, nil, | |
209 | // NewField(fieldUserLogin, []byte(NegatedUserString([]byte("testUser")))), | |
210 | // NewField(fieldUserName, []byte("testUserName")), | |
211 | // NewField(fieldUserPassword, []byte(NegatedUserString([]byte("testPw")))), | |
212 | // NewField(fieldUserAccess, []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}), | |
213 | // ), | |
214 | //}, | |
215 | //{ | |
216 | // name: "when user does not have required permission", | |
217 | // mockHandler: func() map[int]*mockClientHandler { | |
218 | // mockHandlers := make(map[int]*mockClientHandler) | |
219 | // | |
220 | // mh := mockClientHandler{} | |
221 | // mh.On("Handle", mock.AnythingOfType("*hotline.Client"), mock.MatchedBy(func(t *Transaction) bool { | |
222 | // return t.equal(Transaction{ | |
223 | // Type: []byte{0x01, 0x5e}, | |
224 | // IsReply: 1, | |
225 | // ErrorCode: []byte{0, 0, 0, 1}, | |
226 | // Fields: []Field{ | |
227 | // NewField(fieldError, []byte("You are not allowed to create new accounts.")), | |
228 | // }, | |
229 | // }) | |
230 | // })).Return( | |
231 | // []Transaction{}, nil, | |
232 | // ) | |
233 | // mockHandlers[tranNewUser] = &mh | |
234 | // | |
235 | // return mockHandlers | |
236 | // }(), | |
237 | // | |
238 | // teardown: func() { | |
239 | // _ = srv.DeleteUser("testUser") | |
240 | // }, | |
241 | // account: Account{ | |
242 | // Login: "test", | |
243 | // Name: "unnamed", | |
244 | // Password: "test", | |
245 | // Access: &[]byte{0, 0, 0, 0, 0, 0, 0, 0}, | |
246 | // }, | |
247 | // request: NewTransaction( | |
248 | // tranNewUser, nil, | |
249 | // NewField(fieldUserLogin, []byte(NegatedUserString([]byte("testUser")))), | |
250 | // NewField(fieldUserName, []byte("testUserName")), | |
251 | // NewField(fieldUserPassword, []byte(NegatedUserString([]byte("testPw")))), | |
252 | // NewField(fieldUserAccess, []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}), | |
253 | // ), | |
254 | //}, | |
255 | ||
256 | //{ | |
257 | // name: "a request to create a user that already exists", | |
258 | // setup: func() { | |
259 | // | |
260 | // }, | |
261 | // teardown: func() { | |
262 | // _ = srv.DeleteUser("testUser") | |
263 | // }, | |
264 | // account: Account{ | |
265 | // Login: "test", | |
266 | // Name: "unnamed", | |
267 | // Password: "test", | |
268 | // Access: &[]byte{255, 255, 255, 255, 255, 255, 255, 255}, | |
269 | // }, | |
270 | // request: NewTransaction( | |
271 | // tranNewUser, nil, | |
272 | // NewField(fieldUserLogin, []byte(NegatedUserString([]byte("guest")))), | |
273 | // NewField(fieldUserName, []byte("testUserName")), | |
274 | // NewField(fieldUserPassword, []byte(NegatedUserString([]byte("testPw")))), | |
275 | // NewField(fieldUserAccess, []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}), | |
276 | // ), | |
277 | // want: &Transaction{ | |
278 | // Fields: []Field{ | |
279 | // NewField(fieldError, []byte("Cannot create account guest because there is already an account with that login.")), | |
280 | // }, | |
281 | // }, | |
282 | //}, | |
283 | } | |
284 | ||
285 | for _, test := range tests { | |
286 | t.Run(test.name, func(t *testing.T) { | |
287 | test.Setup(srv) | |
288 | ||
289 | // move to Setup? | |
290 | c := NewClient(test.account.Name, NewTestLogger()) | |
291 | err := c.JoinServer(fmt.Sprintf(":%v", srv.APIPort()), test.account.Login, test.account.Password) | |
292 | if err != nil { | |
293 | t.Errorf("login failed: %v", err) | |
294 | } | |
295 | // end move to Setup?? | |
296 | ||
297 | for key, value := range test.mockHandler { | |
298 | c.Handlers[uint16(key)] = value | |
299 | } | |
300 | ||
301 | // send test case request | |
302 | _ = c.Send(*test.request) | |
303 | ||
304 | //time.Sleep(1 * time.Second) | |
305 | // === | |
306 | ||
307 | transactions, _ := readN(c.Connection, 1) | |
308 | for _, t := range transactions { | |
309 | _ = c.HandleTransaction(&t) | |
310 | } | |
311 | ||
312 | // === | |
313 | ||
314 | for _, handler := range test.mockHandler { | |
315 | handler.AssertExpectations(t) | |
316 | } | |
317 | ||
318 | test.Teardown(srv) | |
319 | }) | |
320 | } | |
321 | } | |
322 | ||
00d1ef67 JH |
323 | func tranAssertEqual(t *testing.T, tran1, tran2 []Transaction) bool { |
324 | var newT1 []Transaction | |
325 | var newT2 []Transaction | |
92a7e455 JH |
326 | for _, trans := range tran1 { |
327 | trans.ID = []byte{0, 0, 0, 0} | |
00d1ef67 JH |
328 | newT1 = append(newT1, trans) |
329 | } | |
625b0580 | 330 | |
92a7e455 JH |
331 | for _, trans := range tran2 { |
332 | trans.ID = []byte{0, 0, 0, 0} | |
00d1ef67 | 333 | newT2 = append(newT2, trans) |
625b0580 | 334 | |
00d1ef67 | 335 | } |
625b0580 | 336 | |
00d1ef67 | 337 | return assert.Equal(t, newT1, newT2) |
92a7e455 | 338 | } |