From 9657081d330a75de390dc4410a65aaebb9c20331 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Vladim=C3=ADr=20=C4=8Cun=C3=A1t?= <vladimir.cunat@nic.cz>
Date: Tue, 30 Jun 2020 16:05:32 +0200
Subject: [PATCH] tests check_answer(): support checking RDATA

Also allow using empty set as an alternative to NODATA pseudo-RCODE,
and migrate RPZ tests to this merged function.
---
 modules/policy/policy.rpz.test.lua | 34 ++++--------------------------
 tests/config/test_utils.lua        | 27 +++++++++++++++++++++++-
 2 files changed, 30 insertions(+), 31 deletions(-)

diff --git a/modules/policy/policy.rpz.test.lua b/modules/policy/policy.rpz.test.lua
index 846b642b0..761282fb6 100644
--- a/modules/policy/policy.rpz.test.lua
+++ b/modules/policy/policy.rpz.test.lua
@@ -14,41 +14,15 @@ local function prepare_cache()
 	c:commit()
 end
 
-local function rrset_to_texts(rr)
-	local rr_text = {}
-	for w in rr:txt_dump():gmatch("%S+") do table.insert(rr_text, w) end
-	return rr_text
-end
-
-local function check_answer(desc, qname, qtype, expected_rcode, expected_rdata)
-	qtype_str = kres.tostring.type[qtype]
-	callback = function(pkt)
-		same(pkt:rcode(), expected_rcode,
-			desc .. ': expecting answer for query ' .. qname .. ' ' .. qtype_str
-			.. ' with rcode ' .. kres.tostring.rcode[expected_rcode])
-
-		if expected_rdata then
-			rr_text = rrset_to_texts(pkt:rrsets(kres.section.ANSWER)[1])
-			ok(rr_text[4] == expected_rdata,
-				desc ..': checking rdata of answer for ' .. qname .. ' ' .. qtype_str)
-		else
-			-- check empty section
-			ok(pkt:rrsets(kres.section.ANSWER)[1] == nil,
-				desc ..': checking empty answer section for ' .. qname .. ' ' .. qtype_str)
-		end
-
-	end
-
-	resolve(qname, qtype, kres.class.IN, {}, callback)
-end
+local check_answer = require('test_utils').check_answer
 
 local function test_rpz()
 	check_answer('"CNAME ." return NXDOMAIN',
 		'nxdomain.', kres.type.A, kres.rcode.NXDOMAIN)
 	check_answer('"CNAME *." return NODATA',
-		'nodata.', kres.type.A, kres.rcode.NOERROR)
+		'nodata.', kres.type.A, kres.rcode.NOERROR, {})
 	check_answer('"CNAME *. on wildcard" return NODATA',
-		'nodata.nxdomain.', kres.type.A, kres.rcode.NOERROR)
+		'nodata.nxdomain.', kres.type.A, kres.rcode.NOERROR, {})
 	check_answer('"CNAME rpz-drop." be dropped',
 		'rpzdrop.', kres.type.A, kres.rcode.SERVFAIL)
 	check_answer('"CNAME rpz-passthru" return A rrset',
@@ -60,7 +34,7 @@ local function test_rpz()
 	check_answer('"A 192.168.7.7" with suffixed zone name in owner return local A rrset',
 		'testdomain.rra.', kres.type.A, kres.rcode.NOERROR, '192.168.7.7')
 	check_answer('non existing AAAA on rra domain return NODATA',
-		'rra.', kres.type.AAAA, kres.rcode.NOERROR)
+		'rra.', kres.type.AAAA, kres.rcode.NOERROR, {})
 	check_answer('"A 192.168.8.8" and domain with uppercase and lowercase letters',
 		'case.sensitive.', kres.type.A, kres.rcode.NOERROR, '192.168.8.8')
 	check_answer('"A 192.168.8.8" and domain with uppercase and lowercase letters',
diff --git a/tests/config/test_utils.lua b/tests/config/test_utils.lua
index 8d159c9ca..afc713b28 100644
--- a/tests/config/test_utils.lua
+++ b/tests/config/test_utils.lua
@@ -39,11 +39,26 @@ function M.not_contains(table, value, message)
 	return contains(fail, pass, table, value, message)
 end
 
+local function rrset_to_texts(rr)
+	local rr_text = {}
+	for w in rr:txt_dump():gmatch("%S+") do table.insert(rr_text, w) end
+	return rr_text
+end
 M.NODATA = -1
 -- Resolve a name and check the answer.  Do *not* return until finished.
-function M.check_answer(desc, qname, qtype, expected_rcode)
+-- expected_rdata is one string or a table of strings in presentation format
+-- (not tested beyond IP addresses; TODO: handle ordering somehow?)
+function M.check_answer(desc, qname, qtype, expected_rcode, expected_rdata)
+	if expected_rdata ~= nil and type(expected_rdata) ~= 'table' then
+		expected_rdata = { expected_rdata }
+	end
+
 	local qtype_str = kres.tostring.type[qtype]
 	local wire_rcode = expected_rcode
+	if expected_rcode == kres.rcode.NOERROR and type(expected_rdata) == 'table'
+			and #expected_rdata == 0 then
+		expected_rcode = M.NODATA
+	end
 	if expected_rcode == M.NODATA then wire_rcode = kres.rcode.NOERROR end
 
 	local done = false
@@ -54,6 +69,16 @@ function M.check_answer(desc, qname, qtype, expected_rcode)
 
 		ok((pkt:ancount() > 0) == (expected_rcode == kres.rcode.NOERROR),
 		   desc ..': checking number of answers for ' .. qname .. ' ' .. qtype_str)
+
+		if expected_rdata then
+			local ans_rrs = pkt:rrsets(kres.section.ANSWER)
+			ok(#expected_rdata == #ans_rrs,
+				desc .. ': checking number of answer records for ' .. qname .. ' ' .. qtype_str)
+			for i = 1, #ans_rrs do
+				ok(rrset_to_texts(ans_rrs[i])[4] == expected_rdata[i],
+					desc .. ': checking rdata of answer for ' .. qname .. ' ' .. qtype_str)
+			end
+		end
 		done = true
 	end
 	resolve(qname, qtype, kres.class.IN, {}, callback)
-- 
GitLab