v / vlib / net / websocket / websocket_test.v
215 lines · 185 sloc · 5.93 KB · 9b26b5d46a5c9092d3b2bbfb80f60622b4d04ce7
Raw
1// vtest build: !windows
2import os
3import net
4import net.websocket
5import time
6
7@[heap]
8struct WebsocketTestResults {
9pub mut:
10 nr_messages int
11 nr_pong_received int
12 nr_closes int
13}
14
15// Do not run these tests every time, since they are flaky.
16// They have their own specialized CI runner.
17const github_job = os.getenv('GITHUB_JOB')
18
19const should_skip = get_should_skip()
20
21fn get_should_skip() bool {
22 return github_job != '' && github_job != 'websocket_tests'
23}
24
25// tests with internal ws servers
26fn test_ws_ipv6() {
27 if should_skip {
28 return
29 }
30 start_server(.ip6, 30001)!
31
32 ws_test(.ip6, 'ws://localhost:30001') or {
33 eprintln('> error while connecting .ip6, err: ${err}')
34 assert false
35 }
36}
37
38// tests with internal ws servers
39fn test_ws_ipv4() {
40 if should_skip {
41 return
42 }
43 start_server(.ip, 30002)!
44
45 ws_test(.ip, 'ws://localhost:30002') or {
46 eprintln('> error while connecting .ip, err: ${err}')
47 assert false
48 }
49}
50
51fn start_server(family net.AddrFamily, listen_port int) ! {
52 eprintln('> start_server family:${family} | listen_port: ${listen_port}')
53 mut s := websocket.new_server(family, listen_port, '')
54 // make that in execution test time give time to execute at least one time
55 s.set_ping_interval(1)
56
57 s.on_connect(fn (mut s websocket.ServerClient) !bool {
58 // here you can look att the client info and accept or not accept
59 // just returning a true/false
60 if s.resource_name != '/' {
61 panic('unexpected resource name in test')
62 return false
63 }
64 return true
65 })!
66 s.on_message(fn (mut ws websocket.Client, msg &websocket.Message) ! {
67 match msg.opcode {
68 .pong { ws.write_string('pong')! }
69 else { ws.write(msg.payload, msg.opcode)! }
70 }
71 })
72
73 s.on_close(fn (mut ws websocket.Client, code int, reason string) ! {
74 // not used
75 })
76 start_server_in_thread_and_wait_till_it_is_ready_to_accept_connections(mut s)
77 eprintln('> start_server finished')
78}
79
80fn start_server_in_thread_and_wait_till_it_is_ready_to_accept_connections(mut ws websocket.Server) {
81 eprintln('-----------------------------------------------------------------------------')
82 spawn fn [mut ws] () {
83 ws.listen() or { panic('websocket server could not listen, err: ${err}') }
84 }()
85 for ws.get_state() != .open {
86 time.sleep(10 * time.millisecond)
87 }
88 eprintln('-----------------------------------------------------------------------------')
89}
90
91fn open_cb(mut client websocket.Client) ! {
92 client.pong()!
93 assert true
94}
95
96fn close_cb(mut client websocket.Client, err string) ! {
97 println('error: ${err}')
98 // this can be thrown by internet connection problems
99 assert false
100}
101
102fn message_cb(mut client websocket.Client, msg &websocket.Message, mut res WebsocketTestResults) ! {
103 println('client got type: ${msg.opcode} payload:\n${msg.payload}')
104 if msg.opcode == .text_frame {
105 smessage := msg.payload.bytestr()
106 match smessage {
107 'pong' {
108 res.nr_pong_received++
109 }
110 'a' {
111 res.nr_messages++
112 }
113 else {
114 assert false
115 }
116 }
117 } else {
118 println('Binary message: ${msg}')
119 }
120}
121
122// ws_test tests connect to the websocket server from websocket client
123fn ws_test(family net.AddrFamily, uri string) ! {
124 eprintln('connecting to ${uri} ...')
125
126 mut test_results := WebsocketTestResults{}
127 mut client := websocket.new_client(uri)!
128 client.on_open(open_cb)
129 client.on_error(close_cb)
130 client.on_message_ref(message_cb, test_results)
131 client.connect()!
132 spawn client.listen()
133
134 text := ['a'].repeat(2)
135 for msg in text {
136 client.write(msg.bytes(), .text_frame) or {
137 panic('fail to write to websocket, err: ${err}')
138 }
139 // sleep to give time to receive response before send a new one
140 time.sleep(100 * time.millisecond)
141 }
142 // sleep to give time to receive response before asserts
143 time.sleep(1500 * time.millisecond)
144 // We expect at least 2 pongs, one sent directly and one indirectly
145 assert test_results.nr_pong_received >= 2
146 assert test_results.nr_messages == 2
147}
148
149fn on_message_cb_2(mut cli websocket.Client, msg &websocket.Message) ! {
150 if msg.opcode == .text_frame {
151 cli.close(1000, 'closing connection')!
152 }
153}
154
155fn on_close_cb_2(mut cli websocket.Client, code int, reason string, mut res WebsocketTestResults) ! {
156 res.nr_closes++
157}
158
159fn test_on_close_when_server_closing_connection() ! {
160 mut ws := websocket.new_server(.ip, 30003, '')
161 ws.on_message(on_message_cb_2)
162 mut test_results := WebsocketTestResults{}
163 ws.on_close_ref(on_close_cb_2, test_results)
164 start_server_in_thread_and_wait_till_it_is_ready_to_accept_connections(mut ws)
165
166 mut client := websocket.new_client('ws://localhost:30003')!
167 client.connect()!
168 spawn client.listen()
169 time.sleep(1000 * time.millisecond)
170 client.write_string('a message')!
171 time.sleep(1000 * time.millisecond)
172 assert test_results.nr_closes == 1
173}
174
175fn on_close_cb_3(mut cli websocket.Client, code int, reason string, mut res WebsocketTestResults) ! {
176 res.nr_closes++
177}
178
179fn test_on_close_when_client_closing_connection() ! {
180 mut ws := websocket.new_server(.ip, 30004, '')
181 start_server_in_thread_and_wait_till_it_is_ready_to_accept_connections(mut ws)
182
183 mut client := websocket.new_client('ws://localhost:30004')!
184 mut test_results := WebsocketTestResults{}
185 client.on_close_ref(on_close_cb_3, test_results)
186 client.connect()!
187 spawn client.listen()
188 time.sleep(1000 * time.millisecond)
189 client.close(1000, 'closing connection')!
190 time.sleep(1000 * time.millisecond)
191 assert test_results.nr_closes == 1
192}
193
194fn attached_fail_cb(mut sc websocket.ServerClient) ! {
195 return error('attached hook failure')
196}
197
198fn client_close_ignore_cb(mut cli websocket.Client, code int, reason string) ! {}
199
200fn test_on_attached_error_does_not_leak_client() ! {
201 mut ws := websocket.new_server(.ip, 30005, '')
202 ws.on_attached(attached_fail_cb)
203 start_server_in_thread_and_wait_till_it_is_ready_to_accept_connections(mut ws)
204
205 mut client := websocket.new_client('ws://localhost:30005')!
206 client.on_close(client_close_ignore_cb)
207 client.connect()!
208 spawn client.listen()
209 time.sleep(500 * time.millisecond)
210
211 client_count := rlock ws.server_state {
212 ws.server_state.clients.len
213 }
214 assert client_count == 0
215}
216