summaryrefslogtreecommitdiffstats
path: root/test/functional/lua
diff options
context:
space:
mode:
Diffstat (limited to 'test/functional/lua')
-rw-r--r--test/functional/lua/composites.lua140
-rw-r--r--test/functional/lua/conditions.lua22
-rw-r--r--test/functional/lua/deps.lua30
-rw-r--r--test/functional/lua/dns.lua53
-rw-r--r--test/functional/lua/external_relay.lua10
-rw-r--r--test/functional/lua/flags.lua35
-rw-r--r--test/functional/lua/get_from.lua10
-rw-r--r--test/functional/lua/hashes.lua74
-rw-r--r--test/functional/lua/http.lua169
-rw-r--r--test/functional/lua/magic.lua14
-rw-r--r--test/functional/lua/mapreload.lua20
-rw-r--r--test/functional/lua/maps_kv.lua94
-rw-r--r--test/functional/lua/miltertest/combined.lua35
-rw-r--r--test/functional/lua/miltertest/data.lua26
-rw-r--r--test/functional/lua/miltertest/data_dkim.lua23
-rw-r--r--test/functional/lua/miltertest/dkim_many.lua11
-rw-r--r--test/functional/lua/miltertest/dkim_one.lua11
-rw-r--r--test/functional/lua/miltertest/lib.lua119
-rw-r--r--test/functional/lua/miltertest/mt1.lua11
-rw-r--r--test/functional/lua/miltertest/mt2.lua11
-rw-r--r--test/functional/lua/miltertest/mt3.lua12
-rw-r--r--test/functional/lua/miltertest/mt4.lua11
-rw-r--r--test/functional/lua/neural.lua64
-rw-r--r--test/functional/lua/option_order.lua15
-rw-r--r--test/functional/lua/params.lua80
-rw-r--r--test/functional/lua/prepostfilters.lua65
-rw-r--r--test/functional/lua/preresult.lua3
-rw-r--r--test/functional/lua/recipients.lua14
-rw-r--r--test/functional/lua/redis.lua113
-rw-r--r--test/functional/lua/regex_test.lua12
-rw-r--r--test/functional/lua/remove_result.lua26
-rw-r--r--test/functional/lua/rspamadm/test_batch.lua4
-rw-r--r--test/functional/lua/rspamadm/test_dns_client.lua30
-rw-r--r--test/functional/lua/rspamadm/test_message_callback.lua5
-rw-r--r--test/functional/lua/rspamadm/test_redis_client.lua40
-rw-r--r--test/functional/lua/rspamadm/test_tcp_client.lua64
-rw-r--r--test/functional/lua/rspamadm/test_verbose.lua3
-rw-r--r--test/functional/lua/selector_test.lua23
-rw-r--r--test/functional/lua/settings.lua68
-rw-r--r--test/functional/lua/simple.lua7
-rw-r--r--test/functional/lua/tcp.lua279
-rw-r--r--test/functional/lua/test_coverage.lua46
-rw-r--r--test/functional/lua/test_fname.lua12
-rw-r--r--test/functional/lua/tlds.lua58
-rw-r--r--test/functional/lua/udp.lua81
45 files changed, 2053 insertions, 0 deletions
diff --git a/test/functional/lua/composites.lua b/test/functional/lua/composites.lua
new file mode 100644
index 0000000..648eda0
--- /dev/null
+++ b/test/functional/lua/composites.lua
@@ -0,0 +1,140 @@
+rspamd_config:register_symbol({
+ name = 'EXPRESSIONS_B',
+ score = 1.0,
+ callback = function()
+ return true, 'Fires always'
+ end
+})
+
+rspamd_config:register_symbol({
+ name = 'POLICY_REMOVE_WEIGHT_A',
+ score = 1.0,
+ callback = function()
+ return true, 'Fires always'
+ end
+})
+rspamd_config:register_symbol({
+ name = 'POLICY_REMOVE_WEIGHT_B',
+ score = 1.0,
+ callback = function()
+ return true, 'Fires always'
+ end
+})
+rspamd_config:register_symbol({
+ name = 'POLICY_FORCE_REMOVE_A',
+ score = 1.0,
+ callback = function()
+ return true, 'Fires always'
+ end
+})
+rspamd_config:register_symbol({
+ name = 'POLICY_FORCE_REMOVE_B',
+ score = 1.0,
+ callback = function()
+ return true, 'Fires always'
+ end
+})
+rspamd_config:register_symbol({
+ name = 'POLICY_LEAVE_A',
+ score = 1.0,
+ callback = function()
+ return true, 'Fires always'
+ end
+})
+rspamd_config:register_symbol({
+ name = 'POLICY_LEAVE_B',
+ score = 1.0,
+ callback = function()
+ return true, 'Fires always'
+ end
+})
+
+rspamd_config:register_symbol({
+ name = 'DEFAULT_POLICY_REMOVE_WEIGHT_A',
+ score = 1.0,
+ callback = function()
+ return true, 'Fires always'
+ end
+})
+rspamd_config:register_symbol({
+ name = 'DEFAULT_POLICY_REMOVE_WEIGHT_B',
+ score = 1.0,
+ callback = function()
+ return true, 'Fires always'
+ end
+})
+rspamd_config:register_symbol({
+ name = 'DEFAULT_POLICY_REMOVE_SYMBOL_A',
+ score = 1.0,
+ callback = function()
+ return true, 'Fires always'
+ end
+})
+rspamd_config:register_symbol({
+ name = 'DEFAULT_POLICY_REMOVE_SYMBOL_B',
+ score = 1.0,
+ callback = function()
+ return true, 'Fires always'
+ end
+})
+rspamd_config:register_symbol({
+ name = 'DEFAULT_POLICY_LEAVE_A',
+ score = 1.0,
+ callback = function()
+ return true, 'Fires always'
+ end
+})
+rspamd_config:register_symbol({
+ name = 'DEFAULT_POLICY_LEAVE_B',
+ score = 1.0,
+ callback = function()
+ return true, 'Fires always'
+ end
+})
+
+rspamd_config:register_symbol({
+ name = 'POSITIVE_A',
+ score = -1.0,
+ group = "positive",
+ callback = function()
+ return true, 'Fires always'
+ end
+})
+rspamd_config:register_symbol({
+ name = 'NEGATIVE_A',
+ score = -1.0,
+ group = "negative",
+ callback = function()
+ return true, 'Fires always'
+ end
+})
+rspamd_config:register_symbol({
+ name = 'NEGATIVE_B',
+ score = 1.0,
+ group = "negative",
+ callback = function()
+ return true, 'Fires always'
+ end
+})
+rspamd_config:register_symbol({
+ name = 'ANY_A',
+ score = -1.0,
+ group = "any",
+ callback = function()
+ return true, 'Fires always'
+ end
+})
+
+rspamd_config:register_symbol({
+ name = 'OPTS',
+ score = -1.0,
+ group = "any",
+ callback = function(task)
+ local lua_util = require "lua_util"
+ local woot = lua_util.str_split(tostring(task:get_request_header('opts') or ''), ',')
+
+ if woot and #woot > 0 and #woot[1] > 0 then
+ return true, 1.0, woot
+ end
+ end
+})
diff --git a/test/functional/lua/conditions.lua b/test/functional/lua/conditions.lua
new file mode 100644
index 0000000..2baa04b
--- /dev/null
+++ b/test/functional/lua/conditions.lua
@@ -0,0 +1,22 @@
+local logger = require 'rspamd_logger'
+
+rspamd_config:register_symbol({
+ name = 'ANY_A',
+ score = -1.0,
+ group = "any",
+ callback = function()
+ return true, 'hello3'
+ end
+})
+
+rspamd_config:add_condition('ANY_A', function(task)
+ logger.infox(task, 'hello from condition1')
+ task:insert_result('ANY_A', 1.0, 'hello1')
+ return true
+end)
+
+rspamd_config:add_condition('ANY_A', function(task)
+ logger.infox(task, 'hello from condition2')
+ task:insert_result('ANY_A', 1.0, 'hello2')
+ return true
+end)
diff --git a/test/functional/lua/deps.lua b/test/functional/lua/deps.lua
new file mode 100644
index 0000000..6171db6
--- /dev/null
+++ b/test/functional/lua/deps.lua
@@ -0,0 +1,30 @@
+local cb = function(task)
+ task:insert_result('TOP', 1.0)
+end
+
+local cb_dep1 = function(task)
+ if task:get_symbol('TOP') then
+ task:insert_result('DEP1', 1.0)
+ end
+end
+
+local cb_gen = function(num)
+ local cb_dep = function(task)
+ if task:get_symbol('DEP' .. tostring(num)) then
+ task:insert_result('DEP' .. tostring(num + 1), 1.0)
+ end
+ end
+
+ return cb_dep
+end
+
+local id = rspamd_config:register_callback_symbol(1.0, cb)
+rspamd_config:register_virtual_symbol('TOP', 1.0, id)
+
+rspamd_config:register_symbol('DEP1', 1.0, cb_dep1)
+rspamd_config:register_dependency('DEP1', 'TOP')
+
+for i = 2,10 do
+ rspamd_config:register_symbol('DEP' .. tostring(i), 1.0, cb_gen(i - 1))
+ rspamd_config:register_dependency('DEP' .. tostring(i), 'DEP' .. tostring(i - 1))
+end
diff --git a/test/functional/lua/dns.lua b/test/functional/lua/dns.lua
new file mode 100644
index 0000000..702b985
--- /dev/null
+++ b/test/functional/lua/dns.lua
@@ -0,0 +1,53 @@
+local rspamd_dns = require "rspamd_dns"
+local logger = require "rspamd_logger"
+
+local function dns_sync_symbol(task)
+ local to_resolve = tostring(task:get_request_header('to-resolve'))
+ local is_ok, results = rspamd_dns.request({
+ task = task,
+ type = 'a',
+ name = to_resolve ,
+ })
+
+ logger.errx(task, "is_ok=%1, results=%2, results[1]=%3", is_ok, results, results[1])
+
+ if not is_ok then
+ task:insert_result('DNS_SYNC_ERROR', 1.0, results)
+ else
+ task:insert_result('DNS_SYNC', 1.0, tostring(results[1]))
+ end
+end
+
+rspamd_config:register_symbol({
+ name = 'SIMPLE_DNS_SYNC',
+ score = 1.0,
+ callback = dns_sync_symbol,
+ no_squeeze = true,
+ flags = 'coro',
+})
+
+
+-- Async request
+local function dns_symbol(task)
+ local function dns_cb(_, to_resolve, results, err)
+ logger.errx(task, "_=%1, to_resolve=%2, results=%3, err%4", _, to_resolve, results, err)
+ if err then
+ task:insert_result('DNS_ERROR', 1.0, err)
+ else
+ task:insert_result('DNS', 1.0, tostring(results[1]))
+ end
+ end
+ local to_resolve = tostring(task:get_request_header('to-resolve'))
+
+ task:get_resolver():resolve_a({
+ task = task,
+ name = to_resolve,
+ callback = dns_cb
+ })
+end
+
+rspamd_config:register_symbol({
+ name = 'SIMPLE_DNS',
+ score = 1.0,
+ callback = dns_symbol,
+}) \ No newline at end of file
diff --git a/test/functional/lua/external_relay.lua b/test/functional/lua/external_relay.lua
new file mode 100644
index 0000000..6aa3a29
--- /dev/null
+++ b/test/functional/lua/external_relay.lua
@@ -0,0 +1,10 @@
+rspamd_config:register_symbol({
+ name = 'EXTERNAL_RELAY_TEST',
+ score = 0.0,
+ callback = function(task)
+ local from_ip = string.format('IP=%s', task:get_from_ip() or 'NIL')
+ local hostname = string.format('HOSTNAME=%s', task:get_hostname() or 'NIL')
+ local helo = string.format('HELO=%s', task:get_helo() or 'NIL')
+ return true, from_ip, hostname, helo
+ end
+})
diff --git a/test/functional/lua/flags.lua b/test/functional/lua/flags.lua
new file mode 100644
index 0000000..5d01ab1
--- /dev/null
+++ b/test/functional/lua/flags.lua
@@ -0,0 +1,35 @@
+--[[
+Copyright (c) 2022, Vsevolod Stakhov <vsevolod@rspamd.com>
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice,
+this list of conditions and the following disclaimer in the documentation
+and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+]]--
+
+rspamd_config:register_post_filter(function(task)
+ task:set_flag('no_stat')
+ task:set_flag('no_log', false)
+ task:set_flag('no_log', true)
+ task:set_flag('skip', true)
+ task:set_flag('extended_urls')
+
+ task:insert_result('FLAGS_SYM', 1.0, table.concat(task:get_flags(), ','))
+end)
diff --git a/test/functional/lua/get_from.lua b/test/functional/lua/get_from.lua
new file mode 100644
index 0000000..9282866
--- /dev/null
+++ b/test/functional/lua/get_from.lua
@@ -0,0 +1,10 @@
+rspamd_config:register_symbol({
+ name = 'GET_FROM',
+ score = 1.0,
+ callback = function(task)
+ local a = task:get_from('mime')
+ if not a then return end
+ a = a[1]
+ return true, (a.name or '') .. ',' .. (a.addr or '') .. ',' .. (a.user or '') .. ',' .. (a.domain or '')
+ end
+})
diff --git a/test/functional/lua/hashes.lua b/test/functional/lua/hashes.lua
new file mode 100644
index 0000000..7a1728b
--- /dev/null
+++ b/test/functional/lua/hashes.lua
@@ -0,0 +1,74 @@
+rspamd_config:register_symbol({
+ name = 'TEST_HASHES',
+ score = 1.0,
+ callback = function()
+ local hash = require 'rspamd_cryptobox_hash'
+ local logger = require 'rspamd_logger'
+
+ local worry = {}
+ local test_data = {
+ {
+ ['str'] = 'asdf.qwerty.123',
+ ['hex'] = 'bf22dd95750034b9af93f0e4e5954aca3506bbcdbc051d91bd9af2d1a8fc294e848626b1c1751e58b44c4d3ea69dec5efa5a214dc59c77b1a9ca3bde3babac9d',
+ },
+ {
+ ['specific'] = 'md5',
+ ['str'] = 'asdf.qwerty.123',
+ ['hex'] = 'cf25ddc406c50de0c13de2b79d127646',
+ },
+ {
+ ['init'] = 'asdf.qwerty.123',
+ ['str'] = 'asdf.qwerty.123',
+ ['hex'] = 'bf22dd95750034b9af93f0e4e5954aca3506bbcdbc051d91bd9af2d1a8fc294e848626b1c1751e58b44c4d3ea69dec5efa5a214dc59c77b1a9ca3bde3babac9d',
+ ['reset'] = true,
+ },
+ {
+ ['init'] = 'asdf.qwerty.123',
+ ['str'] = 'asdf.qwerty.123',
+ ['hex'] = 'e445046aa21a705dcce1343795630f88bc0196a0070011fdce789d5a2a349a8f85349834ade555ca21439f65fdc4dbcf82dcff7fcc559ef11c508507515c1532',
+ },
+ {
+ ['init'] = 'asdf.qwerty.123',
+ ['specific'] = 'md5',
+ ['str'] = 'asdf.qwerty.123',
+ ['hex'] = '9ef941c4d050e43b1e665300f4fbe054',
+ },
+ {
+ ['init'] = 'asdf.qwerty.123',
+ ['specific'] = 'md5',
+ ['str'] = 'asdf.qwerty.123',
+ ['hex'] = 'cf25ddc406c50de0c13de2b79d127646',
+ ['reset'] = true,
+ },
+ {
+ ['init'] = 'hello',
+ ['specific'] = 'xxh3',
+ ['str'] = 'hello',
+ ['hex'] = 'c1156ae6cb7ff175',
+ }
+ }
+
+ for _, t in ipairs(test_data) do
+ local h
+ if not t['specific'] then
+ h = hash.create(t['init'])
+ else
+ h = hash.create_specific(t['specific'], t['init'])
+ end
+ if t['reset'] then
+ h:reset()
+ end
+ h:update(t['str'])
+ if h:hex() ~= t['hex'] then
+ t['error'] = 'sum mismatch: ' .. h:hex()
+ table.insert(worry, logger.slog('%1', t))
+ end
+ end
+
+ if (#worry == 0) then
+ return true, "no worry"
+ else
+ return true, table.concat(worry, ",")
+ end
+ end
+})
diff --git a/test/functional/lua/http.lua b/test/functional/lua/http.lua
new file mode 100644
index 0000000..c5b91ff
--- /dev/null
+++ b/test/functional/lua/http.lua
@@ -0,0 +1,169 @@
+local rspamd_http = require "rspamd_http"
+local rspamd_logger = require "rspamd_logger"
+
+local function http_symbol(task)
+
+ local url = tostring(task:get_request_header('url'))
+ local method = tostring(task:get_request_header('method'))
+ task:insert_result('method_' .. method, 1.0)
+
+ local function http_callback(err, code, body)
+ if err then
+ rspamd_logger.errx('http_callback error: ' .. err)
+ task:insert_result('HTTP_ERROR', 1.0, err)
+ else
+ task:insert_result('HTTP_' .. code, 1.0, body)
+ end
+ end
+
+ local function http_dns_callback(err, code, body)
+ if err then
+ rspamd_logger.errx('http_dns_callback error: ' .. err)
+ task:insert_result('HTTP_DNS_ERROR', 1.0, err)
+ else
+ task:insert_result('HTTP_DNS_' .. code, 1.0, body)
+ end
+ end
+
+ rspamd_logger.errx(task, 'do http request with callback')
+ rspamd_http.request({
+ url = 'http://127.0.0.1:18080' .. url,
+ task = task,
+ method = method,
+ callback = http_callback,
+ timeout = 1,
+ })
+
+ --[[ request to this address involved DNS resolver subsystem ]]
+ rspamd_logger.errx(task, 'do http request with callback + dns resolving')
+ rspamd_http.request({
+ url = 'http://site.resolveme:18080' .. url,
+ task = task,
+ method = method,
+ callback = http_dns_callback,
+ timeout = 1,
+ })
+
+ rspamd_logger.errx(task, 'rspamd_http.request[before]')
+
+ local err, response = rspamd_http.request({
+ url = 'http://127.0.0.1:18080' .. url,
+ task = task,
+ method = method,
+ timeout = 1,
+ })
+ rspamd_logger.errx(task, 'rspamd_http.request[done] err: %1 response:%2', err, response)
+
+ if not err then
+ task:insert_result('HTTP_CORO_' .. response.code, 1.0, response.content)
+ else
+ task:insert_result('HTTP_CORO_ERROR', 1.0, err)
+ end
+
+ rspamd_logger.errx(task, 'do http request after coroutine finished')
+ err, response = rspamd_http.request({
+ url = 'http://site.resolveme:18080' .. url,
+ task = task,
+ method = method,
+ timeout = 1,
+ })
+
+ if not err then
+ task:insert_result('HTTP_CORO_DNS_' .. response.code, 1.0, response.content)
+ else
+ task:insert_result('HTTP_CORO_DNS_ERROR', 1.0, err)
+ end
+end
+
+
+local function finish(task)
+ rspamd_logger.errx('function finish')
+ local err, response = rspamd_http.request({
+ url = 'http://site.resolveme:18080/timeout',
+ task = task,
+ method = 'get',
+ timeout = 1,
+ })
+ if err then
+ task:insert_result('HTTP_CORO_DNS_FINISH_ERROR', 1.0, err)
+ else
+ task:insert_result('HTTP_CORO_DNS_FINISH_' .. response.code, 1.0, response.content)
+ end
+end
+
+local function periodic(cfg, ev_base)
+ local err, response = rspamd_http.request({
+ url = 'http://site.resolveme:18080/request/periodic',
+ config = cfg,
+ })
+ if err then
+ rspamd_logger.errx('periodic err ' .. err)
+ else
+ rspamd_logger.errx('periodic success ' .. response.content)
+ end
+
+ return false
+end
+
+rspamd_config:register_symbol({
+ name = 'SIMPLE_HTTP_TEST',
+ score = 1.0,
+ callback = http_symbol,
+ no_squeeze = true,
+ flags = 'coro'
+})
+
+local function http_large_symbol(task)
+ if task:get_queue_id() == 'SSL Large HTTP request' then
+ local data = {}
+ for i = 1,2 do
+ local st = {}
+ for j=1,300000 do
+ st[j] = 't'
+ end
+ data[i] = table.concat(st)
+ end
+ data[#data + 1] = '\n'
+
+ local function http_callback(err, code, body)
+ if err then
+ rspamd_logger.errx('http_callback error: ' .. err)
+ task:insert_result('HTTP_ERROR', 1.0, err)
+ else
+ task:insert_result('HTTP_SSL_LARGE', 1.0)
+ end
+ end
+ rspamd_http.request({
+ url = 'https://127.0.0.1:18081/',
+ task = task,
+ method = 'post',
+ callback = http_callback,
+ timeout = 10,
+ body = data,
+ no_ssl_verify = true,
+ })
+ end
+end
+rspamd_config:register_symbol({
+ name = 'LARGE_HTTP_TEST',
+ score = 1.0,
+ callback = http_large_symbol,
+ no_squeeze = true,
+ flags = 'coro'
+})
+
+rspamd_config:register_finish_script(finish)
+
+rspamd_config:add_on_load(function(cfg, ev_base, worker)
+ local err, response = rspamd_http.request({
+ url = 'http://site.resolveme:18080/request/add_on_load',
+ config = cfg,
+ })
+ if err then
+ rspamd_logger.errx('add_on_load err ' .. err)
+ else
+ rspamd_logger.errx('add_on_load success ' .. response.content)
+ end
+
+ rspamd_config:add_periodic(ev_base, 0, periodic, false)
+end)
diff --git a/test/functional/lua/magic.lua b/test/functional/lua/magic.lua
new file mode 100644
index 0000000..c0d5793
--- /dev/null
+++ b/test/functional/lua/magic.lua
@@ -0,0 +1,14 @@
+rspamd_config.MAGIC_SYM = {
+ callback = function(task)
+ local parts = task:get_parts()
+
+ for i,p in ipairs(parts) do
+ local ext = p:get_detected_ext()
+
+ if ext then
+ task:insert_result('MAGIC_SYM_' .. ext:upper() .. '_' .. tostring(i), 1.0)
+ end
+ end
+ end,
+ type = 'callback'
+} \ No newline at end of file
diff --git a/test/functional/lua/mapreload.lua b/test/functional/lua/mapreload.lua
new file mode 100644
index 0000000..ae20120
--- /dev/null
+++ b/test/functional/lua/mapreload.lua
@@ -0,0 +1,20 @@
+local test_map = rspamd_config:add_map ({
+ url = '${MAP_FILE}',
+ type = 'set',
+})
+
+rspamd_config:register_symbol({
+ name = 'MAP_SET_HIT_AND_MISS',
+ score = 1.0,
+ callback = function()
+ local has_example = test_map:get_key('example.com')
+ local has_rspamdtest = test_map:get_key('rspamd-test.com')
+ if has_example and not has_rspamdtest then
+ return true, 'example.com'
+ elseif has_rspamdtest and not has_example then
+ return true, 'rspamd.com'
+ else
+ return true, string.format('invalid: has_example=%s, has_rspamdtest=%s', has_example, has_rspamdtest)
+ end
+ end
+})
diff --git a/test/functional/lua/maps_kv.lua b/test/functional/lua/maps_kv.lua
new file mode 100644
index 0000000..b90f44d
--- /dev/null
+++ b/test/functional/lua/maps_kv.lua
@@ -0,0 +1,94 @@
+local rspamd_ip = require 'rspamd_ip'
+local rspamd_logger = require 'rspamd_logger'
+local lua_maps = require "lua_maps"
+
+local radix_map = rspamd_config:add_map ({
+ url = rspamd_env.RADIX_MAP,
+ type = 'radix',
+})
+
+local map_map = rspamd_config:add_map ({
+ url = rspamd_env.MAP_MAP,
+ type = 'map',
+})
+
+local regexp_map = rspamd_config:add_map ({
+ url = rspamd_env.REGEXP_MAP,
+ type = 'regexp',
+})
+
+rspamd_config:register_symbol({
+ name = 'RADIX_KV',
+ score = 1.0,
+ callback = function()
+ local sip = {'8.8.8.8', '::1', '192.168.1.1', '10.0.1.1'}
+ local expected = {'test one', 'another', '1', false}
+ for i = 1, #sip do
+ if (radix_map:get_key(rspamd_ip.from_string(sip[i])) ~= expected[i]) then
+ local rip = rspamd_ip.from_string(sip[i])
+ local val = radix_map:get_key(rip)
+ return true, rspamd_logger.slog('plain: get_key(%s) [%s] -> %s [%s] [expected %s]', rip, type(rip), val, type(val), expected[i])
+ end
+ if (radix_map:get_key(sip[i]) ~= expected[i]) then
+ local val = radix_map:get_key(sip[i])
+ return true, rspamd_logger.slog('string: get_key(%s) [%s] -> %s [%s] [expected %s]', sip[i], type(sip[i]), val, type(val), expected[i])
+ end
+ end
+ return true, 'no worry'
+ end
+})
+
+rspamd_config:register_symbol({
+ name = 'MAP_KV',
+ score = 1.0,
+ callback = function()
+ local str = {'foo', 'asdf.example.com', 'asdf', 'barf'}
+ local expected = {'bar', 'value', '', false}
+ for i = 1, #str do
+ if (map_map:get_key(str[i]) ~= expected[i]) then
+ local val = map_map:get_key(str[i])
+ return true, rspamd_logger.slog('get_key(%s) [%s] -> %s [%s] [expected %s]', str[i], type(str[i]), val, type(val), expected[i])
+ end
+ end
+ return true, 'no worry'
+ end,
+})
+
+rspamd_config:register_symbol({
+ name = 'REGEXP_KV',
+ score = 1.0,
+ callback = function()
+ local str = {'foo', 'asdf.example.com', 'asdf', 'barf'}
+ local expected = {'bar', 'value', '1', false}
+ for i = 1, #str do
+ if (regexp_map:get_key(str[i]) ~= expected[i]) then
+ local val = regexp_map:get_key(str[i])
+ return true, rspamd_logger.slog('get_key(%s) [%s] -> %s [%s] [expected %s]', str[i], type(str[i]), val, type(val), expected[i])
+ end
+ end
+ return true, 'no worry'
+ end,
+})
+
+local simple_ext_map = lua_maps.map_add_from_ucl({
+ external = true,
+ backend = "http://127.0.0.1:18080/map-simple",
+ method = "body",
+ encode = "json",
+}, '', 'external map')
+rspamd_config:register_symbol({
+ name = 'EXTERNAL_MAP',
+ score = 1.0,
+ callback = function(task)
+ local function cb(res, data, code)
+ if res then
+ task:insert_result('EXTERNAL_MAP', 1.0, string.format('+%s', data))
+ else
+ task:insert_result('EXTERNAL_MAP', 1.0, string.format('-%s:%s', code, data))
+ end
+ end
+ simple_ext_map:get_key({
+ key = "value",
+ }, cb, task)
+ end,
+})
diff --git a/test/functional/lua/miltertest/combined.lua b/test/functional/lua/miltertest/combined.lua
new file mode 100644
index 0000000..69fa2d6
--- /dev/null
+++ b/test/functional/lua/miltertest/combined.lua
@@ -0,0 +1,35 @@
+-- Combine tests
+
+dofile './lib.lua'
+dofile './data.lua'
+
+setup()
+
+local old_setup = setup
+local old_teardown = teardown
+
+local empty_function = function() end
+setup = empty_function
+teardown = empty_function
+
+local function shuffle(tbl)
+ local size = #tbl
+ for i = size, 1, -1 do
+ local rand = math.random(size)
+ tbl[i], tbl[rand] = tbl[rand], tbl[i]
+ end
+ return tbl
+end
+
+local files = {'mt1.lua','mt2.lua','mt3.lua','mt4.lua'}
+local num_files = #files
+for i = 1, num_files do
+ table.insert(files, files[i])
+end
+files = shuffle(files)
+
+for _, f in ipairs(files) do
+ dofile(f)
+end
+
+old_teardown()
diff --git a/test/functional/lua/miltertest/data.lua b/test/functional/lua/miltertest/data.lua
new file mode 100644
index 0000000..84b953f
--- /dev/null
+++ b/test/functional/lua/miltertest/data.lua
@@ -0,0 +1,26 @@
+innocuous_hdrs = {
+ ['Message-ID'] = '<20180202155326.Horde.GfEWpxCo_Dip2xJswIpQNgK@example.org>',
+ ['From'] = 'Andrew Lewis <nerf@example.org>',
+ ['To'] = 'nerf@example.org',
+ ['Subject'] = 'innocuous test message',
+ ['User-Agent'] = 'Horde Application Framework 5',
+ ['Content-Type'] = 'text/plain; charset=utf-8; format=flowed; DelSp=Yes',
+ ['MIME-Version'] = '1.0',
+ ['Content-Disposition'] = 'inline',
+ ['Date'] = 'Fri, 02 Feb 2018 15:53:26 +0200',
+}
+
+default_hdrs = {
+ ['Subject'] = 'spam message',
+}
+
+innocuous_msg = 'Hello Rupert'
+
+gtube = [[lo
+
+XJS*C4JDBQADN1.NSBN3*2IDNEN*GTUBE-STANDARD-ANTI-UBE-TEST-EMAIL*C.34X
+
+thx]]
+
+gtube_add_header = string.gsub(gtube, "XJS", "YJS")
+gtube_rw_subject = string.gsub(gtube, "XJS", "ZJS")
diff --git a/test/functional/lua/miltertest/data_dkim.lua b/test/functional/lua/miltertest/data_dkim.lua
new file mode 100644
index 0000000..15adf15
--- /dev/null
+++ b/test/functional/lua/miltertest/data_dkim.lua
@@ -0,0 +1,23 @@
+multi_hdrs = {
+ ['Message-ID'] = '<a44q4StVFY04V4_4gOMYXjTgMDvmlSFzZxnoyJPHFwM@cacophony.za.org>',
+ ['From'] = 'Rspamd <foo@cacophony.za.org>',
+ ['To'] = 'nerf@example.org',
+ ['Subject'] = 'dkim test message',
+ ['User-Agent'] = 'Vi IMproved 8.1',
+ ['Content-Type'] = 'text/plain; charset=utf-8;',
+ ['MIME-Version'] = '1.0',
+ ['Date'] = 'Sat, 02 Feb 2019 10:34:54 +0000',
+}
+
+single_hdr = {
+ ['Message-ID'] = '<a44q4StVFY04V4_4gOMYXjTgMDvmlSFzZxnoyJPHFwM@cacophony.za.org>',
+ ['From'] = 'Rspamd <foo@invalid.za.org>',
+ ['To'] = 'nerf@example.org',
+ ['Subject'] = 'dkim test message',
+ ['User-Agent'] = 'Vi IMproved 8.1',
+ ['Content-Type'] = 'text/plain; charset=utf-8;',
+ ['MIME-Version'] = '1.0',
+ ['Date'] = 'Sat, 02 Feb 2019 10:34:54 +0000',
+}
+
+innocuous_msg = 'hello'
diff --git a/test/functional/lua/miltertest/dkim_many.lua b/test/functional/lua/miltertest/dkim_many.lua
new file mode 100644
index 0000000..70a3a1b
--- /dev/null
+++ b/test/functional/lua/miltertest/dkim_many.lua
@@ -0,0 +1,11 @@
+print('Check we get multiple dkim signatures')
+
+dofile './lib.lua'
+dofile './data_dkim.lua'
+
+setup()
+
+send_message(innocuous_msg, multi_hdrs, 'test-id', 'foo@cacophony.za.org', {'nerf@example.org'})
+check_headers(2)
+
+teardown()
diff --git a/test/functional/lua/miltertest/dkim_one.lua b/test/functional/lua/miltertest/dkim_one.lua
new file mode 100644
index 0000000..0c7def8
--- /dev/null
+++ b/test/functional/lua/miltertest/dkim_one.lua
@@ -0,0 +1,11 @@
+print('Check we get single dkim signature')
+
+dofile './lib.lua'
+dofile './data_dkim.lua'
+
+setup()
+
+send_message(innocuous_msg, single_hdr, 'test-id', 'foo@invalid.za.org', {'nerf@example.org'})
+check_headers(1)
+
+teardown()
diff --git a/test/functional/lua/miltertest/lib.lua b/test/functional/lua/miltertest/lib.lua
new file mode 100644
index 0000000..44dc76e
--- /dev/null
+++ b/test/functional/lua/miltertest/lib.lua
@@ -0,0 +1,119 @@
+function setup(c_ip, helo, hn)
+ if not c_ip then c_ip = "127.0.0.1" end
+ if not helo then helo = "it.is.i" end
+ if not hn then hn = "localhost" end
+ conn = mt.connect("inet:" .. port .. "@" .. host)
+ if conn == nil then
+ error "mt.connect() failed"
+ end
+ if mt.conninfo(conn, hn, c_ip) then
+ error "mt.conninfo() failed"
+ end
+ if mt.getreply(conn) ~= SMFIR_CONTINUE then
+ error "mt.conninfo() unexpected reply"
+ end
+ if mt.helo(conn, helo) then
+ error "mt.helo() failed"
+ end
+ if mt.getreply(conn) ~= SMFIR_CONTINUE then
+ error "mt.helo() unexpected reply"
+ end
+end
+
+function teardown()
+ if conn then
+ mt.disconnect(conn)
+ end
+ conn = nil
+end
+
+function send_message(body, hdrs, id, sender, rcpts)
+ mt.macro(conn, SMFIC_MAIL, "i", id or "test-id")
+ if mt.mailfrom(conn, sender or "sender@example.com") then
+ error "mt.mailfrom() failed"
+ end
+ if mt.getreply(conn) ~= SMFIR_CONTINUE then
+ error "mt.mailfrom() unexpected reply"
+ end
+ if not rcpts then
+ rcpts = {"rcpt@example.com"}
+ end
+ for _, r in ipairs(rcpts) do
+ mt.rcptto(conn, r)
+ end
+ if not hdrs then
+ hdrs = default_hdrs
+ end
+ if not hdrs['From'] then
+ hdrs['From'] = sender or "sender@example.com"
+ end
+ for k, v in pairs(hdrs) do
+ if mt.header(conn, k, v) then
+ error (string.format("mt.header(%s) failed", k))
+ end
+ end
+ if mt.eoh(conn) then
+ error "mt.eoh() failed"
+ end
+ if mt.getreply(conn) ~= SMFIR_CONTINUE then
+ error "mt.eoh() unexpected reply"
+ end
+ if mt.bodystring(conn, body .. "\r\n") then
+ error "mt.bodystring() failed"
+ end
+ if mt.getreply(conn) ~= SMFIR_CONTINUE then
+ error "mt.bodystring() unexpected reply"
+ end
+ if mt.eom(conn) then
+ error "mt.eom() failed"
+ end
+end
+
+function check_accept()
+ local rc = mt.getreply(conn)
+ if rc ~= SMFIR_ACCEPT then
+ error (string.format("mt.eom() unexpected reply: %s", rc))
+ end
+end
+
+function check_gtube(code, ecode, msg)
+ if not mt.eom_check(conn, MT_SMTPREPLY, code or '554', ecode or '5.7.1', msg or 'Gtube pattern') then
+ error "mt.eom_check() failed"
+ end
+ local rc = mt.getreply(conn)
+ if rc ~= SMFIR_REPLYCODE then
+ error (string.format("mt.eom() unexpected reply: %s", rc))
+ end
+end
+
+function check_defer(code, ecode, msg)
+ if not mt.eom_check(conn, MT_SMTPREPLY, code or '451', ecode or '4.7.1', msg or 'Try much later') then
+ error "mt.eom_check() failed"
+ end
+ local rc = mt.getreply(conn)
+ if rc ~= SMFIR_REPLYCODE then
+ error (string.format("mt.eom() unexpected reply: %s", rc))
+ end
+end
+
+function check_subject_rw(subj, tmpl)
+ if not subj then
+ subj = default_hdrs['Subject']
+ end
+ if not tmpl then
+ tmpl = "*** SPAM *** %s"
+ end
+ local new_subj = string.format(tmpl, subj)
+ if not mt.eom_check(conn, MT_HDRCHANGE, "Subject", new_subj) then
+ error "subject not rewritten"
+ end
+end
+
+function check_headers(count)
+ for i=0, count-1 do
+ local hdr = mt.getheader(conn, "DKIM-Signature", i)
+ if not hdr then
+ error (string.format("Signature %s not added", i))
+ end
+ end
+end
diff --git a/test/functional/lua/miltertest/mt1.lua b/test/functional/lua/miltertest/mt1.lua
new file mode 100644
index 0000000..019a852
--- /dev/null
+++ b/test/functional/lua/miltertest/mt1.lua
@@ -0,0 +1,11 @@
+print('Check we will accept a message')
+
+dofile './lib.lua'
+dofile './data.lua'
+
+setup()
+
+send_message(innocuous_msg, innocuous_hdrs, 'test-id', 'nerf@example.org', {'nerf@example.org'})
+check_accept()
+
+teardown()
diff --git a/test/functional/lua/miltertest/mt2.lua b/test/functional/lua/miltertest/mt2.lua
new file mode 100644
index 0000000..1c8fa83
--- /dev/null
+++ b/test/functional/lua/miltertest/mt2.lua
@@ -0,0 +1,11 @@
+print('Check we will reject a message')
+
+dofile './lib.lua'
+dofile './data.lua'
+
+setup()
+
+send_message(gtube)
+check_gtube()
+
+teardown()
diff --git a/test/functional/lua/miltertest/mt3.lua b/test/functional/lua/miltertest/mt3.lua
new file mode 100644
index 0000000..6b30126
--- /dev/null
+++ b/test/functional/lua/miltertest/mt3.lua
@@ -0,0 +1,12 @@
+print('Check we will rewrite subjects')
+
+dofile './lib.lua'
+dofile './data.lua'
+
+setup()
+
+send_message(gtube_rw_subject)
+check_accept()
+check_subject_rw()
+
+teardown()
diff --git a/test/functional/lua/miltertest/mt4.lua b/test/functional/lua/miltertest/mt4.lua
new file mode 100644
index 0000000..300cf69
--- /dev/null
+++ b/test/functional/lua/miltertest/mt4.lua
@@ -0,0 +1,11 @@
+print('Check we will defer messages')
+
+dofile './lib.lua'
+dofile './data.lua'
+
+setup()
+
+send_message(innocuous_msg, innocuous_hdrs, 'test-id', 'defer@example.org', {'nerf@example.org'})
+check_defer()
+
+teardown()
diff --git a/test/functional/lua/neural.lua b/test/functional/lua/neural.lua
new file mode 100644
index 0000000..5a09c50
--- /dev/null
+++ b/test/functional/lua/neural.lua
@@ -0,0 +1,64 @@
+local logger = require "rspamd_logger"
+
+for i = 1,14 do
+ rspamd_config:register_symbol({
+ name = 'SPAM_SYMBOL'..tostring(i),
+ score = 5.0,
+ callback = function()
+ return true, 'Fires always'
+ end
+ })
+ rspamd_config:register_symbol({
+ name = 'HAM_SYMBOL'..tostring(i),
+ score = -3.0,
+ callback = function()
+ return true, 'Fires always'
+ end
+ })
+end
+
+
+
+rspamd_config:register_symbol({
+ name = 'NEUTRAL_SYMBOL',
+ score = 1.0,
+ flags = 'explicit_disable',
+ callback = function()
+ return true, 'Fires always'
+ end
+})
+
+rspamd_config.SAVE_NN_ROW = {
+ callback = function(task)
+ local fname = os.tmpname()
+ task:cache_set('nn_row_tmpfile', fname)
+ return true, 1.0, fname
+ end
+}
+
+rspamd_config.SAVE_NN_ROW_IDEMPOTENT = {
+ callback = function(task)
+ local function tohex(str)
+ return (str:gsub('.', function (c)
+ return string.format('%02X', string.byte(c))
+ end))
+ end
+ local fname = task:cache_get('nn_row_tmpfile')
+ if not fname then
+ return
+ end
+ local f, err = io.open(fname, 'w')
+ if not f then
+ logger.errx(task, err)
+ return
+ end
+ f:write(tohex(task:cache_get('SHORT_neural_vec_mpack') or ''))
+ f:close()
+ return
+ end,
+ type = 'idempotent',
+ flags = 'explicit_disable',
+ priority = 100,
+}
+
+dofile(rspamd_env.INSTALLROOT .. "/share/rspamd/rules/controller/init.lua")
diff --git a/test/functional/lua/option_order.lua b/test/functional/lua/option_order.lua
new file mode 100644
index 0000000..caff30f
--- /dev/null
+++ b/test/functional/lua/option_order.lua
@@ -0,0 +1,15 @@
+rspamd_config:register_symbol({
+ name = 'TBL_OPTION_ORDER',
+ score = 1.0,
+ callback = function()
+ return true, {'one', 'two', 'three', '4', '5', 'a'}
+ end
+})
+
+rspamd_config:register_symbol({
+ name = 'OPTION_ORDER',
+ score = 1.0,
+ callback = function()
+ return true, 'one', 'two', 'three', '4', '5', 'a'
+ end
+})
diff --git a/test/functional/lua/params.lua b/test/functional/lua/params.lua
new file mode 100644
index 0000000..2846527
--- /dev/null
+++ b/test/functional/lua/params.lua
@@ -0,0 +1,80 @@
+rspamd_config.TEST_RCPT = {
+ callback = function(task)
+ local l = {}
+ local rcpts = task:get_recipients(1)
+ if not rcpts then return end
+ for _, r in ipairs(rcpts) do
+ table.insert(l, r['addr'])
+ end
+ table.sort(l)
+ local t = table.concat(l, ",")
+ return true, t
+ end
+}
+
+rspamd_config.TEST_HELO = {
+ callback = function(task)
+ local helo = task:get_helo()
+ if not helo then return end
+ return true, helo
+ end
+}
+
+rspamd_config.TEST_HOSTNAME = {
+ callback = function(task)
+ local h = task:get_hostname()
+ if not h then return end
+ return true, h
+ end
+}
+
+rspamd_config.TEST_SMTP_FROM = {
+ callback = function(task)
+ local f = task:get_from('smtp')
+ if not (f and f[1] and f[1].addr) then return end
+ return true, f[1].addr
+ end
+}
+
+rspamd_config.TEST_MTA_TAG = {
+ callback = function(task)
+ local h = task:get_request_header('MTA-Tag')
+ if not h then return end
+ return true, tostring(h)
+ end
+}
+
+rspamd_config.TEST_USER = {
+ callback = function(task)
+ local u = task:get_user()
+ if not u then return end
+ return true, u
+ end
+}
+
+rspamd_config.TEST_QUEUEID = {
+ callback = function(task)
+ local q = task:get_queue_id()
+ if not q then return end
+ return true, q
+ end
+}
+
+rspamd_config.TEST_IPADDR = {
+ callback = function(task)
+ local i = task:get_from_ip()
+ if not (i and i:is_valid()) then return end
+ return true, tostring(i)
+ end
+}
+
+rspamd_config.FORCE_DEFER = {
+ callback = function(task)
+ local f = task:get_from('smtp')
+ if not (f and f[1] and f[1].addr) then return end
+ if f[1].addr == "defer@example.org" then
+ task:set_pre_result('soft reject', 'Try much later')
+ return true
+ end
+ end
+}
diff --git a/test/functional/lua/prepostfilters.lua b/test/functional/lua/prepostfilters.lua
new file mode 100644
index 0000000..c87c958
--- /dev/null
+++ b/test/functional/lua/prepostfilters.lua
@@ -0,0 +1,65 @@
+for i = 1,10 do
+ local name = string.format('DEP_TEST%d', i)
+ local dep_name = string.format('DEP_TEST%d', i - 1)
+ rspamd_config:register_symbol({
+ type = 'normal',
+ name = name,
+ callback = function(task)
+ local function dns_cb()
+ if i ~= 1 then
+ if task:has_symbol(dep_name) then
+ task:insert_result(name, 1.0)
+ end
+ else
+ task:insert_result(name, 1.0)
+ end
+ end
+ if task:has_symbol('TEST_PRE') then
+ local r = task:get_resolver()
+ r:resolve_a({task = task, name = 'example.com', callback = dns_cb})
+ end
+ end
+ })
+
+ if i ~= 1 then
+ rspamd_config:register_dependency(name, dep_name)
+ end
+
+ rspamd_config:set_metric_symbol({
+ name = name,
+ score = 1.0
+ })
+end
+
+
+rspamd_config:register_symbol({
+ type = 'postfilter',
+ name = 'TEST_POST',
+ callback = function(task)
+ for i = 1,10 do
+ local name = string.format('DEP_TEST%d', i)
+ if not task:has_symbol(name) then
+ return
+ end
+ end
+ if task:has_symbol('TEST_PRE') then
+ task:insert_result('TEST_POST', 1.0)
+ end
+ end
+})
+rspamd_config:set_metric_symbol({
+ name = 'TEST_POST',
+ score = 1.0
+})
+
+rspamd_config:register_symbol({
+ type = 'prefilter',
+ name = 'TEST_PRE',
+ callback = function(task)
+ task:insert_result('TEST_PRE', 1.0)
+ end
+})
+rspamd_config:set_metric_symbol({
+ name = 'TEST_PRE',
+ score = 1.0
+})
diff --git a/test/functional/lua/preresult.lua b/test/functional/lua/preresult.lua
new file mode 100644
index 0000000..72a73b1
--- /dev/null
+++ b/test/functional/lua/preresult.lua
@@ -0,0 +1,3 @@
+rspamd_config:register_post_filter(function(task)
+ task:set_pre_result('soft reject', 'Pre Result Set')
+end)
diff --git a/test/functional/lua/recipients.lua b/test/functional/lua/recipients.lua
new file mode 100644
index 0000000..f11c1a8
--- /dev/null
+++ b/test/functional/lua/recipients.lua
@@ -0,0 +1,14 @@
+rspamd_config:register_symbol({
+ name = 'TEST_RCPT',
+ score = 1.0,
+ callback = function(task)
+ local l = {}
+ local rcpts = task:get_recipients(1)
+ for _, r in ipairs(rcpts) do
+ table.insert(l, r['addr'])
+ end
+ table.sort(l)
+ local t = table.concat(l, ",")
+ return true, t
+ end
+})
diff --git a/test/functional/lua/redis.lua b/test/functional/lua/redis.lua
new file mode 100644
index 0000000..1a1eaf2
--- /dev/null
+++ b/test/functional/lua/redis.lua
@@ -0,0 +1,113 @@
+--[[[
+-- Just a test for Redis API
+--]]
+
+local logger = require "rspamd_logger"
+local redis_lua = require "lua_redis"
+
+local redis_params
+local N = 'redis_test'
+
+local lua_script = [[
+local f = function() end
+return "hello from lua on redis"
+]]
+
+local function redis_simple_async_symbol(task)
+ local function redis_cb(err, data)
+ if err then
+ task:insert_result('REDIS_ASYNC_ERROR', 1.0, err)
+ else
+ task:insert_result('REDIS_ASYNC', 1.0, data)
+ end
+ end
+
+ redis_lua.rspamd_redis_make_request(
+ task,
+ redis_params,
+ "test_key",
+ false,
+ redis_cb,
+ 'GET',
+ {'test_key'}
+ )
+end
+
+local function redis_simple_async_api201809(task)
+ local function redis_cb(err, data)
+ if err then
+ task:insert_result('REDIS_ASYNC201809_ERROR', 1.0, err)
+ else
+ task:insert_result('REDIS_ASYNC201809', 1.0, data)
+ end
+ end
+
+ local attrs = {
+ task = task,
+ callback = redis_cb
+ }
+ local request = {
+ 'GET',
+ 'test_key'
+ }
+ redis_lua.request(redis_params, attrs, request)
+end
+
+local function redis_symbol(task)
+
+ local attrs = {task = task}
+ local is_ok, connection = redis_lua.connect(redis_params, attrs)
+
+ logger.infox(task, "connect: %1, %2", is_ok, connection)
+
+ if not is_ok then
+ task:insert_result('REDIS_ERROR', 1.0, connection)
+ return
+ end
+
+ local err, data
+
+ is_ok, err = connection:add_cmd('EVAL', {lua_script, 0})
+ logger.infox(task, "add_cmd: %1, %2", is_ok, err)
+
+ if not is_ok then
+ task:insert_result('REDIS_ERROR_2', 1.0, err)
+ return
+ end
+
+ is_ok,data = connection:exec()
+
+ logger.infox(task, "exec: %1, %2", is_ok, data)
+
+ if not is_ok then
+ task:insert_result('REDIS_ERROR_3', 1.0, data)
+ return
+ end
+
+ task:insert_result('REDIS', 1.0, data)
+
+end
+
+redis_params = rspamd_parse_redis_server(N)
+
+rspamd_config:register_symbol({
+ name = 'SIMPLE_REDIS_ASYNC_TEST',
+ score = 1.0,
+ callback = redis_simple_async_symbol,
+ no_squeeze = true
+})
+
+rspamd_config:register_symbol({
+ name = 'SIMPLE_REDIS_ASYNC201809_TEST',
+ score = 1.0,
+ callback = redis_simple_async_api201809,
+ no_squeeze = true
+})
+
+rspamd_config:register_symbol({
+ name = 'REDIS_TEST',
+ score = 1.0,
+ callback = redis_symbol,
+ flags = 'coro',
+})
+-- ]]
diff --git a/test/functional/lua/regex_test.lua b/test/functional/lua/regex_test.lua
new file mode 100644
index 0000000..01f0e22
--- /dev/null
+++ b/test/functional/lua/regex_test.lua
@@ -0,0 +1,12 @@
+local function get_urls(task)
+ local urls = task:get_urls()
+ for _, u in ipairs(urls) do
+ task:insert_result('FOUND_URL', 1.0, tostring(u))
+ end
+end
+
+rspamd_config:register_symbol({
+ name = 'SIMPLE',
+ score = 1.0,
+ callback = get_urls
+})
diff --git a/test/functional/lua/remove_result.lua b/test/functional/lua/remove_result.lua
new file mode 100644
index 0000000..106d7f4
--- /dev/null
+++ b/test/functional/lua/remove_result.lua
@@ -0,0 +1,26 @@
+local id = rspamd_config:register_symbol({
+ name = 'REMOVE_RESULT_CB',
+ callback = function(task)
+ task:insert_result('REMOVE_RESULT_UNEXPECTED', 1.0, 'ohno')
+ end,
+ type = 'callback',
+})
+
+rspamd_config:register_symbol({
+ name = 'REMOVE_RESULT_UNEXPECTED',
+ type = 'virtual',
+ score = 0.1,
+ group = 'remove_result_test',
+ parent = id,
+})
+
+rspamd_config:register_symbol({
+ name = 'REMOVE_RESULT_EXPECTED',
+ callback = function(task)
+ return task:remove_result('REMOVE_RESULT_UNEXPECTED') and true or false
+ end,
+ type = 'normal',
+ score = 0.1,
+})
+
+rspamd_config:register_dependency('REMOVE_RESULT_EXPECTED', 'REMOVE_RESULT_UNEXPECTED')
diff --git a/test/functional/lua/rspamadm/test_batch.lua b/test/functional/lua/rspamadm/test_batch.lua
new file mode 100644
index 0000000..dd50c9d
--- /dev/null
+++ b/test/functional/lua/rspamadm/test_batch.lua
@@ -0,0 +1,4 @@
+local rspamd_logger = require "rspamd_logger"
+
+rspamd_logger.info(rspamd_config, "nope")
+rspamd_logger.err(rspamd_config, "hello world")
diff --git a/test/functional/lua/rspamadm/test_dns_client.lua b/test/functional/lua/rspamadm/test_dns_client.lua
new file mode 100644
index 0000000..c54d594
--- /dev/null
+++ b/test/functional/lua/rspamadm/test_dns_client.lua
@@ -0,0 +1,30 @@
+local rspamd_dns = require "rspamd_dns"
+local logger = require "rspamd_logger"
+
+local config_path = rspamd_paths['CONFDIR'] .. '/rspamd.conf'
+local _r,err = rspamd_config:load_ucl(config_path)
+
+if not _r then
+ logger.errx('cannot parse %s: %s (r=%s)', config_path, err, _r)
+ os.exit(1)
+end
+
+_r,err = rspamd_config:parse_rcl({'logging', 'worker'})
+if not _r then
+ logger.errx('cannot process %s: %s (r=%s)', config_path, err, _r)
+ os.exit(1)
+end
+
+rspamd_config:init_subsystem('dns', rspamadm_ev_base)
+
+
+local is_ok, results = rspamd_dns.request({
+ config = rspamd_config,
+ session = rspamadm_session,
+
+ type = 'txt',
+ name = 'test._domainkey.example.com',
+ -- name = '_dmarc.google.com',
+ })
+
+print(is_ok, results[1])
diff --git a/test/functional/lua/rspamadm/test_message_callback.lua b/test/functional/lua/rspamadm/test_message_callback.lua
new file mode 100644
index 0000000..6be512a
--- /dev/null
+++ b/test/functional/lua/rspamadm/test_message_callback.lua
@@ -0,0 +1,5 @@
+function message_callback(task)
+ local parts = task:get_text_parts()
+ print("n parts = " .. tostring(#parts))
+ return 1,2,4,6
+end
diff --git a/test/functional/lua/rspamadm/test_redis_client.lua b/test/functional/lua/rspamadm/test_redis_client.lua
new file mode 100644
index 0000000..a7428a8
--- /dev/null
+++ b/test/functional/lua/rspamadm/test_redis_client.lua
@@ -0,0 +1,40 @@
+local logger = require "rspamd_logger"
+local redis = require "lua_redis"
+local upstream_list = require "rspamd_upstream_list"
+
+local upstreams_write = upstream_list.create('127.0.0.1', 56379)
+local upstreams_read = upstream_list.create('127.0.0.1', 56379)
+
+local is_ok, connection = redis.redis_connect_sync({
+ write_servers = upstreams_write,
+ read_servers = upstreams_read,
+-- config = rspamd_config,
+-- ev_base = rspamadm_ev_base,
+-- session = rspamadm_session,
+ timeout = 2
+})
+
+
+local lua_script = [[
+local f = function() end
+--for k = 1,100000000 do
+-- for i=1,100000000 do
+-- f()
+-- end
+--end
+return "hello from lua on redis"
+]]
+
+local a,b = connection:add_cmd('EVAL', {lua_script, 0})
+local is_ok,ver = connection:exec()
+
+print(is_ok, ver)
+
+--[[
+a,b = connection:add_cmd('EVAL', {lua_script, 0})
+print(a,b)
+
+is_ok,ver = connection:exec()
+
+print(is_ok, ver)
+]] \ No newline at end of file
diff --git a/test/functional/lua/rspamadm/test_tcp_client.lua b/test/functional/lua/rspamadm/test_tcp_client.lua
new file mode 100644
index 0000000..eb103db
--- /dev/null
+++ b/test/functional/lua/rspamadm/test_tcp_client.lua
@@ -0,0 +1,64 @@
+local logger = require "rspamd_logger"
+local tcp_sync = require "lua_tcp_sync"
+
+local is_ok, connection = tcp_sync.connect {
+ config = rspamd_config,
+ ev_base = rspamadm_ev_base,
+ session = rspamadm_session,
+ host = '127.0.0.1',
+ timeout = 20,
+ port = 18080,
+}
+if not is_ok then
+ logger.errx(rspamd_config, 'connect error: %1', connection)
+ return
+end
+local err
+is_ok, err = connection:write(string.format('POST /request HTTP/1.1\r\nConnection: close\r\n\r\n'))
+
+logger.info('write %1, %2', is_ok, err)
+if not is_ok then
+ logger.errx(rspamd_config, 'write error: %1', err)
+ return
+end
+
+local content_length, content
+
+while true do
+ local header_line
+ is_ok, header_line = connection:read_until("\r\n")
+ if not is_ok then
+ logger.errx(rspamd_config, 'failed to get header: %1', header_line)
+ return
+ end
+
+ if header_line == "" then
+ logger.info('headers done')
+ break
+ end
+
+ local value
+ local header = header_line:gsub("([%w-]+): (.*)",
+ function (h, v) value = v; return h:lower() end)
+
+ logger.info('parsed header: %1 -> "%2"', header, value)
+
+ if header == "content-length" then
+ content_length = tonumber(value)
+ end
+
+end
+
+if content_length then
+ is_ok, content = connection:read_bytes(content_length)
+ if is_ok then
+ end
+else
+ is_ok, content = connection:read_until_eof()
+ if is_ok then
+ end
+end
+logger.info('(is_ok: %1) content [%2 bytes] %3', is_ok, content_length, content)
+
+
+print(content)
diff --git a/test/functional/lua/rspamadm/test_verbose.lua b/test/functional/lua/rspamadm/test_verbose.lua
new file mode 100644
index 0000000..4470c63
--- /dev/null
+++ b/test/functional/lua/rspamadm/test_verbose.lua
@@ -0,0 +1,3 @@
+local rspamd_logger = require "rspamd_logger"
+
+rspamd_logger.info(rspamd_config, "hello world")
diff --git a/test/functional/lua/selector_test.lua b/test/functional/lua/selector_test.lua
new file mode 100644
index 0000000..dd52ee3
--- /dev/null
+++ b/test/functional/lua/selector_test.lua
@@ -0,0 +1,23 @@
+local lua_selectors = require 'lua_selectors'
+local rspamd_text = require 'rspamd_text'
+
+rspamd_config:register_re_selector('test', 'user.lower;header(Subject).lower', ' ')
+
+config['regexp']['LUA_SELECTOR_RE'] = {
+ re = 'test=/^test@user\\.com some subject$/{selector}',
+ score = 100500,
+}
+
+lua_selectors.register_extractor(rspamd_config, 'some_rspamd_text', {
+ get_value = function()
+ return {rspamd_text.fromstring('hello'), rspamd_text.fromstring('world')}, 'string_list'
+ end,
+ description = 'Return some rspamd_texts',
+})
+
+rspamd_config:register_re_selector('some_rspamd_text_re', 'some_rspamd_text', ' ')
+
+config['regexp']['RSPAMD_TEXT_SELECTOR'] = {
+ re = 'some_rspamd_text_re=/^hello$/{selector}',
+ score = 1,
+}
diff --git a/test/functional/lua/settings.lua b/test/functional/lua/settings.lua
new file mode 100644
index 0000000..384c68e
--- /dev/null
+++ b/test/functional/lua/settings.lua
@@ -0,0 +1,68 @@
+rspamd_config:register_symbol({
+ name = 'SIMPLE_PRE',
+ score = 1.0,
+ priority = 9, -- after settings
+ group = 'a',
+ type = 'prefilter',
+ callback = function()
+ return true, 'Fires always'
+ end
+})
+
+rspamd_config:register_symbol({
+ name = 'SIMPLE_POST',
+ score = 1.0,
+ type = 'postfilter',
+ group = 'c',
+ callback = function()
+ return true, 'Fires always'
+ end
+})
+
+local id = rspamd_config:register_symbol({
+ name = 'SIMPLE_TEST',
+ score = 1.0,
+ group = 'b',
+ callback = function(task)
+ task:insert_result('SIMPLE_VIRTUAL', 1.0)
+ task:insert_result('SIMPLE_VIRTUAL1', 1.0)
+ return true, 'Fires always'
+ end
+})
+
+rspamd_config:register_symbol({
+ name = 'SIMPLE_VIRTUAL',
+ type = 'virtual',
+ score = 1.0,
+ group = 'vg',
+ parent = id,
+})
+
+rspamd_config:register_symbol({
+ name = 'SIMPLE_VIRTUAL1',
+ type = 'virtual',
+ forbidden_ids = 'id_virtual,id_virtual_group',
+ allowed_ids = 'id_virtual1',
+ score = 1.0,
+ group = 'vg',
+ parent = id,
+})
+
+id = rspamd_config:register_symbol({
+ name = 'DEP_REAL',
+ callback = function(task)
+ task:insert_result('DEP_VIRTUAL', 1.0)
+ return true
+ end,
+ score = 1.0,
+})
+
+rspamd_config:register_symbol({
+ name = 'DEP_VIRTUAL',
+ parent = id,
+ type = 'virtual',
+ allowed_ids = 'id_virtual1',
+ score = 1.0,
+})
+
+rspamd_config:register_dependency('DEP_VIRTUAL', 'EXPLICIT_VIRTUAL1') \ No newline at end of file
diff --git a/test/functional/lua/simple.lua b/test/functional/lua/simple.lua
new file mode 100644
index 0000000..22ecde8
--- /dev/null
+++ b/test/functional/lua/simple.lua
@@ -0,0 +1,7 @@
+rspamd_config:register_symbol({
+ name = 'SIMPLE_TEST',
+ score = 1.0,
+ callback = function()
+ return true, 'Fires always'
+ end
+})
diff --git a/test/functional/lua/tcp.lua b/test/functional/lua/tcp.lua
new file mode 100644
index 0000000..e5c765b
--- /dev/null
+++ b/test/functional/lua/tcp.lua
@@ -0,0 +1,279 @@
+--[[[
+-- Just a test for TCP API
+--]]
+
+local rspamd_tcp = require "rspamd_tcp"
+local logger = require "rspamd_logger"
+local tcp_sync = require "lua_tcp_sync"
+
+-- [[ old fashioned callback api ]]
+local function http_simple_tcp_async_symbol(task)
+ logger.errx(task, 'http_tcp_symbol: begin')
+ local function http_get_cb(err, data, conn)
+ logger.errx(task, 'http_get_cb: got reply: %s, error: %s, conn: %s', data, err, conn)
+ task:insert_result('HTTP_ASYNC_RESPONSE_2', 1.0, data)
+ end
+ local function http_read_post_cb(err, conn)
+ logger.errx(task, 'http_read_post_cb: write done: error: %s, conn: %s', err, conn)
+ conn:add_read(http_get_cb)
+ end
+ local function http_read_cb(err, data, conn)
+ logger.errx(task, 'http_read_cb: got reply: %s, error: %s, conn: %s', data, err, conn)
+ conn:add_write(http_read_post_cb, "POST /request2 HTTP/1.1\r\n\r\n")
+ task:insert_result('HTTP_ASYNC_RESPONSE', 1.0, data or err)
+ end
+ rspamd_tcp:request({
+ task = task,
+ callback = http_read_cb,
+ host = '127.0.0.1',
+ data = {'GET /request HTTP/1.1\r\nConnection: keep-alive\r\n\r\n'},
+ read = true,
+ port = 18080,
+ })
+end
+
+local function http_simple_tcp_ssl_symbol(task)
+ logger.errx(task, 'ssl_tcp_symbol: begin')
+ local function ssl_get_cb(err, data, conn)
+ logger.errx(task, 'ssl_get_cb: got reply: %s, error: %s, conn: %s', data, err, conn)
+ task:insert_result('TCP_SSL_RESPONSE_2', 1.0, tostring(data):gsub('%s', ''))
+ end
+ local function ssl_read_post_cb(err, conn)
+ logger.errx(task, 'ssl_read_post_cb: write done: error: %s, conn: %s', err, conn)
+ conn:add_read(ssl_get_cb)
+ end
+ local function ssl_read_cb(err, data, conn)
+ logger.errx(task, 'ssl_read_cb: got reply: %s, error: %s, conn: %s', data, err, conn)
+ conn:add_write(ssl_read_post_cb, "test2\n")
+ task:insert_result('TCP_SSL_RESPONSE', 1.0, tostring(data):gsub('%s', ''))
+ end
+ rspamd_tcp:request({
+ task = task,
+ callback = ssl_read_cb,
+ host = '127.0.0.1',
+ data = {'test\n'},
+ read = true,
+ ssl = true,
+ ssl_noverify = true,
+ port = 18081,
+ })
+end
+
+local function http_large_tcp_ssl_symbol(task)
+ local data = {}
+
+ local function ssl_get_cb(err, rep, conn)
+ logger.errx(task, 'ssl_get_cb: got reply: %s, error: %s, conn: %s', rep, err, conn)
+ task:insert_result('TCP_SSL_LARGE_2', 1.0)
+ end
+ local function ssl_read_post_cb(err, conn)
+ logger.errx(task, 'ssl_large_read_post_cb: write done: error: %s, conn: %s', err, conn)
+ conn:add_read(ssl_get_cb)
+ end
+ local function ssl_read_cb(err, rep, conn)
+ logger.errx(task, 'ssl_large_read_cb: got reply: %s, error: %s, conn: %s', rep, err, conn)
+ conn:add_write(ssl_read_post_cb, 'foo\n')
+ task:insert_result('TCP_SSL_LARGE', 1.0)
+ end
+
+ if task:get_queue_id() == 'SSL Large TCP request' then
+ logger.errx(task, 'ssl_large_tcp_symbol: begin')
+ for i = 1,2 do
+ local st = {}
+ for j=1,300000 do
+ st[j] = 't'
+ end
+ data[i] = table.concat(st)
+ end
+ data[#data + 1] = '\n'
+
+ rspamd_tcp:request({
+ task = task,
+ callback = ssl_read_cb,
+ host = '127.0.0.1',
+ data = data,
+ read = true,
+ ssl = true,
+ stop_pattern = '\n',
+ ssl_noverify = true,
+ port = 18081,
+ timeout = 20,
+ })
+ else
+ logger.errx(task, 'ssl_large_tcp_symbol: skip')
+ end
+end
+
+local function http_simple_tcp_symbol(task)
+ logger.errx(task, 'connect_sync, before')
+
+ local err
+ local is_ok, connection = tcp_sync.connect {
+ task = task,
+ host = '127.0.0.1',
+ timeout = 20,
+ port = 18080,
+ }
+
+ if not is_ok then
+ task:insert_result('HTTP_SYNC_WRITE_ERROR', 1.0, connection)
+ logger.errx(task, 'write error: %1', connection)
+ end
+
+ logger.errx(task, 'connect_sync %1, %2', is_ok, tostring(connection))
+
+ is_ok, err = connection:write('GET /request HTTP/1.1\r\nConnection: keep-alive\r\n\r\n')
+
+ logger.errx(task, 'write %1, %2', is_ok, err)
+ if not is_ok then
+ task:insert_result('HTTP_SYNC_WRITE_ERROR', 1.0, err)
+ logger.errx(task, 'write error: %1', err)
+ end
+
+ local data
+ local got_content = ''
+ repeat
+ is_ok, data = connection:read_once();
+ logger.errx(task, 'read_once: is_ok: %1, data: %2', is_ok, data)
+ if not is_ok then
+ task:insert_result('HTTP_SYNC_ERROR', 1.0, data)
+ return
+ else
+ got_content = got_content .. data
+ end
+ if got_content:find('hello') then
+ -- dummy_http.py responds with either hello world or hello post
+ break
+ end
+ until false
+
+ task:insert_result('HTTP_SYNC_RESPONSE', 1.0, got_content)
+
+ is_ok, err = connection:write("POST /request HTTP/1.1\r\n\r\n")
+ logger.errx(task, 'write[2] %1, %2', is_ok, err)
+
+ got_content = ''
+ repeat
+ is_ok, data = connection:read_once();
+ logger.errx(task, 'read_once[2]: is_ok %1, data: %2', is_ok, data)
+ if not is_ok then
+ task:insert_result('HTTP_SYNC_ERROR_2', 1.0, data)
+ return
+ else
+ got_content = got_content .. data
+ end
+ if got_content:find('hello') then
+ break
+ end
+ until false
+
+ task:insert_result('HTTP_SYNC_RESPONSE_2', 1.0, data)
+
+ connection:close()
+end
+
+local function http_tcp_symbol(task)
+ local url = tostring(task:get_request_header('url'))
+ local method = tostring(task:get_request_header('method'))
+
+ if url == 'nil' then
+ return
+ end
+
+ local err
+ local is_ok, connection = tcp_sync.connect {
+ task = task,
+ host = '127.0.0.1',
+ timeout = 20,
+ port = 18080,
+ }
+
+ logger.errx(task, 'connect_sync %1, %2', is_ok, tostring(connection))
+ if not is_ok then
+ logger.errx(task, 'connect error: %1', connection)
+ return
+ end
+
+ is_ok, err = connection:write(string.format('%s %s HTTP/1.1\r\nConnection: close\r\n\r\n', method:upper(), url))
+
+ logger.errx(task, 'write %1, %2', is_ok, err)
+ if not is_ok then
+ logger.errx(task, 'write error: %1', err)
+ return
+ end
+
+ local content_length, content
+
+ while true do
+ local header_line
+ is_ok, header_line = connection:read_until("\r\n")
+ if not is_ok then
+ logger.errx(task, 'failed to get header: %1', header_line)
+ return
+ end
+
+ if header_line == "" then
+ logger.errx(task, 'headers done')
+ break
+ end
+
+ local value
+ local header = header_line:gsub("([%w-]+): (.*)",
+ function (h, v) value = v; return h:lower() end)
+
+ logger.errx(task, 'parsed header: %1 -> "%2"', header, value)
+
+ if header == "content-length" then
+ content_length = tonumber(value)
+ end
+
+ end
+
+ if content_length then
+ is_ok, content = connection:read_bytes(content_length)
+ if is_ok then
+ task:insert_result('HTTP_SYNC_CONTENT_' .. method, 1.0, content)
+ end
+ else
+ is_ok, content = connection:read_until_eof()
+ if is_ok then
+ task:insert_result('HTTP_SYNC_EOF_' .. method, 1.0, content)
+ end
+ end
+ logger.errx(task, '(is_ok: %1) content [%2 bytes] %3', is_ok, content_length, content)
+end
+
+rspamd_config:register_symbol({
+ name = 'SIMPLE_TCP_ASYNC_TEST',
+ score = 1.0,
+ callback = http_simple_tcp_async_symbol,
+ no_squeeze = true
+})
+rspamd_config:register_symbol({
+ name = 'SIMPLE_TCP_ASYNC_SSL_TEST',
+ score = 1.0,
+ callback = http_simple_tcp_ssl_symbol,
+ no_squeeze = true
+})
+rspamd_config:register_symbol({
+ name = 'LARGE_TCP_ASYNC_SSL_TEST',
+ score = 1.0,
+ callback = http_large_tcp_ssl_symbol,
+ no_squeeze = true
+})
+rspamd_config:register_symbol({
+ name = 'SIMPLE_TCP_TEST',
+ score = 1.0,
+ callback = http_simple_tcp_symbol,
+ no_squeeze = true,
+ flags = 'coro',
+})
+
+rspamd_config:register_symbol({
+ name = 'HTTP_TCP_TEST',
+ score = 1.0,
+ callback = http_tcp_symbol,
+ no_squeeze = true,
+ flags = 'coro',
+})
+-- ]]
diff --git a/test/functional/lua/test_coverage.lua b/test/functional/lua/test_coverage.lua
new file mode 100644
index 0000000..68f2545
--- /dev/null
+++ b/test/functional/lua/test_coverage.lua
@@ -0,0 +1,46 @@
+--[[
+-- This should be the very first file executed during a test
+-- otherwise coverage will be partly missed
+--]]
+local logger = require "rspamd_logger"
+local mempool = require "rspamd_mempool"
+local loaded, luacov = pcall(require, 'luacov.runner')
+if not loaded then
+ logger.errx('luacov is not loaded, will not collect coverage')
+ return
+end
+
+luacov.init()
+
+local pool = mempool.create()
+-- we don't need the pool, we need userdata to put __gc() on it
+-- __gc() is not called for tables, that't why there is such trick
+-- so, we are free to clean memory, let's do this :)
+pool:destroy()
+
+local woker_name
+
+rspamd_config:add_on_load(function(cfg, ev_base, worker)
+ woker_name = worker:get_name()
+ local stats_path = rspamd_paths["DBDIR"] .. '/' .. woker_name .. '.luacov.stats.out'
+ local config = luacov.load_config()
+ config.statsfile = stats_path
+end)
+
+-- use global variable to prevent the object from being GC'ed too early
+__GLOBAL_COVERAGE_WATCHDOG = {pool = pool}
+
+local mt = {
+ __gc = function()
+ --[[
+ -- We could've used finish_script but in that case some coverage would be missed:
+ -- pool destructors are executed after finish_scripts (when Lua state is terminated and that's
+ -- how we can collect coverage of cove executed there
+ --]]
+ if woker_name then
+ luacov.shutdown()
+ end
+ end
+}
+
+debug.setmetatable(__GLOBAL_COVERAGE_WATCHDOG.pool, mt)
diff --git a/test/functional/lua/test_fname.lua b/test/functional/lua/test_fname.lua
new file mode 100644
index 0000000..ffa7bb9
--- /dev/null
+++ b/test/functional/lua/test_fname.lua
@@ -0,0 +1,12 @@
+rspamd_config.TEST_FNAME = {
+ callback = function(task)
+ local r = task:get_parts()
+ local fnames = {}
+ for _,rh in ipairs(r) do
+ if rh:get_filename() then
+ table.insert(fnames, rh:get_filename())
+ end
+ end
+ return true,1.0,fnames
+ end
+} \ No newline at end of file
diff --git a/test/functional/lua/tlds.lua b/test/functional/lua/tlds.lua
new file mode 100644
index 0000000..24836b3
--- /dev/null
+++ b/test/functional/lua/tlds.lua
@@ -0,0 +1,58 @@
+rspamd_config:register_symbol({
+ name = 'TEST_TLD',
+ score = 1.0,
+ callback = function()
+ local prefixes = {
+ '',
+ 'example.'
+ }
+ local test_domains = {
+ 'example.ac',
+ 'example.b.br',
+ 'example.co',
+ 'example.com',
+ 'example.co.za',
+ 'example.in.net',
+ 'example.star.kawasaki.jp',
+ 'example.net',
+ 'example.net.in',
+ 'example.star.nom.br',
+ 'example.org',
+ 'example.org.ac',
+ 'example.ru.com',
+ 'example.za.net',
+ 'example.za.org',
+ 'org.org.za',
+ }
+ local worry = {}
+ local rspamd_mempool = require 'rspamd_mempool'
+ local rspamd_url = require 'rspamd_url'
+ local rspamd_util = require 'rspamd_util'
+ local pool = rspamd_mempool.create()
+ for _, d in ipairs(test_domains) do
+ (function()
+ for _, p in ipairs(prefixes) do
+ local test = rspamd_util.get_tld(p .. d)
+ if (test ~= d) then
+ local opt = string.format('util.get_tld:p=%s;d=%s;got=%s', p, d, test)
+ table.insert(worry, opt)
+ return
+ end
+ local u = rspamd_url.create(pool, p .. d)
+ assert(u, "cannot parse string: " .. p .. d)
+ test = u:get_tld()
+ if (test ~= d) then
+ local opt = string.format('url.create:p=%s;d=%s;got=%s', p, d, test)
+ table.insert(worry, opt)
+ return
+ end
+ end
+ end)()
+ end
+ if (#worry == 0) then
+ return true, 1.0, "no worry"
+ else
+ return true, 1.0, worry
+ end
+ end
+})
diff --git a/test/functional/lua/udp.lua b/test/functional/lua/udp.lua
new file mode 100644
index 0000000..0ed4b15
--- /dev/null
+++ b/test/functional/lua/udp.lua
@@ -0,0 +1,81 @@
+--[[[
+-- Just a test for UDP API
+--]]
+
+local rspamd_udp = require "rspamd_udp"
+local logger = require "rspamd_logger"
+
+-- [[ old fashioned callback api ]]
+local function simple_udp_async_symbol(task)
+ logger.errx(task, 'udp_symbol: begin')
+ local function udp_cb(success, data)
+ logger.errx(task, 'udp_cb: got reply: %s', data)
+
+ if success then
+ task:insert_result('UDP_SUCCESS', 1.0, data)
+ else
+ task:insert_result('UDP_FAIL', 1.0, data)
+ end
+ end
+ rspamd_udp:sendto({
+ task = task,
+ callback = udp_cb,
+ host = '127.0.0.1',
+ data = {'hello', 'world'},
+ port = 5005,
+ })
+end
+
+rspamd_config:register_symbol({
+ name = 'UDP_SUCCESS',
+ score = 0.0,
+ callback = simple_udp_async_symbol,
+})
+
+local function send_only_udp(task)
+ logger.errx(task, 'udp_symbol_sendonly: begin')
+ if rspamd_udp:sendto({
+ task = task,
+ host = '127.0.0.1',
+ data = {'hoho'},
+ port = 5005,
+ }) then
+
+ task:insert_result('UDP_SENDTO', 1.0)
+ end
+end
+
+rspamd_config:register_symbol({
+ name = 'UDP_SENDTO',
+ score = 0.0,
+ callback = send_only_udp,
+})
+
+local function udp_failed_cb(task)
+ logger.errx(task, 'udp_failed_cb: begin')
+ local function udp_cb(success, data)
+ logger.errx(task, 'udp_failed_cb: got reply: %s', data)
+
+ if success then
+ task:insert_result('UDP_SUCCESS', 1.0, data)
+ else
+ task:insert_result('UDP_FAIL', 1.0, data)
+ end
+ end
+ rspamd_udp:sendto({
+ task = task,
+ callback = udp_cb,
+ host = '127.0.0.1',
+ data = {'hello', 'world'},
+ port = 5006,
+ retransmits = 2,
+ timeout = 0.1,
+ })
+end
+
+rspamd_config:register_symbol({
+ name = 'UDP_FAIL',
+ score = 0.0,
+ callback = udp_failed_cb,
+})
+-- ]]