# vi:ft= et ts=4 sw=4

use lib 't/lib';
use Test::Resty;

plan tests => (blocks() * 3 - 2);

run_tests();

__DATA__

=== TEST 1: bad --nginx value
--- opts: --nginx=/tmp/no/such/file
--- src
print("arg 0: ", arg[0])
print("arg 1: ", arg[1])
print("arg 2: ", arg[2])
print("arg 3: ", arg[3])

--- out
--- err_like chop
(?:ERROR: failed to run command|Can't exec|valgrind:) "?/tmp/no/such/file.*?"?: No such file or directory
--- ret: 2



=== TEST 2: -V
--- opts: -V
--- out
--- err_like eval
qr/^resty \d+.\d{2}
nginx version: /s
--- ret: 0



=== TEST 3: -h
--- opts: -h
--- out
resty [options] [lua-file [args]]

Options:
    -c NUM              Set maximal connection count (default: 64).
    -e PROG             Run the inlined Lua code in "prog".

    --errlog-level LEVEL
                        Set nginx error_log level.
                        Can be debug, info, notice, warn, error, crit, alert,
                        or emerg.

    --gdb               Use GDB to run the underlying nginx C process.

    --gdb-opts OPTS     Pass extra command-line options to GDB.

    --help              Print this help.

    --http-conf CONF    Specifies nginx.conf snippet inserted into the http {}
                        configuration block (multiple instances are supported).

    --http-include PATH Include the specified file in the nginx http
                        configuration block (multiple instances are supported).

    -I DIR              Add dir to the search paths for Lua libraries.

    -j dump             Use LuaJIT's jit.dump module to output detailed info of
                        the traces generated by the JIT compiler.

    -j off              Turn off the LuaJIT JIT compiler.

    -j v                Use LuaJIT's jit.v module to output brief info of the
                        traces generated by the JIT compiler.

    -l LIB              Require library "lib".

    --load-module MOD   Specifies nginx module wanted to be loaded.
                        (multiple instances are supported).

    --main-conf CONF    Specifies nginx.conf snippet inserted into the nginx
                        main {} configuration block (multiple instances are
                        supported).

    --main-include PATH Include the specified file in the nginx main
                        configuration block (multiple instances are supported).

    --nginx             Specify the nginx path (this option might be removed
                        in the future).

    --no-stream         Disable the stream {} configuration in auto-generated
                        nginx.conf.

    --ns IP             Specify a custom name server (multiple instances are
                        supported).

    --resolve-ipv6      Make the nginx resolver lookup both IPv4 and IPv6
                        addresses.

    --rr                Use Mozilla rr to record the execution of the
                        underlying nginx C process.

    --shdict 'NAME SIZE'
                        Create the specified lua shared dicts in the http
                        configuration block (multiple instances are supported).

    --stap
                        Use sysetmtap to run the underlying nginx C process.

    --stap-opts OPTS
                        Pass extra systemtap command line options.

    --user-runner CMD   Use CMD as user runner for the underlying nginx process.

    -V                  Print version numbers and nginx configurations.

    --dump-nginx-conf   Print the generated nginx configuration file,
                        without running nginx not the Lua code.

    --valgrind          Use valgrind to run nginx.

    --valgrind-opts OPTS
                        Pass extra options to valgrind.

For bug reporting instructions, please see:

    <https://openresty.org/en/community.html>

Copyright (C) Yichun Zhang (agentzh). All rights reserved.
--- err
--- ret: 0



=== TEST 4: bad --http-include value
--- opts: --http-include=/tmp/no/such/file
--- src
--- out
--- err_like chop
ERROR: could not find http include file '/tmp/no/such/file'
--- ret: 2



=== TEST 5: bad --main-include value
--- opts: --main-include=/tmp/no/such/file
--- src
--- out
--- err_like chop
ERROR: could not find main include file '/tmp/no/such/file'
--- ret: 2



=== TEST 6: bad --main-include file content
--- opts: --main-include=/tmp/a.conf
--- user_files
>>> /tmp/a.conf
lua_shared_dict dogs 1m;
--- src
local dogs = ngx.shared.dogs
dogs:set("Tom", 32)
print(dogs:get("Tom"))
--- out
--- err
nginx: [emerg] "lua_shared_dict" directive is not allowed here in /tmp/a.conf:1
--- ret: 1



=== TEST 7: good --http-include file content
--- opts: --http-include=/tmp/a.conf
--- user_files
>>> /tmp/a.conf
lua_shared_dict dogs 1m;
--- src
local dogs = ngx.shared.dogs
dogs:set("Tom", 32)
print(dogs:get("Tom"))
--- out
32
--- err



=== TEST 8: multiple --http-include options
--- opts: --http-include=/tmp/a.conf --http-include=/tmp/b.conf
--- user_files
>>> /tmp/a.conf
lua_shared_dict dogs 1m;
>>> /tmp/b.conf
lua_shared_dict cats 1m;
--- src
local dogs = ngx.shared.dogs
local cats = ngx.shared.cats
dogs:set("Tom", 32)
cats:set("Tom", 12)
print("dog: ", dogs:get("Tom"))
print("cat: ", cats:get("Tom"))
--- out
dog: 32
cat: 12
--- err



=== TEST 9: multiple --main-include options
--- opts: --main-include=/tmp/a.conf --main-include=/tmp/b.conf
--- user_files
>>> /tmp/a.conf
env foo=32;
>>> /tmp/b.conf
env bar=56;
--- src
print("foo: ", os.getenv("foo"))
print("bar: ", os.getenv("bar"))
--- out
foo: 32
bar: 56
--- err



=== TEST 10: good --http-include file content (relative path)
--- opts: --http-include=t/tmp/a.conf
--- user_files
>>> a.conf
lua_shared_dict dogs 1m;
--- src
local dogs = ngx.shared.dogs
dogs:set("Tom", 32)
print(dogs:get("Tom"))
--- out
32
--- err



=== TEST 11: --http-include + -e
--- opts: --http-include=t/tmp/a.conf -e 'local dogs = ngx.shared.dogs dogs:set("Tom", 32) print(dogs:get("Tom"))'
--- user_files
>>> a.conf
lua_shared_dict dogs 1m;
--- out
32
--- err



=== TEST 12: good --main-include option value (relative path)
--- opts: --main-include=/tmp/a.conf --main-include=/tmp/b.conf
--- user_files
>>> /tmp/a.conf
env foo=32;
>>> /tmp/b.conf
env bar=56;
--- src
print("foo: ", os.getenv("foo"))
print("bar: ", os.getenv("bar"))
--- out
foo: 32
bar: 56
--- err



=== TEST 13: --shdict option
--- opts: '--shdict=dogs 12k'
--- src
local dogs = ngx.shared.dogs
dogs:set("Tom", 32)
print(dogs:get("Tom"))
--- out
32
--- err



=== TEST 14: multiple --shdict option
--- opts: '--shdict=dogs 1m' '--shdict=cats 1m'
--- src
local dogs = ngx.shared.dogs
dogs:set("Tom", 32)
print(dogs:get("Tom"))

local cats = ngx.shared.cats
dogs:set("Max", 64)
print(dogs:get("Max"))
--- out
32
64
--- err



=== TEST 15: --shdict option missing size
--- opts: '--shdict=dogs'
--- out
--- err
ERROR: invalid --shdict option value: dogs
  (expecting NAME SIZE)
--- ret: 255



=== TEST 16: --shdict option case-insensitive size (1/2)
--- opts: '--shdict=dogs 1m'
--- src
local dogs = ngx.shared.dogs
dogs:set("Tom", 32)
print(dogs:get("Tom"))
--- out
32
--- err



=== TEST 17: --shdict option case-insensitive size (2/2)
--- opts: '--shdict=dogs 1M'
--- src
local dogs = ngx.shared.dogs
dogs:set("Tom", 32)
print(dogs:get("Tom"))
--- out
32
--- err



=== TEST 18: --shdict option permissive format
--- opts: '--shdict=cats_dogs   20000'
--- src
local dict = ngx.shared.cats_dogs
dict:set("Tom", 32)
print(dict:get("Tom"))
--- out
32
--- err



=== TEST 19: multiple -e options
--- opts: -e 'print(1)' -e 'print(2)'
--- out
1
2
--- err



=== TEST 20: multiple -e options with file
--- opts: -e 'print(1)' -e 'print(2)'
--- src
print(3)
--- out
1
2
3
--- err



=== TEST 21: multiple -e options with single quotes
--- opts: -e "print('1')" -e 'print(2)'
--- out
1
2
--- err



=== TEST 22: resolver has ipv6=off by default
--- src
local conf = ngx.config.prefix() .. "conf/nginx.conf"
local file = assert(io.open(conf, "r"))
print(file:read "*a")
file:close()
--- out_like
resolver [\s\S]* ipv6=off;
--- err



=== TEST 23: --resolve-ipv6 flag enables ipv6 resolution
--- opts: --resolve-ipv6
--- src
local conf = ngx.config.prefix() .. "conf/nginx.conf"
local file = assert(io.open(conf, "r"))
print(file:read "*a")
file:close()
--- out_not_like
resolver [\s\S]* ipv6=off;
--- err



=== TEST 24: specified name server overrides system name servers
--- opts: --ns 127.0.0.1
--- src
local conf = ngx.config.prefix() .. "conf/nginx.conf"
local file = assert(io.open(conf, "r"))
print(file:read "*a")
file:close()
--- out_like
resolver 127.0.0.1 ipv6=off;
--- err



=== TEST 25: multiple ns flags can be specified
--- opts: --ns 127.0.0.1 --ns 127.0.0.2
--- src
local conf = ngx.config.prefix() .. "conf/nginx.conf"
local file = assert(io.open(conf, "r"))
print(file:read "*a")
file:close()
--- out_like
resolver 127.0.0.1 127.0.0.2 ipv6=off;
--- err



=== TEST 26: ns flags can be specified in both ipv4 and ipv6 formats
--- opts: --ns 127.0.0.1 --ns 127.0.0.2 --ns [::1] --ns [2003:dead:beef:4dad:23:46:bb:101]
--- src
local conf = ngx.config.prefix() .. "conf/nginx.conf"
local file = assert(io.open(conf, "r"))
print(file:read "*a")
file:close()
print(str)
--- out_like
resolver 127.0.0.1 127.0.0.2 \[::1\] \[2003:dead:beef:4dad:23:46:bb:101\] ipv6=off;
--- err



=== TEST 27: ns flag works with resolve ipv6 flag
--- opts: --ns 127.0.0.1 --resolve-ipv6
--- src
local conf = ngx.config.prefix() .. "conf/nginx.conf"
local file = assert(io.open(conf, "r"))
print(file:read "*a")
file:close()
print(str)
--- out_like
resolver 127.0.0.1;
--- err



=== TEST 28: returns error message on invalid name server ip
--- opts: --ns invalid
--- src
--- out
--- err
ERROR: Invalid --ns option value: invalid
  (expecting an IP address)
--- ret: 255



=== TEST 29: --http-conf
--- opts: --http-conf 'lua_shared_dict dogs 1m;'
--- src
ngx.say(ngx.shared.dogs and "yes" or "no")
--- out
yes
--- err



=== TEST 30: --valgrind & --gdb
--- opts: --valgrind --gdb
--- src
print("hi")
--- out
--- err
ERROR: options --gdb and --valgrind cannot be specified at the same time.
--- ret: 25



=== TEST 31: --gdb & --rr
--- opts: --gdb --rr
--- src
print("hi")
--- out
--- err
ERROR: options --gdb and --rr cannot be specified at the same time.
--- ret: 25



=== TEST 32: --rr & --valgrind
--- opts: --rr --valgrind
--- src
print("hi")
--- out
--- err
ERROR: options --rr and --valgrind cannot be specified at the same time.
--- ret: 25



=== TEST 33: --errrlog-level debug
--- opts: --errlog-level debug
--- src
ngx.log(ngx.DEBUG, 'debug message')
--- out
--- err_like
1: debug message



=== TEST 34: -l
--- opts: -l t.tmp.testfile
--- user_files
>>> testfile.lua
print(1)
--- out
1
--- err



=== TEST 35: multiple -l
--- opts: -l t.tmp.testfile -l t.tmp.otherfile
--- user_files
>>> testfile.lua
print(1)
>>> otherfile.lua
print(2)
--- out
1
2
--- err



=== TEST 36: -l with -e
--- opts: -e 'print(1)' -l t.tmp.testfile
--- user_files
>>> testfile.lua
print(2)
--- out
1
2
--- err



=== TEST 37: multiple -l with -e
--- opts: -e print(1) -l t.tmp.testfile -e print(3) -l t.tmp.otherfile
--- user_files
>>> testfile.lua
print(2)
>>> otherfile.lua
print(4)
--- out
1
2
3
4
--- err



=== TEST 38: -l with file
--- opts: -l t.tmp.testfile
--- user_files
>>> testfile.lua
print(1)
--- src
print(2)
--- out
1
2
--- err



=== TEST 39: bad -l value
--- opts: -l missing
--- out
--- err_like chop
module 'missing' not found
--- ret: 1



=== TEST 40: -l is quoted
--- opts: -l t.tmp.testfile.one\'two\"three
--- out
--- err_like chop
module 't.tmp.testfile.one'two"three' not found
--- ret: 1



=== TEST 41: -j v
--- opts: -j v -e 'for i = 1, 200 do end'
--- out
--- err
[TRACE   1 (command line -e):1 loop]



=== TEST 42: -j dump
--- opts: -j dump -e 'for i = 1, 200 do end'
--- out_like chop
TRACE 1 IR.*? TRACE 1 mcode
--- err



=== TEST 43: -j off
--- opts: -j off -e 'require "jit.v".on() for i = 1, 200 do end'
--- out
--- err



=== TEST 44: -I should include *.ljbc and *.lua
--- opts: -I /tmp -e 'print(package.path)'
--- out_like chop
\A/tmp/\?\.ljbc;/tmp/\?\.lua;
--- err



=== TEST 45: --gdb-opts
--- opts: --gdb-opts="--nx -batch -ex 'b main' -ex run -ex bt -ex 'b lj_cf_io_method_write' -ex c -ex bt"
--- src
io.stderr:write("hello world!~~\n")
--- out_like
^Breakpoint 2, lj_cf_io_method_write \(L=0x[0-9a-f]+\) at lib_io\.c:\d+
.*^\#0  lj_cf_io_method_write \(L=0x[0-9a-f]+\) at lib_io\.c:\d+



=== TEST 46: --gdb-opts (leading and trailing spaces)
--- opts: --gdb-opts=" --nx -batch -ex 'b main' -ex run -ex bt -ex 'b lj_cf_io_method_write' -ex c -ex bt  "
--- src
io.stderr:write("hello world!~~\n")
--- out_like
^Breakpoint 2, lj_cf_io_method_write \(L=0x[0-9a-f]+\) at lib_io\.c:\d+
.*^\#0  lj_cf_io_method_write \(L=0x[0-9a-f]+\) at lib_io\.c:\d+



=== TEST 47: --main-conf=<option>
--- opts: --main-conf="debug_points bad;"
--- src
--- out
--- err_like eval
qr/nginx: \[emerg\] invalid value "bad" in/
--- ret: 1



=== TEST 48: --main-conf <option>
--- opts: --main-conf "debug_points bad;"
--- src
--- out
--- err_like eval
qr/nginx: \[emerg\] invalid value "bad" in/
--- ret: 1



=== TEST 49: -I nonexistent-dir
--- opts: -I nonexistent-dir -e 'ngx.say("hi")'
--- out
hi
--- err



=== TEST 50: --no-stream
--- opts: --no-stream -e 'ngx.say("hi")'
--- out
hi
--- err



=== TEST 51: --main-conf <option>
--- opts: --http-conf "server { listen 10049; location = /t { default_type text/plain; content_by_lua_block { ngx.orig_exit(200) } } }"
--- src
local sock = ngx.socket.tcp()
local port = 10049
sock:settimeout(500)
-- ngx.say("connected")

local function req(uri)
    assert(sock:connect("127.0.0.1", port))
    local req = "GET " .. uri .. " HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n\r\n"
    assert(sock:send(req))
    while true do
        local line, err, part = sock:receive()
        if line then
            -- ngx.say(line)

        else
            if err ~= "closed" then
                ngx.say("failed to receive a line: ", err, " [", part, "]")
            end
            break
        end
    end
end

req("/t")
print("OK")
--- out
OK
--- err
--- ret: 0

=== TEST 45: --dump-nginx-conf
--- opts: --dump-nginx-conf --errlog-level notice -e 'print(package.path)'
--- out_like chop
error_log stderr notice;
--- err
