# Path: sygnal/apnstruncate.py
# def json_encode(payload) -> bytes:
#     return json.dumps(payload, ensure_ascii=False).encode()
# 
# def truncate(payload: Dict[str, Any], max_length: int = 2048) -> Dict[str, Any]:
#     """
#     Truncate APNs fields to make the payload fit within the max length
#     specified.
#     Only truncates fields that are safe to do so.
# 
#     Args:
#         payload: nested dict that will be passed to APNs
#         max_length: Maximum length, in bytes, that the payload should occupy
#             when JSON-encoded.
# 
#     Returns:
#         Nested dict which should comply with the maximum length restriction.
# 
#     """
#     payload = payload.copy()
#     if "aps" not in payload:
#         if is_too_long(payload, max_length):
#             raise BodyTooLongException()
#         else:
#             return payload
#     aps = payload["aps"]
# 
#     # first ensure all our choppables are str objects.
#     # We need them to be for truncating to work and this
#     # makes more sense than checking every time.
#     for c in _choppables_for_aps(aps):
#         val = _choppable_get(aps, c)
#         if isinstance(val, bytes):
#             _choppable_put(aps, c, val.decode())
# 
#     # chop off whole unicode characters until it fits (or we run out of chars)
#     while is_too_long(payload, max_length):
#         longest = _longest_choppable(aps)
#         if longest is None:
#             raise BodyTooLongException()
# 
#         txt = _choppable_get(aps, longest)
#         # Note that python's support for this is actually broken on some OSes
#         # (see test_apnstruncate.py)
#         txt = txt[:-1]
#         _choppable_put(aps, longest, txt)
#         payload["aps"] = aps
# 
#     return payload


import string
import unittest
from sygnal.apnstruncate import json_encode, truncate

def sillystring(length, offset=0):
    """
    Deterministically generates a string
    Args:
        length: Length of the string
        offset: Offset of the string

    Returns:
        A string formed of weird and wonderful UTF-8 emoji characters.
    """
    chars = ["\U0001F430", "\U0001F431", "\U0001F432", "\U0001F433"]
    return "".join([chars[(i + offset) % len(chars)] for i in range(length)])


def payload_for_aps(aps):
    """
    Returns the APNS payload for an 'aps' dictionary.
    """
    return {"aps": aps}


class TruncateTestCase(unittest.TestCase):
    def test_dont_truncate(self):
        """
        Tests that truncation is not performed if unnecessary.
        """
        # This shouldn't need to be truncated
        txt = simplestring(20)
        aps = {"alert": txt}

# Path: sygnal/apnstruncate.py
# def json_encode(payload) -> bytes:
#     return json.dumps(payload, ensure_ascii=False).encode()
# 
# def truncate(payload: Dict[str, Any], max_length: int = 2048) -> Dict[str, Any]:
#     """
#     Truncate APNs fields to make the payload fit within the max length
#     specified.
#     Only truncates fields that are safe to do so.
# 
#     Args:
#         payload: nested dict that will be passed to APNs
#         max_length: Maximum length, in bytes, that the payload should occupy
#             when JSON-encoded.
# 
#     Returns:
#         Nested dict which should comply with the maximum length restriction.
# 
#     """
#     payload = payload.copy()
#     if "aps" not in payload:
#         if is_too_long(payload, max_length):
#             raise BodyTooLongException()
#         else:
#             return payload
#     aps = payload["aps"]
# 
#     # first ensure all our choppables are str objects.
#     # We need them to be for truncating to work and this
#     # makes more sense than checking every time.
#     for c in _choppables_for_aps(aps):
#         val = _choppable_get(aps, c)
#         if isinstance(val, bytes):
#             _choppable_put(aps, c, val.decode())
# 
#     # chop off whole unicode characters until it fits (or we run out of chars)
#     while is_too_long(payload, max_length):
#         longest = _longest_choppable(aps)
#         if longest is None:
#             raise BodyTooLongException()
# 
#         txt = _choppable_get(aps, longest)
#         # Note that python's support for this is actually broken on some OSes
#         # (see test_apnstruncate.py)
#         txt = txt[:-1]
#         _choppable_put(aps, longest, txt)
#         payload["aps"] = aps
# 
#     return payload


# Path: tests/test_apnstruncate.py
import string
import unittest
from sygnal.apnstruncate import json_encode, truncate


def sillystring(length, offset=0):
    """
    Deterministically generates a string
    Args:
        length: Length of the string
        offset: Offset of the string

    Returns:
        A string formed of weird and wonderful UTF-8 emoji characters.
    """
    chars = ["\U0001F430", "\U0001F431", "\U0001F432", "\U0001F433"]
    return "".join([chars[(i + offset) % len(chars)] for i in range(length)])


def payload_for_aps(aps):
    """
    Returns the APNS payload for an 'aps' dictionary.
    """
    return {"aps": aps}


class TruncateTestCase(unittest.TestCase):
    def test_dont_truncate(self):
        """
        Tests that truncation is not performed if unnecessary.
        """
        # This shouldn't need to be truncated
        txt = simplestring(20)
        aps = {"alert": txt}

        self.assertEqual(txt, truncate(payload_for_aps(aps), 256)["aps"]["alert"])