from nose.tools import assert_equals
from gixy.parser.raw_parser import *


def test_directive():
    config = '''
access_log syslog:server=127.0.0.1,tag=nginx_sentry toolsformat;
user http;
internal;
set $foo "bar";
set $foo 'bar';
proxy_pass http://unix:/run/sock.socket;
rewrite ^/([a-zA-Z0-9]+)$ /$1/${arg_v}.pb break;
    server_name some.tld ~^(www\.)?podberi.(?:ru|com|ua)$
    ~^(www\.)?guru.yandex.ru$;
        '''

    expected = [
        ['access_log', 'syslog:server=127.0.0.1,tag=nginx_sentry', 'toolsformat'],
        ['user', 'http'],
        ['internal'],
        ['set', '$foo', 'bar'],
        ['set', '$foo', 'bar'],
        ['proxy_pass', 'http://unix:/run/sock.socket'],
        ['rewrite', '^/([a-zA-Z0-9]+)$', '/$1/${arg_v}.pb', 'break'],
        ['server_name', 'some.tld', '~^(www\.)?podberi.(?:ru|com|ua)$', '~^(www\.)?guru.yandex.ru$']
    ]

    assert_config(config, expected)


def test_block():
    config = '''
http {
}
        '''

    expected = [['http', [], []]]
    assert_config(config, expected)


def test_block_with_child():
    config = '''
http {
    gzip  on;
}
        '''

    expected = [['http', [], [['gzip', 'on']]]]
    assert_config(config, expected)


def test_location_simple():
    config = '''
location / {
}
location = /foo {
}
location ~ ^/bar {
}
location ~* ^/baz$ {
}
location ^~ ^/bazz {
}
# Whitespace may be omitted:((
location ~\.(js|css)$ {
}
        '''

    expected = [['location', ['/'], []],
                ['location', ['=', '/foo'], []],
                ['location', ['~', '^/bar'], []],
                ['location', ['~*', '^/baz$'], []],
                ['location', ['^~', '^/bazz'], []],
                ['Whitespace may be omitted:(('],
                ['location', ['~', '\.(js|css)$'], []]]

    assert_config(config, expected)


def test_quoted_strings():
    config = '''
some_sq '\\'la\\.\\/\\"';
some_dq '\\'la\\.\\/\\"';
        '''

    expected = [['some_sq', '\'la\\.\\/\"'],
                ['some_dq', '\'la\\.\\/\"']]

    assert_config(config, expected)


def test_location_child():
    config = '''
location = /foo {
     proxy_pass http://unix:/run/sock.socket;
}
        '''

    expected = [['location', ['=', '/foo'], [
        ['proxy_pass', 'http://unix:/run/sock.socket']
    ]]]
    assert_config(config, expected)


def test_nested_location():
    config = '''
location ~* ^/foo {
    location = /foo/bar {
        internal;
        proxy_pass http://any.yandex.ru;
    }

    location = /foo/baz {
        proxy_pass upstream;
    }
}
        '''

    expected = [['location', ['~*', '^/foo'], [
        ['location', ['=', '/foo/bar'], [
            ['internal'],
            ['proxy_pass', 'http://any.yandex.ru']
        ]],
        ['location', ['=', '/foo/baz'], [
            ['proxy_pass', 'upstream']
        ]],
    ]]]

    assert_config(config, expected)


def test_hash_block():
    config = '''
geo $geo {
    default        0;

    127.0.0.1      2;
    192.168.1.0/24 1;
    10.1.0.0/16    1;

    ::1            2;
    2001:0db8::/32 1;
}
        '''

    expected = [['geo', ['$geo'], [
        ['default', '0'],
        ['127.0.0.1', '2'],
        ['192.168.1.0/24', '1'],
        ['10.1.0.0/16', '1'],
        ['::1', '2'],
        ['2001:0db8::/32', '1']
    ]]]

    assert_config(config, expected)


def test_hash_block_in_location():
    config = '''
location /iphone/ {
    types {
      text/html  html htm shtml;
      application/json json;
      application/rss+xml  rss;
      text/vnd.sun.j2me.app-descriptor  jad;
    }
}
        '''

    expected = [['location', ['/iphone/'], [
        ['types', [], [
            ['text/html', 'html', 'htm', 'shtml'],
            ['application/json', 'json'],
            ['application/rss+xml', 'rss'],
            ['text/vnd.sun.j2me.app-descriptor', 'jad']
        ]],
    ]]]

    assert_config(config, expected)


def test_named_location():
    config = '''
location @foo {
    proxy_pass http://any.yandex.ru;
}
        '''

    expected = [['location', ['@foo'], [
        ['proxy_pass', 'http://any.yandex.ru']
    ]]]

    assert_config(config, expected)


def test_if():
    config = '''
# http://nginx.org/ru/docs/http/ngx_http_rewrite_module.html#if

if ($http_user_agent ~ MSIE) {
    rewrite ^(.*)$ /msie/$1 break;
}

if ($http_cookie ~* "id=([^;]+)(?:;|$)") {
    set $id $1;
}

if ($request_method = POST) {
    return 405;
}

if ($slow) {
    limit_rate 10k;
}

if ($invalid_referer) {
    return 403;
}

if (!-e "/var/data/$dataset") {
    return 503;
}

if ($https_or_slb = (by_\(sl\)b|https)) {
}

if ($host ~* (lori|rage2)\.yandex\.(ru|ua|com|com\.tr)) {
    set $x_frame_options ALLOW;
}

if ($request_filename ~* ^.*?/(\d+_)([^/]+)$) {
}

if ($http_user_agent ~* "^WordPress.*; verifying pingback") {
}

if ($foo = "BAR") { rewrite ^(.*)$ /bar; }
        '''

    expected = [
        ['http://nginx.org/ru/docs/http/ngx_http_rewrite_module.html#if'],
        ['if', ['$http_user_agent', '~', 'MSIE'], [
            ['rewrite', '^(.*)$', '/msie/$1', 'break']
        ]],
        ['if', ['$http_cookie', '~*', 'id=([^;]+)(?:;|$)'], [
            ['set', '$id', '$1']
        ]],
        ['if', ['$request_method', '=', 'POST'], [
            ['return', '405']
        ]],
        ['if', ['$slow'], [
            ['limit_rate', '10k']
        ]],
        ['if', ['$invalid_referer'], [
            ['return', '403']
        ]],
        ['if', ['!-e', '/var/data/$dataset'], [
            ['return', '503']
        ]],
        ['if', ['$https_or_slb', '=', '(by_\(sl\)b|https)'], [
        ]],
        ['if', ['$host', '~*', '(lori|rage2)\.yandex\.(ru|ua|com|com\.tr)'], [
            ['set', '$x_frame_options', 'ALLOW']
        ]],
        ['if', ['$request_filename', '~*', '^.*?/(\d+_)([^/]+)$'], [
        ]],
        ['if', ['$http_user_agent', '~*', '^WordPress.*; verifying pingback'], [
        ]],
        ['if', ['$foo', '=', 'BAR'], [
            ['rewrite', '^(.*)$', '/bar']
        ]]
    ]

    assert_config(config, expected)


def test_hash_block_map():
    config = '''
# http://nginx.org/ru/docs/http/ngx_http_map_module.html

map $http_host $name {
    hostnames;

    default       0;

    example.com   1;
    *.example.com 1;
    example.org   2;
    *.example.org 2;
    .example.net  3;
    wap.*         4;
}

map $http_user_agent $mobile {
    default       0;
    "~Opera Mini" 1;
}
        '''

    expected = [
        ['http://nginx.org/ru/docs/http/ngx_http_map_module.html'],
        ['map', ['$http_host', '$name'], [
            ['hostnames'],
            ['default', '0'],
            ['example.com', '1'],
            ['*.example.com', '1'],
            ['example.org', '2'],
            ['*.example.org', '2'],
            ['.example.net', '3'],
            ['wap.*', '4'],
        ]],
        ['map', ['$http_user_agent', '$mobile'], [
            ['default', '0'],
            ['~Opera Mini', '1'],
        ]]
    ]

    assert_config(config, expected)


def test_upstream():
    config = '''
# http://nginx.org/ru/docs/http/ngx_http_upstream_module.html

upstream backend {
    server backend1.example.com       weight=5;
    server backend2.example.com:8080;
    server unix:/tmp/backend3;

    server backup1.example.com:8080   backup;
    server backup2.example.com:8080   backup;
}

server {
    location / {
        proxy_pass http://backend;
    }
}
        '''

    expected = [
        ['http://nginx.org/ru/docs/http/ngx_http_upstream_module.html'],
        ['upstream', ['backend'], [
            ['server', 'backend1.example.com', 'weight=5'],
            ['server', 'backend2.example.com:8080'],
            ['server', 'unix:/tmp/backend3'],
            ['server', 'backup1.example.com:8080', 'backup'],
            ['server', 'backup2.example.com:8080', 'backup'],
        ]],
        ['server', [], [
            ['location', ['/'], [
                ['proxy_pass', 'http://backend']
            ]]
        ]]]

    assert_config(config, expected)


def test_issue_8():
    config = '''
# http://nginx.org/ru/docs/http/ngx_http_upstream_module.html
if ($http_referer ~* (\.(ru|ua|by|kz)/(pages/music|partners/|page-no-rights\.xml)) ) {
    set $temp A;
}
        '''

    expected = [
        ['http://nginx.org/ru/docs/http/ngx_http_upstream_module.html'],
        ['if', ['$http_referer', '~*', '(\.(ru|ua|by|kz)/(pages/music|partners/|page-no-rights\.xml))'], [
            ['set', '$temp', 'A']
        ]]
    ]

    assert_config(config, expected)


def test_issue_11():
    config = '''
init_by_lua_block {
    tvm = require "nginx.tvm"
}
        '''

    expected = [
        ['init_by_lua_block', [], ['tvm', '=', 'require', '"nginx.tvm"']]
    ]

    assert_config(config, expected)


def test_lua_block():
    config = '''
# https://github.com/openresty/lua-nginx-module#typical-uses
location = /lua {
 # MIME type determined by default_type:
 default_type 'text/plain';

 content_by_lua_block {
     local res = ngx.location.capture("/some_other_location")
     if res then
         ngx.say("status: ", res.status)
         ngx.say("body:")
         ngx.print(res.body)
     end
 }
}
        '''

    expected = [
        ['https://github.com/openresty/lua-nginx-module#typical-uses'],
        ['location', ['=', '/lua'], [
            ['MIME type determined by default_type:'],
            ['default_type', 'text/plain'],
            ['content_by_lua_block', [], [
                'local', 'res', '=', 'ngx.location.capture(', '"/some_other_location"', ')',
                'if', 'res', 'then',
                    'ngx.say(', '"status: "', ',', 'res.status)',
                    'ngx.say(', '"body:"', ')',
                    'ngx.print(res.body)',
                'end']]
        ]]
    ]

    assert_config(config, expected)


def test_lua_block_brackets():
    config = '''
location = /foo {
 rewrite_by_lua_block {
     res = ngx.location.capture("/memc",
         { args = { cmd = "incr", key = ngx.var.uri } }
     )
 }

 proxy_pass http://blah.blah.com;
}
        '''

    expected = [
        ['location', ['=', '/foo'], [
            ['rewrite_by_lua_block', [], [
                'res', '=', 'ngx.location.capture(', '"/memc"', ',',
                    ['args', '=', ['cmd', '=', '"incr"', ',', 'key', '=', 'ngx.var.uri']],
                ')']],
            ['proxy_pass', 'http://blah.blah.com']
        ]]
    ]

    assert_config(config, expected)


def test_file_delims():
    config = '''
# configuration file /etc/nginx/nginx.conf:
http {
    include sites/*.conf;
}

# configuration file /etc/nginx/sites/default.conf:
server {

}
        '''

    expected = [
        ['/etc/nginx/nginx.conf'],
        ['http', [], [
            ['include', 'sites/*.conf']
        ]],
        ['/etc/nginx/sites/default.conf'],
        ['server', [], []]
    ]

    assert_config(config, expected)


def test_comments():
    config = '''
# Some comment
add_header X-Some-Comment some;

# 
# Comment with padding
# 
add_header X-Padding-Comment padding;

#
add_header X-Blank-Comment blank;

if (1) # Comment 
{
    add_header X-Inline blank;
}
        '''

    expected = [
        ['Some comment'],
        ['add_header', 'X-Some-Comment', 'some'],
        [''],
        ['Comment with padding'],
        [''],
        ['add_header', 'X-Padding-Comment', 'padding'],
        [''],
        ['add_header', 'X-Blank-Comment', 'blank'],
        ['if', ['1'], [
            ['add_header', 'X-Inline', 'blank'],
        ]],
    ]

    assert_config(config, expected)


def test_upstream_dot():
    config = '''
upstream test.mysite.com {
    server 127.0.0.1:9009;
}
        '''

    expected = [
        ['upstream', ['test.mysite.com'], [
            ['server', '127.0.0.1:9009']
        ]],
    ]

    assert_config(config, expected)


def test_empty_config():
    config = '''
        '''

    expected = []

    assert_config(config, expected)


def test_utfbom_decoding():
    config = b'''\xef\xbb\xbf
add_header X-Test "Windows-1251";
        '''

    expected = [
        ['add_header', 'X-Test', 'Windows-1251']
    ]

    assert_config(config, expected)


def test_national_comment_decoding():
    config = b'''
# \xeb\xff-\xeb\xff-\xeb\xff = Lya-lya-lya
add_header X-Test "Windows-1251";
        '''

    actual = RawParser().parse(config)
    assert_equals(len(actual.asList()), 2)


def assert_config(config, expected):
    actual = RawParser().parse(config)
    assert_equals(actual.asList(), expected)
