From 712f4bd7f87d3c300b5ba0e962e412d0033f61e9 Mon Sep 17 00:00:00 2001 From: Delyan Angelov Date: Fri, 14 Aug 2020 00:51:57 +0300 Subject: [PATCH] vweb: add tests --- vlib/vweb/tests/vweb_test.v | 189 +++++++++++++++++++++++++++++ vlib/vweb/tests/vweb_test_server.v | 69 +++++++++++ 2 files changed, 258 insertions(+) create mode 100644 vlib/vweb/tests/vweb_test.v create mode 100644 vlib/vweb/tests/vweb_test_server.v diff --git a/vlib/vweb/tests/vweb_test.v b/vlib/vweb/tests/vweb_test.v new file mode 100644 index 0000000000..83dc58c3bc --- /dev/null +++ b/vlib/vweb/tests/vweb_test.v @@ -0,0 +1,189 @@ +import os +import time +import net +import net.http + +const ( + sport = 12380 + exit_after_time = 1000 // milliseconds + vexe = os.getenv('VEXE') + vroot = os.dir(vexe) + serverexe = os.join_path(os.cache_dir(), 'vweb_test_server.exe') +) + +// setup of vweb webserver +fn testsuite_begin() { + os.chdir(vroot) + if os.exists(serverexe) { + os.rm(serverexe) + } + // prevent failing tests when vweb_test.v is rerun quickly + // and the previous webserver has not yet timed out. + for i := 0; i < 10; i++ { + if client := net.dial('127.0.0.1', sport) { + client.close() or { } + eprintln('previous webserver has not yet stopped ($i); waiting...') + time.sleep_ms(exit_after_time / 10) + continue + } else { + return + } + } +} + +fn test_a_simple_vweb_app_can_be_compiled() { + did_server_compile := os.system('$vexe -o $serverexe vlib/vweb/tests/vweb_test_server.v') + assert did_server_compile == 0 + assert os.exists(serverexe) +} + +fn test_a_simple_vweb_app_runs_in_the_background() { + server_exec_cmd := '$serverexe $sport $exit_after_time > /dev/null &' + $if debug_net_socket_client ? { + eprintln('running:\n$server_exec_cmd') + } + res := os.system(server_exec_cmd) + assert res == 0 + time.sleep_ms(100) +} + +// 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() { + received := simple_tcp_client({}) or { + assert err == '' + 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() { + received := simple_tcp_client({ + path: '/simple' + }) or { + assert err == '' + 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') +} + +fn test_a_simple_tcp_client_html_page() { + received := simple_tcp_client({ + path: '/html_page' + }) or { + assert err == '' + return + } + assert_common_headers(received) + assert received.contains('Content-Type: text/html') + assert received.ends_with('

ok

') +} + +// net.http client based tests follow: +fn assert_common_http_headers(x http.Response) { + assert x.status_code == 200 + assert x.headers['Server'] == 'VWeb' + assert x.headers['Content-Length'].int() > 0 + assert x.headers['Connection'] == 'close' +} + +fn test_http_client_index() { + x := http.get('http://127.0.0.1:$sport/') or { + panic(err) + } + assert_common_http_headers(x) + assert x.headers['Content-Type'] == 'text/plain' + assert x.text == 'Welcome to VWeb' +} + +fn test_http_client_simple() { + x := http.get('http://127.0.0.1:$sport/simple') or { + panic(err) + } + assert_common_http_headers(x) + assert x.headers['Content-Type'] == 'text/plain' + assert x.text == 'A simple result' +} + +fn test_http_client_html_page() { + x := http.get('http://127.0.0.1:$sport/html_page') or { + panic(err) + } + assert_common_http_headers(x) + assert x.headers['Content-Type'] == 'text/html' + assert x.text == '

ok

' +} + +fn test_http_client_settings_page() { + x := http.get('http://127.0.0.1:$sport/bilbo/settings') or { + panic(err) + } + assert_common_http_headers(x) + assert x.text == 'username: bilbo' + y := http.get('http://127.0.0.1:$sport/kent/settings') or { + panic(err) + } + assert_common_http_headers(y) + assert y.text == 'username: kent' +} + +fn test_http_client_user_repo_settings_page() { + x := http.get('http://127.0.0.1:$sport/bilbo/gostamp/settings') or { + panic(err) + } + assert_common_http_headers(x) + assert x.text == 'username: bilbo | repository: gostamp' + y := http.get('http://127.0.0.1:$sport/kent/golang/settings') or { + panic(err) + } + assert_common_http_headers(y) + assert y.text == 'username: kent | repository: golang' +} + +// utility code: +struct SimpleTcpClientConfig { + host string = 'static.dev' + path string = '/' + agent string = 'v/net.tcp.v' + headers string = '' + content string = '' +} + +fn simple_tcp_client(config SimpleTcpClientConfig) ?string { + client := net.dial('127.0.0.1', sport) or { + return error(err) + } + defer { + client.close() or { } + } + 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') + } + client.send(message.str, message.len) or { + return error(err) + } + bytes, blen := client.recv(4096) + received := unsafe {bytes.vstring_with_len(blen)} + $if debug_net_socket_client ? { + eprintln('received:\n$received') + } + return received +} diff --git a/vlib/vweb/tests/vweb_test_server.v b/vlib/vweb/tests/vweb_test_server.v new file mode 100644 index 0000000000..f4508095f8 --- /dev/null +++ b/vlib/vweb/tests/vweb_test_server.v @@ -0,0 +1,69 @@ +module main + +import os +import vweb +import time + +struct App { + port int + timeout int +pub mut: + vweb vweb.Context +} + +fn exit_after_timeout(timeout_in_ms int) { + time.sleep_ms(timeout_in_ms) + // eprintln('webserver is exiting ...') + exit(0) +} + +fn main() { + if os.args.len != 3 { + panic('Usage: `vweb_test_server.exe PORT TIMEOUT_IN_MILLISECONDS`') + } + http_port := os.args[1].int() + assert http_port > 0 + timeout := os.args[2].int() + assert timeout > 0 + go exit_after_timeout(timeout) + // + mut app := App{ + port: http_port + timeout: timeout + } + vweb.run_app(mut app, http_port) +} + +pub fn (mut app App) init() { +} + +pub fn (mut app App) init_once() { + eprintln('Started webserver on http://127.0.0.1:$app.port/ , with maximum runtime of $app.timeout milliseconds.') +} + +pub fn (mut app App) index() { + app.vweb.text('Welcome to VWeb') +} + +pub fn (mut app App) simple() vweb.Result { + app.vweb.text('A simple result') + return vweb.Result{} +} + +pub fn (mut app App) html_page() vweb.Result { + app.vweb.html('

ok

') + return vweb.Result{} +} + +// the following serve custom routes +['/:user/settings'] +pub fn (mut app App) settings(username string) vweb.Result { + app.vweb.html('username: $username') + return vweb.Result{} +} + +['/:user/:repo/settings'] +pub fn (mut app App) user_repo_settings(username, repository string) vweb.Result { + app.vweb.html('username: $username | repository: $repository') + return vweb.Result{} +}