import json

import pytest

from indy.did import key_for_did
from plenum.common.constants import TXN_TYPE, DATA, GET_TXN, DOMAIN_LEDGER_ID
from plenum.common.exceptions import NoAuthenticatorFound
from plenum.common.types import f
from plenum.common.util import randomString
from plenum.server.client_authn import SimpleAuthNr, CoreAuthNr
from plenum.server.req_authenticator import ReqAuthenticator
from plenum.test.helper import sdk_sign_and_submit_op, sdk_send_random_and_check
from plenum.test.pool_transactions.helper import new_client_request
from plenum.test.stasher import delay_rules


@pytest.fixture(scope='module')
def pre_reqs():
    simple_authnr = SimpleAuthNr()
    core_authnr = CoreAuthNr()
    req_authnr = ReqAuthenticator()
    return simple_authnr, core_authnr, req_authnr


@pytest.fixture(scope='module')
def registration(pre_reqs):
    simple_authnr, core_authnr, req_authnr = pre_reqs
    assert len(req_authnr._authenticators) == 0
    with pytest.raises(RuntimeError):
        req_authnr.core_authenticator

    req_authnr.register_authenticator(core_authnr)
    assert len(req_authnr._authenticators) == 1
    assert req_authnr.core_authenticator == core_authnr

    req_authnr.register_authenticator(simple_authnr)
    assert len(req_authnr._authenticators) == 2


def test_authenticator_registration(pre_reqs, registration):
    simple_authnr, core_authnr, req_authnr = pre_reqs
    assert req_authnr.get_authnr_by_type(CoreAuthNr) == core_authnr


def test_authentication(looper, pre_reqs, registration,
                        sdk_wallet_client,
                        sdk_pool_handle):
    _, core_authnr, req_authnr = pre_reqs

    # Remove simple_authnr
    req_authnr._authenticators = req_authnr._authenticators[:-1]

    # Exception for unknown txn type
    op = {
        TXN_TYPE: 'random_txn_type',
        f.LEDGER_ID.nm: DOMAIN_LEDGER_ID,
        DATA: 1
    }
    # Just creating the request
    req = sdk_sign_and_submit_op(looper, sdk_pool_handle,
                                 sdk_wallet_client, op)
    with pytest.raises(NoAuthenticatorFound):
        req_authnr.authenticate(req[0])

    # Empty set for query txn type
    op = {
        TXN_TYPE: GET_TXN,
        f.LEDGER_ID.nm: DOMAIN_LEDGER_ID,
        DATA: 1
    }
    # Just creating the request
    req = sdk_sign_and_submit_op(looper, sdk_pool_handle,
                                 sdk_wallet_client, op)
    assert set() == req_authnr.authenticate(req[0])

    # identifier for write type
    wh, did = sdk_wallet_client
    req = new_client_request(None, randomString(), looper, sdk_wallet_client)
    core_authnr.addIdr(did,
                       looper.loop.run_until_complete(key_for_did(sdk_pool_handle, wh, did)))
    assert req_authnr.authenticate(json.loads(req)) == {did, }


def test_propagate_of_ordered_request_doesnt_stash_requests_in_authenticator(
        looper, txnPoolNodeSet, sdk_pool_handle, sdk_wallet_client):
    # Universal delayer
    def stopAll(msg):
        return 100000

    # Make sure that verified req list is empty
    for node in txnPoolNodeSet:
        assert len(node.clientAuthNr._verified_reqs) == 0

    lastNode = txnPoolNodeSet[-1]

    # Order one request while cutting off last node
    with delay_rules(lastNode.nodeIbStasher, stopAll), \
         delay_rules(lastNode.clientIbStasher, stopAll):
        sdk_send_random_and_check(looper, txnPoolNodeSet,
                                  sdk_pool_handle,
                                  sdk_wallet_client, 1)

    # Let last node catch up
    # TODO: Change to some more sensible wait condition
    looper.runFor(5.0)

    # Make sure that verified req list is still empty
    for node in txnPoolNodeSet:
        assert len(node.clientAuthNr._verified_reqs) == 0

@@ -586,16 +586,64 @@
 y_rules%0A
+from stp_core.loop.eventually import eventually%0A
 %0A%0A@pytes
@@ -2996,106 +2996,31 @@
 t):%0A
-    # Universal delayer%0A    def stopAll(msg):%0A        return 100000%0A%0A    # Make sure that 
+%0A    def check_
 verified
  req
@@ -3019,27 +3019,34 @@
 fied
- req 
+_req_
 list
- is 
+_is_
 empty
-%0A
+():%0A    
     
@@ -3065,32 +3065,36 @@
 txnPoolNodeSet:%0A
+    
         assert l
@@ -3144,37 +3144,71 @@
     
-lastNode = txnPoolNodeSet%5B-1%5D
+# Universal delayer%0A    def stopAll(msg):%0A        return 100000
 %0A%0A  
@@ -3257,16 +3257,50 @@
 st node%0A
+    lastNode = txnPoolNodeSet%5B-1%5D%0A
     with
@@ -3575,117 +3575,8 @@
 1)%0A%0A
-    # Let last node catch up%0A    # TODO: Change to some more sensible wait condition%0A    looper.runFor(5.0)%0A%0A
     
@@ -3614,109 +3614,90 @@
 ist 
-is st
+w
 ill 
+be 
 empty
-%0A    for node in txnPoolNodeSet:%0A        assert len(node.clientAuthNr._verified_reqs) == 0
+ eventually%0A    looper.run(eventually(check_verified_req_list_is_empty))
 %0A

Improve test_propagate_of_ordered_request_doesnt_stash_...