Dragon-Knight/internal/monsters/monsters_test.go

408 lines
10 KiB
Go

package monsters
import (
"os"
"testing"
"dk/internal/database"
)
func setupTestDB(t *testing.T) *database.DB {
testDB := "test_monsters.db"
t.Cleanup(func() {
os.Remove(testDB)
})
db, err := database.Open(testDB)
if err != nil {
t.Fatalf("Failed to open test database: %v", err)
}
// Create monsters table
createTable := `CREATE TABLE monsters (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
max_hp INTEGER NOT NULL DEFAULT 0,
max_dmg INTEGER NOT NULL DEFAULT 0,
armor INTEGER NOT NULL DEFAULT 0,
level INTEGER NOT NULL DEFAULT 0,
max_exp INTEGER NOT NULL DEFAULT 0,
max_gold INTEGER NOT NULL DEFAULT 0,
immune INTEGER NOT NULL DEFAULT 0
)`
if err := db.Exec(createTable); err != nil {
t.Fatalf("Failed to create monsters table: %v", err)
}
// Insert test data
testMonsters := `INSERT INTO monsters (name, max_hp, max_dmg, armor, level, max_exp, max_gold, immune) VALUES
('Blue Slime', 4, 3, 1, 1, 1, 1, 0),
('Red Slime', 6, 5, 1, 1, 2, 1, 0),
('Shadow', 10, 9, 3, 2, 6, 2, 1),
('Silver Slime', 15, 100, 200, 30, 15, 1000, 2),
('Raven', 16, 13, 5, 4, 18, 6, 0)`
if err := db.Exec(testMonsters); err != nil {
t.Fatalf("Failed to insert test monsters: %v", err)
}
return db
}
func TestFind(t *testing.T) {
db := setupTestDB(t)
defer db.Close()
// Test finding existing monster
monster, err := Find(db, 1)
if err != nil {
t.Fatalf("Failed to find monster: %v", err)
}
if monster.ID != 1 {
t.Errorf("Expected ID 1, got %d", monster.ID)
}
if monster.Name != "Blue Slime" {
t.Errorf("Expected name 'Blue Slime', got '%s'", monster.Name)
}
if monster.MaxHP != 4 {
t.Errorf("Expected max_hp 4, got %d", monster.MaxHP)
}
if monster.MaxDmg != 3 {
t.Errorf("Expected max_dmg 3, got %d", monster.MaxDmg)
}
if monster.Armor != 1 {
t.Errorf("Expected armor 1, got %d", monster.Armor)
}
if monster.Level != 1 {
t.Errorf("Expected level 1, got %d", monster.Level)
}
if monster.MaxExp != 1 {
t.Errorf("Expected max_exp 1, got %d", monster.MaxExp)
}
if monster.MaxGold != 1 {
t.Errorf("Expected max_gold 1, got %d", monster.MaxGold)
}
if monster.Immune != ImmuneNone {
t.Errorf("Expected immune %d, got %d", ImmuneNone, monster.Immune)
}
// Test finding non-existent monster
_, err = Find(db, 999)
if err == nil {
t.Error("Expected error when finding non-existent monster")
}
}
func TestAll(t *testing.T) {
db := setupTestDB(t)
defer db.Close()
monsters, err := All(db)
if err != nil {
t.Fatalf("Failed to get all monsters: %v", err)
}
if len(monsters) != 5 {
t.Errorf("Expected 5 monsters, got %d", len(monsters))
}
// Check first monster (should be ordered by level, then id)
if monsters[0].Name != "Blue Slime" {
t.Errorf("Expected first monster to be 'Blue Slime', got '%s'", monsters[0].Name)
}
}
func TestByLevel(t *testing.T) {
db := setupTestDB(t)
defer db.Close()
// Test level 1 monsters
level1Monsters, err := ByLevel(db, 1)
if err != nil {
t.Fatalf("Failed to get level 1 monsters: %v", err)
}
if len(level1Monsters) != 2 {
t.Errorf("Expected 2 level 1 monsters, got %d", len(level1Monsters))
}
for _, monster := range level1Monsters {
if monster.Level != 1 {
t.Errorf("Expected level 1, got %d for monster %s", monster.Level, monster.Name)
}
}
// Test level that doesn't exist
noMonsters, err := ByLevel(db, 999)
if err != nil {
t.Fatalf("Failed to query non-existent level: %v", err)
}
if len(noMonsters) != 0 {
t.Errorf("Expected 0 monsters at level 999, got %d", len(noMonsters))
}
}
func TestByLevelRange(t *testing.T) {
db := setupTestDB(t)
defer db.Close()
// Test level range 1-2
monsters, err := ByLevelRange(db, 1, 2)
if err != nil {
t.Fatalf("Failed to get monsters by level range: %v", err)
}
if len(monsters) != 3 {
t.Errorf("Expected 3 monsters in level range 1-2, got %d", len(monsters))
}
// Verify all monsters are within range
for _, monster := range monsters {
if monster.Level < 1 || monster.Level > 2 {
t.Errorf("Monster %s level %d is outside range 1-2", monster.Name, monster.Level)
}
}
// Verify ordering (by level, then id)
if monsters[0].Level > monsters[len(monsters)-1].Level {
t.Error("Expected monsters to be ordered by level")
}
}
func TestByImmunity(t *testing.T) {
db := setupTestDB(t)
defer db.Close()
// Test Hurt immune monsters
hurtImmune, err := ByImmunity(db, ImmuneHurt)
if err != nil {
t.Fatalf("Failed to get Hurt immune monsters: %v", err)
}
if len(hurtImmune) != 1 {
t.Errorf("Expected 1 Hurt immune monster, got %d", len(hurtImmune))
}
if len(hurtImmune) > 0 && hurtImmune[0].Name != "Shadow" {
t.Errorf("Expected Hurt immune monster to be 'Shadow', got '%s'", hurtImmune[0].Name)
}
// Test Sleep immune monsters
sleepImmune, err := ByImmunity(db, ImmuneSleep)
if err != nil {
t.Fatalf("Failed to get Sleep immune monsters: %v", err)
}
if len(sleepImmune) != 1 {
t.Errorf("Expected 1 Sleep immune monster, got %d", len(sleepImmune))
}
// Test no immunity monsters
noImmunity, err := ByImmunity(db, ImmuneNone)
if err != nil {
t.Fatalf("Failed to get non-immune monsters: %v", err)
}
if len(noImmunity) != 3 {
t.Errorf("Expected 3 non-immune monsters, got %d", len(noImmunity))
}
}
func TestBuilder(t *testing.T) {
db := setupTestDB(t)
defer db.Close()
// Create new monster using builder
monster, err := NewBuilder(db).
WithName("Test Dragon").
WithMaxHP(100).
WithMaxDmg(25).
WithArmor(10).
WithLevel(15).
WithMaxExp(500).
WithMaxGold(100).
WithImmunity(ImmuneHurt).
Create()
if err != nil {
t.Fatalf("Failed to create monster with builder: %v", err)
}
if monster.ID == 0 {
t.Error("Expected non-zero ID after creation")
}
if monster.Name != "Test Dragon" {
t.Errorf("Expected name 'Test Dragon', got '%s'", monster.Name)
}
if monster.MaxHP != 100 {
t.Errorf("Expected max_hp 100, got %d", monster.MaxHP)
}
if monster.MaxDmg != 25 {
t.Errorf("Expected max_dmg 25, got %d", monster.MaxDmg)
}
if monster.Armor != 10 {
t.Errorf("Expected armor 10, got %d", monster.Armor)
}
if monster.Level != 15 {
t.Errorf("Expected level 15, got %d", monster.Level)
}
if monster.MaxExp != 500 {
t.Errorf("Expected max_exp 500, got %d", monster.MaxExp)
}
if monster.MaxGold != 100 {
t.Errorf("Expected max_gold 100, got %d", monster.MaxGold)
}
if monster.Immune != ImmuneHurt {
t.Errorf("Expected immune %d, got %d", ImmuneHurt, monster.Immune)
}
// Verify it was saved to database
foundMonster, err := Find(db, monster.ID)
if err != nil {
t.Fatalf("Failed to find created monster: %v", err)
}
if foundMonster.Name != "Test Dragon" {
t.Errorf("Created monster not found in database")
}
}
func TestSave(t *testing.T) {
db := setupTestDB(t)
defer db.Close()
monster, err := Find(db, 1)
if err != nil {
t.Fatalf("Failed to find monster: %v", err)
}
// Modify monster
monster.Name = "Updated Blue Slime"
monster.MaxHP = 8
monster.Level = 2
// Save changes
err = monster.Save()
if err != nil {
t.Fatalf("Failed to save monster: %v", err)
}
// Verify changes were saved
updatedMonster, err := Find(db, 1)
if err != nil {
t.Fatalf("Failed to find updated monster: %v", err)
}
if updatedMonster.Name != "Updated Blue Slime" {
t.Errorf("Expected updated name 'Updated Blue Slime', got '%s'", updatedMonster.Name)
}
if updatedMonster.MaxHP != 8 {
t.Errorf("Expected updated max_hp 8, got %d", updatedMonster.MaxHP)
}
if updatedMonster.Level != 2 {
t.Errorf("Expected updated level 2, got %d", updatedMonster.Level)
}
}
func TestDelete(t *testing.T) {
db := setupTestDB(t)
defer db.Close()
monster, err := Find(db, 1)
if err != nil {
t.Fatalf("Failed to find monster: %v", err)
}
// Delete monster
err = monster.Delete()
if err != nil {
t.Fatalf("Failed to delete monster: %v", err)
}
// Verify monster was deleted
_, err = Find(db, 1)
if err == nil {
t.Error("Expected error when finding deleted monster")
}
}
func TestImmunityMethods(t *testing.T) {
db := setupTestDB(t)
defer db.Close()
blueSlime, _ := Find(db, 1) // No immunity
shadow, _ := Find(db, 3) // Hurt immune
silverSlime, _ := Find(db, 4) // Sleep immune
// Test IsHurtImmune
if blueSlime.IsHurtImmune() {
t.Error("Expected blue slime not to be Hurt immune")
}
if !shadow.IsHurtImmune() {
t.Error("Expected shadow to be Hurt immune")
}
if silverSlime.IsHurtImmune() {
t.Error("Expected silver slime not to be Hurt immune")
}
// Test IsSleepImmune
if blueSlime.IsSleepImmune() {
t.Error("Expected blue slime not to be Sleep immune")
}
if shadow.IsSleepImmune() {
t.Error("Expected shadow not to be Sleep immune")
}
if !silverSlime.IsSleepImmune() {
t.Error("Expected silver slime to be Sleep immune")
}
// Test HasImmunity
if blueSlime.HasImmunity() {
t.Error("Expected blue slime to have no immunity")
}
if !shadow.HasImmunity() {
t.Error("Expected shadow to have immunity")
}
if !silverSlime.HasImmunity() {
t.Error("Expected silver slime to have immunity")
}
// Test ImmunityName
if blueSlime.ImmunityName() != "None" {
t.Errorf("Expected blue slime immunity name 'None', got '%s'", blueSlime.ImmunityName())
}
if shadow.ImmunityName() != "Hurt Spells" {
t.Errorf("Expected shadow immunity name 'Hurt Spells', got '%s'", shadow.ImmunityName())
}
if silverSlime.ImmunityName() != "Sleep Spells" {
t.Errorf("Expected silver slime immunity name 'Sleep Spells', got '%s'", silverSlime.ImmunityName())
}
}
func TestUtilityMethods(t *testing.T) {
db := setupTestDB(t)
defer db.Close()
blueSlime, _ := Find(db, 1)
// Test DifficultyRating
expectedDifficulty := float64(4+3+1) / float64(1) // (HP + Damage + Armor) / Level
if blueSlime.DifficultyRating() != expectedDifficulty {
t.Errorf("Expected difficulty rating %.2f, got %.2f", expectedDifficulty, blueSlime.DifficultyRating())
}
// Test ExpPerHP
expectedExpPerHP := float64(1) / float64(4) // Exp / HP
if blueSlime.ExpPerHP() != expectedExpPerHP {
t.Errorf("Expected exp per HP %.2f, got %.2f", expectedExpPerHP, blueSlime.ExpPerHP())
}
// Test GoldPerHP
expectedGoldPerHP := float64(1) / float64(4) // Gold / HP
if blueSlime.GoldPerHP() != expectedGoldPerHP {
t.Errorf("Expected gold per HP %.2f, got %.2f", expectedGoldPerHP, blueSlime.GoldPerHP())
}
}