class Hero:
    username: str
    health: float
    damage: float
    level: int
 
    def __init__(self, username: str, level: int, health: float, damage: float):
        self.username = username
        self.level = level
        self.health = health
        self.damage = damage
 
    def battle(self, enemy_hero):
        if enemy_hero.username == self.username:
            raise Exception("You cannot fight yourself")
 
        if self.health <= 0:
            raise ValueError("Your health is lower than or equal to 0. You need to rest")
 
        if enemy_hero.health <= 0:
            raise ValueError(f"You cannot fight {enemy_hero.username}. He needs to rest")
 
        player_damage = self.damage * self.level
        enemy_hero_damage = enemy_hero.damage * enemy_hero.level
 
        self.health -= enemy_hero_damage
        enemy_hero.health -= player_damage
 
        if self.health <= 0 and enemy_hero.health <= 0:
            return "Draw"
 
        if enemy_hero.health <= 0:
            self.level += 1
            self.health += 5
            self.damage += 5
            return "You win"
 
        enemy_hero.level += 1
        enemy_hero.health += 5
        enemy_hero.damage += 5
        return "You lose"
 
    def __str__(self):
        return f"Hero {self.username}: {self.level} lvl\n" \
               f"Health: {self.health}\n" \
               f"Damage: {self.damage}\n"
               
import unittest

class TestHero(unittest.TestCase):
    def setUp(self):
        self.hero = Hero('Hero', 1, 100, 100)
        self.enemy = Hero('Enemy', 1, 50, 50)
        
        
    def test_init(self):
        self.assertEqual('Hero', self.hero.username)
        self.assertEqual(1, self.hero.level)
        self.assertEqual(100, self.hero.health)
        self.assertEqual(100, self.hero.damage)
        
    def test_hero_is_the_same_as_enemy_raise_exception(self):
        with self.assertRaises(Exception) as ex:
            self.hero.battle(self.hero)
        self.assertEqual("You cannot fight yourself", str(ex.exception))
        
    def test_hero_with_zero_energy(self):
        self.hero.health = 0
        with self.assertRaises(ValueError) as ex:
            self.hero.battle(self.enemy)
        self.assertEqual("Your health is lower than or equal to 0. You need to rest", str(ex.exception))
        
    def test_enemy_with_zero_energy(self):
        self.enemy.health = 0
        with self.assertRaises(ValueError) as ex:
            self.hero.battle(self.enemy)
        self.assertEqual("You cannot fight Enemy. He needs to rest", str(ex.exception))
        
    def test_battle_returns_draw(self):
        self.hero.health = 50
        result = self.hero.battle(self.enemy)
        
        self.assertEqual(0, self.hero.health)
        self.assertEqual(-50, self.enemy.health)
        self.assertEqual('Draw', result)
        
    def test_battle_enemy_and_win_expect(self):
        result = self.hero.battle(self.enemy)
        self.assertEqual(2, self.hero.level)
        self.assertEqual(55, self.hero.health)
        self.assertEqual(105, self.hero.damage)
        self.assertEqual('You win', result)
        
    def test_battle_enemy_and_lose_expect_hero_stats_improve(self):
        self.hero, self.enemy = self.enemy, self.hero
        result = self.hero.battle(self.enemy)
        
        self.assertEqual(2, self.enemy.level)
        self.assertEqual(55, self.enemy.health)
        self.assertEqual(105, self.enemy.damage)
        self.assertEqual("You lose", result)
        
    
    def test_correct_str(self):
        self.assertEqual(
            f"Hero Hero: 1 lvl\n" \
            f"Health: 100\n" \
            f"Damage: 100\n", str(self.hero))
        
if __name__ == '__main__':
    unittest.main()