github.com/muhammedhassanm/blockchain@v0.0.0-20200120143007-697261defd4d/sawtooth-core-master/integration/sawtooth_integration/tests/test_intkey_smoke.py (about)

     1  # Copyright 2017 Intel Corporation
     2  #
     3  # Licensed under the Apache License, Version 2.0 (the "License");
     4  # you may not use this file except in compliance with the License.
     5  # You may obtain a copy of the License at
     6  #
     7  #     http://www.apache.org/licenses/LICENSE-2.0
     8  #
     9  # Unless required by applicable law or agreed to in writing, software
    10  # distributed under the License is distributed on an "AS IS" BASIS,
    11  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  # See the License for the specific language governing permissions and
    13  # limitations under the License.
    14  # ------------------------------------------------------------------------------
    15  
    16  # pylint: disable = attribute-defined-outside-init
    17  
    18  import unittest
    19  import logging
    20  import operator  # used by verifier
    21  # -- used by rest_api callers --
    22  import urllib.request
    23  import urllib.error
    24  import json
    25  from base64 import b64decode
    26  
    27  import cbor
    28  
    29  from sawtooth_intkey.intkey_message_factory import IntkeyMessageFactory
    30  from sawtooth_integration.tests.integration_tools import wait_for_rest_apis
    31  
    32  
    33  LOGGER = logging.getLogger(__name__)
    34  LOGGER.setLevel(logging.INFO)
    35  
    36  WAIT = 300
    37  INTKEY_PREFIX = '1cf126'
    38  
    39  
    40  class TestIntkeySmoke(unittest.TestCase):
    41      @classmethod
    42      def setUpClass(cls):
    43          wait_for_rest_apis(['rest-api:8008'])
    44  
    45      def test_intkey_smoke(self):
    46          '''
    47          After starting up a validator, intkey processor, and rest api,
    48          generate three batches of transactions: a batch of 'set' transactions
    49          (the "populate" batch), a batch of valid 'inc'/'dec' transactions,
    50          and a batch of invalid of invalid 'inc'/'dec' transactions (invalid
    51          because they target words not set by the populate batch).
    52  
    53          First, verify that the state is initially empty. Next, load the
    54          batches in the following order, verifying that the state is
    55          as it should be after each load:
    56  
    57          1. Send the batch of 'set' transactions, populating the state.
    58          2. Send the batch of valid transactions, altering the state.
    59          3. Send the batch of invalid transactions, leaving the state the same.
    60          4. Send the batch of valid transactions, again altering the state.
    61          5. Send the same batch of 'set' transactions from before.
    62             Because the state has already been populated, these transactions
    63             are invalid, and shouldn't affect the state.
    64          6. Send the batch of valid transactions, again altering the state.
    65          '''
    66  
    67          self.verifier = IntkeyTestVerifier()
    68  
    69          populate, valid_txns, invalid_txns = self.make_txn_batches()
    70  
    71          self.verify_empty_state()
    72  
    73          batches = (
    74              populate,
    75              valid_txns,
    76              invalid_txns,
    77              valid_txns,
    78              populate,
    79              valid_txns
    80          )
    81  
    82          how_many_updates = 0
    83  
    84          for batch in batches:
    85              if batch == valid_txns:
    86                  how_many_updates += 1
    87              self.post_and_verify(batch, how_many_updates)
    88  
    89      # assertions
    90  
    91      def post_and_verify(self, batch, how_many_updates):
    92          batch = IntkeyMessageFactory().create_batch(batch)
    93          LOGGER.info('Posting batch')
    94          _post_batch(batch)
    95          self.verify_state_after_n_updates(how_many_updates)
    96  
    97      def verify_state_after_n_updates(self, num):
    98          LOGGER.debug('Verifying state after %s updates', num)
    99          expected_state = self.verifier.state_after_n_updates(num)
   100          actual_state = _get_data()
   101          LOGGER.info('Current state: %s', actual_state)
   102          self.assertEqual(
   103              expected_state,
   104              actual_state,
   105              'Updated state error')
   106  
   107      def verify_empty_state(self):
   108          LOGGER.debug('Verifying empty state')
   109          self.assertEqual(
   110              [],
   111              _get_state(),
   112              'Empty state error')
   113  
   114      # utilities
   115  
   116      def make_txn_batches(self):
   117          LOGGER.debug('Making txn batches')
   118          batches = self.verifier.make_txn_batches()
   119          return batches
   120  
   121  # rest_api calls
   122  
   123  
   124  def _post_batch(batch):
   125      headers = {'Content-Type': 'application/octet-stream'}
   126      response = _query_rest_api(
   127          '/batches',
   128          data=batch, headers=headers, expected_code=202)
   129      response = _submit_request('{}&wait={}'.format(response['link'], WAIT))
   130      return response
   131  
   132  
   133  def _get_data():
   134      state = _get_state()
   135      # state is a list of dictionaries: { data: ..., address: ... }
   136      dicts = [cbor.loads(b64decode(entry['data'])) for entry in state]
   137      data = {k: v for d in dicts for k, v in d.items()}  # merge dicts
   138      return data
   139  
   140  
   141  def _get_state():
   142      response = _query_rest_api('/state?address={}'.format(INTKEY_PREFIX))
   143      return response['data']
   144  
   145  
   146  def _query_rest_api(suffix='', data=None, headers=None, expected_code=200):
   147      if headers is None:
   148          headers = {}
   149      url = 'http://rest-api:8008' + suffix
   150      return _submit_request(urllib.request.Request(url, data, headers),
   151                             expected_code=expected_code)
   152  
   153  
   154  def _submit_request(request, expected_code=200):
   155      conn = urllib.request.urlopen(request)
   156      assert expected_code == conn.getcode()
   157  
   158      response = conn.read().decode('utf-8')
   159      return json.loads(response)
   160  
   161  
   162  # separate file?
   163  class IntkeyTestVerifier:
   164      def __init__(self,
   165                   valid=('lark', 'thrush', 'jay', 'wren', 'finch'),
   166                   invalid=('cow', 'pig', 'sheep', 'goat', 'horse'),
   167                   verbs=('inc', 'inc', 'dec', 'inc', 'dec'),
   168                   incdec=(1, 2, 3, 5, 8),
   169                   initial=(415, 325, 538, 437, 651)):
   170          self.valid = valid
   171          self.invalid = invalid
   172          self.verbs = verbs
   173          self.incdec = incdec
   174          self.initial = initial
   175          self.sets = ['set' for _ in range(len(self.initial))]
   176  
   177      def make_txn_batches(self):
   178          populate = tuple(zip(self.sets, self.valid, self.initial))
   179          valid_txns = tuple(zip(self.verbs, self.valid, self.incdec))
   180          invalid_txns = tuple(zip(self.verbs, self.invalid, self.incdec))
   181  
   182          return populate, valid_txns, invalid_txns
   183  
   184      def state_after_n_updates(self, num):
   185          ops = {
   186              'inc': operator.add,
   187              'dec': operator.sub
   188          }
   189  
   190          expected_values = [
   191              ops[verb](init, (val * num))
   192              for verb, init, val
   193              in zip(self.verbs, self.initial, self.incdec)
   194          ]
   195  
   196          return {word: val for word, val in zip(self.valid, expected_values)}