source: code/trunk/server_test.go@ 603

Last change on this file since 603 was 603, checked in by contact, 4 years ago

test: add NOTICE broadcast

File size: 4.7 KB
RevLine 
[600]1package soju
2
3import (
4 "net"
5 "testing"
6
7 "golang.org/x/crypto/bcrypt"
8 "gopkg.in/irc.v3"
9)
10
[602]11var testServerPrefix = &irc.Prefix{Name: "soju-test-server"}
12
[600]13const (
14 testUsername = "soju-test-user"
15 testPassword = testUsername
16)
17
18func createTempDB(t *testing.T) Database {
19 db, err := OpenSqliteDB("sqlite3", ":memory:")
20 if err != nil {
21 t.Fatalf("failed to create temporary SQLite database: %v", err)
22 }
23 // :memory: will open a separate database for each new connection. Make
24 // sure the sql package only uses a single connection. An alternative
25 // solution is to use "file::memory:?cache=shared".
26 db.(*SqliteDB).db.SetMaxOpenConns(1)
27 return db
28}
29
[602]30func createTestUser(t *testing.T, db Database) *User {
[600]31 hashed, err := bcrypt.GenerateFromPassword([]byte(testPassword), bcrypt.DefaultCost)
32 if err != nil {
33 t.Fatalf("failed to generate bcrypt hash: %v", err)
34 }
35
36 record := &User{Username: testUsername, Password: string(hashed)}
37 if err := db.StoreUser(record); err != nil {
38 t.Fatalf("failed to store test user: %v", err)
39 }
40
[602]41 return record
[600]42}
43
[603]44func createTestDownstream(t *testing.T, srv *Server) ircConn {
45 c1, c2 := net.Pipe()
46 go srv.handle(newNetIRCConn(c1))
47 return newNetIRCConn(c2)
48}
49
[602]50type testUpstream struct {
51 net.Listener
52 Accept chan ircConn
53}
54
55func createTestUpstream(t *testing.T, db Database, user *User) (*Network, *testUpstream) {
56 ln, err := net.Listen("tcp", "localhost:0")
57 if err != nil {
58 t.Fatalf("failed to create TCP listener: %v", err)
59 }
60
61 tu := &testUpstream{
62 Listener: ln,
63 Accept: make(chan ircConn),
64 }
65
66 go func() {
67 defer close(tu.Accept)
68
69 for {
70 c, err := ln.Accept()
71 if isErrClosed(err) {
72 break
73 } else if err != nil {
74 t.Fatalf("failed accepting connection: %v", err)
75 }
76 tu.Accept <- newNetIRCConn(c)
77 }
78 }()
79
80 network := &Network{
81 Name: "testnet",
82 Addr: "irc+insecure://" + ln.Addr().String(),
83 Nick: user.Username,
84 Enabled: true,
85 }
86 if err := db.StoreNetwork(user.ID, network); err != nil {
87 t.Fatalf("failed to store test network: %v", err)
88 }
89
90 return network, tu
91}
92
[600]93func expectMessage(t *testing.T, c ircConn, cmd string) *irc.Message {
94 msg, err := c.ReadMessage()
95 if err != nil {
96 t.Fatalf("failed to read IRC message (want %q): %v", cmd, err)
97 }
98 if msg.Command != cmd {
99 t.Fatalf("invalid message received: want %q, got: %v", cmd, msg)
100 }
101 return msg
102}
103
[602]104func registerDownstreamConn(t *testing.T, c ircConn, network *Network) {
[600]105 c.WriteMessage(&irc.Message{
106 Command: "PASS",
107 Params: []string{testPassword},
108 })
109 c.WriteMessage(&irc.Message{
110 Command: "NICK",
111 Params: []string{testUsername},
112 })
113 c.WriteMessage(&irc.Message{
114 Command: "USER",
[602]115 Params: []string{testUsername + "/" + network.Name, "0", "*", testUsername},
[600]116 })
117
118 expectMessage(t, c, irc.RPL_WELCOME)
119}
120
[602]121func registerUpstreamConn(t *testing.T, c ircConn) {
122 msg := expectMessage(t, c, "CAP")
123 if msg.Params[0] != "LS" {
124 t.Fatalf("invalid CAP LS: got: %v", msg)
125 }
126 msg = expectMessage(t, c, "NICK")
127 nick := msg.Params[0]
128 if nick != testUsername {
129 t.Fatalf("invalid NICK: want %q, got: %v", testUsername, msg)
130 }
131 expectMessage(t, c, "USER")
132
133 c.WriteMessage(&irc.Message{
134 Prefix: testServerPrefix,
135 Command: irc.RPL_WELCOME,
136 Params: []string{nick, "Welcome!"},
137 })
138 c.WriteMessage(&irc.Message{
139 Prefix: testServerPrefix,
140 Command: irc.RPL_YOURHOST,
141 Params: []string{nick, "Your host is soju-test-server"},
142 })
143 c.WriteMessage(&irc.Message{
144 Prefix: testServerPrefix,
145 Command: irc.RPL_CREATED,
146 Params: []string{nick, "Who cares when the server was created?"},
147 })
148 c.WriteMessage(&irc.Message{
149 Prefix: testServerPrefix,
150 Command: irc.RPL_MYINFO,
151 Params: []string{nick, testServerPrefix.Name, "soju", "aiwroO", "OovaimnqpsrtklbeI"},
152 })
153 c.WriteMessage(&irc.Message{
154 Prefix: testServerPrefix,
155 Command: irc.ERR_NOMOTD,
156 Params: []string{nick, "No MOTD"},
157 })
158}
159
[600]160func TestServer(t *testing.T) {
[602]161 db := createTempDB(t)
162 user := createTestUser(t, db)
163 network, upstream := createTestUpstream(t, db, user)
164 defer upstream.Close()
165
166 srv := NewServer(db)
[600]167 if err := srv.Start(); err != nil {
168 t.Fatalf("failed to start server: %v", err)
169 }
170 defer srv.Shutdown()
171
[602]172 uc := <-upstream.Accept
173 defer uc.Close()
174 registerUpstreamConn(t, uc)
[600]175
[602]176 dc := createTestDownstream(t, srv)
177 defer dc.Close()
[603]178 registerDownstreamConn(t, dc, network)
[602]179
[603]180 noticeText := "This is a very important server notice."
181 uc.WriteMessage(&irc.Message{
182 Prefix: testServerPrefix,
183 Command: "NOTICE",
184 Params: []string{testUsername, noticeText},
185 })
186
187 var msg *irc.Message
188 for {
189 var err error
190 msg, err = dc.ReadMessage()
191 if err != nil {
192 t.Fatalf("failed to read IRC message: %v", err)
193 }
194 if msg.Command == "NOTICE" {
195 break
196 }
197 }
198
199 if msg.Params[1] != noticeText {
200 t.Fatalf("invalid NOTICE text: want %q, got: %v", noticeText, msg)
201 }
[600]202}
Note: See TracBrowser for help on using the repository browser.