source: code/trunk/server_test.go@ 779

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

Use background context in tests

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