]> git.ipfire.org Git - thirdparty/knot-resolver.git/commitdiff
daf: config tests for HTTP API
authorPetr Špaček <petr.spacek@nic.cz>
Tue, 31 Mar 2020 15:29:53 +0000 (17:29 +0200)
committerTomas Krizek <tomas.krizek@nic.cz>
Thu, 2 Apr 2020 11:57:32 +0000 (13:57 +0200)
modules/daf/daf_http.test.lua [new file with mode: 0644]
modules/meson.build

diff --git a/modules/daf/daf_http.test.lua b/modules/daf/daf_http.test.lua
new file mode 100644 (file)
index 0000000..bdd486e
--- /dev/null
@@ -0,0 +1,212 @@
+-- SPDX-License-Identifier: GPL-3.0-or-later
+-- check prerequisites
+local has_http = pcall(require, 'kres_modules.http') and pcall(require, 'http.request')
+if not has_http then
+       pass('skipping daf module test because http its not installed')
+       done()
+else
+       local request = require('http.request')
+
+       modules.load('http')
+       modules.load('daf')
+
+       local bound
+       for _ = 1,1000 do
+               bound, _err = pcall(net.listen, '127.0.0.1', math.random(40000, 49999), { kind = 'webmgmt'})
+               if bound then
+                       break
+               end
+       end
+       assert(bound, 'unable to bind a port for HTTP module (1000 attempts)')
+
+       -- globals for this module
+       local _, host, port, baseuri
+       local function start_server()
+               local server_fd = next(http.servers)
+               assert(server_fd)
+               local server = http.servers[server_fd].server
+               ok(server ~= nil, 'creates server instance')
+               _, host, port = server:localname()
+               ok(host and port, 'binds to an interface')
+               baseuri = string.format('http://%s:%d/daf', host, port)
+       end
+
+       -- helper for returning useful values to test on
+--     local function http_get(uri)
+--             local headers, stream = assert(request.new_from_uri(uri):go())
+--             local body = assert(stream:get_body_as_string())
+--             return tonumber(headers:get(':status')), body, headers:get('content-type')
+--     end
+
+       local function http_req(uri, method, reqbody)
+               local req = assert(request.new_from_uri(baseuri .. uri))
+               req.headers:upsert(':method', method)
+               req:set_body(reqbody)
+               local headers, stream = assert(req:go())
+               local ansbody = assert(stream:get_body_as_string())
+               return tonumber(headers:get(':status')), ansbody, headers:get('content-type')
+       end
+
+       local function http_get(uri)
+               return http_req(uri, 'GET')
+       end
+
+       -- compare two tables, expected value is specified as JSON
+       -- comparison relies on table_print which sorts table keys
+       local function compare_tables(expectedjson, gotjson, desc)
+               same(
+                       table_print(fromjson(expectedjson)),
+                       table_print(fromjson(gotjson)),
+                       desc)
+       end
+
+       -- test whether http interface responds and binds
+       local function test_daf_api()
+               local code, body, mime
+               -- rule listing /daf
+               code, body, mime = http_get('/')
+               same(code, 200, 'rule listing return 200 OK')
+               same(body, '{}', 'daf rule list is empty after start')
+               same(mime, 'application/json', 'daf rule list has application/json content type')
+               -- get non-existing rule
+               code, body = http_req('/0', 'GET')
+               same(code, 404, 'non-existing rule retrieval returns 404')
+               same(body, '"No such rule"', 'explanatory message is present')
+
+               -- delete non-existing rule
+               code, body = http_req('/0', 'DELETE')
+               same(code, 404, 'non-existing rule deletion returns 404')
+               same(body, '"No such rule"', 'explanatory message is present')
+
+               -- bad PATCH
+               code = http_req('/0', 'PATCH')
+               same(code, 400, 'PATCH detects missing parameters')
+
+               -- bad POST
+               code = http_req('/', 'POST')
+               same(code, 500, 'POST without parameters is detected')
+
+               -- POST first new rule
+               code, body, mime = http_req('/', 'POST', 'src = 192.0.2.0 pass')
+               same(code, 200, 'first POST succeeds')
+               compare_tables(body,
+                       '{"count":0,"active":true,"id":0,"info":"src = 192.0.2.0 pass"}',
+                       'POST returns new rule in JSON')
+               same(mime, 'application/json', 'rule has application/json content type')
+
+               -- GET first rule
+               code, body, mime = http_req('/0', 'GET')
+               same(code, 200, 'GET for first rule succeeds')
+               compare_tables(body,
+                               '{"count":0,"active":true,"id":0,"info":"src = 192.0.2.0 pass"}',
+                               'POST returns new rule in JSON')
+               same(mime, 'application/json', 'rule has application/json content type')
+
+               -- POST second new rule
+               code, body, mime = http_req('/', 'POST', 'src = 192.0.2.1 pass')
+               same(code, 200, 'second POST succeeds')
+               compare_tables(body,
+                               '{"count":0,"active":true,"id":1,"info":"src = 192.0.2.1 pass"}',
+                               'POST returns new rule in JSON')
+               same(mime, 'application/json', 'rule has application/json content type')
+
+               -- GET second rule
+               code, body, mime = http_req('/1', 'GET')
+               same(code, 200, 'GET for second rule succeeds')
+               compare_tables(body,
+                               '{"count":0,"active":true,"id":1,"info":"src = 192.0.2.1 pass"}',
+                               'POST returns new rule in JSON')
+               same(mime, 'application/json', 'rule has application/json content type')
+
+               -- PATCH first rule
+               code, body, mime = http_req('/0/active/false', 'PATCH')
+               same(code, 200, 'PATCH for first rule succeeds')
+               same(body, 'true', 'PATCH returns success in body')
+               same(mime, 'application/json', 'PATCH return value has application/json content type')
+
+               -- GET modified first rule
+               code, body, mime = http_req('/0', 'GET')
+               same(code, 200, 'GET for first rule succeeds')
+               compare_tables(body,
+                               '{"count":0,"active":false,"id":0,"info":"src = 192.0.2.0 pass"}',
+                               'GET returns modified rule in JSON')
+               same(mime, 'application/json', 'rule has application/json content type')
+
+               -- GET both rules
+               code, body, mime = http_req('/', 'GET')
+               same(code, 200, 'GET for both rule succeeds')
+               compare_tables(body, [[
+                               [
+                                       {"count":0,"active":false,"info":"src = 192.0.2.0 pass","id":0},
+                                       {"count":0,"active":true,"info":"src = 192.0.2.1 pass","id":1}]
+                               ]],
+                               'GET returns both rules in JSON including modifications')
+               same(mime, 'application/json', 'rule list has application/json content type')
+
+               -- PATCH first rule back to original state
+               code, body, mime = http_req('/0/active/true', 'PATCH')
+               same(code, 200, 'PATCH for first rule succeeds')
+               same(body, 'true', 'PATCH returns success in body')
+               same(mime, 'application/json', 'PATCH return value has application/json content type')
+
+               -- GET modified (reversed) first rule
+               code, body, mime = http_req('/0', 'GET')
+               same(code, 200, 'GET for first rule succeeds')
+               compare_tables(body,
+                               '{"count":0,"active":true,"id":0,"info":"src = 192.0.2.0 pass"}',
+                               'GET returns modified rule in JSON')
+               same(mime, 'application/json', 'rule has application/json content type')
+
+               -- DELETE first rule
+               code, body, mime = http_req('/0', 'DELETE')
+               same(code, 200, 'DELETE for first rule succeeds')
+               same(body, 'true', 'DELETE returns success in body')
+               same(mime, 'application/json', 'DELETE return value has application/json content type')
+
+               -- GET deleted (first) rule
+               code, body = http_req('/0', 'GET')
+               same(code, 404, 'GET for deleted fails with 404')
+               same(body, '"No such rule"', 'failed GET contains explanatory message')
+
+               -- GET second rule
+               code, body, mime = http_req('/1', 'GET')
+               same(code, 200, 'GET for second rule still succeeds')
+               compare_tables(body,
+                               '{"count":0,"active":true,"id":1,"info":"src = 192.0.2.1 pass"}',
+                               'POST returns new rule in JSON')
+               same(mime, 'application/json', 'rule has application/json content type')
+
+               -- GET list of all rules
+               code, body, mime = http_req('/', 'GET')
+               same(code, 200, 'GET returns list with the remaining rule')
+               compare_tables(body,
+                               '[{"count":0,"active":true,"id":1,"info":"src = 192.0.2.1 pass"}]',
+                               'rule list contains only the remaining rule in JSON')
+               same(mime, 'application/json', 'rule has application/json content type')
+
+               -- try to DELETE first rule again
+               code, body = http_req('/0', 'DELETE')
+               same(code, 404, 'DELETE for already deleted rule fails with 404')
+               same(body, '"No such rule"', 'DELETE explains failure')
+
+               -- DELETE second rule
+               code, body, mime = http_req('/1', 'DELETE')
+               same(code, 200, 'DELETE for second rule succeeds')
+               same(body, 'true', 'DELETE returns success in body')
+               same(mime, 'application/json', 'DELETE return value has application/json content type')
+
+               -- GET (supposedly empty) list of all rules
+               code, body, mime = http_req('/', 'GET')
+               same(code, 200, 'GET returns list with the remaining rule')
+               compare_tables(body, '[]', 'rule list is now empty JSON list')
+               same(mime, 'application/json', 'rule has application/json content type')
+       end
+
+       -- plan tests
+       local tests = {
+               start_server,
+               test_daf_api,
+       }
+
+       return tests
+end
index 84061d0b7504862cb6bfc465680c8b437a8e5fa6..5cc5404e20a8521290aba9e731873c5712d3e567 100644 (file)
@@ -21,6 +21,7 @@ lua_mod_src = [  # add lua modules without separate meson.build
 ]
 
 config_tests += [
+  ['daf_http', files('daf/daf_http.test.lua')],
   ['predict', files('predict/predict.test.lua')],
   ['hints', files('hints/tests/hints.test.lua'), ['skip_asan']],
   ['nsid', files('nsid/nsid.test.lua')],