# Copyright © 2020 Interplanetary Database Association e.V.,
# Planetmint and IPDB software contributors.
# SPDX-License-Identifier: (Apache-2.0 AND CC-BY-4.0)
# Code is Apache-2.0 and docs are CC-BY-4.0

# ## Testing potentially hazardous strings
# This test uses a library of `naughty` strings (code injections, weird unicode chars., etc.) as both keys and values.
# We look for either a successful tx, or in the case that we use a naughty string as a key, and it violates some key
# constraints, we expect to receive a well formatted error message.

# ## Imports
# We need some utils from the `os` package, we will interact with
# env variables.
import os

# Since the naughty strings get encoded and decoded in odd ways,
# we'll use a regex to sweep those details under the rug.
import re

# We'll use a nice library of naughty strings...
from blns import blns

# And parameterize our test so each one is treated as a separate test case
import pytest

# For this test case we import and use the Python Driver.
from planetmint_driver import Planetmint
from planetmint_driver.crypto import generate_keypair
from planetmint_driver.exceptions import BadRequest

naughty_strings = blns.all()


# This is our base test case, but we'll reuse it to send naughty strings as both keys and values.
def send_naughty_tx(asset, metadata):
    # ## Set up a connection to Planetmint
    # Check [test_basic.py](./test_basic.html) to get some more details
    # about the endpoint.
    bdb = Planetmint(os.environ.get('PLANETMINT_ENDPOINT'))

    # Here's Alice.
    alice = generate_keypair()

    # Alice is in a naughty mood today, so she creates a tx with some naughty strings
    prepared_transaction = bdb.transactions.prepare(
        operation='CREATE',
        signers=alice.public_key,
        asset=asset,
        metadata=metadata)

    # She fulfills the transaction
    fulfilled_transaction = bdb.transactions.fulfill(
        prepared_transaction,
        private_keys=alice.private_key)

    # The fulfilled tx gets sent to the BDB network
    try:
        sent_transaction = bdb.transactions.send_commit(fulfilled_transaction)
    except BadRequest as e:
        sent_transaction = e

    # If her key contained a '.', began with a '$', or contained a NUL character
    regex = '.*\..*|\$.*|.*\x00.*'
    key = next(iter(metadata))
    if re.match(regex, key):
        # Then she expects a nicely formatted error code
        status_code = sent_transaction.status_code
        error = sent_transaction.error
        regex = (
            r'\{\s*\n*'
            r'\s*"message":\s*"Invalid transaction \(ValidationError\):\s*'
            r'Invalid key name.*The key name cannot contain characters.*\n*'
            r'\s*"status":\s*400\n*'
            r'\s*\}\n*')
        assert status_code == 400
        assert re.fullmatch(regex, error), sent_transaction
    # Otherwise, she expects to see her transaction in the database
    elif 'id' in sent_transaction.keys():
        tx_id = sent_transaction['id']
        assert bdb.transactions.retrieve(tx_id)
    # If neither condition was true, then something weird happened...
    else:
        raise TypeError(sent_transaction)


@pytest.mark.parametrize("naughty_string", naughty_strings, ids=naughty_strings)
def test_naughty_keys(naughty_string):

    asset = {'data': {naughty_string: 'nice_value'}}
    metadata = {naughty_string: 'nice_value'}

    send_naughty_tx(asset, metadata)


@pytest.mark.parametrize("naughty_string", naughty_strings, ids=naughty_strings)
def test_naughty_values(naughty_string):

    asset = {'data': {'nice_key': naughty_string}}
    metadata = {'nice_key': naughty_string}

    send_naughty_tx(asset, metadata)
