Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Chore: Increase Test Coverage #56

Merged
merged 8 commits into from
Jul 21, 2023
11 changes: 2 additions & 9 deletions internal/core/entity/battle.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package entity

import (
"errors"
"fmt"
"math/rand"
"time"

Expand All @@ -27,10 +26,7 @@ func (b *Battle) PartnerAttack() error {
return ErrInvalidState
}
// inflict damage to enemy
damage, err := b.Enemy.InflictDamage(*b.Partner)
if err != nil {
return fmt.Errorf("unable to inflict damage to enemy due: %w", err)
}
damage := b.Enemy.InflictDamage(*b.Partner)
// set enemy last damage
b.LastDamage.Enemy = damage
// set battle state accordingly
Expand Down Expand Up @@ -63,10 +59,7 @@ func (b *Battle) EnemyAttack() error {
return ErrInvalidState
}
// inflict damage to partner
damage, err := b.Partner.InflictDamage(*b.Enemy)
if err != nil {
return fmt.Errorf("unable to inflict damage to partner due: %w", err)
}
damage := b.Partner.InflictDamage(*b.Enemy)
// set partner last damage
b.LastDamage.Partner = damage
// set battle state accordingly
Expand Down
55 changes: 41 additions & 14 deletions internal/core/entity/battle_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,22 @@ package entity_test

import (
"fmt"
"math"
"testing"
"time"

"github.com/Haraj-backend/hex-monscape/internal/core/entity"
"github.com/Haraj-backend/hex-monscape/internal/core/testutil"
"github.com/google/uuid"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

func TestNewBattle(t *testing.T) {
// define function for validating battle
validateBattle := func(t *testing.T, battle entity.Battle, cfg entity.BattleConfig) {
assert.NotEmpty(t, battle.GameID, "GameID is empty")
assert.NotEmpty(t, battle.Partner, "Partner is empty")
assert.NotEmpty(t, battle.Enemy, "Enemy is empty")
require.NotEmpty(t, battle.GameID, "GameID is empty")
require.NotEmpty(t, battle.Partner, "Partner is empty")
require.NotEmpty(t, battle.Enemy, "Enemy is empty")
}
// define test cases
testCases := []struct {
Expand All @@ -43,7 +44,7 @@ func TestNewBattle(t *testing.T) {
for _, testCase := range testCases {
t.Run(testCase.Name, func(t *testing.T) {
battle, err := entity.NewBattle(testCase.Config)
assert.Equal(t, testCase.IsError, (err != nil), "unexpected error")
require.Equal(t, testCase.IsError, (err != nil), "unexpected error")
if battle == nil {
return
}
Expand Down Expand Up @@ -118,14 +119,27 @@ func TestPartnerAttack(t *testing.T) {

battle.State = testCase.State
err := battle.PartnerAttack()
assert.Equal(t, testCase.IsError, (err != nil), "unexpected error")
require.Equal(t, testCase.IsError, (err != nil), "unexpected error")
if !testCase.IsError {
assert.Equal(t, battle.Enemy.BattleStats.Health, testCase.ExpectedEnemyHealth, "enemy health is not valid")
require.Equal(t, battle.Enemy.BattleStats.Health, testCase.ExpectedEnemyHealth, "enemy health is not valid")
}
})
}
}

func TestPartnerAttackWin(t *testing.T) {
battle := initNewBattle()
// set partner attack to very high number so that enemy will be dead
battle.Partner.BattleStats.Attack = math.MaxInt
// set battle state to partner attack
battle.State = entity.StatePartnerTurn
// execute partner attack, enemy should be dead
err := battle.PartnerAttack()
require.NoError(t, err)
// validate battle state
require.Equal(t, entity.StateWin, battle.State)
}

func TestPartnerSurrender(t *testing.T) {
battle := initNewBattle()
// define test cases
Expand Down Expand Up @@ -155,9 +169,9 @@ func TestPartnerSurrender(t *testing.T) {
t.Run(testCase.Name, func(t *testing.T) {
battle.State = testCase.State
err := battle.PartnerSurrender()
assert.Equal(t, testCase.IsError, (err != nil), "unexpected error")
require.Equal(t, testCase.IsError, (err != nil), "unexpected error")
if !testCase.IsError {
assert.Equal(t, entity.StateLose, battle.State)
require.Equal(t, entity.StateLose, battle.State)
}
})
}
Expand Down Expand Up @@ -235,14 +249,27 @@ func TestEnemyAttack(t *testing.T) {
})
battle.State = testCase.State
err := battle.EnemyAttack()
assert.Equal(t, testCase.IsError, (err != nil), "unexpected error")
require.Equal(t, testCase.IsError, (err != nil), "unexpected error")
if !testCase.IsError {
assert.Equal(t, battle.Partner.BattleStats.Health, testCase.ExpectedPartnerHealth, "partner health is not valid")
require.Equal(t, battle.Partner.BattleStats.Health, testCase.ExpectedPartnerHealth, "partner health is not valid")
}
})
}
}

func TestEnemyAttackWin(t *testing.T) {
battle := initNewBattle()
// set enemy attack to very high number so that partner will be dead
battle.Enemy.BattleStats.Attack = math.MaxInt
// set battle state to enemy attack
battle.State = entity.StateEnemyTurn
// execute enemy attack, partner should be dead
err := battle.EnemyAttack()
require.NoError(t, err)
// validate battle state
require.Equal(t, entity.StateLose, battle.State)
}

func TestIsEnded(t *testing.T) {
battle := initNewBattle()
// define test cases
Expand Down Expand Up @@ -283,7 +310,7 @@ func TestIsEnded(t *testing.T) {
t.Run(testCase.Name, func(t *testing.T) {
battle.State = testCase.State
actual := battle.IsEnded()
assert.Equal(t, testCase.Expected, actual, "unexpected dead")
require.Equal(t, testCase.Expected, actual, "unexpected dead")
})
}
}
Expand Down Expand Up @@ -383,9 +410,9 @@ func TestDecideTurn(t *testing.T) {
})
battle.State = testCase.State
state, err := battle.DecideTurn()
assert.Equal(t, testCase.IsError, (err != nil), "unexpected error")
require.Equal(t, testCase.IsError, (err != nil), "unexpected error")
if !testCase.IsError {
assert.Equal(t, testCase.ExpectedState, state, "expected state is not valid")
require.Equal(t, testCase.ExpectedState, state, "expected state is not valid")
}
})
}
Expand Down
9 changes: 5 additions & 4 deletions internal/core/entity/monster.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package entity

const minDamage = 5
const MinDamage = 5

type Monster struct {
ID string
Expand All @@ -21,10 +21,11 @@ func (p Monster) IsDead() bool {

// InflictDamage is used for inflicting damage to self based
// on given enemy. Returned the damage amount.
func (p *Monster) InflictDamage(enemy Monster) (int, error) {
dmg := max(enemy.BattleStats.Attack-p.BattleStats.Defense, minDamage)
func (p *Monster) InflictDamage(enemy Monster) int {
dmg := max(enemy.BattleStats.Attack-p.BattleStats.Defense, MinDamage)
p.BattleStats.Health -= dmg
return dmg, nil

return dmg
}

type BattleStats struct {
Expand Down
95 changes: 73 additions & 22 deletions internal/core/entity/monster_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@ package entity_test
import (
"fmt"
"testing"
"time"

"github.com/Haraj-backend/hex-monscape/internal/core/entity"
"github.com/Haraj-backend/hex-monscape/internal/core/testutil"
"github.com/google/uuid"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

func TestIsDead(t *testing.T) {
Expand All @@ -21,47 +21,47 @@ func TestIsDead(t *testing.T) {
Name: "Monster is Not Dead",
Monster: entity.Monster{
ID: uuid.NewString(),
Name: fmt.Sprintf("monster_%v", time.Now().Unix()),
Name: generateMonsterName(false),
BattleStats: entity.BattleStats{
Health: 100,
MaxHealth: 100,
Attack: 100,
Defense: 100,
Speed: 100,
},
AvatarURL: fmt.Sprintf("https://example.com/%v", time.Now().Unix()),
AvatarURL: generateAvatarURL(),
},
Expected: false,
},
{
Name: "Monster Has 0 Health",
Monster: entity.Monster{
ID: uuid.NewString(),
Name: fmt.Sprintf("monster_%v", time.Now().Unix()),
Name: generateMonsterName(false),
BattleStats: entity.BattleStats{
Health: 0,
MaxHealth: 100,
Attack: 100,
Defense: 100,
Speed: 100,
},
AvatarURL: fmt.Sprintf("https://example.com/%v", time.Now().Unix()),
AvatarURL: generateAvatarURL(),
},
Expected: true,
},
{
Name: "Monster Has Negative Health",
Monster: entity.Monster{
ID: uuid.NewString(),
Name: fmt.Sprintf("monster_%v", time.Now().Unix()),
Name: generateMonsterName(false),
BattleStats: entity.BattleStats{
Health: -100,
MaxHealth: 100,
Attack: 100,
Defense: 100,
Speed: 100,
},
AvatarURL: fmt.Sprintf("https://example.com/%v", time.Now().Unix()),
AvatarURL: generateAvatarURL(),
},
Expected: true,
},
Expand All @@ -71,7 +71,7 @@ func TestIsDead(t *testing.T) {
for _, testCase := range testCases {
t.Run(testCase.Name, func(t *testing.T) {
actual := testCase.Monster.IsDead()
assert.Equal(t, testCase.Expected, actual, "unexpected dead")
require.Equal(t, testCase.Expected, actual, "unexpected dead")
})
}
}
Expand All @@ -88,68 +88,119 @@ func TestInflictDamage(t *testing.T) {
Name: "Monster Get Zero Damage",
Monster: entity.Monster{
ID: uuid.NewString(),
Name: fmt.Sprintf("monster_%v", time.Now().Unix()),
Name: generateMonsterName(false),
BattleStats: entity.BattleStats{
Health: 100,
MaxHealth: 100,
Attack: 100,
Defense: 0,
Speed: 100,
},
AvatarURL: fmt.Sprintf("https://example.com/%v", time.Now().Unix()),
AvatarURL: generateAvatarURL(),
},
Enemy: entity.Monster{
ID: uuid.NewString(),
Name: fmt.Sprintf("enemy_%v", time.Now().Unix()),
Name: generateMonsterName(true),
BattleStats: entity.BattleStats{
Health: 100,
MaxHealth: 100,
Attack: 100,
Defense: 100,
Speed: 100,
},
AvatarURL: fmt.Sprintf("https://example.com/%v", time.Now().Unix()),
AvatarURL: generateAvatarURL(),
},
ExpectedHealthAmount: 0,
},
{
Name: "Monster Get 50 Damage",
Monster: entity.Monster{
ID: uuid.NewString(),
Name: fmt.Sprintf("monster_%v", time.Now().Unix()),
Name: generateMonsterName(false),
BattleStats: entity.BattleStats{
Health: 100,
MaxHealth: 100,
Attack: 100,
Defense: 50,
Speed: 100,
},
AvatarURL: fmt.Sprintf("https://example.com/%v", time.Now().Unix()),
AvatarURL: generateAvatarURL(),
},
Enemy: entity.Monster{
ID: uuid.NewString(),
Name: fmt.Sprintf("enemy_%v", time.Now().Unix()),
Name: generateMonsterName(true),
BattleStats: entity.BattleStats{
Health: 100,
MaxHealth: 100,
Attack: 100,
Defense: 100,
Speed: 100,
},
AvatarURL: fmt.Sprintf("https://example.com/%v", time.Now().Unix()),
AvatarURL: generateAvatarURL(),
},
ExpectedHealthAmount: 50,
},
{
Name: "Enemy Attack is Lower Than Monster Defense",
Monster: entity.Monster{
ID: uuid.NewString(),
Name: generateMonsterName(false),
BattleStats: entity.BattleStats{
Health: 100,
MaxHealth: 100,
Attack: 100,
Defense: 100,
Speed: 100,
},
AvatarURL: generateAvatarURL(),
},
Enemy: entity.Monster{
ID: uuid.NewString(),
Name: generateMonsterName(true),
BattleStats: entity.BattleStats{
Health: 100,
MaxHealth: 100,
Attack: 10,
Defense: 100,
Speed: 100,
},
AvatarURL: generateAvatarURL(),
},
ExpectedHealthAmount: 100 - entity.MinDamage,
},
}

// execute test cases
for _, testCase := range testCases {
t.Run(testCase.Name, func(t *testing.T) {
_, err := testCase.Monster.InflictDamage(testCase.Enemy)
if err != nil {
t.Errorf("unable to inflict damage, due: %v", err)
}
assert.Equal(t, testCase.ExpectedHealthAmount, testCase.Monster.BattleStats.Health, "unexpected health amount")
testCase.Monster.InflictDamage(testCase.Enemy)
require.Equal(t, testCase.ExpectedHealthAmount, testCase.Monster.BattleStats.Health, "unexpected health amount")
})
}
}

func TestResetBattleStats(t *testing.T) {
// create new monster
m := testutil.NewTestMonster()

// set the monster health to 0
m.BattleStats.Health = 0

// reset the battle stats
m.ResetBattleStats()

// the monster health should be equal to max health
require.Equal(t, m.BattleStats.MaxHealth, m.BattleStats.Health, "unexpected health amount")
}

func generateMonsterName(isEnemy bool) string {
prefix := "monster"
if isEnemy {
prefix = "enemy"
}
return fmt.Sprintf("%v_%v", prefix, uuid.NewString())
}

func generateAvatarURL() string {
return fmt.Sprintf("https://example.com/%v.jpg", uuid.NewString())
}
Loading