/*
 * JBoss, Home of Professional Open Source.
 * Copyright 2014 Red Hat, Inc., and individual contributors
 * as indicated by the @author tags.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

package io.undertow.predicate;

import io.undertow.testutils.category.UnitTest;
import io.undertow.server.HttpServerExchange;
import io.undertow.util.Headers;
import io.undertow.util.HttpString;
import org.junit.Assert;
import org.junit.Test;
import org.junit.experimental.categories.Category;

import java.util.HashMap;

/**
 * @author Stuart Douglas
 */
@Category(UnitTest.class)
public class PredicateParsingTestCase {

    @Test
    public void testPredicateParser() {
        Predicate predicate = PredicateParser.parse("path[foo]", PredicateParsingTestCase.class.getClassLoader());
        Assert.assertTrue(predicate instanceof PathMatchPredicate);
        HttpServerExchange e = new HttpServerExchange(null);
        e.setRelativePath("/foo");
        Assert.assertTrue(predicate.resolve(e));
        e.setRelativePath("/bob");
        Assert.assertFalse(predicate.resolve(e));

        for (String string : new String[]{
                "not path[\"/foo\"]",
                "not path[foo] and true",
                "false or not path[path=/foo]",
                "false or not path[/foo]",
                "true and not path[foo] or not path[foo] and false"}) {
            try {
                predicate = PredicateParser.parse(string, PredicateParsingTestCase.class.getClassLoader());
                e = new HttpServerExchange(null);
                e.setRelativePath("/foo");
                Assert.assertFalse(predicate.resolve(e));
                e.setRelativePath("/bob");
                Assert.assertTrue(predicate.resolve(e));
            } catch (Throwable ex) {
                throw new RuntimeException("String " + string, ex);
            }
        }
    }

    @Test
    public void testPredicateContextVariable() {
        Predicate predicate = PredicateParser.parse("regex[pattern=\"/publicdb/(.*?)/.*\", value=\"%R\", full-match=false] and equals[%{i,username}, ${1}]", PredicateParsingTestCase.class.getClassLoader());

        HttpServerExchange e = new HttpServerExchange(null);
        e.setRelativePath("/publicdb/foo/bar");
        Assert.assertFalse(predicate.resolve(e));
        e.getRequestHeaders().add(new HttpString("username"), "foo");
        Assert.assertTrue(predicate.resolve(e));
    }

    @Test
    public void testRegularExpressionsWithPredicateContext() {
        Predicate predicate = PredicateParser.parse("regex[pattern=a* , value=%{RELATIVE_PATH}] and equals[{$0, aaa}]", PredicateParsingTestCase.class.getClassLoader());
        HttpServerExchange e = new HttpServerExchange(null);
        e.putAttachment(Predicate.PREDICATE_CONTEXT, new HashMap<String, Object>());
        e.setRelativePath("aaab");
        Assert.assertTrue(predicate.resolve(e));
        e.setRelativePath("aaaab");
        Assert.assertFalse(predicate.resolve(e));

        predicate = PredicateParser.parse("regex[pattern='a(b*)a*' , value=%{RELATIVE_PATH}] and equals[$1, bb]", PredicateParsingTestCase.class.getClassLoader());
        e.putAttachment(Predicate.PREDICATE_CONTEXT, new HashMap<String, Object>());
        e.setRelativePath("abb");
        Assert.assertTrue(predicate.resolve(e));
        e.setRelativePath("abbaaa");
        Assert.assertTrue(predicate.resolve(e));
        e.setRelativePath("abbb");
        Assert.assertFalse(predicate.resolve(e));
    }

    @Test
    public void testArrayValues() {
        Predicate predicate;
        for (String string : new String[]{
                "contains[value=%{i,Content-Type}, search=text]",
                "contains[value=\"%{i,Content-Type}\", search={text}]",
                "contains[value=\"%{i,Content-Type}\", search={text, \"other text\"}]",
        }) {
            try {
                predicate = PredicateParser.parse(string, PredicateParsingTestCase.class.getClassLoader());
                HttpServerExchange e = new HttpServerExchange(null);
                Assert.assertFalse(predicate.resolve(e));
                e.getRequestHeaders().add(Headers.CONTENT_TYPE, "text");
                Assert.assertTrue(predicate.resolve(e));
            } catch (Throwable ex) {
                throw new RuntimeException("String " + string, ex);
            }
        }
    }

    @Test
    public void testDefaultArrayValue() {
        Predicate predicate = PredicateParser.parse("equals[%{i,Content-Type},\"text/plain\"]", PredicateParsingTestCase.class.getClassLoader());
        HttpServerExchange e = new HttpServerExchange(null);
        Assert.assertFalse(predicate.resolve(e));
        e.getRequestHeaders().add(Headers.CONTENT_TYPE, "text/plain");
        Assert.assertTrue(predicate.resolve(e));
    }

    @Test
    public void testOrderOfOperations() {
        expect("exists[%{i,Content-Length}] or exists[value=%{i,Trailer}] and exists[%{i,Other}]", false, true);
        expect("(exists[%{i,Content-Length}] or exists[value=%{i,Trailer}]) and exists[%{i,Other}]", false, false);
    }

    private void expect(String string, boolean result1, boolean result2) {
        try {
            Predicate predicate = PredicateParser.parse(string, PredicateParsingTestCase.class.getClassLoader());
            HttpServerExchange e = new HttpServerExchange(null);
            e.getRequestHeaders().add(Headers.TRAILER, "a");
            Assert.assertEquals(result1, predicate.resolve(e));
            e.getRequestHeaders().add(Headers.CONTENT_LENGTH, "a");
            Assert.assertEquals(result2, predicate.resolve(e));
        } catch (Throwable ex) {
            throw new RuntimeException("String " + string, ex);
        }
    }
}
