# -*- coding: utf-8 -*-
#
# This file is part of pypuppetdbquery.
# Copyright © 2016  Chris Boot <bootc@bootc.net>
#
# 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.

import json
import mock
import unittest

from pypuppetdbquery import parse, query_facts


class FakeNode(object):
    def __init__(self, node, name, value):
        self.node = node
        self.name = name
        self.value = value


class TestFrontend(unittest.TestCase):
    """
    Test cases targetting :mod:`pypuppetdbquery`, and particularly
    :func:`pypuppetdbquery.parse`.
    """
    def _parse(self, s, **kwargs):
        return parse(
            s,
            lex_options={
                'debug': False,
                'optimize': False,
            },
            yacc_options={
                'debug': False,
                'optimize': False,
                'write_tables': False,
            },
            **kwargs)

    def _query_facts(self, pdb, s, facts=None, raw=False):
        return query_facts(
            pdb, s, facts, raw,
            lex_options={
                'debug': False,
                'optimize': False,
            },
            yacc_options={
                'debug': False,
                'optimize': False,
                'write_tables': False,
            })

    def test_empty_queries(self):
        out = self._parse('')
        self.assertTrue(out is None)

    def test_simple_json(self):
        out = self._parse('foo=bar')
        expect = json.dumps([
            'in', 'certname',
            ['extract', 'certname',
             ['select_fact_contents',
              ['and',
               ['=', 'path', ['foo']],
               ['=', 'value', 'bar']]]]])
        self.assertEqual(out, expect)

    def test_simple_raw(self):
        out = self._parse('foo=bar', json=False)
        expect = [
            'in', 'certname',
            ['extract', 'certname',
             ['select_fact_contents',
              ['and',
               ['=', 'path', ['foo']],
               ['=', 'value', 'bar']]]]]
        self.assertEqual(out, expect)

    def test_query_facts_with_query_and_facts_list(self):
        mock_pdb = mock.NonCallableMock()
        mock_pdb.facts = mock.Mock(return_value=[
            FakeNode('alpha', 'foo', 'bar'),
        ])

        node_facts = self._query_facts(mock_pdb, 'foo=bar', ['foo'])

        mock_pdb.facts.assert_called_once_with(query=json.dumps([
            'and',
            ['in', 'certname',
             ['extract', 'certname',
              ['select_fact_contents',
               ['and',
                ['=', 'path', ['foo']],
                ['=', 'value', 'bar']]]]],
            ['or',
             ['=', 'name', 'foo']]]))

        self.assertEquals(node_facts, {
            'alpha': {'foo': 'bar'},
        })

    def test_query_facts_with_query_and_facts_list_regex(self):
        mock_pdb = mock.NonCallableMock()
        mock_pdb.facts = mock.Mock(return_value=[
            FakeNode('alpha', 'foo', 'bar'),
        ])

        node_facts = self._query_facts(mock_pdb, 'foo=bar', ['/foo/'])

        mock_pdb.facts.assert_called_once_with(query=json.dumps([
            'and',
            ['in', 'certname',
             ['extract', 'certname',
              ['select_fact_contents',
               ['and',
                ['=', 'path', ['foo']],
                ['=', 'value', 'bar']]]]],
            ['or',
             ['~', 'name', 'foo']]]))

        self.assertEquals(node_facts, {
            'alpha': {'foo': 'bar'},
        })

    def test_query_facts_with_facts_list_only(self):
        mock_pdb = mock.NonCallableMock()
        mock_pdb.facts = mock.Mock(return_value=[
            FakeNode('alpha', 'foo', 'bar'),
        ])

        node_facts = self._query_facts(mock_pdb, '', ['foo'])

        mock_pdb.facts.assert_called_once_with(query=json.dumps([
            'or',
            ['=', 'name', 'foo']]))

        self.assertEquals(node_facts, {
            'alpha': {'foo': 'bar'},
        })

    def test_query_facts_without_query_or_facts(self):
        node_facts = self._query_facts(None, '')
        self.assertTrue(node_facts is None)

    def test_query_facts_in_raw_mode(self):
        mock_pdb = mock.NonCallableMock()
        mock_pdb.facts = mock.Mock(return_value=[
            FakeNode('alpha', 'foo', 'bar'),
        ])

        node_facts = self._query_facts(mock_pdb, 'foo=bar', raw=True)

        self.assertEquals(node_facts, mock_pdb.facts.return_value)

@@ -751,16 +751,17 @@
 %0A%0Aclass 
+_
 FakeNode
@@ -910,16 +910,21 @@
 Frontend
+Parse
 (unittes
@@ -974,53 +974,8 @@
 ing 
-:mod:%60pypuppetdbquery%60, and particularly%0A    
 :fun
@@ -1005,24 +1005,25 @@
 e%60.%0A    %22%22%22%0A
+%0A
     def _par
@@ -1365,384 +1365,8 @@
 s)%0A%0A
-    def _query_facts(self, pdb, s, facts=None, raw=False):%0A        return query_facts(%0A            pdb, s, facts, raw,%0A            lex_options=%7B%0A                'debug': False,%0A                'optimize': False,%0A            %7D,%0A            yacc_options=%7B%0A                'debug': False,%0A                'optimize': False,%0A                'write_tables': False,%0A            %7D)%0A%0A
     
@@ -2144,32 +2144,538 @@
 l(out, expect)%0A%0A
+%0Aclass TestFrontendQueryFacts(unittest.TestCase):%0A    %22%22%22%0A    Test cases targetting :func:%60pypuppetdbquery.query_facts%60.%0A    %22%22%22%0A%0A    def _query_facts(self, pdb, s, facts=None, raw=False):%0A        return query_facts(%0A            pdb, s, facts, raw,%0A            lex_options=%7B%0A                'debug': False,%0A                'optimize': False,%0A            %7D,%0A            yacc_options=%7B%0A                'debug': False,%0A                'optimize': False,%0A                'write_tables': False,%0A            %7D)%0A%0A
     def test_que
@@ -2812,32 +2812,33 @@
 e=%5B%0A            
+_
 FakeNode('alpha'
@@ -3541,32 +3541,33 @@
 e=%5B%0A            
+_
 FakeNode('alpha'
@@ -4261,32 +4261,33 @@
 e=%5B%0A            
+_
 FakeNode('alpha'
@@ -4885,16 +4885,17 @@
         
+_
 FakeNode

split up into multiple classes