text: import pytest
  from eth_tester.exceptions import TransactionFailed
  
  
  # web3 returns f"execution reverted: {err_str}"
  # TODO move exception string parsing logic into assert_tx_failed
  def _fixup_err_str(s):
      return s.replace("execution reverted: ", "")
  
  
  def test_assert_refund(w3, get_contract_with_gas_estimation, assert_tx_failed):
      code = """
  @external
  def foo():
      raise
      """
      c = get_contract_with_gas_estimation(code)
      a0 = w3.eth.accounts[0]
      gas_sent = 10 ** 6
      tx_hash = c.foo(transact={"from": a0, "gas": gas_sent, "gasPrice": 10})
      # More info on receipt status:
      # https://github.com/ethereum/EIPs/blob/master/EIPS/eip-658.md#specification.
      tx_receipt = w3.eth.get_transaction_receipt(tx_hash)
      assert tx_receipt["status"] == 0
      # Checks for gas refund from revert
      assert tx_receipt["gasUsed"] < gas_sent
  
  
  def test_assert_reason(w3, get_contract_with_gas_estimation, assert_tx_failed, memory_mocker):
      code = """
  @external
  def test(a: int128) -> int128:
      assert a > 1, "larger than one please"
      return 1 + a
  
  @external
  def test2(a: int128, b: int128, extra_reason: String[32]) -> int128:
      c: int128 = 11
      assert a > 1, "a is not large enough"
      assert b == 1, concat("b may only be 1", extra_reason)
      return a + b + c
  
  @external
  def test3(reason_str: String[32]):
      raise reason_str
      """
      c = get_contract_with_gas_estimation(code)
  
      assert c.test(2) == 3
      with pytest.raises(TransactionFailed) as e_info:
          c.test(0)
  
      assert _fixup_err_str(e_info.value.args[0]) == "larger than one please"
      # a = 0, b = 1
      with pytest.raises(TransactionFailed) as e_info:
          c.test2(0, 1, "")
      assert _fixup_err_str(e_info.value.args[0]) == "a is not large enough"
      # a = 1, b = 0
      with pytest.raises(TransactionFailed) as e_info:
          c.test2(2, 2, " because I said so")
      assert _fixup_err_str(e_info.value.args[0]) == "b may only be 1" + " because I said so"
      # return correct value
      assert c.test2(5, 1, "") == 17
  
      with pytest.raises(TransactionFailed) as e_info:
          c.test3("An exception")
      assert _fixup_err_str(e_info.value.args[0]) == "An exception"
  
  
  invalid_code = [
      """
  @external
  def test(a: int128) -> int128:
      assert a > 1, ""
      return 1 + a
      """,
      """
  @external
  def test(a: int128) -> int128:
      raise ""
      """,
      """
  @external
  def test():
      assert create_forwarder_to(self)
      """,
  ]
  
  
  @pytest.mark.parametrize("code", invalid_code)
  def test_invalid_assertions(get_contract, assert_compile_failed, code):
      assert_compile_failed(lambda: get_contract(code))
  
  
  valid_code = [
      """
  @external
  def mint(_to: address, _value: uint256):
      raise
      """,
      """
  @internal
  def ret1() -> int128:
      return 1
  @external
  def test():
      assert self.ret1() == 1
      """,
      """
  @internal
  def valid_address(sender: address) -> bool:
      selfdestruct(sender)
  @external
  def test():
      assert self.valid_address(msg.sender)
      """,
      """
  @external
  def test():
      assert raw_call(msg.sender, b'', max_outsize=1, gas=10, value=1000*1000) == b''
      """,
      """
  @external
  def test():
      assert create_forwarder_to(self) == self
      """,
  ]
  
  
  @pytest.mark.parametrize("code", valid_code)
  def test_valid_assertions(get_contract, code):
      get_contract(code)
  
  
  def test_assert_staticcall(get_contract, assert_tx_failed, memory_mocker):
      foreign_code = """
  state: uint256
  @external
  def not_really_constant() -> uint256:
      self.state += 1
      return self.state
      """
      code = """
  interface ForeignContract:
      def not_really_constant() -> uint256: view
  
  @external
  def test():
      assert ForeignContract(msg.sender).not_really_constant() == 1
      """
      c1 = get_contract(foreign_code)
      c2 = get_contract(code, *[c1.address])
      # static call prohibits state change
      assert_tx_failed(lambda: c2.test())
  
  
  def test_assert_in_for_loop(get_contract, assert_tx_failed, memory_mocker):
      code = """
  @external
  def test(x: uint256[3]) -> bool:
      for i in range(3):
          assert x[i] < 5
      return True
      """
  
      c = get_contract(code)
  
      c.test([1, 2, 3])
      assert_tx_failed(lambda: c.test([5, 1, 3]))
      assert_tx_failed(lambda: c.test([1, 5, 3]))
      assert_tx_failed(lambda: c.test([1, 3, 5]))
  
  
  def test_assert_with_reason_in_for_loop(get_contract, assert_tx_failed, memory_mocker):
      code = """
  @external
  def test(x: uint256[3]) -> bool:
      for i in range(3):
          assert x[i] < 5, "because reasons"
      return True
      """
  
      c = get_contract(code)
  
      c.test([1, 2, 3])
      assert_tx_failed(lambda: c.test([5, 1, 3]))
      assert_tx_failed(lambda: c.test([1, 5, 3]))
      assert_tx_failed(lambda: c.test([1, 3, 5]))
  
  
  def test_assert_reason_revert_length(w3, get_contract, assert_tx_failed, memory_mocker):
      code = """
  @external
  def test() -> int128:
      assert 1 == 2, "oops"
      return 1
  """
      c = get_contract(code)
      assert_tx_failed(lambda: c.test(), exc_text="oops")
  