refactor: User.Config -> User.Config() and User.SetConfig(UserConfig)
[ssh-chat] / chat / room_test.go
1 package chat
2
3 import (
4         "errors"
5         "fmt"
6         "reflect"
7         "testing"
8         "time"
9
10         "github.com/shazow/ssh-chat/chat/message"
11 )
12
13 // Used for testing
14 type MockScreen struct {
15         buffer []byte
16 }
17
18 func (s *MockScreen) Write(data []byte) (n int, err error) {
19         s.buffer = append(s.buffer, data...)
20         return len(data), nil
21 }
22
23 func (s *MockScreen) Read(p *[]byte) (n int, err error) {
24         *p = s.buffer
25         s.buffer = []byte{}
26         return len(*p), nil
27 }
28
29 func (s *MockScreen) Close() error {
30         return nil
31 }
32
33 func TestRoomServe(t *testing.T) {
34         ch := NewRoom()
35         ch.Send(message.NewAnnounceMsg("hello"))
36
37         received := <-ch.broadcast
38         actual := received.String()
39         expected := " * hello"
40
41         if actual != expected {
42                 t.Errorf("Got: %q; Expected: %q", actual, expected)
43         }
44 }
45
46 type ScreenedUser struct {
47         user   *message.User
48         screen *MockScreen
49 }
50
51 func TestIgnore(t *testing.T) {
52         var buffer []byte
53
54         ch := NewRoom()
55         go ch.Serve()
56         defer ch.Close()
57
58         // Create 3 users, join the room and clear their screen buffers
59         users := make([]ScreenedUser, 3)
60         for i := 0; i < 3; i++ {
61                 screen := &MockScreen{}
62                 user := message.NewUserScreen(message.SimpleID(fmt.Sprintf("user%d", i)), screen)
63                 users[i] = ScreenedUser{
64                         user:   user,
65                         screen: screen,
66                 }
67
68                 _, err := ch.Join(user)
69                 if err != nil {
70                         t.Fatal(err)
71                 }
72         }
73
74         for _, u := range users {
75                 for i := 0; i < 3; i++ {
76                         u.user.HandleMsg(u.user.ConsumeOne())
77                         u.screen.Read(&buffer)
78                 }
79         }
80
81         // Use some handy variable names for distinguish between roles
82         ignorer := users[0]
83         ignored := users[1]
84         other := users[2]
85
86         // test ignoring unexisting user
87         if err := sendCommand("/ignore test", ignorer, ch, &buffer); err != nil {
88                 t.Fatal(err)
89         }
90         expectOutput(t, buffer, "-> Err: user not found: test"+message.Newline)
91
92         // test ignoring existing user
93         if err := sendCommand("/ignore "+ignored.user.Name(), ignorer, ch, &buffer); err != nil {
94                 t.Fatal(err)
95         }
96         expectOutput(t, buffer, "-> Ignoring: "+ignored.user.Name()+message.Newline)
97
98         // ignoring the same user twice returns an error message and doesn't add the user twice
99         if err := sendCommand("/ignore "+ignored.user.Name(), ignorer, ch, &buffer); err != nil {
100                 t.Fatal(err)
101         }
102         expectOutput(t, buffer, "-> Err: user already ignored: user1"+message.Newline)
103         if ignoredList := ignorer.user.Ignored.ListPrefix(""); len(ignoredList) != 1 {
104                 t.Fatalf("should have %d ignored users, has %d", 1, len(ignoredList))
105         }
106
107         // when a message is sent from the ignored user, it is delivered to non-ignoring users
108         ch.Send(message.NewPublicMsg("hello", ignored.user))
109         other.user.HandleMsg(other.user.ConsumeOne())
110         other.screen.Read(&buffer)
111         expectOutput(t, buffer, ignored.user.Name()+": hello"+message.Newline)
112
113         // ensure ignorer doesn't have received any message
114         if ignorer.user.HasMessages() {
115                 t.Fatal("should not have messages")
116         }
117
118         // `/ignore` returns a list of ignored users
119         if err := sendCommand("/ignore", ignorer, ch, &buffer); err != nil {
120                 t.Fatal(err)
121         }
122         expectOutput(t, buffer, "-> 1 ignored: "+ignored.user.Name()+message.Newline)
123
124         // `/unignore [USER]` removes the user from ignored ones
125         if err := sendCommand("/unignore "+ignored.user.Name(), users[0], ch, &buffer); err != nil {
126                 t.Fatal(err)
127         }
128         expectOutput(t, buffer, "-> No longer ignoring: user1"+message.Newline)
129
130         if err := sendCommand("/ignore", users[0], ch, &buffer); err != nil {
131                 t.Fatal(err)
132         }
133         expectOutput(t, buffer, "-> 0 users ignored."+message.Newline)
134
135         if ignoredList := ignorer.user.Ignored.ListPrefix(""); len(ignoredList) != 0 {
136                 t.Fatalf("should have %d ignored users, has %d", 0, len(ignoredList))
137         }
138
139         // after unignoring a user, its messages can be received again
140         ch.Send(message.NewPublicMsg("hello again!", ignored.user))
141
142         // give some time for the channel to get the message
143         time.Sleep(100)
144
145         // ensure ignorer has received the message
146         if !ignorer.user.HasMessages() {
147                 // FIXME: This is flaky :/
148                 t.Fatal("should have messages")
149         }
150         ignorer.user.HandleMsg(ignorer.user.ConsumeOne())
151         ignorer.screen.Read(&buffer)
152         expectOutput(t, buffer, ignored.user.Name()+": hello again!"+message.Newline)
153 }
154
155 func expectOutput(t *testing.T, buffer []byte, expected string) {
156         bytes := []byte(expected)
157         if !reflect.DeepEqual(buffer, bytes) {
158                 t.Errorf("Got: %q; Expected: %q", buffer, expected)
159         }
160 }
161
162 func sendCommand(cmd string, mock ScreenedUser, room *Room, buffer *[]byte) error {
163         msg, ok := message.NewPublicMsg(cmd, mock.user).ParseCommand()
164         if !ok {
165                 return errors.New("cannot parse command message")
166         }
167
168         room.Send(msg)
169         mock.user.HandleMsg(mock.user.ConsumeOne())
170         mock.screen.Read(buffer)
171
172         return nil
173 }
174
175 func TestRoomJoin(t *testing.T) {
176         var expected, actual []byte
177
178         s := &MockScreen{}
179         u := message.NewUserScreen(message.SimpleID("foo"), s)
180
181         ch := NewRoom()
182         go ch.Serve()
183         defer ch.Close()
184
185         _, err := ch.Join(u)
186         if err != nil {
187                 t.Fatal(err)
188         }
189
190         u.HandleMsg(u.ConsumeOne())
191         expected = []byte(" * foo joined. (Connected: 1)" + message.Newline)
192         s.Read(&actual)
193         if !reflect.DeepEqual(actual, expected) {
194                 t.Errorf("Got: %q; Expected: %q", actual, expected)
195         }
196
197         ch.Send(message.NewSystemMsg("hello", u))
198         u.HandleMsg(u.ConsumeOne())
199         expected = []byte("-> hello" + message.Newline)
200         s.Read(&actual)
201         if !reflect.DeepEqual(actual, expected) {
202                 t.Errorf("Got: %q; Expected: %q", actual, expected)
203         }
204
205         ch.Send(message.ParseInput("/me says hello.", u))
206         u.HandleMsg(u.ConsumeOne())
207         expected = []byte("** foo says hello." + message.Newline)
208         s.Read(&actual)
209         if !reflect.DeepEqual(actual, expected) {
210                 t.Errorf("Got: %q; Expected: %q", actual, expected)
211         }
212 }
213
214 func TestRoomDoesntBroadcastAnnounceMessagesWhenQuiet(t *testing.T) {
215         u := message.NewUser(message.SimpleID("foo"))
216         u.SetConfig(message.UserConfig{
217                 Quiet: true,
218         })
219
220         ch := NewRoom()
221         defer ch.Close()
222
223         _, err := ch.Join(u)
224         if err != nil {
225                 t.Fatal(err)
226         }
227
228         // Drain the initial Join message
229         <-ch.broadcast
230
231         go func() {
232                 /*
233                         for {
234                                 msg := u.ConsumeChan()
235                                 if _, ok := msg.(*message.AnnounceMsg); ok {
236                                         t.Errorf("Got unexpected `%T`", msg)
237                                 }
238                         }
239                 */
240                 // XXX: Fix this
241         }()
242
243         // Call with an AnnounceMsg and all the other types
244         // and assert we received only non-announce messages
245         ch.HandleMsg(message.NewAnnounceMsg("Ignored"))
246         // Assert we still get all other types of messages
247         ch.HandleMsg(message.NewEmoteMsg("hello", u))
248         ch.HandleMsg(message.NewSystemMsg("hello", u))
249         ch.HandleMsg(message.NewPrivateMsg("hello", u, u))
250         ch.HandleMsg(message.NewPublicMsg("hello", u))
251 }
252
253 func TestRoomQuietToggleBroadcasts(t *testing.T) {
254         u := message.NewUser(message.SimpleID("foo"))
255         u.SetConfig(message.UserConfig{
256                 Quiet: true,
257         })
258
259         ch := NewRoom()
260         defer ch.Close()
261
262         _, err := ch.Join(u)
263         if err != nil {
264                 t.Fatal(err)
265         }
266
267         // Drain the initial Join message
268         <-ch.broadcast
269
270         u.SetConfig(message.UserConfig{
271                 Quiet: false,
272         })
273
274         expectedMsg := message.NewAnnounceMsg("Ignored")
275         ch.HandleMsg(expectedMsg)
276         msg := u.ConsumeOne()
277         if _, ok := msg.(*message.AnnounceMsg); !ok {
278                 t.Errorf("Got: `%T`; Expected: `%T`", msg, expectedMsg)
279         }
280
281         u.SetConfig(message.UserConfig{
282                 Quiet: true,
283         })
284
285         ch.HandleMsg(message.NewAnnounceMsg("Ignored"))
286         ch.HandleMsg(message.NewSystemMsg("hello", u))
287         msg = u.ConsumeOne()
288         if _, ok := msg.(*message.AnnounceMsg); ok {
289                 t.Errorf("Got unexpected `%T`", msg)
290         }
291 }
292
293 func TestQuietToggleDisplayState(t *testing.T) {
294         var expected, actual []byte
295
296         s := &MockScreen{}
297         u := message.NewUserScreen(message.SimpleID("foo"), s)
298
299         ch := NewRoom()
300         go ch.Serve()
301         defer ch.Close()
302
303         _, err := ch.Join(u)
304         if err != nil {
305                 t.Fatal(err)
306         }
307
308         u.HandleMsg(u.ConsumeOne())
309         expected = []byte(" * foo joined. (Connected: 1)" + message.Newline)
310         s.Read(&actual)
311         if !reflect.DeepEqual(actual, expected) {
312                 t.Errorf("Got: %q; Expected: %q", actual, expected)
313         }
314
315         ch.Send(message.ParseInput("/quiet", u))
316
317         u.HandleMsg(u.ConsumeOne())
318         expected = []byte("-> Quiet mode is toggled ON" + message.Newline)
319         s.Read(&actual)
320         if !reflect.DeepEqual(actual, expected) {
321                 t.Errorf("Got: %q; Expected: %q", actual, expected)
322         }
323
324         ch.Send(message.ParseInput("/quiet", u))
325
326         u.HandleMsg(u.ConsumeOne())
327         expected = []byte("-> Quiet mode is toggled OFF" + message.Newline)
328         s.Read(&actual)
329         if !reflect.DeepEqual(actual, expected) {
330                 t.Errorf("Got: %q; Expected: %q", actual, expected)
331         }
332 }
333
334 func TestRoomNames(t *testing.T) {
335         var expected, actual []byte
336
337         s := &MockScreen{}
338         u := message.NewUserScreen(message.SimpleID("foo"), s)
339
340         ch := NewRoom()
341         go ch.Serve()
342         defer ch.Close()
343
344         _, err := ch.Join(u)
345         if err != nil {
346                 t.Fatal(err)
347         }
348
349         u.HandleMsg(u.ConsumeOne())
350         expected = []byte(" * foo joined. (Connected: 1)" + message.Newline)
351         s.Read(&actual)
352         if !reflect.DeepEqual(actual, expected) {
353                 t.Errorf("Got: %q; Expected: %q", actual, expected)
354         }
355
356         ch.Send(message.ParseInput("/names", u))
357
358         u.HandleMsg(u.ConsumeOne())
359         expected = []byte("-> 1 connected: foo" + message.Newline)
360         s.Read(&actual)
361         if !reflect.DeepEqual(actual, expected) {
362                 t.Errorf("Got: %q; Expected: %q", actual, expected)
363         }
364 }