import os import time import json import net import net.http import io const ( sport = 12381 localserver = '127.0.0.1:${sport}' exit_after_time = 12000 // milliseconds vexe = os.getenv('VEXE') vweb_logfile = os.getenv('VWEB_LOGFILE') vroot = os.dir(vexe) serverexe = os.join_path(os.cache_dir(), 'middleware_test_server.exe') tcp_r_timeout = 30 * time.second tcp_w_timeout = 30 * time.second ) // setup of vweb webserver fn testsuite_begin() { os.chdir(vroot) or {} if os.exists(serverexe) { os.rm(serverexe) or {} } } fn test_middleware_vweb_app_can_be_compiled() { // did_server_compile := os.system('${os.quoted_path(vexe)} -g -o ${os.quoted_path(serverexe)} vlib/vweb/tests/middleware_test_server.vv') // TODO: find out why it does not compile with -usecache and -g did_server_compile := os.system('${os.quoted_path(vexe)} -o ${os.quoted_path(serverexe)} vlib/vweb/tests/middleware_test_server.v') assert did_server_compile == 0 assert os.exists(serverexe) } fn test_middleware_vweb_app_runs_in_the_background() { mut suffix := '' $if !windows { suffix = ' > /dev/null &' } if vweb_logfile != '' { suffix = ' 2>> ${os.quoted_path(vweb_logfile)} >> ${os.quoted_path(vweb_logfile)} &' } server_exec_cmd := '${os.quoted_path(serverexe)} ${sport} ${exit_after_time} ${suffix}' $if debug_net_socket_client ? { eprintln('running:\n${server_exec_cmd}') } $if windows { spawn os.system(server_exec_cmd) } $else { res := os.system(server_exec_cmd) assert res == 0 } $if macos { time.sleep(1000 * time.millisecond) } $else { time.sleep(100 * time.millisecond) } } // normal routes: fn test_app_middleware() { x := http.get('http://${localserver}/') or { panic(err) } assert x.body == '0app_middlewareindex' } fn test_single_middleware() { received := simple_tcp_client(path: '/single') or { assert err.msg() == '' return } assert received.starts_with('m1HTTP/') assert received.ends_with('0single') } fn test_multiple_middleware() { received := simple_tcp_client(path: '/multiple') or { assert err.msg() == '' return } assert received.starts_with('m1m2HTTP/') assert received.ends_with('0multiple') } fn test_combined_middleware() { received := simple_tcp_client(path: '/combined') or { assert err.msg() == '' return } assert received.starts_with('m1m2HTTP/') assert received.ends_with('0app_middlewarecombined') } fn test_nested_middleware() { received := simple_tcp_client(path: '/admin/nested') or { assert err.msg() == '' return } assert received.starts_with('m1HTTP/') assert received.ends_with('0nested') } // above routes + post struct Post { msg string } fn test_app_post_middleware() { test_object := Post{ msg: 'HI' } json_test := json.encode(test_object) mut x := http.post_json('http://${localserver}/index_post', json_test) or { panic(err) } assert x.body == '0app_middlewareindex_post:${json_test}' } fn test_single_post_middleware() { test_object := Post{ msg: 'HI' } json_test := json.encode(test_object) received := simple_tcp_client_post_json( path: '/single_post' headers: 'Content-Length: ${json_test.len}\r\n' content: json_test ) or { assert err.msg() == '' return } assert received.starts_with('m1') assert received.ends_with('0single_post:${json_test}') } fn test_multiple_post_middleware() { test_object := Post{ msg: 'HI' } json_test := json.encode(test_object) received := simple_tcp_client_post_json( path: '/multiple_post' headers: 'Content-Length: ${json_test.len}\r\n' content: json_test ) or { assert err.msg() == '' return } assert received.starts_with('m1m2') assert received.ends_with('0multiple_post:${json_test}') } fn test_combined_post_middleware() { test_object := Post{ msg: 'HI' } json_test := json.encode(test_object) received := simple_tcp_client_post_json( path: '/combined_post' headers: 'Content-Length: ${json_test.len}\r\n' content: json_test ) or { assert err.msg() == '' return } assert received.starts_with('m1m2') assert received.ends_with('0app_middlewarecombined_post:${json_test}') } fn test_nested_post_middleware() { test_object := Post{ msg: 'HI' } json_test := json.encode(test_object) received := simple_tcp_client_post_json( path: '/admin/nested_post' headers: 'Content-Length: ${json_test.len}\r\n' content: json_test ) or { assert err.msg() == '' return } assert received.starts_with('m1') assert received.ends_with('0nested_post:${json_test}') } // dynamic routes: fn test_dynamic_middleware() { dynamic_path := 'test' received := simple_tcp_client(path: '/admin/${dynamic_path}') or { assert err.msg() == '' return } assert received.starts_with('m1HTTP/') assert received.ends_with('0admin_dynamic:${dynamic_path}') } fn test_combined_dynamic_middleware() { dynamic_path := 'test' received := simple_tcp_client(path: '/other/${dynamic_path}') or { assert err.msg() == '' return } assert received.starts_with('m1m2HTTP/') assert received.ends_with('0app_middlewarecombined_dynamic:${dynamic_path}') } // redirect routes: fn test_app_redirect_middleware() { x := http.get('http://${localserver}/app_redirect') or { panic(err) } x_home := http.get('http://${localserver}/') or { panic(err) } assert x.body == x_home.body received := simple_tcp_client(path: '/app_redirect') or { assert err.msg() == '' return } assert received.starts_with('HTTP/1.1 302 Found') assert received.ends_with('302 Found') } fn test_redirect_middleware() { received := simple_tcp_client(path: '/redirect') or { assert err.msg() == '' return } println(received) assert received.starts_with('m_redirect') assert received.contains('HTTP/1.1 302 Found') assert received.ends_with('302 Found') } // Context's fn test_middleware_with_context() { x := http.get('http://${localserver}/with-context') or { panic(err) } assert x.body == 'b' } fn testsuite_end() { // This test is guaranteed to be called last. // It sends a request to the server to shutdown. x := http.fetch( url: 'http://${localserver}/shutdown' method: .get cookies: { 'skey': 'superman' } ) or { assert err.msg() == '' return } assert x.status() == .ok assert x.body == 'good bye' } // utility code: struct SimpleTcpClientConfig { retries int = 20 host string = 'static.dev' path string = '/' agent string = 'v/net.tcp.v' headers string = '\r\n' content string } fn simple_tcp_client(config SimpleTcpClientConfig) !string { mut client := &net.TcpConn(unsafe { nil }) mut tries := 0 for tries < config.retries { tries++ eprintln('> client retries: ${tries}') client = net.dial_tcp(localserver) or { if tries > config.retries { return err } time.sleep(100 * time.millisecond) continue } break } if client == unsafe { nil } { eprintln('coult not create a tcp client connection to ${localserver} after ${config.retries} retries') exit(1) } client.set_read_timeout(tcp_r_timeout) client.set_write_timeout(tcp_w_timeout) 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.write(message.bytes())! read := io.read_all(reader: client)! $if debug_net_socket_client ? { eprintln('received:\n${read}') } return read.bytestr() } fn simple_tcp_client_post_json(config SimpleTcpClientConfig) !string { mut client := &net.TcpConn(unsafe { nil }) mut tries := 0 for tries < config.retries { tries++ eprintln('> client retries: ${tries}') client = net.dial_tcp(localserver) or { if tries > config.retries { return err } time.sleep(100 * time.millisecond) continue } break } if client == unsafe { nil } { eprintln('coult not create a tcp client connection to ${localserver} after ${config.retries} retries') exit(1) } client.set_read_timeout(tcp_r_timeout) client.set_write_timeout(tcp_w_timeout) defer { client.close() or {} } message := 'POST ${config.path} HTTP/1.1 Host: ${config.host} User-Agent: ${config.agent} Accept: */* Content-Type: application/json ${config.headers} ${config.content}' $if debug_net_socket_client ? { eprintln('sending:\n${message}') } client.write(message.bytes())! read := io.read_all(reader: client)! $if debug_net_socket_client ? { eprintln('received:\n${read}') } return read.bytestr() }