"""
    Test cases for ldaptor.protocols.ldap.ldif module
"""

import base64

from twisted.trial import unittest

from ldaptor.protocols.ldap.ldif import attributeAsLDIF, asLDIF, manyAsLDIF


def encode(value):
    return b''.join(base64.encodestring(value).split(b'\n'))


class WireableObject(object):
    """
    Object with bytes representation as a constant toWire value
    """

    def toWire(self):
        return b'wire'


class AttributeAsLDIFTests(unittest.TestCase):
    """
    Converting pairs of attribute keys and values to LDIF.
    The result is a byte string with key and value
    separated by a semicolon and a space.
    In several special cases value is base64 encoded and separated
    from key by two semicolons and a space.
    """

    def test_byte_string(self):
        """Key and value are byte strings"""
        result = attributeAsLDIF(b'some key', b'some value')
        self.assertEqual(result, b'some key: some value\n')

    def test_unicode_string(self):
        """Key and value are unicode strings"""
        result = attributeAsLDIF(u'another key', u'another value')
        self.assertEqual(result, b'another key: another value\n')

    def test_wireable_object(self):
        """Value is an object with toWire method returning its bytes representation"""
        result = attributeAsLDIF('dn', WireableObject())
        self.assertEqual(result, b'dn: wire\n')

    def test_startswith_special_character(self):
        """
        Value is a string starting with one of the reserved characters.
        Returned value is base64 encoded.
        """
        for c in b'\0', b'\n', b'\r', b' ', b':', b'<':

            value = c + b'value'
            result = attributeAsLDIF(b'key', value)
            self.assertEqual(result, b'key:: %s\n' % encode(value))

    def test_endswith_special_character(self):
        """
        Value is a string ending with one of the reserved characters.
        Returned value is base64 encoded.
        """
        for c in b'\0', b'\n', b'\r', b' ':

            value = b'value' + c
            result = attributeAsLDIF(b'key', value)
            self.assertEqual(result, b'key:: %s\n' % encode(value))

    def test_contains_special_characters(self):
        """
        Value is a string with one of the reserved characters
        somewhere in its middle.
        Returned value is base64 encoded.
        """
        for c in b'\0', b'\n', b'\r':

            value = b'foo' + c + b'bar'
            result = attributeAsLDIF(b'key', value)
            self.assertEqual(result, b'key:: %s\n' % encode(value))

    def test_contains_nonprintable_characters(self):
        """
        Value is a string containing nonprintable characters.
        Returned value is base64 encoded.
        """
        result = attributeAsLDIF(b'key', b'val\xFFue')
        self.assertEqual(result, b'key:: %s\n' % encode(b'val\xFFue'))


class AsLDIFTests(unittest.TestCase):
    """
    Converting LDAP objects to LDIF.
    Object consists of DN and pairs of attribute keys and lists of values.
    The result is a number of lines in LDIF format for every key/value pair
    including DN.
    """

    def test_byte_string(self):
        """DN and attribute keys and values are byte strings"""
        attributes = [
            (b'key1', [b'value11', b'value12']),
            (b'key2', [b'value21', b'value22']),
        ]
        result = asLDIF(b'entry', attributes)
        self.assertEqual(result, b'''\
dn: entry
key1: value11
key1: value12
key2: value21
key2: value22

''')

    def test_unicode_string(self):
        """DN and attribute keys and values are unicode string"""
        attributes = [
            (u'key1', [u'value11', u'value12']),
            (u'key2', [u'value21', u'value22']),
        ]
        result = asLDIF(u'entry', attributes)
        self.assertEqual(result, b'''\
dn: entry
key1: value11
key1: value12
key2: value21
key2: value22

''')

    def test_wireable_object(self):
        """
        DN and attribute values are objects with
        toWire method returning bytes representation
        """
        attributes = [
            (b'key', [WireableObject()]),
        ]
        result = asLDIF(WireableObject(), attributes)
        self.assertEqual(result, b'dn: wire\nkey: wire\n\n')


class ManyAsLDIFTests(unittest.TestCase):
    """
    Converting multiple LDAP objects to LDIF.
    Every object consists of pair of DN and pairs of attribute keys and lists of values.
    The result is a number of blocks representing each object in LDIF format
    separated by an empty lines.
    The result contains a header with version number separated from other blocks
    by an empty line.
    """

    def test_multiple_objects(self):
        objects = [
            (b'object1', (
                (b'foo1', (b'value11', b'value12')),
                (b'foo2', (b'value21', b'value22')),
            )),
            (b'object2', (
                (b'bar1', (b'value31', b'value32')),
                (b'bar2', (b'value41', b'value42')),
            )),
        ]
        result = manyAsLDIF(objects)
        self.assertEqual(result, b'''\
version: 1

dn: object1
foo1: value11
foo1: value12
foo2: value21
foo2: value22

dn: object2
bar1: value31
bar1: value32
bar2: value41
bar2: value42

''')
