2020-08-13 23:51:57 +02:00
|
|
|
import os
|
|
|
|
import time
|
2020-08-14 01:43:57 +02:00
|
|
|
import json
|
2020-08-13 23:51:57 +02:00
|
|
|
import net
|
|
|
|
import net.http
|
2020-11-15 21:54:47 +01:00
|
|
|
import io
|
2020-08-13 23:51:57 +02:00
|
|
|
|
|
|
|
const (
|
|
|
|
sport = 12380
|
2022-02-01 17:41:12 +01:00
|
|
|
localserver = 'localhost:$sport'
|
2021-04-05 21:38:55 +02:00
|
|
|
exit_after_time = 12000 // milliseconds
|
2020-08-13 23:51:57 +02:00
|
|
|
vexe = os.getenv('VEXE')
|
2021-01-02 12:21:30 +01:00
|
|
|
vweb_logfile = os.getenv('VWEB_LOGFILE')
|
2020-08-13 23:51:57 +02:00
|
|
|
vroot = os.dir(vexe)
|
|
|
|
serverexe = os.join_path(os.cache_dir(), 'vweb_test_server.exe')
|
2020-11-15 21:54:47 +01:00
|
|
|
tcp_r_timeout = 30 * time.second
|
|
|
|
tcp_w_timeout = 30 * time.second
|
2020-08-13 23:51:57 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
// setup of vweb webserver
|
|
|
|
fn testsuite_begin() {
|
2021-08-28 08:35:39 +02:00
|
|
|
os.chdir(vroot) or {}
|
2021-01-25 12:08:43 +01:00
|
|
|
if os.exists(serverexe) {
|
2021-03-06 20:04:51 +01:00
|
|
|
os.rm(serverexe) or {}
|
2020-08-13 23:51:57 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn test_a_simple_vweb_app_can_be_compiled() {
|
2022-01-22 20:13:16 +01:00
|
|
|
// did_server_compile := os.system('${os.quoted_path(vexe)} -g -o ${os.quoted_path(serverexe)} vlib/vweb/tests/vweb_test_server.v')
|
2021-01-20 06:04:59 +01:00
|
|
|
// TODO: find out why it does not compile with -usecache and -g
|
2022-01-22 20:13:16 +01:00
|
|
|
did_server_compile := os.system('${os.quoted_path(vexe)} -o ${os.quoted_path(serverexe)} vlib/vweb/tests/vweb_test_server.v')
|
2020-08-13 23:51:57 +02:00
|
|
|
assert did_server_compile == 0
|
2021-01-25 12:08:43 +01:00
|
|
|
assert os.exists(serverexe)
|
2020-08-13 23:51:57 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
fn test_a_simple_vweb_app_runs_in_the_background() {
|
2021-01-02 12:21:30 +01:00
|
|
|
mut suffix := ''
|
|
|
|
$if !windows {
|
|
|
|
suffix = ' > /dev/null &'
|
|
|
|
}
|
2021-01-25 12:08:43 +01:00
|
|
|
if vweb_logfile != '' {
|
2022-01-22 20:13:16 +01:00
|
|
|
suffix = ' 2>> ${os.quoted_path(vweb_logfile)} >> ${os.quoted_path(vweb_logfile)} &'
|
2021-01-02 12:21:30 +01:00
|
|
|
}
|
2022-01-22 20:13:16 +01:00
|
|
|
server_exec_cmd := '${os.quoted_path(serverexe)} $sport $exit_after_time $suffix'
|
2020-08-13 23:51:57 +02:00
|
|
|
$if debug_net_socket_client ? {
|
|
|
|
eprintln('running:\n$server_exec_cmd')
|
|
|
|
}
|
2020-11-15 21:54:47 +01:00
|
|
|
$if windows {
|
|
|
|
go os.system(server_exec_cmd)
|
|
|
|
} $else {
|
|
|
|
res := os.system(server_exec_cmd)
|
|
|
|
assert res == 0
|
|
|
|
}
|
2021-03-03 08:56:22 +01:00
|
|
|
$if macos {
|
|
|
|
time.sleep(1000 * time.millisecond)
|
|
|
|
} $else {
|
|
|
|
time.sleep(100 * time.millisecond)
|
|
|
|
}
|
2020-08-13 23:51:57 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// web client tests follow
|
|
|
|
fn assert_common_headers(received string) {
|
|
|
|
assert received.starts_with('HTTP/1.1 200 OK\r\n')
|
|
|
|
assert received.contains('Server: VWeb\r\n')
|
|
|
|
assert received.contains('Content-Length:')
|
|
|
|
assert received.contains('Connection: close\r\n')
|
|
|
|
}
|
|
|
|
|
|
|
|
fn test_a_simple_tcp_client_can_connect_to_the_vweb_server() {
|
2021-01-05 01:30:27 +01:00
|
|
|
received := simple_tcp_client(path: '/') or {
|
2022-02-11 14:52:33 +01:00
|
|
|
assert err.msg() == ''
|
2020-08-13 23:51:57 +02:00
|
|
|
return
|
|
|
|
}
|
|
|
|
assert_common_headers(received)
|
|
|
|
assert received.contains('Content-Type: text/plain')
|
|
|
|
assert received.contains('Content-Length: 15')
|
|
|
|
assert received.ends_with('Welcome to VWeb')
|
|
|
|
}
|
|
|
|
|
|
|
|
fn test_a_simple_tcp_client_simple_route() {
|
2020-12-27 10:38:12 +01:00
|
|
|
received := simple_tcp_client(path: '/simple') or {
|
2022-02-11 14:52:33 +01:00
|
|
|
assert err.msg() == ''
|
2020-08-13 23:51:57 +02:00
|
|
|
return
|
|
|
|
}
|
|
|
|
assert_common_headers(received)
|
|
|
|
assert received.contains('Content-Type: text/plain')
|
|
|
|
assert received.contains('Content-Length: 15')
|
|
|
|
assert received.ends_with('A simple result')
|
|
|
|
}
|
|
|
|
|
2021-03-02 20:31:57 +01:00
|
|
|
fn test_a_simple_tcp_client_zero_content_length() {
|
|
|
|
// tests that sending a content-length header of 0 doesn't hang on a read timeout
|
|
|
|
watch := time.new_stopwatch(auto_start: true)
|
|
|
|
simple_tcp_client(path: '/', headers: 'Content-Length: 0\r\n\r\n') or {
|
2022-02-11 14:52:33 +01:00
|
|
|
assert err.msg() == ''
|
2021-03-02 20:31:57 +01:00
|
|
|
return
|
|
|
|
}
|
|
|
|
assert watch.elapsed() < 1 * time.second
|
|
|
|
}
|
|
|
|
|
2020-08-13 23:51:57 +02:00
|
|
|
fn test_a_simple_tcp_client_html_page() {
|
2020-12-27 10:38:12 +01:00
|
|
|
received := simple_tcp_client(path: '/html_page') or {
|
2022-02-11 14:52:33 +01:00
|
|
|
assert err.msg() == ''
|
2020-08-13 23:51:57 +02:00
|
|
|
return
|
|
|
|
}
|
|
|
|
assert_common_headers(received)
|
|
|
|
assert received.contains('Content-Type: text/html')
|
|
|
|
assert received.ends_with('<h1>ok</h1>')
|
|
|
|
}
|
|
|
|
|
|
|
|
// net.http client based tests follow:
|
2021-04-09 18:17:33 +02:00
|
|
|
fn assert_common_http_headers(x http.Response) ? {
|
2021-07-24 19:47:45 +02:00
|
|
|
assert x.status() == .ok
|
2022-05-13 05:56:21 +02:00
|
|
|
assert x.header.get(.server)? == 'VWeb'
|
|
|
|
assert x.header.get(.content_length)?.int() > 0
|
|
|
|
assert x.header.get(.connection)? == 'close'
|
2020-08-13 23:51:57 +02:00
|
|
|
}
|
|
|
|
|
2021-04-09 18:17:33 +02:00
|
|
|
fn test_http_client_index() ? {
|
2022-02-01 17:41:12 +01:00
|
|
|
x := http.get('http://$localserver/') or { panic(err) }
|
2022-05-13 05:56:21 +02:00
|
|
|
assert_common_http_headers(x)?
|
|
|
|
assert x.header.get(.content_type)? == 'text/plain'
|
2020-08-13 23:51:57 +02:00
|
|
|
assert x.text == 'Welcome to VWeb'
|
|
|
|
}
|
|
|
|
|
2021-04-09 18:17:33 +02:00
|
|
|
fn test_http_client_404() ? {
|
2020-08-14 01:43:57 +02:00
|
|
|
url_404_list := [
|
2022-02-01 17:41:12 +01:00
|
|
|
'http://$localserver/zxcnbnm',
|
|
|
|
'http://$localserver/JHKAJA',
|
|
|
|
'http://$localserver/unknown',
|
2020-11-15 21:54:47 +01:00
|
|
|
]
|
2020-08-14 01:43:57 +02:00
|
|
|
for url in url_404_list {
|
2021-03-01 00:18:14 +01:00
|
|
|
res := http.get(url) or { panic(err) }
|
2021-07-24 19:47:45 +02:00
|
|
|
assert res.status() == .not_found
|
2020-08-14 01:43:57 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-04-09 18:17:33 +02:00
|
|
|
fn test_http_client_simple() ? {
|
2022-02-01 17:41:12 +01:00
|
|
|
x := http.get('http://$localserver/simple') or { panic(err) }
|
2022-05-13 05:56:21 +02:00
|
|
|
assert_common_http_headers(x)?
|
|
|
|
assert x.header.get(.content_type)? == 'text/plain'
|
2020-08-13 23:51:57 +02:00
|
|
|
assert x.text == 'A simple result'
|
|
|
|
}
|
|
|
|
|
2021-04-09 18:17:33 +02:00
|
|
|
fn test_http_client_html_page() ? {
|
2022-02-01 17:41:12 +01:00
|
|
|
x := http.get('http://$localserver/html_page') or { panic(err) }
|
2022-05-13 05:56:21 +02:00
|
|
|
assert_common_http_headers(x)?
|
|
|
|
assert x.header.get(.content_type)? == 'text/html'
|
2020-08-13 23:51:57 +02:00
|
|
|
assert x.text == '<h1>ok</h1>'
|
|
|
|
}
|
|
|
|
|
2021-04-09 18:17:33 +02:00
|
|
|
fn test_http_client_settings_page() ? {
|
2022-02-01 17:41:12 +01:00
|
|
|
x := http.get('http://$localserver/bilbo/settings') or { panic(err) }
|
2022-05-13 05:56:21 +02:00
|
|
|
assert_common_http_headers(x)?
|
2020-08-13 23:51:57 +02:00
|
|
|
assert x.text == 'username: bilbo'
|
2020-08-14 01:43:57 +02:00
|
|
|
//
|
2022-02-01 17:41:12 +01:00
|
|
|
y := http.get('http://$localserver/kent/settings') or { panic(err) }
|
2022-05-13 05:56:21 +02:00
|
|
|
assert_common_http_headers(y)?
|
2020-08-13 23:51:57 +02:00
|
|
|
assert y.text == 'username: kent'
|
|
|
|
}
|
|
|
|
|
2021-04-09 18:17:33 +02:00
|
|
|
fn test_http_client_user_repo_settings_page() ? {
|
2022-02-01 17:41:12 +01:00
|
|
|
x := http.get('http://$localserver/bilbo/gostamp/settings') or { panic(err) }
|
2022-05-13 05:56:21 +02:00
|
|
|
assert_common_http_headers(x)?
|
2020-08-13 23:51:57 +02:00
|
|
|
assert x.text == 'username: bilbo | repository: gostamp'
|
2020-08-14 01:43:57 +02:00
|
|
|
//
|
2022-02-01 17:41:12 +01:00
|
|
|
y := http.get('http://$localserver/kent/golang/settings') or { panic(err) }
|
2022-05-13 05:56:21 +02:00
|
|
|
assert_common_http_headers(y)?
|
2020-08-13 23:51:57 +02:00
|
|
|
assert y.text == 'username: kent | repository: golang'
|
2020-08-14 01:43:57 +02:00
|
|
|
//
|
2022-02-01 17:41:12 +01:00
|
|
|
z := http.get('http://$localserver/missing/golang/settings') or { panic(err) }
|
2021-07-24 19:47:45 +02:00
|
|
|
assert z.status() == .not_found
|
2020-08-14 01:43:57 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
struct User {
|
|
|
|
name string
|
|
|
|
age int
|
|
|
|
}
|
|
|
|
|
2021-04-09 18:17:33 +02:00
|
|
|
fn test_http_client_json_post() ? {
|
2020-08-14 01:43:57 +02:00
|
|
|
ouser := User{
|
|
|
|
name: 'Bilbo'
|
|
|
|
age: 123
|
|
|
|
}
|
|
|
|
json_for_ouser := json.encode(ouser)
|
2022-02-01 17:41:12 +01:00
|
|
|
mut x := http.post_json('http://$localserver/json_echo', json_for_ouser) or { panic(err) }
|
2020-08-14 01:43:57 +02:00
|
|
|
$if debug_net_socket_client ? {
|
2021-01-02 12:21:30 +01:00
|
|
|
eprintln('/json_echo endpoint response: $x')
|
2020-08-14 01:43:57 +02:00
|
|
|
}
|
2022-05-13 05:56:21 +02:00
|
|
|
assert x.header.get(.content_type)? == 'application/json'
|
2020-08-14 01:43:57 +02:00
|
|
|
assert x.text == json_for_ouser
|
2020-12-27 10:38:12 +01:00
|
|
|
nuser := json.decode(User, x.text) or { User{} }
|
2020-08-14 01:43:57 +02:00
|
|
|
assert '$ouser' == '$nuser'
|
2021-01-01 21:29:14 +01:00
|
|
|
//
|
2022-02-01 17:41:12 +01:00
|
|
|
x = http.post_json('http://$localserver/json', json_for_ouser) or { panic(err) }
|
2021-01-01 21:29:14 +01:00
|
|
|
$if debug_net_socket_client ? {
|
2021-01-02 12:21:30 +01:00
|
|
|
eprintln('/json endpoint response: $x')
|
2021-01-01 21:29:14 +01:00
|
|
|
}
|
2022-05-13 05:56:21 +02:00
|
|
|
assert x.header.get(.content_type)? == 'application/json'
|
2021-01-01 21:29:14 +01:00
|
|
|
assert x.text == json_for_ouser
|
|
|
|
nuser2 := json.decode(User, x.text) or { User{} }
|
|
|
|
assert '$ouser' == '$nuser2'
|
2020-08-14 01:43:57 +02:00
|
|
|
}
|
|
|
|
|
2021-04-16 07:46:06 +02:00
|
|
|
fn test_http_client_multipart_form_data() ? {
|
|
|
|
boundary := '6844a625b1f0b299'
|
|
|
|
name := 'foo'
|
2021-11-10 17:41:25 +01:00
|
|
|
ct := 'multipart/form-data; boundary=$boundary'
|
2021-04-16 07:46:06 +02:00
|
|
|
contents := 'baz buzz'
|
2021-11-10 17:41:25 +01:00
|
|
|
data := '--$boundary\r
|
|
|
|
Content-Disposition: form-data; name="$name"\r
|
|
|
|
\r
|
|
|
|
$contents\r
|
|
|
|
--$boundary--\r
|
|
|
|
'
|
2021-08-17 17:36:10 +02:00
|
|
|
mut x := http.fetch(
|
2022-02-01 17:41:12 +01:00
|
|
|
url: 'http://$localserver/form_echo'
|
2021-04-16 07:46:06 +02:00
|
|
|
method: .post
|
|
|
|
header: http.new_header(
|
|
|
|
key: .content_type
|
|
|
|
value: ct
|
|
|
|
)
|
|
|
|
data: data
|
2022-05-13 05:56:21 +02:00
|
|
|
)?
|
2021-04-16 07:46:06 +02:00
|
|
|
$if debug_net_socket_client ? {
|
|
|
|
eprintln('/form_echo endpoint response: $x')
|
|
|
|
}
|
|
|
|
assert x.text == contents
|
|
|
|
}
|
|
|
|
|
2020-08-14 01:43:57 +02:00
|
|
|
fn test_http_client_shutdown_does_not_work_without_a_cookie() {
|
2022-02-01 17:41:12 +01:00
|
|
|
x := http.get('http://$localserver/shutdown') or {
|
2022-02-11 14:52:33 +01:00
|
|
|
assert err.msg() == ''
|
2020-08-14 01:43:57 +02:00
|
|
|
return
|
|
|
|
}
|
2021-07-24 19:47:45 +02:00
|
|
|
assert x.status() == .not_found
|
2020-08-14 01:43:57 +02:00
|
|
|
assert x.text == '404 Not Found'
|
|
|
|
}
|
|
|
|
|
|
|
|
fn testsuite_end() {
|
|
|
|
// This test is guaranteed to be called last.
|
|
|
|
// It sends a request to the server to shutdown.
|
2021-08-17 17:36:10 +02:00
|
|
|
x := http.fetch(
|
2022-02-01 17:41:12 +01:00
|
|
|
url: 'http://$localserver/shutdown'
|
2021-01-10 17:39:37 +01:00
|
|
|
method: .get
|
2021-08-04 11:44:41 +02:00
|
|
|
cookies: {
|
2020-08-14 01:43:57 +02:00
|
|
|
'skey': 'superman'
|
2021-01-10 17:39:37 +01:00
|
|
|
}
|
|
|
|
) or {
|
2022-02-11 14:52:33 +01:00
|
|
|
assert err.msg() == ''
|
2020-08-14 01:43:57 +02:00
|
|
|
return
|
|
|
|
}
|
2021-07-24 19:47:45 +02:00
|
|
|
assert x.status() == .ok
|
2020-08-14 01:43:57 +02:00
|
|
|
assert x.text == 'good bye'
|
2020-08-13 23:51:57 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// utility code:
|
|
|
|
struct SimpleTcpClientConfig {
|
2021-01-03 21:10:25 +01:00
|
|
|
retries int = 20
|
2020-08-13 23:51:57 +02:00
|
|
|
host string = 'static.dev'
|
|
|
|
path string = '/'
|
|
|
|
agent string = 'v/net.tcp.v'
|
2021-01-21 11:08:38 +01:00
|
|
|
headers string = '\r\n'
|
2020-09-09 14:14:44 +02:00
|
|
|
content string
|
2020-08-13 23:51:57 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
fn simple_tcp_client(config SimpleTcpClientConfig) ?string {
|
2021-01-20 11:11:01 +01:00
|
|
|
mut client := &net.TcpConn(0)
|
2020-08-25 16:05:40 +02:00
|
|
|
mut tries := 0
|
|
|
|
for tries < config.retries {
|
|
|
|
tries++
|
2022-02-01 17:41:12 +01:00
|
|
|
client = net.dial_tcp(localserver) or {
|
2020-08-25 16:05:40 +02:00
|
|
|
if tries > config.retries {
|
2021-02-28 21:20:21 +01:00
|
|
|
return err
|
2020-08-25 16:05:40 +02:00
|
|
|
}
|
2021-02-27 18:41:06 +01:00
|
|
|
time.sleep(100 * time.millisecond)
|
2020-08-25 16:05:40 +02:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
break
|
2020-08-13 23:51:57 +02:00
|
|
|
}
|
2021-01-25 12:08:43 +01:00
|
|
|
client.set_read_timeout(tcp_r_timeout)
|
|
|
|
client.set_write_timeout(tcp_w_timeout)
|
2020-08-13 23:51:57 +02:00
|
|
|
defer {
|
2021-03-06 20:04:51 +01:00
|
|
|
client.close() or {}
|
2020-08-13 23:51:57 +02:00
|
|
|
}
|
|
|
|
message := 'GET $config.path HTTP/1.1
|
|
|
|
Host: $config.host
|
|
|
|
User-Agent: $config.agent
|
|
|
|
Accept: */*
|
|
|
|
$config.headers
|
|
|
|
$config.content'
|
|
|
|
$if debug_net_socket_client ? {
|
|
|
|
eprintln('sending:\n$message')
|
|
|
|
}
|
2022-05-13 05:56:21 +02:00
|
|
|
client.write(message.bytes())?
|
|
|
|
read := io.read_all(reader: client)?
|
2020-08-13 23:51:57 +02:00
|
|
|
$if debug_net_socket_client ? {
|
2020-11-15 21:54:47 +01:00
|
|
|
eprintln('received:\n$read')
|
2020-08-13 23:51:57 +02:00
|
|
|
}
|
2020-11-15 21:54:47 +01:00
|
|
|
return read.bytestr()
|
2020-08-13 23:51:57 +02:00
|
|
|
}
|