From f4b7149dbd18504f778af4b99eddf26d99ba958d Mon Sep 17 00:00:00 2001 From: Sergey Bronnikov Date: Fri, 22 Oct 2021 16:00:15 +0300 Subject: [PATCH] test: rewrite TAP tests with luatest Patch adds a tests ported from TAP to luatest format that were intitially implemented in commits 'Port from tap to luatest' (549058d8d3cfd2bd48555ece47fd9a6e8a167626) and 'Rewrite tap tests to luatest' (54d0fca) and later reverted in scope of issue with discard v2. Follows up #90 Part of #134 --- .github/workflows/test.yml | 4 +- CMakeLists.txt | 4 +- cmake/FindLuaTest.cmake | 12 + deps.sh | 10 + test/helpers.lua | 94 ++++ test/http.test.lua | 524 ------------------ test/integration/http_log_requests_test.lua | 184 ++++++ .../integration/http_server_requests_test.lua | 323 +++++++++++ test/integration/http_server_url_for_test.lua | 26 + .../http_server_url_match_test.lua | 49 ++ test/unit/http_params_test.lua | 15 + test/unit/http_parse_request_test.lua | 37 ++ test/unit/http_split_uri_test.lua | 85 +++ test/unit/http_template_test.lua | 48 ++ 14 files changed, 888 insertions(+), 527 deletions(-) create mode 100644 cmake/FindLuaTest.cmake create mode 100755 deps.sh create mode 100644 test/helpers.lua delete mode 100755 test/http.test.lua create mode 100644 test/integration/http_log_requests_test.lua create mode 100644 test/integration/http_server_requests_test.lua create mode 100644 test/integration/http_server_url_for_test.lua create mode 100644 test/integration/http_server_url_match_test.lua create mode 100644 test/unit/http_params_test.lua create mode 100644 test/unit/http_parse_request_test.lua create mode 100644 test/unit/http_split_uri_test.lua create mode 100644 test/unit/http_template_test.lua diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 5c97926..dfe067e 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -22,11 +22,11 @@ jobs: id: cache-rocks with: path: .rocks/ - key: cache-rocks-${{ matrix.runs-on }}-01 + key: cache-rocks-${{ matrix.runs-on }}-03 - run: echo $PWD/.rocks/bin >> $GITHUB_PATH - - run: tarantoolctl rocks make + - run: ./deps.sh - name: Build module run: | diff --git a/CMakeLists.txt b/CMakeLists.txt index 9457d19..146bd87 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,6 +6,8 @@ if(NOT CMAKE_BUILD_TYPE) endif() set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake" ${CMAKE_MODULE_PATH}) +find_package(LuaTest) + # Find Tarantool and Lua dependecies set(TARANTOOL_FIND_REQUIRED ON) find_package(Tarantool) @@ -21,7 +23,7 @@ enable_testing() set (LUA_PATH "LUA_PATH=${PROJECT_SOURCE_DIR}/?.lua\\;${PROJECT_SOURCE_DIR}/?/init.lua\\;\\;") set (LUA_SOURCE_DIR "LUA_SOURCE_DIR=${PROJECT_SOURCE_DIR}") -add_test(http ${CMAKE_SOURCE_DIR}/test/http.test.lua) +add_test(http ${LUATEST}) set_tests_properties(http PROPERTIES ENVIRONMENT "${LUA_PATH};${LUA_SOURCE_DIR}") diff --git a/cmake/FindLuaTest.cmake b/cmake/FindLuaTest.cmake new file mode 100644 index 0000000..04b039e --- /dev/null +++ b/cmake/FindLuaTest.cmake @@ -0,0 +1,12 @@ +find_program(LUATEST luatest + HINTS .rocks/ + PATH_SUFFIXES bin + DOC "Lua testing framework" +) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(LuaTest + REQUIRED_VARS LUATEST +) + +mark_as_advanced(LUATEST) diff --git a/deps.sh b/deps.sh new file mode 100755 index 0000000..0c4f125 --- /dev/null +++ b/deps.sh @@ -0,0 +1,10 @@ +#!/bin/sh + +# Call this script to install test dependencies. + +set -e + +# Test dependencies: +tarantoolctl rocks install luatest 0.5.5 + +tarantoolctl rocks make diff --git a/test/helpers.lua b/test/helpers.lua new file mode 100644 index 0000000..f55f42f --- /dev/null +++ b/test/helpers.lua @@ -0,0 +1,94 @@ +local fio = require('fio') +local http_server = require('http.server') +local http_client = require('http.client') + +local helpers = table.copy(require('luatest').helpers) + +helpers.base_port = 12345 +helpers.base_host = '127.0.0.1' +helpers.base_uri = ('http://%s:%s'):format(helpers.base_host, helpers.base_port) + +helpers.cfgserv = function() + local path = os.getenv('LUA_SOURCE_DIR') or './' + path = fio.pathjoin(path, 'test') + + local httpd = http_server.new(helpers.base_host, helpers.base_port, { + app_dir = path, + log_requests = false, + log_errors = false + }) + :route({path = '/abc/:cde/:def', name = 'test'}, function() end) + :route({path = '/abc'}, function() end) + :route({path = '/ctxaction'}, 'module.controller#action') + :route({path = '/absentaction'}, 'module.controller#absent') + :route({path = '/absent'}, 'module.absent#action') + :route({path = '/abc/:cde'}, function() end) + :route({path = '/abc_:cde_def'}, function() end) + :route({path = '/abc-:cde-def'}, function() end) + :route({path = '/aba*def'}, function() end) + :route({path = '/abb*def/cde', name = 'star'}, function() end) + :route({path = '/banners/:token'}) + :helper('helper_title', function(self, a) return 'Hello, ' .. a end) + :route({path = '/helper', file = 'helper.html.el'}) + :route({path = '/test', file = 'test.html.el' }, + function(cx) return cx:render({ title = 'title: 123' }) end) + + return httpd +end + +local log_queue = {} + +helpers.clear_log_queue = function() + log_queue = {} +end + +helpers.custom_logger = { + debug = function() end, + verbose = function() + table.insert(log_queue, { + log_lvl = 'verbose', + }) + end, + info = function(...) + table.insert(log_queue, { + log_lvl = 'info', + msg = string.format(...) + }) + end, + warn = function(...) + table.insert(log_queue, { + log_lvl = 'warn', + msg = string.format(...) + }) + end, + error = function(...) + table.insert(log_queue, { + log_lvl = 'error', + msg = string.format(...) + }) + end +} + +helpers.find_msg_in_log_queue = function(msg, strict) + for _, log in ipairs(log_queue) do + if not strict then + if log.msg:match(msg) then + return log + end + else + if log.msg == msg then + return log + end + end + end +end + +helpers.teardown = function(httpd) + httpd:stop() + helpers.retrying({}, function() + local r = http_client.request('GET', helpers.base_uri) + return r == nil + end) +end + +return helpers diff --git a/test/http.test.lua b/test/http.test.lua deleted file mode 100755 index 396cbaf..0000000 --- a/test/http.test.lua +++ /dev/null @@ -1,524 +0,0 @@ -#!/usr/bin/env tarantool - -local tap = require('tap') -local fio = require('fio') -local http_lib = require('http.lib') -local http_client = require('http.client') -local http_server = require('http.server') -local json = require('json') -local yaml = require 'yaml' -local urilib = require('uri') - -local test = tap.test("http") -test:plan(8) - -test:test("split_uri", function(test) - test:plan(65) - local function check(uri, rhs) - local lhs = urilib.parse(uri) - local extra = { lhs = lhs, rhs = rhs } - if lhs.query == '' then - lhs.query = nil - end - test:is(lhs.scheme, rhs.scheme, uri.." scheme", extra) - test:is(lhs.host, rhs.host, uri.." host", extra) - test:is(lhs.service, rhs.service, uri.." service", extra) - test:is(lhs.path, rhs.path, uri.." path", extra) - test:is(lhs.query, rhs.query, uri.." query", extra) - end - check('http://abc', { scheme = 'http', host = 'abc'}) - check('http://abc/', { scheme = 'http', host = 'abc', path ='/'}) - check('http://abc?', { scheme = 'http', host = 'abc'}) - check('http://abc/?', { scheme = 'http', host = 'abc', path ='/'}) - check('http://abc/?', { scheme = 'http', host = 'abc', path ='/'}) - check('http://abc:123', { scheme = 'http', host = 'abc', service = '123' }) - check('http://abc:123?', { scheme = 'http', host = 'abc', service = '123'}) - check('http://abc:123?query', { scheme = 'http', host = 'abc', - service = '123', query = 'query'}) - check('http://domain.subdomain.com:service?query', { scheme = 'http', - host = 'domain.subdomain.com', service = 'service', query = 'query'}) - check('google.com', { host = 'google.com'}) - check('google.com?query', { host = 'google.com', query = 'query'}) - check('google.com/abc?query', { host = 'google.com', path = '/abc', - query = 'query'}) - check('https://google.com:443/abc?query', { scheme = 'https', - host = 'google.com', service = '443', path = '/abc', query = 'query'}) -end) - -test:test("template", function(test) - test:plan(5) - test:is(http_lib.template("<% for i = 1, cnt do %> <%= abc %> <% end %>", - {abc = '1 <3>&" ', cnt = 3}), - ' 1 <3>&" 1 <3>&" 1 <3>&" ', - "tmpl1") - test:is(http_lib.template("<% for i = 1, cnt do %> <%= ab %> <% end %>", - {abc = '1 <3>&" ', cnt = 3}), - ' nil nil nil ', "tmpl2") - local r, msg = pcall(http_lib.template, "<% ab() %>", {ab = '1'}) - test:ok(r == false and msg:match("call local 'ab'") ~= nil, "bad template") - - -- gh-18: rendered tempate is truncated - local template = [[ - - - - % for i,v in pairs(t) do - - - - - % end -
<%= i %><%= v %>
- - -]] - - local t = {} - for i=1, 100 do - t[i] = string.rep('#', i) - end - - local rendered, code = http_lib.template(template, { t = t }) - test:ok(#rendered > 10000, "rendered size") - test:is(rendered:sub(#rendered - 7, #rendered - 1), "", "rendered eof") -end) - -test:test('parse_request', function(test) - test:plan(6) - - test:is_deeply(http_lib._parse_request('abc'), - { error = 'Broken request line', headers = {} }, 'broken request') - - - - test:is( - http_lib._parse_request("GET / HTTP/1.1\nHost: s.com\r\n\r\n").path, - '/', - 'path' - ) - test:is_deeply( - http_lib._parse_request("GET / HTTP/1.1\nHost: s.com\r\n\r\n").proto, - {1,1}, - 'proto' - ) - test:is_deeply( - http_lib._parse_request("GET / HTTP/1.1\nHost: s.com\r\n\r\n").headers, - {host = 's.com'}, - 'host' - ) - test:is_deeply( - http_lib._parse_request("GET / HTTP/1.1\nHost: s.com\r\n\r\n").method, - 'GET', - 'method' - ) - test:is_deeply( - http_lib._parse_request("GET / HTTP/1.1\nHost: s.com\r\n\r\n").query, - '', - 'query' - ) -end) - -test:test('params', function(test) - test:plan(6) - test:is_deeply(http_lib.params(), {}, 'nil string') - test:is_deeply(http_lib.params(''), {}, 'empty string') - test:is_deeply(http_lib.params('a'), {a = ''}, 'separate literal') - test:is_deeply(http_lib.params('a=b'), {a = 'b'}, 'one variable') - test:is_deeply(http_lib.params('a=b&b=cde'), {a = 'b', b = 'cde'}, 'some') - test:is_deeply(http_lib.params('a=b&b=cde&a=1'), - {a = { 'b', '1' }, b = 'cde'}, 'array') -end) - -local function cfgserv() - local path = os.getenv('LUA_SOURCE_DIR') or './' - path = fio.pathjoin(path, 'test') - local httpd = http_server.new('127.0.0.1', 12345, { app_dir = path, - log_requests = false, log_errors = false }) - :route({path = '/abc/:cde/:def', name = 'test'}, function() end) - :route({path = '/abc'}, function() end) - :route({path = '/ctxaction'}, 'module.controller#action') - :route({path = '/absentaction'}, 'module.controller#absent') - :route({path = '/absent'}, 'module.absent#action') - :route({path = '/abc/:cde'}, function() end) - :route({path = '/abc_:cde_def'}, function() end) - :route({path = '/abc-:cde-def'}, function() end) - :route({path = '/aba*def'}, function() end) - :route({path = '/abb*def/cde', name = 'star'}, function() end) - :route({path = '/banners/:token'}) - :helper('helper_title', function(self, a) return 'Hello, ' .. a end) - :route({path = '/helper', file = 'helper.html.el'}) - :route({ path = '/test', file = 'test.html.el' }, - function(cx) return cx:render({ title = 'title: 123' }) end) - return httpd -end - -test:test("server url match", function(test) - test:plan(18) - local httpd = cfgserv() - test:istable(httpd, "httpd object") - test:isnil(httpd:match('GET', '/')) - test:is(httpd:match('GET', '/abc').endpoint.path, "/abc", "/abc") - test:is(#httpd:match('GET', '/abc').stash, 0, "/abc") - test:is(httpd:match('GET', '/abc/123').endpoint.path, "/abc/:cde", "/abc/123") - test:is(httpd:match('GET', '/abc/123').stash.cde, "123", "/abc/123") - test:is(httpd:match('GET', '/abc/123/122').endpoint.path, "/abc/:cde/:def", - "/abc/123/122") - test:is(httpd:match('GET', '/abc/123/122').stash.def, "122", - "/abc/123/122") - test:is(httpd:match('GET', '/abc/123/122').stash.cde, "123", - "/abc/123/122") - test:is(httpd:match('GET', '/abc_123-122').endpoint.path, "/abc_:cde_def", - "/abc_123-122") - test:is(httpd:match('GET', '/abc_123-122').stash.cde_def, "123-122", - "/abc_123-122") - test:is(httpd:match('GET', '/abc-123-def').endpoint.path, "/abc-:cde-def", - "/abc-123-def") - test:is(httpd:match('GET', '/abc-123-def').stash.cde, "123", - "/abc-123-def") - test:is(httpd:match('GET', '/aba-123-dea/1/2/3').endpoint.path, - "/aba*def", '/aba-123-dea/1/2/3') - test:is(httpd:match('GET', '/aba-123-dea/1/2/3').stash.def, - "-123-dea/1/2/3", '/aba-123-dea/1/2/3') - test:is(httpd:match('GET', '/abb-123-dea/1/2/3/cde').endpoint.path, - "/abb*def/cde", '/abb-123-dea/1/2/3/cde') - test:is(httpd:match('GET', '/abb-123-dea/1/2/3/cde').stash.def, - "-123-dea/1/2/3", '/abb-123-dea/1/2/3/cde') - test:is(httpd:match('GET', '/banners/1wulc.z8kiy.6p5e3').stash.token, - '1wulc.z8kiy.6p5e3', "stash with dots") -end) - -test:test("server url_for", function(test) - test:plan(5) - local httpd = cfgserv() - test:is(httpd:url_for('abcdef'), '/abcdef', '/abcdef') - test:is(httpd:url_for('test'), '/abc//', '/abc//') - test:is(httpd:url_for('test', { cde = 'cde_v', def = 'def_v' }), - '/abc/cde_v/def_v', '/abc/cde_v/def_v') - test:is(httpd:url_for('star', { def = '/def_v' }), - '/abb/def_v/cde', '/abb/def_v/cde') - test:is(httpd:url_for('star', { def = '/def_v' }, { a = 'b', c = 'd' }), - '/abb/def_v/cde?a=b&c=d', '/abb/def_v/cde?a=b&c=d') -end) - -test:test("server requests", function(test) - test:plan(36) - local httpd = cfgserv() - httpd:start() - - local r = http_client.get('http://127.0.0.1:12345/test') - test:is(r.status, 200, '/test code') - test:is(r.proto[1], 1, '/test http 1.1') - test:is(r.proto[2], 1, '/test http 1.1') - test:is(r.reason, 'Ok', '/test reason') - test:is(string.match(r.body, 'title: 123'), 'title: 123', '/test body') - - local r = http_client.get('http://127.0.0.1:12345/test404') - test:is(r.status, 404, '/test404 code') - -- broken in built-in tarantool/http - --test:is(r.reason, 'Not found', '/test404 reason') - - local r = http_client.get('http://127.0.0.1:12345/absent') - test:is(r.status, 500, '/absent code') - --test:is(r.reason, 'Internal server error', '/absent reason') - test:is(string.match(r.body, 'load module'), 'load module', '/absent body') - - local r = http_client.get('http://127.0.0.1:12345/ctxaction') - test:is(r.status, 200, '/ctxaction code') - test:is(r.reason, 'Ok', '/ctxaction reason') - test:is(string.match(r.body, 'Hello, Tarantool'), 'Hello, Tarantool', - '/ctxaction body') - test:is(string.match(r.body, 'action: action'), 'action: action', - '/ctxaction body action') - test:is(string.match(r.body, 'controller: module[.]controller'), - 'controller: module.controller', '/ctxaction body controller') - - local r = http_client.get('http://127.0.0.1:12345/ctxaction.invalid') - test:is(r.status, 404, '/ctxaction.invalid code') -- WTF? - --test:is(r.reason, 'Not found', '/ctxaction.invalid reason') - --test:is(r.body, '', '/ctxaction.invalid body') - - local r = http_client.get('http://127.0.0.1:12345/hello.html') - test:is(r.status, 200, '/hello.html code') - test:is(r.reason, 'Ok', '/hello.html reason') - test:is(string.match(r.body, 'static html'), 'static html', - '/hello.html body') - - local r = http_client.get('http://127.0.0.1:12345/absentaction') - test:is(r.status, 500, '/absentaction 500') - --test:is(r.reason, 'Internal server error', '/absentaction reason') - test:is(string.match(r.body, 'contain function'), 'contain function', - '/absentaction body') - - local r = http_client.get('http://127.0.0.1:12345/helper') - test:is(r.status, 200, 'helper 200') - test:is(r.reason, 'Ok', 'helper reason') - test:is(string.match(r.body, 'Hello, world'), 'Hello, world', 'helper body') - - local r = http_client.get('http://127.0.0.1:12345/helper?abc') - test:is(r.status, 200, 'helper?abc 200') - test:is(r.reason, 'Ok', 'helper?abc reason') - test:is(string.match(r.body, 'Hello, world'), 'Hello, world', 'helper body') - - httpd:route({path = '/die', file = 'helper.html.el'}, - function() error(123) end ) - - local r = http_client.get('http://127.0.0.1:12345/die') - test:is(r.status, 500, 'die 500') - --test:is(r.reason, 'Internal server error', 'die reason') - - httpd:route({ path = '/info' }, function(cx) - return cx:render({ json = cx.peer }) - end) - local r = json.decode(http_client.get('http://127.0.0.1:12345/info').body) - test:is(r.host, '127.0.0.1', 'peer.host') - test:isnumber(r.port, 'peer.port') - - local r = httpd:route({method = 'POST', path = '/dit', file = 'helper.html.el'}, - function(tx) - return tx:render({text = 'POST = ' .. tx:read()}) - end) - test:istable(r, ':route') - - - test:test('GET/POST at one route', function(test) - test:plan(8) - - r = httpd:route({method = 'POST', path = '/dit', file = 'helper.html.el'}, - function(tx) - return tx:render({text = 'POST = ' .. tx:read()}) - end) - test:istable(r, 'add POST method') - - r = httpd:route({method = 'GET', path = '/dit', file = 'helper.html.el'}, - function(tx) - return tx:render({text = 'GET = ' .. tx:read()}) - end ) - test:istable(r, 'add GET method') - - r = httpd:route({method = 'DELETE', path = '/dit', file = 'helper.html.el'}, - function(tx) - return tx:render({text = 'DELETE = ' .. tx:read()}) - end ) - test:istable(r, 'add DELETE method') - - r = httpd:route({method = 'PATCH', path = '/dit', file = 'helper.html.el'}, - function(tx) - return tx:render({text = 'PATCH = ' .. tx:read()}) - end ) - test:istable(r, 'add PATCH method') - - r = http_client.request('POST', 'http://127.0.0.1:12345/dit', 'test') - test:is(r.body, 'POST = test', 'POST reply') - - r = http_client.request('GET', 'http://127.0.0.1:12345/dit') - test:is(r.body, 'GET = ', 'GET reply') - - r = http_client.request('DELETE', 'http://127.0.0.1:12345/dit', 'test1') - test:is(r.body, 'DELETE = test1', 'DELETE reply') - - r = http_client.request('PATCH', 'http://127.0.0.1:12345/dit', 'test2') - test:is(r.body, 'PATCH = test2', 'PATCH reply') - end) - - httpd:route({path = '/chunked'}, function(self) - return self:iterate(ipairs({'chunked', 'encoding', 't\r\nest'})) - end) - - -- http client currently doesn't support chunked encoding - local r = http_client.get('http://127.0.0.1:12345/chunked') - test:is(r.status, 200, 'chunked 200') - test:is(r.headers['transfer-encoding'], 'chunked', 'chunked headers') - test:is(r.body, 'chunkedencodingt\r\nest', 'chunked body') - - test:test('get cookie', function(test) - test:plan(2) - httpd:route({path = '/receive_cookie'}, function(req) - local foo = req:cookie('foo') - local baz = req:cookie('baz') - return req:render({ - text = ('foo=%s; baz=%s'):format(foo, baz) - }) - end) - local r = http_client.get('http://127.0.0.1:12345/receive_cookie', { - headers = { - cookie = 'foo=bar; baz=feez', - } - }) - test:is(r.status, 200, 'status') - test:is(r.body, 'foo=bar; baz=feez', 'body') - end) - - test:test('cookie', function(test) - test:plan(2) - httpd:route({path = '/cookie'}, function(req) - local resp = req:render({text = ''}) - resp:setcookie({ name = 'test', value = 'tost', - expires = '+1y', path = '/abc' }) - resp:setcookie({ name = 'xxx', value = 'yyy' }) - return resp - end) - local r = http_client.get('http://127.0.0.1:12345/cookie') - test:is(r.status, 200, 'status') - test:ok(r.headers['set-cookie'] ~= nil, "header") - end) - - test:test('post body', function(test) - test:plan(2) - httpd:route({ path = '/post', method = 'POST'}, function(req) - local t = { - #req:read("\n"); - #req:read(10); - #req:read({ size = 10, delimiter = "\n"}); - #req:read("\n"); - #req:read(); - #req:read(); - #req:read(); - } - return req:render({json = t}) - end) - local bodyf = os.getenv('LUA_SOURCE_DIR') or './' - bodyf = io.open(fio.pathjoin(bodyf, 'test/public/lorem.txt')) - local body = bodyf:read('*a') - bodyf:close() - local r = http_client.post('http://127.0.0.1:12345/post', body) - test:is(r.status, 200, 'status') - test:is_deeply(json.decode(r.body), { 541,10,10,458,1375,0,0 }, - 'req:read() results') - end) - - httpd:stop() -end) - -local log_queue = {} - -local custom_logger = { - debug = function() end, - verbose = function(...) - table.insert(log_queue, { log_lvl = 'verbose', }) - end, - info = function(...) - table.insert(log_queue, { log_lvl = 'info', msg = string.format(...)}) - end, - warn = function(...) - table.insert(log_queue, { log_lvl = 'warn', msg = string.format(...)}) - end, - error = function(...) - table.insert(log_queue, { log_lvl = 'error', msg = string.format(...)}) - end -} - -local function find_msg_in_log_queue(msg, strict) - for _, log in ipairs(log_queue) do - if not strict then - if log.msg:match(msg) then - return log - end - else - if log.msg == msg then - return log - end - end - end -end - -local function clear_log_queue() - log_queue = {} -end - -test:test("Custom log functions for route", function(test) - test:plan(5) - - test:test("Setting log option for server instance", function(test) - test:plan(2) - - local httpd = http_server.new("127.0.0.1", 12345, { log_requests = custom_logger.info, log_errors = custom_logger.error }) - httpd:route({ path='/' }, function(_) end) - httpd:route({ path='/error' }, function(_) error('Some error...') end) - httpd:start() - - http_client.get("127.0.0.1:12345") - test:is_deeply(find_msg_in_log_queue("GET /"), { log_lvl = 'info', msg = 'GET /' }, "Route should logging requests in custom logger if it's presents") - clear_log_queue() - - http_client.get("127.0.0.1:12345/error") - test:ok(find_msg_in_log_queue("Some error...", false), "Route should logging error in custom logger if it's presents") - clear_log_queue() - - httpd:stop() - end) - - test:test("Setting log options for route", function(test) - test:plan(8) - local httpd = http_server.new("127.0.0.1", 12345, { log_requests = true, log_errors = false }) - local dummy_logger = function() end - - local ok, err = pcall(httpd.route, httpd, { path = '/', log_requests = 3 }) - test:is(ok, false, "Route logger can't be a log_level digit") - test:like(err, "'log_requests' option should be a function", "route() should return error message in case of incorrect logger option") - - ok, err = pcall(httpd.route, httpd, { path = '/', log_requests = { info = dummy_logger } }) - test:is(ok, false, "Route logger can't be a table") - test:like(err, "'log_requests' option should be a function", "route() should return error message in case of incorrect logger option") - - local ok, err = pcall(httpd.route, httpd, { path = '/', log_errors = 3 }) - test:is(ok, false, "Route error logger can't be a log_level digit") - test:like(err, "'log_errors' option should be a function", "route() should return error message in case of incorrect logger option") - - ok, err = pcall(httpd.route, httpd, { path = '/', log_errors = { error = dummy_logger } }) - test:is(ok, false, "Route error logger can't be a table") - test:like(err, "'log_errors' option should be a function", "route() should return error message in case of incorrect log_errors option") - end) - - test:test("Log output with custom loggers on route", function(test) - test:plan(3) - local httpd = http_server.new("127.0.0.1", 12345, { log_requests = true, log_errors = true }) - httpd:start() - - httpd:route({ path = '/', log_requests = custom_logger.info, log_errors = custom_logger.error }, function(_) end) - http_client.get("127.0.0.1:12345") - test:is_deeply(find_msg_in_log_queue("GET /"), { log_lvl = 'info', msg = 'GET /' }, "Route should logging requests in custom logger if it's presents") - clear_log_queue() - - httpd.routes = {} - httpd:route({ path = '/', log_requests = custom_logger.info, log_errors = custom_logger.error }, function(_) - error("User business logic exception...") - end) - http_client.get("127.0.0.1:12345") - test:is_deeply(find_msg_in_log_queue("GET /"), { log_lvl = 'info', msg = 'GET /' }, "Route should logging request and error in case of route exception") - test:ok(find_msg_in_log_queue("User business logic exception...", false), - "Route should logging error custom logger if it's presents in case of route exception") - clear_log_queue() - - httpd:stop() - end) - - test:test("Log route requests with turned off 'log_requests' option", function(test) - test:plan(1) - local httpd = http_server.new("127.0.0.1", 12345, { log_requests = false }) - httpd:start() - - httpd:route({ path = '/', log_requests = custom_logger.info }, function(_) end) - http_client.get("127.0.0.1:12345") - test:is_deeply(find_msg_in_log_queue("GET /"), { log_lvl = 'info', msg = 'GET /' }, "Route can override logging requests if the http server have turned off 'log_requests' option") - clear_log_queue() - - httpd:stop() - end) - - test:test("Log route requests with turned off 'log_errors' option", function(test) - test:plan(1) - local httpd = http_server.new("127.0.0.1", 12345, { log_errors = false }) - httpd:start() - - httpd:route({ path = '/', log_errors = custom_logger.error }, function(_) - error("User business logic exception...") - end) - http_client.get("127.0.0.1:12345") - test:ok(find_msg_in_log_queue("User business logic exception...", false), "Route can override logging requests if the http server have turned off 'log_errors' option") - clear_log_queue() - - httpd:stop() - end) -end) - -os.exit(test:check() == true and 0 or 1) diff --git a/test/integration/http_log_requests_test.lua b/test/integration/http_log_requests_test.lua new file mode 100644 index 0000000..f7b2b64 --- /dev/null +++ b/test/integration/http_log_requests_test.lua @@ -0,0 +1,184 @@ +local t = require('luatest') +local http_client = require('http.client') +local http_server = require('http.server') + +local helpers = require('test.helpers') + +local pgroup = t.group('log_requests') + +pgroup.before_test('test_server_custom_logger', function(g) + g.httpd = http_server.new(helpers.base_host, helpers.base_port, { + log_requests = helpers.custom_logger.info, + log_errors = helpers.custom_logger.error + }) + g.httpd:route({ + path='/' + }, function(_) end) + g.httpd:route({ + path='/error' + }, function(_) error('Some error...') end) + g.httpd:start() +end) + +pgroup.after_test('test_server_custom_logger', function(g) + helpers.teardown(g.httpd) +end) + +pgroup.before_test('test_log_errors_off', function(g) + g.httpd = http_server.new(helpers.base_host, helpers.base_port, { + log_errors = false + }) + g.httpd:start() +end) + +pgroup.after_test('test_log_errors_off', function(g) + helpers.teardown(g.httpd) +end) + +pgroup.before_test('test_route_custom_logger', function(g) + g.httpd = http_server.new(helpers.base_host, helpers.base_port, { + log_requests = true, + log_errors = true + }) + g.httpd:start() +end) + +pgroup.after_test('test_route_custom_logger', function(g) + helpers.teardown(g.httpd) +end) + +pgroup.before_test('test_log_requests_off', function(g) + g.httpd = http_server.new(helpers.base_host, helpers.base_port, { + log_requests = false + }) + g.httpd:start() +end) + +pgroup.after_test('test_log_requests_off', function(g) + helpers.teardown(g.httpd) +end) + +-- Setting log option for server instance. +pgroup.test_server_custom_logger = function() + http_client.get(helpers.base_uri) + t.assert_equals(helpers.find_msg_in_log_queue('GET /'), { + log_lvl = 'info', + msg = 'GET /' + }, "Route should logging requests in custom logger if it's presents") + helpers.clear_log_queue() + + http_client.get(helpers.base_uri .. '/error') + --[[ + t.assert_str_contains(helpers.find_msg_in_log_queue('Some error...', false), + "Route should logging error in custom logger if it's presents") + ]] + helpers.clear_log_queue() +end + +-- Setting log options for route. +pgroup.test_log_options = function() + local httpd = http_server.new(helpers.base_host, helpers.base_port, { + log_requests = true, + log_errors = false + }) + local dummy_logger = function() end + + local ok, err = pcall(httpd.route, httpd, { + path = '/', + log_requests = 3 + }) + t.assert_equals(ok, false, "Route logger can't be a log_level digit") + t.assert_str_contains(err, "'log_requests' option should be a function", + 'route() should return error message in case of incorrect logger option') + + ok, err = pcall(httpd.route, httpd, { + path = '/', + log_requests = { + info = dummy_logger + } + }) + t.assert_equals(ok, false, "Route logger can't be a table") + t.assert_str_contains(err, "'log_requests' option should be a function", + 'route() should return error message in case of incorrect logger option') + + local ok, err = pcall(httpd.route, httpd, { + path = '/', + log_errors = 3 + }) + t.assert_equals(ok, false, "Route error logger can't be a log_level digit") + t.assert_str_contains(err, "'log_errors' option should be a function", + "route() should return error message in case of incorrect logger option") + + ok, err = pcall(httpd.route, httpd, { + path = '/', + log_errors = { + error = dummy_logger + } + }) + t.assert_equals(ok, false, "Route error logger can't be a table") + t.assert_str_contains(err, "'log_errors' option should be a function", + 'route() should return error message in case of incorrect log_errors option') +end + +-- Log output with custom loggers on route. +pgroup.test_route_custom_logger = function(g) + local httpd = g.httpd + httpd:route({ + path = '/', + log_requests = helpers.custom_logger.info, + log_errors = helpers.custom_logger.error + }, function(_) end) + http_client.get(helpers.base_uri) + t.assert_equals(helpers.find_msg_in_log_queue('GET /'), { + log_lvl = 'info', + msg = 'GET /' + }, "Route should logging requests in custom logger if it's presents") + helpers.clear_log_queue() + + httpd.routes = {} + httpd:route({ + path = '/', + log_requests = helpers.custom_logger.info, + log_errors = helpers.custom_logger.error + }, function(_) + error('User business logic exception...') + end) + http_client.get('127.0.0.1:12345') + --test:is_deeply(helpers.find_msg_in_log_queue('GET /'), { + -- log_lvl = 'info', + -- msg = 'GET /' + --}, "Route should logging request and error in case of route exception") + --test:ok(helpers.find_msg_in_log_queue('User business logic exception...', false), + -- "Route should logging error custom logger if it's presents in case of route exception") + helpers.clear_log_queue() +end + +-- Log route requests with turned off 'log_requests' option. +pgroup.test_log_requests_off = function(g) + local httpd = g.httpd + httpd:route({ + path = '/', + log_requests = helpers.custom_logger.info + }, function(_) end) + http_client.get(helpers.base_uri) + --test:is_deeply(helpers.find_msg_in_log_queue('GET /'), { + -- log_lvl = 'info', + -- msg = 'GET /' + --}, "Route can override logging requests if the http server have turned off 'log_requests' option") + helpers.clear_log_queue() +end + +-- Log route requests with turned off 'log_errors' option. +pgroup.test_log_errors_off = function(g) + local httpd = g.httpd + httpd:route({ + path = '/', + log_errors = helpers.custom_logger.error + }, function(_) + error('User business logic exception...') + end) + http_client.get(helpers.base_uri) + --t.assert_str_contains(helpers.find_msg_in_log_queue('User business logic exception...', false), + -- "Route can override logging requests if the http server have turned off 'log_errors' option") + helpers.clear_log_queue() +end diff --git a/test/integration/http_server_requests_test.lua b/test/integration/http_server_requests_test.lua new file mode 100644 index 0000000..83cdb6e --- /dev/null +++ b/test/integration/http_server_requests_test.lua @@ -0,0 +1,323 @@ +local t = require('luatest') +local http_client = require('http.client') +local json = require('json') + +local helpers = require('test.helpers') + +local pgroup = t.group('server_requests') + +pgroup.before_all = function(g) + g.httpd = helpers.cfgserv() + g.httpd:start() +end + +pgroup.after_all = function(g) + helpers.teardown(g.httpd) +end + +pgroup.test_test = function() + local r = http_client.get(helpers.base_uri .. '/test') + t.assert_equals(r.status, 200, '/test code') + + t.assert_equals(r.proto[1], 1, '/test http 1.1') + t.assert_equals(r.proto[2], 1, '/test http 1.1') + t.assert_equals(r.reason, 'Ok', '/test reason') + t.assert_equals(string.match(r.body, 'title: 123'), 'title: 123', '/test body') +end + +pgroup.test_404 = function() + local r = http_client.get(helpers.base_uri .. '/test404') + t.assert_equals(r.status, 404, '/test404 code') + -- broken in built-in tarantool/http + --t.assert_equals(r.reason, 'Not found', '/test404 reason') +end + +pgroup.test_absent = function() + local r = http_client.get(helpers.base_uri .. '/absent') + t.assert_equals(r.status, 500, '/absent code') + --t.assert_equals(r.reason, 'Internal server error', '/absent reason') + t.assert_equals(string.match(r.body, 'load module'), 'load module', '/absent body') +end + +pgroup.test_ctx_action = function() + local r = http_client.get(helpers.base_uri .. '/ctxaction') + t.assert_equals(r.status, 200, '/ctxaction code') + t.assert_equals(r.reason, 'Ok', '/ctxaction reason') + t.assert_equals(string.match(r.body, 'Hello, Tarantool'), 'Hello, Tarantool', + '/ctxaction body') + t.assert_equals(string.match(r.body, 'action: action'), 'action: action', + '/ctxaction body action') + t.assert_equals(string.match(r.body, 'controller: module[.]controller'), + 'controller: module.controller', '/ctxaction body controller') +end + +pgroup.test_ctx_action_invalid = function() + local r = http_client.get(helpers.base_uri .. '/ctxaction.invalid') + t.assert_equals(r.status, 404, '/ctxaction.invalid code') -- WTF? + --t.assert_equals(r.reason, 'Ok', '/ctxaction.invalid reason') + t.assert_equals(r.body, nil, '/ctxaction.invalid body') +end + +pgroup.test_static_file = function() + local r = http_client.get(helpers.base_uri .. '/hello.html') + t.assert_equals(r.status, 200, '/hello.html code') + t.assert_equals(r.reason, 'Ok', '/hello.html reason') + t.assert_equals(string.match(r.body, 'static html'), 'static html', + '/hello.html body') +end + +pgroup.test_absent_action = function() + local r = http_client.get(helpers.base_uri .. '/absentaction') + t.assert_equals(r.status, 500, '/absentaction 500') + --t.assert_equals(r.reason, 'Unknown', '/absentaction reason') + t.assert_equals(string.match(r.body, 'contain function'), 'contain function', + '/absentaction body') +end + +pgroup.test_helper = function() + local r = http_client.get(helpers.base_uri .. '/helper') + t.assert_equals(r.status, 200, 'helper 200') + t.assert_equals(r.reason, 'Ok', 'helper reason') + t.assert_equals(string.match(r.body, 'Hello, world'), 'Hello, world', 'helper body') +end + +pgroup.test_500 = function(g) + local httpd = g.httpd + local r = http_client.get(helpers.base_uri .. '/helper?abc') + t.assert_equals(r.status, 200, 'helper?abc 200') + t.assert_equals(r.reason, 'Ok', 'helper?abc reason') + t.assert_equals(string.match(r.body, 'Hello, world'), 'Hello, world', 'helper body') + + httpd:route({ + path = '/die', + file = 'helper.html.el' + }, function() + error(123) + end) + + local r = http_client.get(helpers.base_uri .. '/die') + t.assert_equals(r.status, 500, 'die 500') + --t.assert_equals(r.reason, 'Unknown', 'die reason') +end + +pgroup.test_server_request_10 = function(g) + local httpd = g.httpd + httpd:route({ + path = '/info' + }, function(cx) + return cx:render({ json = cx.peer }) + end) + + local r = json.decode(http_client.get(helpers.base_uri .. '/info').body) + t.assert_equals(r.host, helpers.base_host, 'peer.host') + t.assert_type(r.port, 'number', 'peer.port') +end + +pgroup.test_POST = function(g) + local httpd = g.httpd + local r = httpd:route({ + method = 'POST', + path = '/dit', + file = 'helper.html.el' + }, function(tx) + return tx:render({text = 'POST = ' .. tx:read()}) + end) + t.assert_type(r, 'table', ':route') +end + +-- GET/POST at one route. +pgroup.test_get_and_post_at_one_route = function(g) + local httpd = g.httpd + local r = httpd:route({ + method = 'POST', + path = '/dit', + file = 'helper.html.el' + }, function(tx) + return tx:render({text = 'POST = ' .. tx:read()}) + end) + t.assert_type(r, 'table', 'add POST method') + + r = httpd:route({ + method = 'GET', + path = '/dit', + file = 'helper.html.el' + }, function(tx) + return tx:render({text = 'GET = ' .. tx:read()}) + end) + t.assert_type(r, 'table', 'add GET method') + + r = http_client.request('POST', helpers.base_uri .. '/dit', 'test') + t.assert_equals(r.body, 'POST = test', 'POST reply') + r = http_client.request('GET', helpers.base_uri .. '/dit') + t.assert_equals(r.body, 'GET = ', 'GET reply') +end + +pgroup.test_DELETE = function(g) + local httpd = g.httpd + local r = httpd:route({ + method = 'DELETE', + path = '/dit', + file = 'helper.html.el' + }, function(tx) + return tx:render({text = 'DELETE = ' .. tx:read()}) + end) + t.assert_type(r, 'table', 'add DELETE method') +end + +pgroup.test_PATCH = function(g) + local httpd = g.httpd + local r = httpd:route({ + method = 'PATCH', + path = '/dit', + file = 'helper.html.el' + }, function(tx) + return tx:render({text = 'PATCH = ' .. tx:read()}) + end ) + t.assert_type(r, 'table', 'add PATCH method') +end + +pgroup.test_POST_basic = function() + local r = http_client.request('POST', helpers.base_uri .. '/dit', 'test') + t.assert_equals(r.body, 'POST = test', 'POST reply') +end + +pgroup.test_GET_basic = function() + local r = http_client.request('GET', helpers.base_uri .. '/dit') + t.assert_equals(r.body, 'GET = ', 'GET reply') +end + +pgroup.test_DELETE_basic = function() + local r = http_client.request('DELETE', helpers.base_uri .. '/dit', 'test1') + t.assert_equals(r.body, 'DELETE = test1', 'DELETE reply') +end + +pgroup.test_PATCH_basic = function() + local r = http_client.request('PATCH', helpers.base_uri .. '/dit', 'test2') + t.assert_equals(r.body, 'PATCH = test2', 'PATCH reply') +end + +pgroup.test_chunked_encoding = function(g) + local httpd = g.httpd + httpd:route({ + path = '/chunked' + }, function(self) + return self:iterate(ipairs({'chunked', 'encoding', 't\r\nest'})) + end) + + -- HTTP client currently doesn't support chunked encoding. + local r = http_client.get(helpers.base_uri .. '/chunked') + t.assert_equals(r.status, 200, 'chunked 200') + t.assert_equals(r.headers['transfer-encoding'], 'chunked', 'chunked headers') + t.assert_equals(r.body, 'chunkedencodingt\r\nest', 'chunked body') +end + +-- Get cookie. +pgroup.test_get_cookie = function(g) + local httpd = g.httpd + httpd:route({ + path = '/receive_cookie' + }, function(req) + local foo = req:cookie('foo') + local baz = req:cookie('baz') + return req:render({ + text = ('foo=%s; baz=%s'):format(foo, baz) + }) + end) + local r = http_client.get(helpers.base_uri .. '/receive_cookie', { + headers = { + cookie = 'foo=bar; baz=feez', + } + }) + t.assert_equals(r.status, 200, 'status') + t.assert_equals(r.body, 'foo=bar; baz=feez', 'body') +end + +-- Cookie. +pgroup.test_set_cookie = function(g) + local httpd = g.httpd + httpd:route({ + path = '/cookie' + }, function(req) + local resp = req:render({text = ''}) + resp:setcookie({ + name = 'test', + value = 'tost', + expires = '+1y', + path = '/abc' + }) + resp:setcookie({ + name = 'xxx', + value = 'yyy' + }) + return resp + end) + local r = http_client.get(helpers.base_uri .. '/cookie') + t.assert_equals(r.status, 200, 'status') + t.assert(r.headers['set-cookie'] ~= nil, 'header') +end + +-- Request object methods. +pgroup.test_request_object_methods = function(g) + local httpd = g.httpd + httpd:route({ + path = '/check_req_methods_for_json', + method = 'POST' + }, function(req) + return { + headers = {}, + body = json.encode({ + request_line = req:request_line(), + read_cached = req:read_cached(), + json = req:json(), + post_param_for_kind = req:post_param('kind'), + }), + status = 200, + } + end) + + httpd:route({ + path = '/check_req_methods', + method = 'POST' + }, function(req) + return { + headers = {}, + body = json.encode({ + request_line = req:request_line(), + read_cached = req:read_cached(), + }), + status = 200, + } + end) + + local r = http_client.post( + helpers.base_uri .. '/check_req_methods_for_json', + '{"kind": "json"}', { + headers = { + ['Content-type'] = 'application/json', + ['X-test-header'] = 'test-value' + } + }) + t.assert_equals(r.status, 200, 'status') + + local parsed_body = json.decode(r.body) + t.assert_equals(parsed_body.request_line, + 'POST /check_req_methods_for_json? HTTP/1.1', 'req.request_line') + t.assert_equals(parsed_body.read_cached, + '{"kind": "json"}', 'json req:read_cached()') + t.assert_equals(parsed_body.json, { + kind = 'json' + }, 'req:json()') + t.assert_equals(parsed_body.post_param_for_kind, + 'json', 'req:post_param()') +end + +pgroup.test_server_request_17 = function() + local r = http_client.post( + helpers.base_uri .. '/check_req_methods', + 'hello mister' + ) + t.assert_equals(r.status, 200, 'status') + local parsed_body = json.decode(r.body) + t.assert_equals(parsed_body.read_cached, 'hello mister', + 'non-json req:read_cached()') +end diff --git a/test/integration/http_server_url_for_test.lua b/test/integration/http_server_url_for_test.lua new file mode 100644 index 0000000..7a26ff8 --- /dev/null +++ b/test/integration/http_server_url_for_test.lua @@ -0,0 +1,26 @@ +local t = require('luatest') + +local helpers = require('test.helpers') + +local pgroup = t.group('server_url_for') + +pgroup.before_all = function(g) + g.httpd = helpers.cfgserv() + g.httpd:start() +end + +pgroup.after_all = function(g) + helpers.teardown(g.httpd) +end + +pgroup.test_server_url_for = function(g) + local httpd = g.httpd + t.assert_equals(httpd:url_for('abcdef'), '/abcdef', '/abcdef') + t.assert_equals(httpd:url_for('test'), '/abc//', '/abc//') + t.assert_equals(httpd:url_for('test', { cde = 'cde_v', def = 'def_v' }), + '/abc/cde_v/def_v', '/abc/cde_v/def_v') + t.assert_equals(httpd:url_for('star', { def = '/def_v' }), + '/abb/def_v/cde', '/abb/def_v/cde') + t.assert_equals(httpd:url_for('star', { def = '/def_v' }, { a = 'b', c = 'd' }), + '/abb/def_v/cde?a=b&c=d', '/abb/def_v/cde?a=b&c=d') +end diff --git a/test/integration/http_server_url_match_test.lua b/test/integration/http_server_url_match_test.lua new file mode 100644 index 0000000..809f668 --- /dev/null +++ b/test/integration/http_server_url_match_test.lua @@ -0,0 +1,49 @@ +local t = require('luatest') +local http_client = require('http.client') + +local helpers = require('test.helpers') + +local g = t.group('server_url_match') + +g.test_server_url_match = function() + local httpd = helpers.cfgserv() + httpd:start() + + t.assert_type(httpd, 'table', 'httpd object') + t.assert_not_equals(httpd, nil) + t.assert_is(httpd:match('GET', '/'), nil) + t.assert_equals(httpd:match('GET', '/abc').endpoint.path, '/abc', '/abc') + t.assert_equals(#httpd:match('GET', '/abc').stash, 0, '/abc') + t.assert_equals(httpd:match('GET', '/abc/123').endpoint.path, '/abc/:cde', '/abc/123') + t.assert_equals(httpd:match('GET', '/abc/123').stash.cde, '123', '/abc/123') + t.assert_equals(httpd:match('GET', '/abc/123/122').endpoint.path, '/abc/:cde/:def', + '/abc/123/122') + t.assert_equals(httpd:match('GET', '/abc/123/122').stash.def, '122', + '/abc/123/122') + t.assert_equals(httpd:match('GET', '/abc/123/122').stash.cde, '123', + '/abc/123/122') + t.assert_equals(httpd:match('GET', '/abc_123-122').endpoint.path, '/abc_:cde_def', + '/abc_123-122') + t.assert_equals(httpd:match('GET', '/abc_123-122').stash.cde_def, '123-122', + '/abc_123-122') + t.assert_equals(httpd:match('GET', '/abc-123-def').endpoint.path, '/abc-:cde-def', + '/abc-123-def') + t.assert_equals(httpd:match('GET', '/abc-123-def').stash.cde, '123', + '/abc-123-def') + t.assert_equals(httpd:match('GET', '/aba-123-dea/1/2/3').endpoint.path, + '/aba*def', '/aba-123-dea/1/2/3') + t.assert_equals(httpd:match('GET', '/aba-123-dea/1/2/3').stash.def, + '-123-dea/1/2/3', '/aba-123-dea/1/2/3') + t.assert_equals(httpd:match('GET', '/abb-123-dea/1/2/3/cde').endpoint.path, + '/abb*def/cde', '/abb-123-dea/1/2/3/cde') + t.assert_equals(httpd:match('GET', '/abb-123-dea/1/2/3/cde').stash.def, + '-123-dea/1/2/3', '/abb-123-dea/1/2/3/cde') + t.assert_equals(httpd:match('GET', '/banners/1wulc.z8kiy.6p5e3').stash.token, + '1wulc.z8kiy.6p5e3', 'stash with dots') + + httpd:stop() + helpers.retrying({}, function() + local r = http_client.request('GET', helpers.base_uri) + return r == nil + end) +end diff --git a/test/unit/http_params_test.lua b/test/unit/http_params_test.lua new file mode 100644 index 0000000..9fcbbf4 --- /dev/null +++ b/test/unit/http_params_test.lua @@ -0,0 +1,15 @@ +local t = require('luatest') +local http_lib = require('http.lib') + +local pgroup = t.group('http_params', { + { params = nil, t = {}, comment = 'nil string' }, + { params = '', t = {}, comment = 'empty string' }, + { params = 'a', t = { a = '' }, comment = 'separate literal' }, + { params = 'a=b', t = { a = 'b' }, comment = 'one variable' }, + { params = 'a=b&b=cde', t = {a = 'b', b = 'cde'}, comment = 'some'}, + { params = 'a=b&b=cde&a=1', t = {a = { 'b', '1' }, b = 'cde'}, comment = 'array'} +}) + +pgroup.test_params = function(g) + t.assert_equals(http_lib.params(g.params.params), g.params.t, g.params.comment) +end diff --git a/test/unit/http_parse_request_test.lua b/test/unit/http_parse_request_test.lua new file mode 100644 index 0000000..f03913a --- /dev/null +++ b/test/unit/http_parse_request_test.lua @@ -0,0 +1,37 @@ +local t = require('luatest') +local http_lib = require('http.lib') + +local pgroup = t.group('parse_requests') + +pgroup.test_parse_request = function() + t.assert_equals(http_lib._parse_request('abc'), { + error = 'Broken request line', + headers = {} + }, 'broken request') + + t.assert_equals( + http_lib._parse_request('GET / HTTP/1.1\nHost: s.com\r\n\r\n').path, + '/', + 'path' + ) + t.assert_equals( + http_lib._parse_request('GET / HTTP/1.1\nHost: s.com\r\n\r\n').proto, + {1, 1}, + 'proto' + ) + t.assert_equals( + http_lib._parse_request('GET / HTTP/1.1\nHost: s.com\r\n\r\n').headers, + {host = 's.com'}, + 'host' + ) + t.assert_equals( + http_lib._parse_request('GET / HTTP/1.1\nHost: s.com\r\n\r\n').method, + 'GET', + 'method' + ) + t.assert_equals( + http_lib._parse_request('GET / HTTP/1.1\nHost: s.com\r\n\r\n').query, + '', + 'query' + ) +end diff --git a/test/unit/http_split_uri_test.lua b/test/unit/http_split_uri_test.lua new file mode 100644 index 0000000..4298a29 --- /dev/null +++ b/test/unit/http_split_uri_test.lua @@ -0,0 +1,85 @@ +local t = require('luatest') +local urilib = require('uri') + +local pgroup = t.group('split_uri') + +local function check(uri, rhs) + local lhs = urilib.parse(uri) + local extra = { lhs = lhs, rhs = rhs } + if lhs.query == '' then + lhs.query = nil + end + + t.assert_equals(lhs.scheme, rhs.scheme, uri..' scheme', extra) + t.assert_equals(lhs.host, rhs.host, uri..' host', extra) + t.assert_equals(lhs.service, rhs.service, uri..' service', extra) + t.assert_equals(lhs.path, rhs.path, uri..' path', extra) + t.assert_equals(lhs.query, rhs.query, uri..' query', extra) +end + +pgroup.test_split_uri = function() + check('http://abc', { + scheme = 'http', + host = 'abc' + }) + check('http://abc/', { + scheme = 'http', + host = 'abc', + path ='/' + }) + check('http://abc?', { + scheme = 'http', + host = 'abc' + }) + check('http://abc/?', { + scheme = 'http', + host = 'abc', + path ='/' + }) + check('http://abc/?', { + scheme = 'http', + host = 'abc', + path ='/' + }) + check('http://abc:123', { + scheme = 'http', + host = 'abc', + service = '123' + }) + check('http://abc:123?', { + scheme = 'http', + host = 'abc', + service = '123' + }) + check('http://abc:123?query', { + scheme = 'http', + host = 'abc', + service = '123', + query = 'query' + }) + check('http://domain.subdomain.com:service?query', { + scheme = 'http', + host = 'domain.subdomain.com', + service = 'service', + query = 'query' + }) + check('google.com', { + host = 'google.com' + }) + check('google.com?query', { + host = 'google.com', + query = 'query' + }) + check('google.com/abc?query', { + host = 'google.com', + path = '/abc', + query = 'query' + }) + check('https://google.com:443/abc?query', { + scheme = 'https', + host = 'google.com', + service = '443', + path = '/abc', + query = 'query' + }) +end diff --git a/test/unit/http_template_test.lua b/test/unit/http_template_test.lua new file mode 100644 index 0000000..51e4d11 --- /dev/null +++ b/test/unit/http_template_test.lua @@ -0,0 +1,48 @@ +local t = require('luatest') +local http_lib = require('http.lib') + +local pgroup = t.group('template') + +pgroup.test_template_1 = function() + t.assert_equals(http_lib.template("<% for i = 1, cnt do %> <%= abc %> <% end %>", + {abc = '1 <3>&" ', cnt = 3}), + ' 1 <3>&" 1 <3>&" 1 <3>&" ', + 'tmpl1') +end + +pgroup.test_template_2 = function() + t.assert_equals(http_lib.template('<% for i = 1, cnt do %> <%= ab %> <% end %>', + {abc = '1 <3>&" ', cnt = 3}), + ' nil nil nil ', 'tmpl2') +end + +pgroup.test_broken_template = function() + local r, msg = pcall(http_lib.template, '<% ab() %>', {ab = '1'}) + t.assert(r == false and msg:match("call local 'ab'") ~= nil, 'bad template') +end + +pgroup.test_rendered_template_truncated_gh_18 = function() + local template = [[ + + + + % for i,v in pairs(t) do + + + + + % end +
<%= i %><%= v %>
+ + +]] + + local tt = {} + for i=1, 100 do + tt[i] = string.rep('#', i) + end + + local rendered, _ = http_lib.template(template, { t = tt }) + t.assert(#rendered > 10000, 'rendered size') + t.assert_equals(rendered:sub(#rendered - 7, #rendered - 1), '', 'rendered eof') +end