aboutsummaryrefslogtreecommitdiff
path: root/internal/validate/hardening_mob_stats_validation_test.go
diff options
context:
space:
mode:
Diffstat (limited to 'internal/validate/hardening_mob_stats_validation_test.go')
-rw-r--r--internal/validate/hardening_mob_stats_validation_test.go107
1 files changed, 107 insertions, 0 deletions
diff --git a/internal/validate/hardening_mob_stats_validation_test.go b/internal/validate/hardening_mob_stats_validation_test.go
new file mode 100644
index 0000000..1ea30f9
--- /dev/null
+++ b/internal/validate/hardening_mob_stats_validation_test.go
@@ -0,0 +1,107 @@
+package validate
+
+import (
+ "path/filepath"
+ "testing"
+)
+
+// Regression: validateMobs must reject a fighting mob authored without an
+// attack speed (engine.ToTicks clips 0 to 1, so the mob would attack every
+// tick) or without a max hit for a declared attack type (a silent no-op).
+// Task worksites are explicitly exempt: data/mobs/examples/solar_panel_frame.yaml
+// ships max_melee_hit: 0 and must remain valid.
+
+func writeMob(t *testing.T, dir, name, body string) {
+ t.Helper()
+ writeFile(t, filepath.Join(dir, "mobs", name+".yaml"), body)
+}
+
+func TestMobMissingSpeedIsError(t *testing.T) {
+ dir := t.TempDir()
+ writeMob(t, dir, "test_mob", `id: test_mob
+name: test mob
+combat:
+ kind: combat
+ attack_types: [crush]
+ stats:
+ hp: 10
+ speed: 0
+ max_melee_hit: 5
+`)
+ issues := validateMobs(newSource(dir))
+ if !containsMsg(issues, "stats.speed") {
+ t.Fatalf("expected missing-speed error, got: %+v", issues)
+ }
+}
+
+func TestMobMissingMaxMeleeHitIsError(t *testing.T) {
+ dir := t.TempDir()
+ writeMob(t, dir, "test_mob", `id: test_mob
+name: test mob
+combat:
+ kind: combat
+ attack_types: [crush]
+ stats:
+ hp: 10
+ speed: 5
+ max_melee_hit: 0
+`)
+ issues := validateMobs(newSource(dir))
+ if !containsMsg(issues, "max_melee_hit") {
+ t.Fatalf("expected missing-melee-hit error, got: %+v", issues)
+ }
+}
+
+func TestMobMissingMaxRangedHitIsError(t *testing.T) {
+ dir := t.TempDir()
+ writeMob(t, dir, "test_mob", `id: test_mob
+name: test mob
+combat:
+ kind: combat
+ attack_types: [crush, ranged]
+ stats:
+ hp: 10
+ speed: 5
+ max_melee_hit: 3
+`)
+ issues := validateMobs(newSource(dir))
+ if !containsMsg(issues, "max_ranged_hit") {
+ t.Fatalf("expected missing-ranged-hit error, got: %+v", issues)
+ }
+}
+
+func TestTaskWorksiteMobExemptFromCombatStatChecks(t *testing.T) {
+ dir := t.TempDir()
+ writeMob(t, dir, "test_worksite", `id: test_worksite
+name: test worksite
+combat:
+ kind: task
+ attack_types: [crush]
+ stats:
+ hp: 10
+ speed: 0
+ max_melee_hit: 0
+`)
+ if issues := validateMobs(newSource(dir)); len(issues) != 0 {
+ t.Fatalf("task worksite must be exempt from combat-stat checks, got: %+v", issues)
+ }
+}
+
+func TestValidCombatMobProducesNoIssue(t *testing.T) {
+ dir := t.TempDir()
+ writeMob(t, dir, "test_mob", `id: test_mob
+name: test mob
+combat:
+ kind: combat
+ attack_types: [slash, ranged, science]
+ stats:
+ hp: 10
+ speed: 4
+ max_melee_hit: 3
+ max_ranged_hit: 4
+ max_science_hit: 5
+`)
+ if issues := validateMobs(newSource(dir)); len(issues) != 0 {
+ t.Fatalf("valid combat mob must validate cleanly, got: %+v", issues)
+ }
+}