def test_hack_and_slash_execute(fx_user, fx_novice_status): move = fx_user.create_novice(fx_novice_status) Block.create(fx_user, [move]) avatar = fx_user.avatar() avatar.hp = 0 move = fx_user.move(HackAndSlash()) Block.create(fx_user, [move]) with raises(InvalidMoveError): move.execute(avatar)
def test_execute_raise_invalid_move_error( fx_user: User, fx_novice_status: typing.Mapping[str, str]): move = fx_user.create_novice(fx_novice_status) Block.create(fx_user, [move]) for move_type in Move.__subclasses__(): move = fx_user.move(move_type()) assert not move.block assert not move.block_id with raises(InvalidMoveError): move.execute()
def test_sync_node_unavailable_on_get_last_block( fx_user: User, fx_session: scoped_session, fx_other_session: Session, fx_server: WSGIServer, fx_novice_status: typing.Mapping[str, str], code: int): move = fx_user.create_novice(fx_novice_status) Block.create(fx_user, [move]) with Mocker() as m: m.get(url=f'{fx_server.url}/blocks/last', status_code=code) Block.sync(Node(url=fx_server.url), fx_other_session) assert fx_other_session.query(Block).count() == 0
def test_ensure_block(fx_user: User): class ValidMove(Move): __mapper_args__ = { 'polymorphic_identity': 'valid', } move = fx_user.move(ValidMove()) Block.create(fx_user, [move]) assert move.block assert move.block_id with raises(NotImplementedError): move.execute()
def test_sync(fx_user, fx_session, fx_other_user, fx_other_session, fx_server, fx_novice_status): assert fx_other_session.query(Block).count() == 0 assert fx_session.query(Block).count() == 0 Block.sync(Node(url=fx_server.url), fx_other_session) assert fx_other_session.query(Block).count() == 0 assert fx_session.query(Block).count() == 0 Block.create(fx_other_user, []) Block.sync(Node(url=fx_server.url), fx_other_session) assert fx_other_session.query(Block).count() == 1 assert fx_session.query(Block).count() == 0 move = fx_user.create_novice(fx_novice_status) Block.create(fx_user, [move]) Block.create(fx_user, []) Block.create(fx_user, []) assert fx_other_session.query(Block).count() == 1 assert fx_other_session.query(Move).count() == 0 assert fx_session.query(Block).count() == 3 assert fx_session.query(Move).count() == 1 Block.sync(Node(url=fx_server.url), fx_other_session) assert fx_other_session.query(Block).count() == 3 assert fx_other_session.query(Move).count() == 1 assert fx_session.query(Block).count() == 3 assert fx_session.query(Move).count() == 1
def test_move(fx_test_client, fx_session, fx_user, fx_private_key): rv = fx_test_client.post('/login', data={ 'private_key': fx_private_key.to_hex(), 'name': 'test_user', }, follow_redirects=True) rv = fx_test_client.post('/new') Block.create(fx_user, fx_session.query(Move).filter_by(block_id=None).all()) rv = fx_test_client.post('/session_moves', data={ 'name': 'first_class', 'class_': 'swordman', }, follow_redirects=True) Block.create(fx_user, fx_session.query(Move).filter_by(block_id=None).all()) avatar = fx_user.avatar() assert avatar.class_ == 'swordman' rv = fx_test_client.get('/') assert rv.status == '200 OK' rv = fx_test_client.post('/session_moves', data={'name': 'hack_and_slash'}, follow_redirects=True) assert rv.status == '200 OK' Block.create(fx_user, fx_session.query(Move).filter_by(block_id=None).all()) rv = fx_test_client.post('/session_moves', data={'name': 'sleep'}, follow_redirects=True) assert rv.status == '200 OK' Block.create(fx_user, fx_session.query(Move).filter_by(block_id=None).all()) rv = fx_test_client.post('/session_moves', data={ 'name': 'say', 'content': 'hi!', }, follow_redirects=True) assert rv.status == '200 OK' Block.create(fx_user, fx_session.query(Move).filter_by(block_id=None).all())
def test_sync_node_unaviable_on_branch_point( fx_user: User, fx_session: scoped_session, fx_server: WSGIServer, fx_other_session: Session, fx_novice_status: typing.Mapping[str, str], code: int): move = fx_user.create_novice(fx_novice_status) block = Block.create(fx_user, [move]) Block.sync(Node(url=fx_server.url), fx_other_session) assert fx_other_session.query(Block).count() == 1 serialized = block.serialize(use_bencode=False, include_suffix=True, include_moves=True, include_hash=True) serialized['id'] = block.id + 1 with Mocker() as m: m.register_uri( 'GET', f'{fx_server.url}/blocks/last', json={'block': serialized}, status_code=200, ) m.register_uri( 'GET', f'{fx_server.url}/blocks/1', status_code=code, ) assert not Block.sync(Node(url=fx_server.url), fx_other_session)
def test_broadcast_block( fx_server: WSGIServer, fx_session: scoped_session, fx_other_session: Session, fx_other_server: WSGIServer, fx_user: User ): now = datetime.datetime.utcnow() node = Node(url=fx_server.url, last_connected_at=now) node2 = Node(url=fx_other_server.url, last_connected_at=datetime.datetime.utcnow()) block = Block.create(fx_user, []) fx_session.add_all([node, node2, block]) fx_session.flush() assert fx_session.query(Block).get(block.id) assert not fx_other_session.query(Block).get(block.id) multicast( serialized=block.serialize( use_bencode=False, include_suffix=True, include_moves=True, include_hash=True ), broadcast=broadcast_block, ) assert node.last_connected_at > now assert fx_session.query(Block).count() == 1 assert fx_other_session.query(Block).get(block.id)
def test_block_validation(fx_user, fx_novice_status): move = fx_user.create_novice(fx_novice_status) block = Block.create(fx_user, [move]) assert block.valid move.id = ('00000000000000000000000000000000' '00000000000000000000000000000000') assert not block.valid
def test_broadcast_block_retry(fx_session: scoped_session, fx_user: User, limit: int, blocks: int, expected: int): for i in range(blocks): block = Block.create(fx_user, []) url = 'http://test.neko' now = datetime.datetime.utcnow() node = Node(url=url, last_connected_at=now) fx_session.add(node) fx_session.flush() patch = unittest.mock.patch('nekoyume.broadcast.BROADCAST_LIMIT', limit) with mock() as m, patch: m.register_uri('POST', 'http://test.neko/blocks', [{ 'json': { 'result': 'failed', 'block_id': 0, 'mesage': "new block isn't our next block." }, 'status_code': 403 }, { 'json': { 'result': 'success', }, 'status_code': 200 }]) broadcast_block( block.serialize(use_bencode=False, include_suffix=True, include_moves=True, include_hash=True)) assert m.call_count == expected assert node.last_connected_at > now
def test_get_blocks(fx_test_client, fx_user): move = fx_user.sleep() rv = fx_test_client.get(f'/moves/{move.id}') assert rv.status == '200 OK' assert move.id.encode() in rv.data block = Block.create(fx_user, [move]) rv = fx_test_client.get(f'/blocks') assert rv.status == '200 OK' assert block.hash.encode() in rv.data assert move.id.encode() in rv.data rv = fx_test_client.get(f'/blocks/{block.id}') assert rv.status == '200 OK' assert block.hash.encode() in rv.data assert move.id.encode() in rv.data rv = fx_test_client.get(f'/blocks/{block.hash}') assert rv.status == '200 OK' assert block.hash.encode() in rv.data rv = fx_test_client.get(f'/blocks/last') assert rv.status == '200 OK' assert block.hash.encode() in rv.data
def test_post_block_return_block_id(fx_test_client: FlaskClient, fx_user: User, fx_session: scoped_session): block = Block.create(fx_user, []) fx_session.add(block) fx_session.commit() block2 = Block.create(fx_user, []) des = block2.serialize(use_bencode=False, include_suffix=True, include_moves=True, include_hash=True) des['id'] = 3 resp = fx_test_client.post('/blocks', data=json.dumps(des), content_type='application/json') assert resp.status_code == 403 data = json.loads(resp.get_data()) assert data['result'] == 'failed' assert data['message'] == "new block isn't our next block." assert data['block_id'] == 2
def test_prevent_hack_and_slash_when_dead( fx_test_client: FlaskClient, fx_session: Session, fx_user: User, fx_private_key: PrivateKey, fx_novice_status: typing.Dict[str, str], ): move = fx_user.create_novice(fx_novice_status) Block.create(fx_user, [move]) assert fx_user.avatar().dead is False while fx_user.avatar().hp > 0: move = fx_user.hack_and_slash() Block.create(fx_user, [move]) assert fx_user.avatar().dead is True response = fx_test_client.post('/session_moves', data={'name': 'hack_and_slash'}) assert response.status_code == 302
def test_broadcast_block_same_node(fx_session: scoped_session, fx_user: User): block = Block.create(fx_user, []) url = 'http://test.neko' now = datetime.datetime.utcnow() node = Node(url=url, last_connected_at=now) fx_session.add(node) fx_session.flush() broadcast_block(block.serialize(use_bencode=False, include_suffix=True, include_moves=True, include_hash=True), sent_node=node) assert node.last_connected_at == now
def test_post_move_broadcasting( fx_test_client: FlaskClient, fx_user: User, fx_private_key: PrivateKey, fx_session: scoped_session, ): with unittest.mock.patch('nekoyume.game.multicast') as m: fx_test_client.post('/login', data={ 'private_key': fx_private_key.to_hex(), 'name': 'test_user', }, follow_redirects=True) fx_test_client.post('/new') Block.create(fx_user, fx_session.query(Move).filter_by(block_id=None).all()) assert not get_unconfirmed_move(fx_user.address) res = fx_test_client.post('/session_moves', data={'name': 'hack_and_slash'}) assert res.status_code == 302 move = fx_session.query(Move).filter( Move.name == 'hack_and_slash', ).first() assert move serialized = move.serialize( use_bencode=False, include_signature=True, include_id=True, ) assert m.called args = m.call_args[1] assert serialized == args['serialized'] my_node = args['my_node'] assert isinstance(my_node, Node) assert my_node.url == 'http://localhost' broadcast = args['broadcast'] assert isinstance(broadcast, typing.Callable) assert broadcast.__name__ == 'broadcast_move'
def test_avatar_basic_moves(fx_user, fx_novice_status): moves = [ CreateNovice(details=fx_novice_status), HackAndSlash(), Sleep(), Say(details={'content': 'hi'}), LevelUp(details={'new_status': 'strength'}), ] for move in moves: move = fx_user.move(move) block = Block.create(fx_user, [move]) assert move.valid assert move.confirmed assert block.valid assert fx_user.avatar(block.id)
def test_broadcast_block_raise_exception(fx_session: scoped_session, fx_user: User, error: typing.Union[ConnectionError, Timeout]): block = Block.create(fx_user, []) url = 'http://test.neko' now = datetime.datetime.utcnow() node = Node(url=url, last_connected_at=now) fx_session.add(node) fx_session.flush() with Mocker() as m: m.post('http://test.neko/blocks', exc=error) broadcast_block( block.serialize(use_bencode=False, include_suffix=True, include_moves=True, include_hash=True)) assert node.last_connected_at == now
def test_move_confirmed_and_validation(fx_user, fx_novice_status): move = Move() assert not move.confirmed assert not move.valid move = fx_user.create_novice(fx_novice_status) assert not move.confirmed assert move.valid block = Block.create(fx_user, [move]) assert move.block_id assert move.confirmed assert move.valid move.tax = 1 assert not move.valid assert not block.valid
def test_broadcast_block_my_node(fx_session: scoped_session, fx_user: User): block = Block.create(fx_user, []) url = 'http://test.neko' now = datetime.datetime.utcnow() node = Node(url=url, last_connected_at=now) fx_session.add(node) fx_session.flush() with Mocker() as m: m.post('http://test.neko/blocks', text='success') expected = serialized = block.serialize(use_bencode=False, include_suffix=True, include_moves=True, include_hash=True) broadcast_block(serialized, my_node=node) expected['sent_node'] = url assert node.last_connected_at > now assert node.last_connected_at > now # check request.json value assert m.request_history[0].json() == expected
def test_block_broadcast(fx_session: scoped_session, fx_user: User): block = Block.create(fx_user, []) fx_session.add(block) fx_session.commit() with unittest.mock.patch('nekoyume.tasks.broadcast_block') as m: block_broadcast(block.id, 'http://localhost:5000', 'http://localhost:5001', session=fx_session) serialized = block.serialize(use_bencode=False, include_suffix=True, include_moves=True, include_hash=True) assert m.called args = m.call_args[1] assert serialized == args['serialized'] assert isinstance(args['sent_node'], Node) assert args['sent_node'].url == 'http://localhost:5000' assert isinstance(args['my_node'], Node) assert args['my_node'].url == 'http://localhost:5001'
def test_level_up(fx_user, fx_novice_status): move = fx_user.create_novice(fx_novice_status) Block.create(fx_user, [move]) while True: if fx_user.avatar().exp >= 8: break move = fx_user.hack_and_slash() Block.create(fx_user, [move]) if fx_user.avatar().hp <= 0: move = fx_user.sleep() Block.create(fx_user, [move]) assert fx_user.avatar().level >= 2
def test_find_branch_point(fx_session: scoped_session, fx_server: WSGIServer, fx_user: User, value: int, high: int, expected: int): node = Node(url=fx_server.url) Block.create(fx_user, []) assert find_branch_point(node, fx_session, value, high) == expected
def test_flush_session_while_syncing(fx_user, fx_session, fx_other_session, fx_novice_status): # 1. block validation failure scenario # syncing without flushing can cause block validation failure move = fx_user.create_novice(fx_novice_status) invalid_block = Block.create(fx_user, [move]) fx_session.delete(invalid_block) # syncing valid blocks from another node new_blocks = [ { "created_at": "2018-04-13 11:36:17.920869", "creator": "ET8ngv45qwhkDiJS1ZrUxndcGTzHxjPZDs", "difficulty": 0, "hash": "da0182c494660af0d9dd288839ceb86498708f38c800363cd46ed1730013a4d8", # noqa "id": 1, "version": 2, "moves": [], "prev_hash": None, "root_hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", # noqa "suffix": "00" }, { "created_at": "2018-04-13 11:36:17.935392", "creator": "ET8ngv45qwhkDiJS1ZrUxndcGTzHxjPZDs", "difficulty": 1, "hash": "014c44b9382a45c2a70d817c090e6b78af22b8f34b57fd7edb474344f25c439c", # noqa "id": 2, "version": 2, "moves": [], "prev_hash": "da0182c494660af0d9dd288839ceb86498708f38c800363cd46ed1730013a4d8", # noqa "root_hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", # noqa "suffix": "0b" } ] def add_block(new_block): block = Block.deserialize(new_block) fx_session.add(block) return block valid_block1 = add_block(new_blocks[0]) valid_block2 = add_block(new_blocks[1]) assert invalid_block.hash == \ fx_session.query(Block).get(valid_block2.id - 1).hash assert valid_block2.valid is False fx_session.query(Block).delete() # 2. valid scenario # flush session after deleting the invalid block move = fx_user.create_novice(fx_novice_status) invalid_block = Block.create(fx_user, [move]) fx_session.delete(invalid_block) fx_session.flush() valid_block1 = add_block(new_blocks[0]) valid_block2 = add_block(new_blocks[1]) assert valid_block1.hash == \ fx_session.query(Block).get(valid_block2.id - 1).hash assert valid_block2.valid