summaryrefslogtreecommitdiffstats
path: root/modules/daf/daf_http.test.lua
blob: 20d5f9058b7154c04ce1263b4926f43d0004d6db (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
-- 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
	-- skipping daf module test because http its not installed
	os.exit(77)
else
	local path = worker.cwd..'/control/'..worker.pid
	same(true, net.listen(path, nil, {kind = 'control'}),
		'new control sockets were created so map() can work')

	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(16))
--		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(16))
		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