diff --git a/.gitignore b/.gitignore index 316d19b..53ef314 100644 --- a/.gitignore +++ b/.gitignore @@ -32,4 +32,5 @@ ChangeLog.txt target/ repo/ out/ -META-INF/ \ No newline at end of file +META-INF/ +build.log diff --git a/AGENTS.md b/AGENTS.md index 3635121..1319f55 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -5,12 +5,16 @@ This file is the project's committed home for project-intrinsic agent knowledge: - Build with JDK 25 or newer - `maven-enforcer-plugin` (`requireJavaVersion [25,)`) fails `validate` on anything older. Emitted plugin bytecode is intentionally Java 8 (major version 52) via `8` in `pom.xml`; the target runtime is a Java 17+ Spigot server. Do not "modernize" the bytecode level. Build/verify: `JAVA_HOME= mvn -B clean verify`. - `de.tr7zw:functional-annotations` is pinned to `0.1-SNAPSHOT` because no released version has ever been published (CodeMC metadata lists only the SNAPSHOT). It is a real compile dependency (`de.tr7zw.annotations.FAUtil`, used by the `nbt` package), so it cannot be dropped. Revisit if upstream ever cuts a release. - Test harness: JUnit 5 + MockBukkit + Mockito + JaCoCo. `mvn verify` runs tests and produces `target/site/jacoco/` coverage report. JaCoCo excludes `**/nbt/**` (vendored) and `**/ArmorPlus.class` (bootstrap). -- Coverage gate (CORE set): JaCoCo `check-core` execution enforces 100% line AND branch on these pure-logic classes: `PlayerDirectionUtil`, `ColorUtil`, `Piece`, `SetType`, `SetDataType`. Build fails if core coverage drops below 100%. Everything else remains report-only. Excluded from core: `LogUtil` (calls `ArmorPlus.get()` singleton), `nbt.NBTType` (vendored `**/nbt/**` exclusion). When modifying a core class, add tests in the same commit. +- Coverage gate (CORE set): JaCoCo `check-core` execution enforces 100% line AND branch on these pure-logic classes: `PlayerDirectionUtil`, `ColorUtil`, `Piece`, `SetType`, `SetDataType`, `ArmorHandItem`, `model.id.TypedString`, `model.id.StringId`. Build fails if core coverage drops below 100%. Everything else remains report-only. Excluded from core: `LogUtil` (calls `ArmorPlus.get()` singleton), `nbt.NBTType` (vendored `**/nbt/**` exclusion). When modifying a core class, add tests in the same commit. - JDK/bytecode split: main code compiles to Java 8 (`maven.compiler.release=8`), test code compiles to Java 17 (`maven.compiler.testRelease=17`). The `testRelease` property is picked up automatically by maven-compiler-plugin's default-testCompile execution - no custom execution block needed. Both compile under the same JDK 25 build. MockBukkit + paper-api are Java 17 bytecode, so tests cannot target 8. - MockBukkit limitation: the plugin cannot be loaded via `MockBukkit.load(ArmorPlus.class)` because the vendored NBT-API performs reflection at enable time that MockBukkit does not model. Use pure-logic unit tests or Mockito for testing pieces that interact with Bukkit APIs. - Mockito on JDK 25: surefire must pass `-Dnet.bytebuddy.experimental=true` (configured in pom.xml ``). Without it, ByteBuddy fails to recognize the JDK 25 class file version. - Characterization test safety net (Phase 4A): `src/test/java/gg/steve/mc/ap/{data,player,armor,message}` contains characterization tests pinning current behavior of damage calc (`HandSetData.calculateFinalDamage`, `BasicSetData.onHit/onDamage`), wearer tracking (`SetPlayerManager`), warp safety (`WarpUtil.isSafe`), XP multiplier (`ExperienceSetData.onTargetDeath`), potion checks (`SetStatusEffectsManager.potionCheck`), set detection (`Set.isWearingSet/verifyPiece`), and messaging (`MessageType/CommandDebug`). Rearch phases 4B+ MUST keep these tests green - they prove behavior is preserved during extraction. Do NOT "fix" surprising behavior these tests pin; document it and defer to a separate bug-fix PR. - Integration-only (not netted): `Set.isWearingSet` full integration path (NBT reflection in real server), `SetPlayerManager.init()` (iterates Bukkit.getOnlinePlayers), GUI rendering, PAPI expansion, scheduler-dependent abilities (Fairy/ColorWay/Lightning/Engineer/Traveller tick logic). +- Domain model package: `gg.steve.mc.ap.model` with sub-packages `set`, `ability`, `combat`, `effect`, `notification`, `player`, `id`. Contains pure-Java value types with ZERO Bukkit/NMS/NBT imports. Uses Lombok (`@Value`, `@Builder`) for boilerplate elimination. The `lombok.config` at repo root sets `lombok.addLombokGeneratedAnnotation=true` so JaCoCo auto-excludes generated code. +- Domain model conventions: No class may use the `Manager` suffix (use Registry, Service, Store, Factory, Source, Resolver, etc.). Use the typed ID wrappers in model.id (PlayerId, ArmorSetId, DamageCauseId, PotionEffectId, SoundId) as identity currency - never reference Bukkit Player/ItemStack/World in the model layer. ID wrappers extend the `TypedString`/`StringId` base hierarchy: `TypedString` (abstract) rejects null via commons-lang3 `Validate.notNull` in its constructor and implements `equals`/`hashCode` with `getClass()` checks (different subtypes wrapping the same string are never equal); `StringId` extends it adding `Validate.notEmpty`. Leaf classes are final with a private constructor and an `of()` factory; `PlayerId` extends `TypedString` directly, wrapping a `UUID`. Use `@Value` + `@Builder` for multi-field value types. Annotate `List`/`Set`/`Map` fields with `@Singular` so Lombok's builder produces genuinely unmodifiable defensive copies at `build()` time. +- Lombok dependency: `org.projectlombok:lombok:1.18.38` (provided scope). Annotation processor configured in maven-compiler-plugin's ``. Works with JDK 25 and emits Java 8 bytecode. +- No-Bukkit-in-model rule: verified by grep (no ArchUnit yet). The `model` package must have zero imports of `org.bukkit.*`, `net.minecraft.*`, `de.tr7zw.*`, or any plugin class outside `model`. ## Maintaining this file diff --git a/README.md b/README.md index ac93a9d..a399c3b 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ For more information about the plugin, permissions, and commands please refer to ## Code Coverage JaCoCo runs on every CI build (`mvn verify`). -The build enforces 100% line and branch coverage on the pure-logic core classes (`PlayerDirectionUtil`, `ColorUtil`, `Piece`, `SetType`, `SetDataType`); a coverage drop on any of these will fail the build. +The build enforces 100% line and branch coverage on the pure-logic core classes (`PlayerDirectionUtil`, `ColorUtil`, `Piece`, `SetType`, `SetDataType`, `ArmorHandItem`, `TypedString`, `StringId`); a coverage drop on any of these will fail the build. The HTML coverage report is uploaded as the **jacoco-report** artifact on each workflow run - download it from the [Actions tab](https://github.com/nbdSteve/ArmorPlus/actions/workflows/ci.yml). ## Soft Dependencies diff --git a/lombok.config b/lombok.config new file mode 100644 index 0000000..df71bb6 --- /dev/null +++ b/lombok.config @@ -0,0 +1,2 @@ +config.stopBubbling = true +lombok.addLombokGeneratedAnnotation = true diff --git a/pom.xml b/pom.xml index 905db77..3bd700a 100644 --- a/pom.xml +++ b/pom.xml @@ -35,6 +35,13 @@ 3.14.0 ${maven.compiler.release} + + + org.projectlombok + lombok + 1.18.38 + + @@ -107,6 +114,9 @@ gg.steve.mc.ap.armor.Piece gg.steve.mc.ap.armor.SetType gg.steve.mc.ap.data.SetDataType + gg.steve.mc.ap.model.ability.ArmorHandItem + gg.steve.mc.ap.model.id.TypedString + gg.steve.mc.ap.model.id.StringId @@ -137,6 +147,10 @@ gg.steve.mc.ap + + org.apache.commons.lang3 + gg.steve.mc.ap.lib.commons.lang3 + @@ -229,12 +243,24 @@ 1.7 compile + + org.apache.commons + commons-lang3 + 3.17.0 + compile + com.mojang authlib 1.5.21 provided + + org.projectlombok + lombok + 1.18.38 + provided + org.junit.jupiter diff --git a/src/main/java/gg/steve/mc/ap/model/ability/ArmorHandItem.java b/src/main/java/gg/steve/mc/ap/model/ability/ArmorHandItem.java new file mode 100644 index 0000000..b103012 --- /dev/null +++ b/src/main/java/gg/steve/mc/ap/model/ability/ArmorHandItem.java @@ -0,0 +1,25 @@ +package gg.steve.mc.ap.model.ability; + +import gg.steve.mc.ap.model.id.DamageCauseId; +import lombok.Builder; +import lombok.Value; + +@Value +@Builder +public class ArmorHandItem { + double increase; + boolean requireSet; + DamageCauseId damageCause; + + /** + * Pure damage calculation mirroring HandSetData.calculateFinalDamage. + * When setIncrease is -1, the set bonus is not applied. + */ + public double calculateFinalDamage(double damage, double setIncrease) { + if (setIncrease != -1) { + double set = setIncrease - 1; + return damage * (set + this.increase); + } + return damage * this.increase; + } +} diff --git a/src/main/java/gg/steve/mc/ap/model/ability/ArmorSetAbilityType.java b/src/main/java/gg/steve/mc/ap/model/ability/ArmorSetAbilityType.java new file mode 100644 index 0000000..62ef7e2 --- /dev/null +++ b/src/main/java/gg/steve/mc/ap/model/ability/ArmorSetAbilityType.java @@ -0,0 +1,16 @@ +package gg.steve.mc.ap.model.ability; + +public enum ArmorSetAbilityType { + BASIC, + LIGHTNING, + WARP, + POTION, + FALL, + HUNGER, + TRAVELLER, + STUN, + HAND, + ENGINEER, + COLOR_WAY, + EXPERIENCE; +} diff --git a/src/main/java/gg/steve/mc/ap/model/ability/ArmorSetBasicStats.java b/src/main/java/gg/steve/mc/ap/model/ability/ArmorSetBasicStats.java new file mode 100644 index 0000000..2b658ad --- /dev/null +++ b/src/main/java/gg/steve/mc/ap/model/ability/ArmorSetBasicStats.java @@ -0,0 +1,17 @@ +package gg.steve.mc.ap.model.ability; + +import lombok.Builder; +import lombok.Value; + +@Value +@Builder +public class ArmorSetBasicStats { + double increase; + double reduction; + double knockback; + double health; + float walkSpeed; + float walkSpeedDefault; + float flySpeed; + float flySpeedDefault; +} diff --git a/src/main/java/gg/steve/mc/ap/model/combat/CombatDamageContext.java b/src/main/java/gg/steve/mc/ap/model/combat/CombatDamageContext.java new file mode 100644 index 0000000..2415276 --- /dev/null +++ b/src/main/java/gg/steve/mc/ap/model/combat/CombatDamageContext.java @@ -0,0 +1,16 @@ +package gg.steve.mc.ap.model.combat; + +import gg.steve.mc.ap.model.id.DamageCauseId; +import gg.steve.mc.ap.model.id.PlayerId; +import lombok.Builder; +import lombok.Value; + +@Value +@Builder +public class CombatDamageContext { + PlayerId attacker; + PlayerId target; + double baseDamage; + DamageCauseId cause; + boolean projectile; +} diff --git a/src/main/java/gg/steve/mc/ap/model/combat/CombatDamageModification.java b/src/main/java/gg/steve/mc/ap/model/combat/CombatDamageModification.java new file mode 100644 index 0000000..bae2ecd --- /dev/null +++ b/src/main/java/gg/steve/mc/ap/model/combat/CombatDamageModification.java @@ -0,0 +1,14 @@ +package gg.steve.mc.ap.model.combat; + +import lombok.Value; + +/** + * Represents a modification to damage and knockback. + * A null CombatDamageModification at call sites means "no change" - callers should use + * Optional to represent the absence of modification. + */ +@Value +public class CombatDamageModification { + double newDamage; + double knockbackMultiplier; +} diff --git a/src/main/java/gg/steve/mc/ap/model/effect/NotificationSound.java b/src/main/java/gg/steve/mc/ap/model/effect/NotificationSound.java new file mode 100644 index 0000000..779784e --- /dev/null +++ b/src/main/java/gg/steve/mc/ap/model/effect/NotificationSound.java @@ -0,0 +1,11 @@ +package gg.steve.mc.ap.model.effect; + +import gg.steve.mc.ap.model.id.SoundId; +import lombok.Value; + +@Value +public class NotificationSound { + SoundId name; + float volume; + float pitch; +} diff --git a/src/main/java/gg/steve/mc/ap/model/effect/PotionEffect.java b/src/main/java/gg/steve/mc/ap/model/effect/PotionEffect.java new file mode 100644 index 0000000..30eb24e --- /dev/null +++ b/src/main/java/gg/steve/mc/ap/model/effect/PotionEffect.java @@ -0,0 +1,11 @@ +package gg.steve.mc.ap.model.effect; + +import gg.steve.mc.ap.model.id.PotionEffectId; +import lombok.Value; + +@Value +public class PotionEffect { + PotionEffectId type; + int duration; + int amplifier; +} diff --git a/src/main/java/gg/steve/mc/ap/model/id/ArmorSetId.java b/src/main/java/gg/steve/mc/ap/model/id/ArmorSetId.java new file mode 100644 index 0000000..f23f913 --- /dev/null +++ b/src/main/java/gg/steve/mc/ap/model/id/ArmorSetId.java @@ -0,0 +1,6 @@ +package gg.steve.mc.ap.model.id; + +public final class ArmorSetId extends StringId { + private ArmorSetId(String value) { super(value); } + public static ArmorSetId of(String value) { return new ArmorSetId(value); } +} diff --git a/src/main/java/gg/steve/mc/ap/model/id/DamageCauseId.java b/src/main/java/gg/steve/mc/ap/model/id/DamageCauseId.java new file mode 100644 index 0000000..0c243b4 --- /dev/null +++ b/src/main/java/gg/steve/mc/ap/model/id/DamageCauseId.java @@ -0,0 +1,6 @@ +package gg.steve.mc.ap.model.id; + +public final class DamageCauseId extends StringId { + private DamageCauseId(String value) { super(value); } + public static DamageCauseId of(String value) { return new DamageCauseId(value); } +} diff --git a/src/main/java/gg/steve/mc/ap/model/id/PlayerId.java b/src/main/java/gg/steve/mc/ap/model/id/PlayerId.java new file mode 100644 index 0000000..a1f9732 --- /dev/null +++ b/src/main/java/gg/steve/mc/ap/model/id/PlayerId.java @@ -0,0 +1,19 @@ +package gg.steve.mc.ap.model.id; + +import java.util.UUID; + +public final class PlayerId extends TypedString { + private final UUID uuid; + + private PlayerId(UUID value) { + super(value.toString()); + this.uuid = value; + } + + public UUID getValue() { return uuid; } + + public static PlayerId of(UUID value) { + if (value == null) throw new NullPointerException("value must not be null"); + return new PlayerId(value); + } +} diff --git a/src/main/java/gg/steve/mc/ap/model/id/PotionEffectId.java b/src/main/java/gg/steve/mc/ap/model/id/PotionEffectId.java new file mode 100644 index 0000000..be170c5 --- /dev/null +++ b/src/main/java/gg/steve/mc/ap/model/id/PotionEffectId.java @@ -0,0 +1,6 @@ +package gg.steve.mc.ap.model.id; + +public final class PotionEffectId extends StringId { + private PotionEffectId(String value) { super(value); } + public static PotionEffectId of(String value) { return new PotionEffectId(value); } +} diff --git a/src/main/java/gg/steve/mc/ap/model/id/SoundId.java b/src/main/java/gg/steve/mc/ap/model/id/SoundId.java new file mode 100644 index 0000000..874e262 --- /dev/null +++ b/src/main/java/gg/steve/mc/ap/model/id/SoundId.java @@ -0,0 +1,6 @@ +package gg.steve.mc.ap.model.id; + +public final class SoundId extends StringId { + private SoundId(String value) { super(value); } + public static SoundId of(String value) { return new SoundId(value); } +} diff --git a/src/main/java/gg/steve/mc/ap/model/id/StringId.java b/src/main/java/gg/steve/mc/ap/model/id/StringId.java new file mode 100644 index 0000000..b550245 --- /dev/null +++ b/src/main/java/gg/steve/mc/ap/model/id/StringId.java @@ -0,0 +1,10 @@ +package gg.steve.mc.ap.model.id; + +import org.apache.commons.lang3.Validate; + +public abstract class StringId extends TypedString { + protected StringId(String id) { + super(id); + Validate.notEmpty(id); + } +} diff --git a/src/main/java/gg/steve/mc/ap/model/id/TypedString.java b/src/main/java/gg/steve/mc/ap/model/id/TypedString.java new file mode 100644 index 0000000..fb5dbed --- /dev/null +++ b/src/main/java/gg/steve/mc/ap/model/id/TypedString.java @@ -0,0 +1,40 @@ +package gg.steve.mc.ap.model.id; + +import org.apache.commons.lang3.builder.EqualsBuilder; +import org.apache.commons.lang3.builder.HashCodeBuilder; + +import java.util.Collection; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; + +public abstract class TypedString { + private final String id; + + protected TypedString(String id) { + this.id = Objects.requireNonNull(id, "id must not be null"); + } + + @Override + public int hashCode() { + return new HashCodeBuilder().append(id).toHashCode(); + } + + @Override + public boolean equals(Object obj) { + if (obj == null) return false; + if (obj == this) return true; + if (obj.getClass() != getClass()) return false; + TypedString other = (TypedString) obj; + return new EqualsBuilder().append(id, other.id).isEquals(); + } + + @Override + public String toString() { + return id; + } + + public static List toStrings(Collection typedStrings) { + return typedStrings.stream().map(TypedString::toString).collect(Collectors.toList()); + } +} diff --git a/src/main/java/gg/steve/mc/ap/model/notification/ArmorSetNotification.java b/src/main/java/gg/steve/mc/ap/model/notification/ArmorSetNotification.java new file mode 100644 index 0000000..427cb81 --- /dev/null +++ b/src/main/java/gg/steve/mc/ap/model/notification/ArmorSetNotification.java @@ -0,0 +1,16 @@ +package gg.steve.mc.ap.model.notification; + +import gg.steve.mc.ap.model.effect.NotificationSound; +import lombok.Builder; +import lombok.Singular; +import lombok.Value; + +import java.util.List; + +@Value +@Builder +public class ArmorSetNotification { + @Singular List messages; + NotificationSound sound; + @Singular List commands; +} diff --git a/src/main/java/gg/steve/mc/ap/model/player/ArmorSetWearer.java b/src/main/java/gg/steve/mc/ap/model/player/ArmorSetWearer.java new file mode 100644 index 0000000..b8276b8 --- /dev/null +++ b/src/main/java/gg/steve/mc/ap/model/player/ArmorSetWearer.java @@ -0,0 +1,11 @@ +package gg.steve.mc.ap.model.player; + +import gg.steve.mc.ap.model.id.ArmorSetId; +import gg.steve.mc.ap.model.id.PlayerId; +import lombok.Value; + +@Value +public class ArmorSetWearer { + PlayerId playerId; + ArmorSetId setId; +} diff --git a/src/main/java/gg/steve/mc/ap/model/set/ArmorPieceSlot.java b/src/main/java/gg/steve/mc/ap/model/set/ArmorPieceSlot.java new file mode 100644 index 0000000..e610647 --- /dev/null +++ b/src/main/java/gg/steve/mc/ap/model/set/ArmorPieceSlot.java @@ -0,0 +1,9 @@ +package gg.steve.mc.ap.model.set; + +public enum ArmorPieceSlot { + HELMET, + CHESTPLATE, + LEGGINGS, + BOOTS, + HAND; +} diff --git a/src/main/java/gg/steve/mc/ap/model/set/ArmorSetPurchaseResult.java b/src/main/java/gg/steve/mc/ap/model/set/ArmorSetPurchaseResult.java new file mode 100644 index 0000000..b04af51 --- /dev/null +++ b/src/main/java/gg/steve/mc/ap/model/set/ArmorSetPurchaseResult.java @@ -0,0 +1,7 @@ +package gg.steve.mc.ap.model.set; + +public enum ArmorSetPurchaseResult { + SUCCESS, + INSUFFICIENT_FUNDS, + NO_PERMISSION; +} diff --git a/src/test/java/gg/steve/mc/ap/model/ability/ArmorHandItemTest.java b/src/test/java/gg/steve/mc/ap/model/ability/ArmorHandItemTest.java new file mode 100644 index 0000000..f38fc93 --- /dev/null +++ b/src/test/java/gg/steve/mc/ap/model/ability/ArmorHandItemTest.java @@ -0,0 +1,119 @@ +package gg.steve.mc.ap.model.ability; + +import gg.steve.mc.ap.model.id.DamageCauseId; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; + +import static org.junit.jupiter.api.Assertions.*; + +class ArmorHandItemTest { + + private static final DamageCauseId ENTITY_ATTACK = DamageCauseId.of("ENTITY_ATTACK"); + private static final DamageCauseId PROJECTILE = DamageCauseId.of("PROJECTILE"); + + @Test + void builderCreatesExpectedValues() { + ArmorHandItem item = ArmorHandItem.builder() + .increase(2.5) + .requireSet(true) + .damageCause(ENTITY_ATTACK) + .build(); + + assertEquals(2.5, item.getIncrease()); + assertTrue(item.isRequireSet()); + assertEquals(ENTITY_ATTACK, item.getDamageCause()); + } + + @Test + void calculateFinalDamage_withSetIncrease_combinesBothMultipliers() { + ArmorHandItem item = ArmorHandItem.builder() + .increase(1.5) + .requireSet(false) + .damageCause(ENTITY_ATTACK) + .build(); + + assertEquals(25.0, item.calculateFinalDamage(10.0, 2.0), 0.0001); + } + + @Test + void calculateFinalDamage_withoutSetIncrease_usesHandMultiplierOnly() { + ArmorHandItem item = ArmorHandItem.builder() + .increase(1.5) + .requireSet(false) + .damageCause(ENTITY_ATTACK) + .build(); + + assertEquals(15.0, item.calculateFinalDamage(10.0, -1), 0.0001); + } + + @ParameterizedTest + @CsvSource({ + "10.0, 1.5, 3.0, 35.0", + "5.0, 2.0, 1.0, 10.0", + "8.0, 1.0, 2.0, 16.0", + "0.0, 1.5, 2.0, 0.0", + }) + void calculateFinalDamage_withSetIncrease_parametrized( + double damage, double increase, double setIncrease, double expected) { + ArmorHandItem item = ArmorHandItem.builder() + .increase(increase) + .requireSet(false) + .damageCause(ENTITY_ATTACK) + .build(); + + assertEquals(expected, item.calculateFinalDamage(damage, setIncrease), 0.0001); + } + + @ParameterizedTest + @CsvSource({ + "10.0, 1.5, 15.0", + "5.0, 2.0, 10.0", + "0.0, 3.0, 0.0", + "8.0, 0.5, 4.0", + }) + void calculateFinalDamage_withoutSetIncrease_parametrized( + double damage, double increase, double expected) { + ArmorHandItem item = ArmorHandItem.builder() + .increase(increase) + .requireSet(false) + .damageCause(ENTITY_ATTACK) + .build(); + + assertEquals(expected, item.calculateFinalDamage(damage, -1), 0.0001); + } + + @Test + void calculateFinalDamage_setIncreaseOfOne_yieldsHandMultiplierOnly() { + ArmorHandItem item = ArmorHandItem.builder() + .increase(2.0) + .requireSet(true) + .damageCause(PROJECTILE) + .build(); + + assertEquals(20.0, item.calculateFinalDamage(10.0, 1.0), 0.0001); + } + + @Test + void equalsAndHashCode() { + ArmorHandItem a = ArmorHandItem.builder() + .increase(1.5).requireSet(true).damageCause(ENTITY_ATTACK).build(); + ArmorHandItem b = ArmorHandItem.builder() + .increase(1.5).requireSet(true).damageCause(ENTITY_ATTACK).build(); + ArmorHandItem c = ArmorHandItem.builder() + .increase(2.0).requireSet(false).damageCause(PROJECTILE).build(); + + assertEquals(a, b); + assertEquals(a.hashCode(), b.hashCode()); + assertNotEquals(a, c); + } + + @Test + void toStringContainsFields() { + ArmorHandItem item = ArmorHandItem.builder() + .increase(1.5).requireSet(true).damageCause(ENTITY_ATTACK).build(); + String s = item.toString(); + assertTrue(s.contains("1.5")); + assertTrue(s.contains("ENTITY_ATTACK")); + } +} diff --git a/src/test/java/gg/steve/mc/ap/model/ability/ArmorSetAbilityTypeTest.java b/src/test/java/gg/steve/mc/ap/model/ability/ArmorSetAbilityTypeTest.java new file mode 100644 index 0000000..d508a05 --- /dev/null +++ b/src/test/java/gg/steve/mc/ap/model/ability/ArmorSetAbilityTypeTest.java @@ -0,0 +1,21 @@ +package gg.steve.mc.ap.model.ability; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +class ArmorSetAbilityTypeTest { + + @Test + void allValuesPresent() { + ArmorSetAbilityType[] values = ArmorSetAbilityType.values(); + assertEquals(12, values.length); + } + + @Test + void valueOfRoundTrips() { + for (ArmorSetAbilityType type : ArmorSetAbilityType.values()) { + assertEquals(type, ArmorSetAbilityType.valueOf(type.name())); + } + } +} diff --git a/src/test/java/gg/steve/mc/ap/model/ability/ArmorSetBasicStatsTest.java b/src/test/java/gg/steve/mc/ap/model/ability/ArmorSetBasicStatsTest.java new file mode 100644 index 0000000..aa97410 --- /dev/null +++ b/src/test/java/gg/steve/mc/ap/model/ability/ArmorSetBasicStatsTest.java @@ -0,0 +1,57 @@ +package gg.steve.mc.ap.model.ability; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +class ArmorSetBasicStatsTest { + + @Test + void builderCreatesExpectedValues() { + ArmorSetBasicStats stats = ArmorSetBasicStats.builder() + .increase(1.5) + .reduction(0.8) + .knockback(0.3) + .health(30.0) + .walkSpeed(0.25f) + .walkSpeedDefault(0.2f) + .flySpeed(0.15f) + .flySpeedDefault(0.1f) + .build(); + + assertEquals(1.5, stats.getIncrease()); + assertEquals(0.8, stats.getReduction()); + assertEquals(0.3, stats.getKnockback()); + assertEquals(30.0, stats.getHealth()); + assertEquals(0.25f, stats.getWalkSpeed()); + assertEquals(0.2f, stats.getWalkSpeedDefault()); + assertEquals(0.15f, stats.getFlySpeed()); + assertEquals(0.1f, stats.getFlySpeedDefault()); + } + + @Test + void equalsAndHashCode() { + ArmorSetBasicStats a = ArmorSetBasicStats.builder() + .increase(1.5).reduction(0.8).knockback(0.3).health(30.0) + .walkSpeed(0.25f).walkSpeedDefault(0.2f) + .flySpeed(0.15f).flySpeedDefault(0.1f).build(); + ArmorSetBasicStats b = ArmorSetBasicStats.builder() + .increase(1.5).reduction(0.8).knockback(0.3).health(30.0) + .walkSpeed(0.25f).walkSpeedDefault(0.2f) + .flySpeed(0.15f).flySpeedDefault(0.1f).build(); + + assertEquals(a, b); + assertEquals(a.hashCode(), b.hashCode()); + } + + @Test + void disabledValuesUseNegativeOne() { + ArmorSetBasicStats stats = ArmorSetBasicStats.builder() + .increase(-1).reduction(-1).knockback(-1).health(-1) + .walkSpeed(-1f).walkSpeedDefault(-1f) + .flySpeed(-1f).flySpeedDefault(-1f).build(); + + assertEquals(-1, stats.getIncrease()); + assertEquals(-1, stats.getReduction()); + } +} diff --git a/src/test/java/gg/steve/mc/ap/model/combat/CombatDamageContextTest.java b/src/test/java/gg/steve/mc/ap/model/combat/CombatDamageContextTest.java new file mode 100644 index 0000000..14ae6bf --- /dev/null +++ b/src/test/java/gg/steve/mc/ap/model/combat/CombatDamageContextTest.java @@ -0,0 +1,50 @@ +package gg.steve.mc.ap.model.combat; + +import gg.steve.mc.ap.model.id.DamageCauseId; +import gg.steve.mc.ap.model.id.PlayerId; +import org.junit.jupiter.api.Test; + +import java.util.UUID; + +import static org.junit.jupiter.api.Assertions.*; + +class CombatDamageContextTest { + + @Test + void builderCreatesExpectedValues() { + PlayerId attacker = PlayerId.of(UUID.randomUUID()); + PlayerId target = PlayerId.of(UUID.randomUUID()); + DamageCauseId cause = DamageCauseId.of("ENTITY_ATTACK"); + + CombatDamageContext ctx = CombatDamageContext.builder() + .attacker(attacker) + .target(target) + .baseDamage(10.0) + .cause(cause) + .projectile(false) + .build(); + + assertEquals(attacker, ctx.getAttacker()); + assertEquals(target, ctx.getTarget()); + assertEquals(10.0, ctx.getBaseDamage()); + assertEquals(cause, ctx.getCause()); + assertFalse(ctx.isProjectile()); + } + + @Test + void equalsAndHashCode() { + PlayerId attacker = PlayerId.of(UUID.fromString("00000000-0000-0000-0000-000000000001")); + PlayerId target = PlayerId.of(UUID.fromString("00000000-0000-0000-0000-000000000002")); + DamageCauseId cause = DamageCauseId.of("PROJECTILE"); + + CombatDamageContext a = CombatDamageContext.builder() + .attacker(attacker).target(target) + .baseDamage(5.0).cause(cause).projectile(true).build(); + CombatDamageContext b = CombatDamageContext.builder() + .attacker(attacker).target(target) + .baseDamage(5.0).cause(cause).projectile(true).build(); + + assertEquals(a, b); + assertEquals(a.hashCode(), b.hashCode()); + } +} diff --git a/src/test/java/gg/steve/mc/ap/model/combat/CombatDamageModificationTest.java b/src/test/java/gg/steve/mc/ap/model/combat/CombatDamageModificationTest.java new file mode 100644 index 0000000..0dd866d --- /dev/null +++ b/src/test/java/gg/steve/mc/ap/model/combat/CombatDamageModificationTest.java @@ -0,0 +1,26 @@ +package gg.steve.mc.ap.model.combat; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +class CombatDamageModificationTest { + + @Test + void constructsWithExpectedValues() { + CombatDamageModification mod = new CombatDamageModification(15.0, 0.5); + assertEquals(15.0, mod.getNewDamage()); + assertEquals(0.5, mod.getKnockbackMultiplier()); + } + + @Test + void equalsAndHashCode() { + CombatDamageModification a = new CombatDamageModification(10.0, 1.0); + CombatDamageModification b = new CombatDamageModification(10.0, 1.0); + CombatDamageModification c = new CombatDamageModification(10.0, 2.0); + + assertEquals(a, b); + assertEquals(a.hashCode(), b.hashCode()); + assertNotEquals(a, c); + } +} diff --git a/src/test/java/gg/steve/mc/ap/model/effect/NotificationSoundTest.java b/src/test/java/gg/steve/mc/ap/model/effect/NotificationSoundTest.java new file mode 100644 index 0000000..0eaa139 --- /dev/null +++ b/src/test/java/gg/steve/mc/ap/model/effect/NotificationSoundTest.java @@ -0,0 +1,28 @@ +package gg.steve.mc.ap.model.effect; + +import gg.steve.mc.ap.model.id.SoundId; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +class NotificationSoundTest { + + @Test + void constructsWithExpectedValues() { + NotificationSound sound = new NotificationSound(SoundId.of("ENTITY_PLAYER_LEVELUP"), 1.0f, 0.5f); + assertEquals(SoundId.of("ENTITY_PLAYER_LEVELUP"), sound.getName()); + assertEquals(1.0f, sound.getVolume()); + assertEquals(0.5f, sound.getPitch()); + } + + @Test + void equalsAndHashCode() { + NotificationSound a = new NotificationSound(SoundId.of("DING"), 1.0f, 1.0f); + NotificationSound b = new NotificationSound(SoundId.of("DING"), 1.0f, 1.0f); + NotificationSound c = new NotificationSound(SoundId.of("BOOM"), 0.5f, 2.0f); + + assertEquals(a, b); + assertEquals(a.hashCode(), b.hashCode()); + assertNotEquals(a, c); + } +} diff --git a/src/test/java/gg/steve/mc/ap/model/effect/PotionEffectTest.java b/src/test/java/gg/steve/mc/ap/model/effect/PotionEffectTest.java new file mode 100644 index 0000000..0afe6bc --- /dev/null +++ b/src/test/java/gg/steve/mc/ap/model/effect/PotionEffectTest.java @@ -0,0 +1,28 @@ +package gg.steve.mc.ap.model.effect; + +import gg.steve.mc.ap.model.id.PotionEffectId; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +class PotionEffectTest { + + @Test + void constructsWithExpectedValues() { + PotionEffect effect = new PotionEffect(PotionEffectId.of("SPEED"), 200, 1); + assertEquals(PotionEffectId.of("SPEED"), effect.getType()); + assertEquals(200, effect.getDuration()); + assertEquals(1, effect.getAmplifier()); + } + + @Test + void equalsAndHashCode() { + PotionEffect a = new PotionEffect(PotionEffectId.of("SPEED"), 200, 1); + PotionEffect b = new PotionEffect(PotionEffectId.of("SPEED"), 200, 1); + PotionEffect c = new PotionEffect(PotionEffectId.of("JUMP"), 100, 2); + + assertEquals(a, b); + assertEquals(a.hashCode(), b.hashCode()); + assertNotEquals(a, c); + } +} diff --git a/src/test/java/gg/steve/mc/ap/model/id/ArmorSetIdTest.java b/src/test/java/gg/steve/mc/ap/model/id/ArmorSetIdTest.java new file mode 100644 index 0000000..ec55c95 --- /dev/null +++ b/src/test/java/gg/steve/mc/ap/model/id/ArmorSetIdTest.java @@ -0,0 +1,35 @@ +package gg.steve.mc.ap.model.id; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +class ArmorSetIdTest { + + @Test + void ofCreatesExpectedValue() { + ArmorSetId id = ArmorSetId.of("dragon"); + assertEquals("dragon", id.toString()); + } + + @Test + void ofNullThrows() { + assertThrows(NullPointerException.class, () -> ArmorSetId.of(null)); + } + + @Test + void ofEmptyThrows() { + assertThrows(IllegalArgumentException.class, () -> ArmorSetId.of("")); + } + + @Test + void equalsAndHashCode() { + ArmorSetId a = ArmorSetId.of("dragon"); + ArmorSetId b = ArmorSetId.of("dragon"); + ArmorSetId c = ArmorSetId.of("knight"); + + assertEquals(a, b); + assertEquals(a.hashCode(), b.hashCode()); + assertNotEquals(a, c); + } +} diff --git a/src/test/java/gg/steve/mc/ap/model/id/DamageCauseIdTest.java b/src/test/java/gg/steve/mc/ap/model/id/DamageCauseIdTest.java new file mode 100644 index 0000000..ab19d18 --- /dev/null +++ b/src/test/java/gg/steve/mc/ap/model/id/DamageCauseIdTest.java @@ -0,0 +1,35 @@ +package gg.steve.mc.ap.model.id; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +class DamageCauseIdTest { + + @Test + void ofCreatesExpectedValue() { + DamageCauseId id = DamageCauseId.of("ENTITY_ATTACK"); + assertEquals("ENTITY_ATTACK", id.toString()); + } + + @Test + void ofNullThrows() { + assertThrows(NullPointerException.class, () -> DamageCauseId.of(null)); + } + + @Test + void ofEmptyThrows() { + assertThrows(IllegalArgumentException.class, () -> DamageCauseId.of("")); + } + + @Test + void equalsAndHashCode() { + DamageCauseId a = DamageCauseId.of("ENTITY_ATTACK"); + DamageCauseId b = DamageCauseId.of("ENTITY_ATTACK"); + DamageCauseId c = DamageCauseId.of("PROJECTILE"); + + assertEquals(a, b); + assertEquals(a.hashCode(), b.hashCode()); + assertNotEquals(a, c); + } +} diff --git a/src/test/java/gg/steve/mc/ap/model/id/PlayerIdTest.java b/src/test/java/gg/steve/mc/ap/model/id/PlayerIdTest.java new file mode 100644 index 0000000..6eaf4c7 --- /dev/null +++ b/src/test/java/gg/steve/mc/ap/model/id/PlayerIdTest.java @@ -0,0 +1,41 @@ +package gg.steve.mc.ap.model.id; + +import org.junit.jupiter.api.Test; + +import java.util.UUID; + +import static org.junit.jupiter.api.Assertions.*; + +class PlayerIdTest { + + @Test + void ofCreatesExpectedValue() { + UUID uuid = UUID.randomUUID(); + PlayerId id = PlayerId.of(uuid); + assertEquals(uuid, id.getValue()); + } + + @Test + void ofNullThrows() { + assertThrows(NullPointerException.class, () -> PlayerId.of(null)); + } + + @Test + void equalsAndHashCode() { + UUID uuid = UUID.fromString("00000000-0000-0000-0000-000000000001"); + PlayerId a = PlayerId.of(uuid); + PlayerId b = PlayerId.of(uuid); + PlayerId c = PlayerId.of(UUID.fromString("00000000-0000-0000-0000-000000000002")); + + assertEquals(a, b); + assertEquals(a.hashCode(), b.hashCode()); + assertNotEquals(a, c); + } + + @Test + void toStringReturnsUuidString() { + UUID uuid = UUID.fromString("00000000-0000-0000-0000-000000000001"); + PlayerId id = PlayerId.of(uuid); + assertEquals("00000000-0000-0000-0000-000000000001", id.toString()); + } +} diff --git a/src/test/java/gg/steve/mc/ap/model/id/PotionEffectIdTest.java b/src/test/java/gg/steve/mc/ap/model/id/PotionEffectIdTest.java new file mode 100644 index 0000000..b03bf1e --- /dev/null +++ b/src/test/java/gg/steve/mc/ap/model/id/PotionEffectIdTest.java @@ -0,0 +1,35 @@ +package gg.steve.mc.ap.model.id; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +class PotionEffectIdTest { + + @Test + void ofCreatesExpectedValue() { + PotionEffectId id = PotionEffectId.of("SPEED"); + assertEquals("SPEED", id.toString()); + } + + @Test + void ofNullThrows() { + assertThrows(NullPointerException.class, () -> PotionEffectId.of(null)); + } + + @Test + void ofEmptyThrows() { + assertThrows(IllegalArgumentException.class, () -> PotionEffectId.of("")); + } + + @Test + void equalsAndHashCode() { + PotionEffectId a = PotionEffectId.of("SPEED"); + PotionEffectId b = PotionEffectId.of("SPEED"); + PotionEffectId c = PotionEffectId.of("JUMP"); + + assertEquals(a, b); + assertEquals(a.hashCode(), b.hashCode()); + assertNotEquals(a, c); + } +} diff --git a/src/test/java/gg/steve/mc/ap/model/id/SoundIdTest.java b/src/test/java/gg/steve/mc/ap/model/id/SoundIdTest.java new file mode 100644 index 0000000..58afd15 --- /dev/null +++ b/src/test/java/gg/steve/mc/ap/model/id/SoundIdTest.java @@ -0,0 +1,35 @@ +package gg.steve.mc.ap.model.id; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +class SoundIdTest { + + @Test + void ofCreatesExpectedValue() { + SoundId id = SoundId.of("ENTITY_PLAYER_LEVELUP"); + assertEquals("ENTITY_PLAYER_LEVELUP", id.toString()); + } + + @Test + void ofNullThrows() { + assertThrows(NullPointerException.class, () -> SoundId.of(null)); + } + + @Test + void ofEmptyThrows() { + assertThrows(IllegalArgumentException.class, () -> SoundId.of("")); + } + + @Test + void equalsAndHashCode() { + SoundId a = SoundId.of("DING"); + SoundId b = SoundId.of("DING"); + SoundId c = SoundId.of("BOOM"); + + assertEquals(a, b); + assertEquals(a.hashCode(), b.hashCode()); + assertNotEquals(a, c); + } +} diff --git a/src/test/java/gg/steve/mc/ap/model/id/StringIdTest.java b/src/test/java/gg/steve/mc/ap/model/id/StringIdTest.java new file mode 100644 index 0000000..6930a2d --- /dev/null +++ b/src/test/java/gg/steve/mc/ap/model/id/StringIdTest.java @@ -0,0 +1,13 @@ +package gg.steve.mc.ap.model.id; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +class StringIdTest { + + @Test + void ofEmptyThrowsIllegalArgumentException() { + assertThrows(IllegalArgumentException.class, () -> ArmorSetId.of("")); + } +} diff --git a/src/test/java/gg/steve/mc/ap/model/id/TypedStringTest.java b/src/test/java/gg/steve/mc/ap/model/id/TypedStringTest.java new file mode 100644 index 0000000..119a1df --- /dev/null +++ b/src/test/java/gg/steve/mc/ap/model/id/TypedStringTest.java @@ -0,0 +1,71 @@ +package gg.steve.mc.ap.model.id; + +import org.junit.jupiter.api.Test; + +import java.util.Arrays; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.*; + +class TypedStringTest { + + @Test + void differentSubtypesWithSameStringAreNotEqual() { + ArmorSetId armorSetId = ArmorSetId.of("SPEED"); + PotionEffectId potionEffectId = PotionEffectId.of("SPEED"); + + assertNotEquals(armorSetId, potionEffectId); + } + + @Test + void toStringsReturnsRawStrings() { + List ids = Arrays.asList( + ArmorSetId.of("dragon"), + ArmorSetId.of("knight") + ); + + List result = TypedString.toStrings(ids); + + assertEquals(Arrays.asList("dragon", "knight"), result); + } + + @Test + void toStringReturnsRawIdString() { + ArmorSetId id = ArmorSetId.of("dragon"); + assertEquals("dragon", id.toString()); + } + + @Test + void nullConstructorThrows() { + assertThrows(NullPointerException.class, () -> ArmorSetId.of(null)); + } + + @SuppressWarnings("EqualsWithItself") + @Test + void equalsNullReturnsFalse() { + ArmorSetId id = ArmorSetId.of("dragon"); + assertFalse(id.equals(null)); + } + + @SuppressWarnings("EqualsWithItself") + @Test + void equalsSelfReturnsTrue() { + ArmorSetId id = ArmorSetId.of("dragon"); + assertTrue(id.equals(id)); + } + + @Test + void sameTypeDifferentValueNotEqual() { + ArmorSetId a = ArmorSetId.of("dragon"); + ArmorSetId b = ArmorSetId.of("knight"); + assertNotEquals(a, b); + } + + @Test + void sameTypeSameValueEqual() { + ArmorSetId a = ArmorSetId.of("dragon"); + ArmorSetId b = ArmorSetId.of("dragon"); + assertEquals(a, b); + assertEquals(a.hashCode(), b.hashCode()); + } +} diff --git a/src/test/java/gg/steve/mc/ap/model/notification/ArmorSetNotificationTest.java b/src/test/java/gg/steve/mc/ap/model/notification/ArmorSetNotificationTest.java new file mode 100644 index 0000000..13e1b60 --- /dev/null +++ b/src/test/java/gg/steve/mc/ap/model/notification/ArmorSetNotificationTest.java @@ -0,0 +1,108 @@ +package gg.steve.mc.ap.model.notification; + +import gg.steve.mc.ap.model.effect.NotificationSound; +import gg.steve.mc.ap.model.id.SoundId; +import org.junit.jupiter.api.Test; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.*; + +class ArmorSetNotificationTest { + + @Test + void builderCreatesExpectedValues() { + NotificationSound sound = new NotificationSound(SoundId.of("DING"), 1.0f, 1.0f); + + ArmorSetNotification notification = ArmorSetNotification.builder() + .message("Welcome!") + .message("Enjoy your armor.") + .sound(sound) + .command("give %player% diamond 1") + .build(); + + assertEquals(Arrays.asList("Welcome!", "Enjoy your armor."), notification.getMessages()); + assertEquals(sound, notification.getSound()); + assertEquals(Arrays.asList("give %player% diamond 1"), notification.getCommands()); + } + + @Test + void equalsAndHashCode() { + NotificationSound sound = new NotificationSound(SoundId.of("DING"), 1.0f, 1.0f); + ArmorSetNotification a = ArmorSetNotification.builder() + .message("Hi") + .sound(sound) + .build(); + ArmorSetNotification b = ArmorSetNotification.builder() + .message("Hi") + .sound(sound) + .build(); + + assertEquals(a, b); + assertEquals(a.hashCode(), b.hashCode()); + } + + @Test + void emptyListsWhenNoItemsAdded() { + ArmorSetNotification notification = ArmorSetNotification.builder() + .sound(null) + .build(); + + assertNotNull(notification.getMessages()); + assertTrue(notification.getMessages().isEmpty()); + assertNotNull(notification.getCommands()); + assertTrue(notification.getCommands().isEmpty()); + } + + @Test + void bulkMessagesMethodDefensivelyCopies() { + List original = new ArrayList<>(Arrays.asList("A", "B")); + + ArmorSetNotification notification = ArmorSetNotification.builder() + .messages(original) + .command("cmd") + .build(); + + original.add("C"); + + assertEquals(Arrays.asList("A", "B"), notification.getMessages()); + } + + @Test + void bulkCommandsMethodDefensivelyCopies() { + List original = new ArrayList<>(Arrays.asList("x", "y")); + + ArmorSetNotification notification = ArmorSetNotification.builder() + .commands(original) + .message("msg") + .build(); + + original.add("z"); + + assertEquals(Arrays.asList("x", "y"), notification.getCommands()); + } + + @Test + void getMessagesReturnsUnmodifiableList() { + ArmorSetNotification notification = ArmorSetNotification.builder() + .message("hello") + .command("cmd") + .build(); + + assertThrows(UnsupportedOperationException.class, + () -> notification.getMessages().add("sneaky")); + } + + @Test + void getCommandsReturnsUnmodifiableList() { + ArmorSetNotification notification = ArmorSetNotification.builder() + .message("msg") + .command("run") + .build(); + + assertThrows(UnsupportedOperationException.class, + () -> notification.getCommands().add("sneaky")); + } +} diff --git a/src/test/java/gg/steve/mc/ap/model/player/ArmorSetWearerTest.java b/src/test/java/gg/steve/mc/ap/model/player/ArmorSetWearerTest.java new file mode 100644 index 0000000..856d661 --- /dev/null +++ b/src/test/java/gg/steve/mc/ap/model/player/ArmorSetWearerTest.java @@ -0,0 +1,34 @@ +package gg.steve.mc.ap.model.player; + +import gg.steve.mc.ap.model.id.ArmorSetId; +import gg.steve.mc.ap.model.id.PlayerId; +import org.junit.jupiter.api.Test; + +import java.util.UUID; + +import static org.junit.jupiter.api.Assertions.*; + +class ArmorSetWearerTest { + + @Test + void constructsWithExpectedValues() { + PlayerId id = PlayerId.of(UUID.randomUUID()); + ArmorSetId setId = ArmorSetId.of("dragon"); + ArmorSetWearer wearer = new ArmorSetWearer(id, setId); + + assertEquals(id, wearer.getPlayerId()); + assertEquals(setId, wearer.getSetId()); + } + + @Test + void equalsAndHashCode() { + PlayerId id = PlayerId.of(UUID.fromString("00000000-0000-0000-0000-000000000001")); + ArmorSetWearer a = new ArmorSetWearer(id, ArmorSetId.of("dragon")); + ArmorSetWearer b = new ArmorSetWearer(id, ArmorSetId.of("dragon")); + ArmorSetWearer c = new ArmorSetWearer(id, ArmorSetId.of("knight")); + + assertEquals(a, b); + assertEquals(a.hashCode(), b.hashCode()); + assertNotEquals(a, c); + } +} diff --git a/src/test/java/gg/steve/mc/ap/model/set/ArmorPieceSlotTest.java b/src/test/java/gg/steve/mc/ap/model/set/ArmorPieceSlotTest.java new file mode 100644 index 0000000..8cfc53b --- /dev/null +++ b/src/test/java/gg/steve/mc/ap/model/set/ArmorPieceSlotTest.java @@ -0,0 +1,30 @@ +package gg.steve.mc.ap.model.set; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +class ArmorPieceSlotTest { + + @Test + void allValuesPresent() { + ArmorPieceSlot[] values = ArmorPieceSlot.values(); + assertEquals(5, values.length); + } + + @Test + void valueOfRoundTrips() { + for (ArmorPieceSlot slot : ArmorPieceSlot.values()) { + assertEquals(slot, ArmorPieceSlot.valueOf(slot.name())); + } + } + + @Test + void expectedSlots() { + assertNotNull(ArmorPieceSlot.valueOf("HELMET")); + assertNotNull(ArmorPieceSlot.valueOf("CHESTPLATE")); + assertNotNull(ArmorPieceSlot.valueOf("LEGGINGS")); + assertNotNull(ArmorPieceSlot.valueOf("BOOTS")); + assertNotNull(ArmorPieceSlot.valueOf("HAND")); + } +} diff --git a/src/test/java/gg/steve/mc/ap/model/set/ArmorSetPurchaseResultTest.java b/src/test/java/gg/steve/mc/ap/model/set/ArmorSetPurchaseResultTest.java new file mode 100644 index 0000000..49d0165 --- /dev/null +++ b/src/test/java/gg/steve/mc/ap/model/set/ArmorSetPurchaseResultTest.java @@ -0,0 +1,21 @@ +package gg.steve.mc.ap.model.set; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +class ArmorSetPurchaseResultTest { + + @Test + void allValuesPresent() { + ArmorSetPurchaseResult[] values = ArmorSetPurchaseResult.values(); + assertEquals(3, values.length); + } + + @Test + void valueOfRoundTrips() { + for (ArmorSetPurchaseResult result : ArmorSetPurchaseResult.values()) { + assertEquals(result, ArmorSetPurchaseResult.valueOf(result.name())); + } + } +}