github.com/muhammedhassanm/blockchain@v0.0.0-20200120143007-697261defd4d/sawtooth-core-master/families/battleship/tests/test_tp_battleship.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  import logging
    17  import json
    18  
    19  from sawtooth_sdk.protobuf.validator_pb2 import Message
    20  from sawtooth_processor_test.transaction_processor_test_case \
    21      import TransactionProcessorTestCase
    22  from sawtooth_battleship_test.battleship_message_factory \
    23      import BattleshipMessageFactory
    24  
    25  from sawtooth_battleship.battleship_board import BoardLayout
    26  from sawtooth_battleship.battleship_board import create_nonces
    27  from sawtooth_battleship.battleship_board import ShipPosition
    28  
    29  LOGGER = logging.getLogger(__name__)
    30  
    31  
    32  class TestBattleship(TransactionProcessorTestCase):
    33  
    34      @classmethod
    35      def setUpClass(cls):
    36          super().setUpClass()
    37  
    38          cls.player_1 = BattleshipMessageFactory()
    39          cls.public_key_1 = cls.player_1.get_public_key()
    40  
    41          cls.player_2 = BattleshipMessageFactory()
    42          cls.public_key_2 = cls.player_2.get_public_key()
    43  
    44          cls.test_ships = ["AAAAA", "BBBB", "CCC", "DD", "DD", "SSS", "SSS"]
    45          cls.validator.register_comparator(
    46              Message.TP_STATE_SET_REQUEST, compare_set_request)
    47  
    48      # invalid inputs
    49  
    50      def test_no_action(self):
    51          payload = {
    52              'Name': 'noActionGame',
    53              'Action': None
    54          }
    55  
    56          self.validator.send(
    57              self.player_1.create_tp_process_request(payload)
    58          )
    59  
    60          self.send_state_to_tp('noActionGame')
    61  
    62          self.expect_invalid()
    63  
    64      def test_invalid_action(self):
    65          payload = {
    66              'Name': 'invalidAction',
    67              'Action': 'MAKE_TACOS'
    68          }
    69  
    70          self.validator.send(
    71              self.player_1.create_tp_process_request(payload)
    72          )
    73  
    74          self.send_state_to_tp('invalidAction')
    75  
    76          self.expect_invalid()
    77  
    78      def test_no_name(self):
    79          payload = {
    80              'Name': '',
    81              'Action': 'CREATE'
    82          }
    83  
    84          self.validator.send(
    85              self.player_1.create_tp_process_request(payload)
    86          )
    87  
    88          self.send_state_to_tp('')
    89  
    90          self.expect_invalid()
    91  
    92      def test_invalid_name(self):
    93          payload = {
    94              'Name': 'invalid-name',
    95              'Action': 'CREATE'
    96          }
    97  
    98          self.validator.send(
    99              self.player_1.create_tp_process_request(payload)
   100          )
   101  
   102          self.send_state_to_tp('invalid-name')
   103  
   104          self.expect_invalid()
   105  
   106      # create
   107  
   108      def test_create_game_valid(self):
   109          self.create_game('createGame')
   110  
   111          self.send_state_to_tp('createGame')
   112  
   113          self.update_state('createGame', {
   114              'Ships': self.test_ships,
   115              'State': 'NEW'
   116          })
   117  
   118          self.expect_ok()
   119  
   120      def test_create_already_exists(self):
   121          self.create_game('alreadyExists')
   122  
   123          self.send_state_to_tp('alreadyExists', {
   124              'Ships': self.test_ships,
   125              'State': 'NEW'
   126          })
   127  
   128          self.expect_invalid()
   129  
   130      # join
   131  
   132      def test_join_game_valid(self):
   133          layout = BoardLayout.generate(self.test_ships)
   134          nonces = create_nonces(10)
   135  
   136          # Send join payload to tp
   137  
   138          join_req = create_join_payload('join_game', layout, nonces)
   139          self.validator.send(
   140              self.player_1.create_tp_process_request(join_req)
   141          )
   142  
   143          # give state back to tp
   144  
   145          self.send_state_to_tp('join_game', {
   146              'Ships': self.test_ships,
   147              'State': 'NEW'
   148          })
   149  
   150          # mock state set
   151          LOGGER.debug('state update')
   152          self.update_state('join_game', {
   153              'Ships': self.test_ships,
   154              'TargetBoard1': [['?'] * 10 for _ in range(10)],
   155              'Player1': self.public_key_1,
   156              'HashedBoard1': layout.render_hashed(nonces),
   157              'State': 'NEW'
   158          })
   159  
   160          self.expect_ok()
   161  
   162      def test_join_game_too_many_players(self):
   163          layout = BoardLayout.generate(self.test_ships)
   164          nonces = create_nonces(10)
   165  
   166          # Send join payload to tp
   167          join_req = create_join_payload('full_game', layout, nonces)
   168          self.validator.send(
   169              self.player_1.create_tp_process_request(join_req)
   170          )
   171  
   172          # give state back to tp
   173  
   174          self.send_state_to_tp('full_game', {
   175              'Ships': self.test_ships,
   176              'TargetBoard1': [['?'] * 10 for _ in range(10)],
   177              'TargetBoard2': [['?'] * 10 for _ in range(10)],
   178              'Player1': self.public_key_1,
   179              'Player2': self.public_key_2,
   180              'HashedBoard1': layout.render_hashed(nonces),
   181              'HashedBoard2': layout.render_hashed(nonces),
   182              'State': 'P1-NEXT'
   183          })
   184  
   185          self.expect_invalid()
   186  
   187      def test_join_nonexistent_game(self):
   188          layout = BoardLayout.generate(self.test_ships)
   189          nonces = create_nonces(10)
   190  
   191          # Send join payload to tp
   192          join_req = create_join_payload('nonexistent_game', layout, nonces)
   193          self.validator.send(
   194              self.player_1.create_tp_process_request(join_req)
   195          )
   196  
   197          # give state back to tp
   198  
   199          self.send_state_to_tp('nonexistent_game')
   200  
   201          self.expect_invalid()
   202  
   203      # fire
   204  
   205      def test_fire_first_move(self):
   206          # Create test board
   207          layout = BoardLayout(10)
   208          layout.append(
   209              ShipPosition(text='AA', row=0, column=0, orientation='horizontal'))
   210          nonces = create_nonces(10)
   211  
   212          # Player 1 fires
   213          fire_req = create_fire_payload('fire_game', '1', 'B')
   214  
   215          self.validator.send(
   216              self.player_1.create_tp_process_request(fire_req))
   217  
   218          self.send_state_to_tp('fire_game', {
   219              'Ships': ['AA'],
   220              'TargetBoard1': [['?'] * 10 for _ in range(10)],
   221              'TargetBoard2': [['?'] * 10 for _ in range(10)],
   222              'Player1': self.public_key_1,
   223              'Player2': self.public_key_2,
   224              'HashedBoard1': layout.render_hashed(nonces),
   225              'HashedBoard2': layout.render_hashed(nonces),
   226              'State': 'P1-NEXT'
   227          })
   228  
   229          self.update_state('fire_game', {
   230              'Ships': ['AA'],
   231              'TargetBoard1': [['?'] * 10 for _ in range(10)],
   232              'TargetBoard2': [['?'] * 10 for _ in range(10)],
   233              'Player1': self.public_key_1,
   234              'Player2': self.public_key_2,
   235              'HashedBoard1': layout.render_hashed(nonces),
   236              'HashedBoard2': layout.render_hashed(nonces),
   237              'LastFireRow': '1',
   238              'LastFireColumn': 'B',
   239              'State': 'P2-NEXT'
   240          })
   241  
   242          self.expect_ok()
   243  
   244      def test_fire_hit_ship(self):
   245          # Create test board
   246          layout = BoardLayout(10)
   247          layout.append(
   248              ShipPosition(text='AA', row=0, column=0, orientation='horizontal'))
   249          nonces = create_nonces(10)
   250  
   251          # Player 2 fires
   252          fire_req = \
   253              create_fire_payload('fire_game', '1', 'A', 'A', nonces[0][0])
   254  
   255          self.validator.send(
   256              self.player_2.create_tp_process_request(fire_req))
   257  
   258          self.send_state_to_tp('fire_game', {
   259              'Ships': ['AA'],
   260              'TargetBoard1': [['?'] * 10 for _ in range(10)],
   261              'TargetBoard2': [['?'] * 10 for _ in range(10)],
   262              'Player1': self.public_key_1,
   263              'Player2': self.public_key_2,
   264              'HashedBoard1': layout.render_hashed(nonces),
   265              'HashedBoard2': layout.render_hashed(nonces),
   266              'State': 'P2-NEXT',
   267              'LastFireRow': '1',
   268              'LastFireColumn': 'A'
   269          }, self.player_2)
   270  
   271          target_board1 = [['?'] * 10 for _ in range(10)]
   272          target_board1[0][0] = 'H'
   273  
   274          self.update_state('fire_game', {
   275              'Ships': ['AA'],
   276              'TargetBoard1': target_board1,
   277              'TargetBoard2': [['?'] * 10 for _ in range(10)],
   278              'Player1': self.public_key_1,
   279              'Player2': self.public_key_2,
   280              'HashedBoard1': layout.render_hashed(nonces),
   281              'HashedBoard2': layout.render_hashed(nonces),
   282              'LastFireRow': '1',
   283              'LastFireColumn': 'A',
   284              'State': 'P1-NEXT'
   285          }, self.player_2)
   286  
   287          self.expect_ok()
   288  
   289      def test_fire_miss_ship(self):
   290          # Create test board
   291          layout = BoardLayout(10)
   292          layout.append(
   293              ShipPosition(text='AA', row=0, column=0, orientation='horizontal'))
   294          nonces = create_nonces(10)
   295  
   296          # Player 2 fires
   297          fire_req = \
   298              create_fire_payload('fire_game', '2', 'A', '-', nonces[1][0])
   299  
   300          self.validator.send(
   301              self.player_2.create_tp_process_request(fire_req))
   302  
   303          self.send_state_to_tp('fire_game', {
   304              'Ships': ['AA'],
   305              'TargetBoard1': [['?'] * 10 for _ in range(10)],
   306              'TargetBoard2': [['?'] * 10 for _ in range(10)],
   307              'Player1': self.public_key_1,
   308              'Player2': self.public_key_2,
   309              'HashedBoard1': layout.render_hashed(nonces),
   310              'HashedBoard2': layout.render_hashed(nonces),
   311              'State': 'P2-NEXT',
   312              'LastFireRow': '2',
   313              'LastFireColumn': 'A'
   314          }, self.player_2)
   315  
   316          target_board1 = [['?'] * 10 for _ in range(10)]
   317          target_board1[1][0] = 'M'
   318  
   319          self.update_state('fire_game', {
   320              'Ships': ['AA'],
   321              'TargetBoard1': target_board1,
   322              'TargetBoard2': [['?'] * 10 for _ in range(10)],
   323              'Player1': self.public_key_1,
   324              'Player2': self.public_key_2,
   325              'HashedBoard1': layout.render_hashed(nonces),
   326              'HashedBoard2': layout.render_hashed(nonces),
   327              'LastFireRow': '2',
   328              'LastFireColumn': 'A',
   329              'State': 'P1-NEXT'
   330          }, self.player_2)
   331  
   332          self.expect_ok()
   333  
   334      def test_fire_game_won(self):
   335          # Create test board
   336          layout = BoardLayout(10)
   337          layout.append(
   338              ShipPosition(text='AA', row=0, column=0, orientation='horizontal'))
   339          nonces = create_nonces(10)
   340  
   341          # Player 2 fires
   342          fire_req = \
   343              create_fire_payload('fire_game', '1', 'B', 'A', nonces[0][1])
   344  
   345          self.validator.send(
   346              self.player_2.create_tp_process_request(fire_req))
   347  
   348          target_board1 = [['?'] * 10 for _ in range(10)]
   349          target_board1[0][0] = 'H'
   350  
   351          self.send_state_to_tp('fire_game', {
   352              'Ships': ['AA'],
   353              'TargetBoard1': target_board1,
   354              'TargetBoard2': [['?'] * 10 for _ in range(10)],
   355              'Player1': self.public_key_1,
   356              'Player2': self.public_key_2,
   357              'HashedBoard1': layout.render_hashed(nonces),
   358              'HashedBoard2': layout.render_hashed(nonces),
   359              'State': 'P2-NEXT',
   360              'LastFireRow': '1',
   361              'LastFireColumn': 'B'
   362          }, self.player_2)
   363  
   364          target_board1[0][1] = 'H'
   365  
   366          self.update_state('fire_game', {
   367              'Ships': ['AA'],
   368              'TargetBoard1': target_board1,
   369              'TargetBoard2': [['?'] * 10 for _ in range(10)],
   370              'Player1': self.public_key_1,
   371              'Player2': self.public_key_2,
   372              'HashedBoard1': layout.render_hashed(nonces),
   373              'HashedBoard2': layout.render_hashed(nonces),
   374              'LastFireRow': '1',
   375              'LastFireColumn': 'B',
   376              'State': 'P1-WIN'
   377          }, self.player_2)
   378  
   379          self.expect_ok()
   380  
   381      def test_fire_nonexistent_game(self):
   382          # Send join payload to tp
   383  
   384          fire_req = create_fire_payload('nonexistent_game', '1', 'A')
   385  
   386          self.validator.send(
   387              self.player_1.create_tp_process_request(fire_req)
   388          )
   389  
   390          # give state back to tp
   391  
   392          self.send_state_to_tp('nonexistent_game')
   393  
   394          self.expect_invalid()
   395  
   396      def test_fire_invalid_row(self):
   397          layout = BoardLayout(10)
   398          layout.append(
   399              ShipPosition(text='AA', row=0, column=0, orientation='horizontal'))
   400          nonces = create_nonces(10)
   401  
   402          # Send join payload to tp
   403  
   404          fire_req = create_fire_payload('invalid_row', '100', 'A')
   405  
   406          self.validator.send(
   407              self.player_1.create_tp_process_request(fire_req)
   408          )
   409  
   410          # give state back to tp
   411  
   412          self.send_state_to_tp('invalid_row', {
   413              'Ships': ['AA'],
   414              'TargetBoard1': [['?'] * 10 for _ in range(10)],
   415              'TargetBoard2': [['?'] * 10 for _ in range(10)],
   416              'Player1': self.public_key_1,
   417              'Player2': self.public_key_2,
   418              'HashedBoard1': layout.render_hashed(nonces),
   419              'HashedBoard2': layout.render_hashed(nonces),
   420              'State': 'P1-NEXT'
   421          })
   422  
   423          self.expect_invalid()
   424  
   425      def test_fire_invalid_column(self):
   426          layout = BoardLayout(10)
   427          layout.append(
   428              ShipPosition(text='AA', row=0, column=0, orientation='horizontal'))
   429          nonces = create_nonces(10)
   430  
   431          # Send join payload to tp
   432          fire_req = create_fire_payload('invalid_row', '1', 'Z')
   433  
   434          self.validator.send(
   435              self.player_1.create_tp_process_request(fire_req)
   436          )
   437  
   438          # give state back to tp
   439  
   440          self.send_state_to_tp('invalid_row', {
   441              'Ships': ['AA'],
   442              'TargetBoard1': [['?'] * 10 for _ in range(10)],
   443              'TargetBoard2': [['?'] * 10 for _ in range(10)],
   444              'Player1': self.public_key_1,
   445              'Player2': self.public_key_2,
   446              'HashedBoard1': layout.render_hashed(nonces),
   447              'HashedBoard2': layout.render_hashed(nonces),
   448              'State': 'P1-NEXT'
   449          })
   450  
   451          self.expect_invalid()
   452  
   453      def test_fire_game_over(self):
   454          layout = BoardLayout(10)
   455          layout.append(
   456              ShipPosition(text='AA', row=0, column=0, orientation='horizontal'))
   457          nonces = create_nonces(10)
   458  
   459          # Send join payload to tp
   460          fire_req = create_fire_payload('invalid_row', '1', 'A')
   461  
   462          self.validator.send(
   463              self.player_1.create_tp_process_request(fire_req)
   464          )
   465  
   466          # give state back to tp
   467  
   468          self.send_state_to_tp('invalid_row', {
   469              'Ships': ['AA'],
   470              'TargetBoard1': [['?'] * 10 for _ in range(10)],
   471              'TargetBoard2': [['?'] * 10 for _ in range(10)],
   472              'Player1': self.public_key_1,
   473              'Player2': self.public_key_2,
   474              'HashedBoard1': layout.render_hashed(nonces),
   475              'HashedBoard2': layout.render_hashed(nonces),
   476              'State': 'P1-WIN'
   477          })
   478  
   479          self.expect_invalid()
   480  
   481      def test_fire_wrong_turn(self):
   482          layout = BoardLayout(10)
   483          layout.append(
   484              ShipPosition(text='AA', row=0, column=0, orientation='horizontal'))
   485          nonces = create_nonces(10)
   486  
   487          # Send join payload to tp
   488          fire_req = create_fire_payload('invalid_row', '1', 'Z')
   489  
   490          self.validator.send(
   491              self.player_1.create_tp_process_request(fire_req)
   492          )
   493  
   494          # give state back to tp
   495  
   496          self.send_state_to_tp('invalid_row', {
   497              'Ships': ['AA'],
   498              'TargetBoard1': [['?'] * 10 for _ in range(10)],
   499              'TargetBoard2': [['?'] * 10 for _ in range(10)],
   500              'Player1': self.public_key_1,
   501              'Player2': self.public_key_2,
   502              'HashedBoard1': layout.render_hashed(nonces),
   503              'HashedBoard2': layout.render_hashed(nonces),
   504              'State': 'P2-NEXT'
   505          })
   506  
   507          self.expect_invalid()
   508  
   509      # Helper methods
   510  
   511      def create_game(self, name, ships=None, player=None):
   512          ships = ships if ships else self.test_ships
   513  
   514          player = player if player else self.player_1
   515  
   516          req_to_tp = player.create_tp_process_request({
   517              'Action': 'CREATE',
   518              'Name': name,
   519              'Ships': ships
   520          })
   521          self.validator.send(req_to_tp)
   522  
   523      def send_state_to_tp(self, name, state=None, player=None):
   524          state = state if state else {}
   525          player = player if player else self.player_1
   526  
   527          get_req_from_tp = self.validator.expect(
   528              player.create_get_request(name))
   529          get_res_from_validator = player.create_get_response(name, state)
   530          self.validator.respond(get_res_from_validator, get_req_from_tp)
   531  
   532      def update_state(self, name, state=None, player=None):
   533          state = state if state else {}
   534          player = player if player else self.player_1
   535          LOGGER.debug('expecing set')
   536          set_req_from_tp = self.validator.expect(
   537              player.create_set_request(name, state))
   538          self.validator.respond(
   539              player.create_set_response(name), set_req_from_tp)
   540  
   541      def expect_ok(self):
   542          self.expect_tp_response('OK')
   543  
   544      def expect_invalid(self):
   545          self.expect_tp_response('INVALID_TRANSACTION')
   546  
   547      def expect_tp_response(self, response):
   548          self.validator.expect(
   549              self.player_1.create_tp_response(
   550                  response))
   551  
   552  
   553  def compare_set_request(req1, req2):
   554      if len(req1.entries) != len(req2.entries):
   555          return False
   556  
   557      entries1 = sorted(
   558          [(e.address, json.loads(e.data.decode(), encoding="utf-8"))
   559              for e in req1.entries])
   560      entries2 = sorted(
   561          [(e.address, json.loads(e.data.decode(), encoding="utf-8"))
   562              for e in req2.entries])
   563      if entries1 != entries2:
   564          return False
   565  
   566      return True
   567  
   568  
   569  def create_join_payload(name, board_layout, nonces):
   570      return {
   571          'Action': 'JOIN',
   572          'Name': name,
   573          'Board': board_layout.render_hashed(nonces)
   574      }
   575  
   576  
   577  def create_fire_payload(
   578          name,
   579          row,
   580          column,
   581          reveal_space=None,
   582          reveal_nonce=None):
   583      return {
   584          'Action': 'FIRE',
   585          'Name': name,
   586          'Row': row,
   587          'Column': column,
   588          'RevealSpace': reveal_space,
   589          'RevealNonce': reveal_nonce
   590      }