Sprint 2 Test Plan — Card Upgrade, Status Effects & Rewards System - UQcsse3200/2026-studio-3 GitHub Wiki
What was tested:
- Positive amount heals, clamped at max health (via existing
heal()logic) - Negative amount reduces health directly, bypassing armor and block entirely
- Health reaching 0 correctly triggers the
entityIsDeadevent - Calling again after the entity is already dead does NOT retrigger the death event (guard added following Team 2's code review)
- Zero amount is a no-op
Test coverage: 5 dedicated unit tests in CombatStatsComponentTest.java, all
passing. Full suite: 15/15 tests passing, verified via
./gradlew core:test --tests "com.csse3200.game.components.CombatStatsComponentTest".
Sufficiency: Covers every boundary condition relevant to non-combat health changes used by Team 2's Chance Encounter system — armor/block bypass, positive/negative amounts, death-state transitions, and repeated calls after death.
Status: Merged to main (PR #215), integrated and used by Team 2's Sprint 2 Non-Battle Encounter feature (PR #232).
Notes on manual testing: This component is pure logic with no UI or rendering involved, so full coverage was achieved through JUnit unit tests without requiring manual or video testing.
RewardService.claimReward(Entity, RewardOption) / RewardGenerator / GoldReward.calculateFinalGold(Entity, int)
What was tested:
- generateRewardOptions() always returns exactly one GOLD option and one ITEM option, never more or fewer
- Generated gold amount stays within the configured range (20–30), verified across 100 samples with a seeded Random for reproducibility
- claimReward() correctly writes gold into InventoryComponent when the selected option is GOLD
- claimReward() applies only the selected option — the unselected option has no side effect on player state
- Gold bonus multiplier (goldBonusMultiplier) is correctly applied to gold rewards via calculateFinalGold() — verified with and without a bonus present, and with multiple stacked bonuses
- LuckyCoinEffect correctly increases goldBonusMultiplier, and the bonus persists into subsequent gold claims within the same Player instance
- Full RunState → RewardService → InventoryComponent flow, mirroring the real BattleScreen logic, correctly transfers a pending reward to a live player entity and clears it after application
- The known cross-battle persistence limitation (bonuses reset when a new Player entity is created via PlayerFactory.createPlayer()) is reproduced and documented as an explicit test case, rather than left undocumented
Test coverage: 14 dedicated unit and integration tests across GoldRewardTest.java, LuckyCoinEffectTest.java, RewardGeneratorTest.java, RewardServiceTest.java, and RewardIntegrationTest.java, all passing. Full suite verified via ./gradlew build.
Sufficiency: Covers every boundary condition relevant to the gold reward economy used by the Settlement UI (Celeste's RewardDisplay) — correct reward shape, bonus calculation and stacking, claim isolation between options, and the RunState bridging logic that carries a reward across the battle → map → battle screen transition.
Status: Merged to Feature-7 via PR #<191>, integrated with and used by Celeste's Settlement UI & Process Link (RewardDisplay, BattleScreen wiring).
Notes on manual testing: The core reward logic is pure and has no UI or rendering involved, so full coverage was achieved through JUnit unit and integration tests. The one aspect that cannot be captured by automated tests — whether a permanent item bonus actually survives into a real subsequent battle in-game — was investigated via code inspection instead (tracing PlayerFactory.createPlayer() through BattleScreen and ForestGameArea) rather than manual play testing, since the current architecture makes this observably impossible to verify either way; this is documented as a known limitation rather than claimed as tested.
RewardDisplay / RunState.pendingReward flow / EnergyCrystalEffect / MerchantsFavorEffect
What was tested:
RewardDisplay renders exactly 2 option buttons (1 GOLD + 1 ITEM) — regression-tested after fixing a merge artifact that had rendered 4 duplicate buttons
Clicking a reward button correctly stores the selection in RunState.pendingReward and does not apply it immediately (no live player entity exists at claim time)
The pending reward is correctly read and applied via RewardService.claimReward() once the next BattleScreen's player entity is constructed, then cleared from RunState
EnergyCrystalEffect.apply() correctly increases EnergyComponent.maxEnergy by +1, verified both at the unit level and end-to-end (HUD reflects the new max energy on the following battle)
EnergyComponent.setMaxEnergy() correctly fires the updateEnergy event with (currentEnergy, maxEnergy) — regression-tested after fixing a mismatched event name (updateMaxEnergy) that previously left the HUD unrefreshed
MerchantsFavorEffect.apply() correctly increases InventoryComponent.shopDiscount by +5% per application, capped at 50%, verified for single application, repeated stacking, and cap enforcement
Reward button clicks no longer bubble up and trigger EndBattleDisplay's global "click anywhere to return to menu" listener (event.stop() added), and claimOption() now explicitly fires RETURN_TO_MENU_EVENT so claiming a reward returns to the map in a single click
Test coverage: 6 dedicated unit tests across EnergyCrystalEffectTest.java and MerchantsFavorEffectTest.java, all passing. Full suite verified via ./gradlew test.
Sufficiency: Covers every boundary condition relevant to the item effects consumed by the Settlement UI — single application, stacking, cap enforcement, and the event-firing contract the HUD depends on for correct display. The RunState bridging behaviour and the four UI bugs (duplicate buttons, misclick-triggered exit, HUD not refreshing, extra click required to return to menu) were verified end-to-end in a live build rather than via unit test, since they involve Scene2D rendering and screen-transition timing that aren't practical to unit test. Status: Merged to Feature7-Rewards-System (commit efb542f), PR opened against Card-upgrade-system-&-New-Status-effects-&-Reward-System.
Notes on manual testing: RewardDisplay is a UI component with no meaningful unit-test surface for its rendering/click behaviour (Scene2D actors), so verification was done through live builds: console logging (create() call count, options.size(), claimOption() payload) plus repeated in-game playthroughs confirming correct button count, correct claim-and-return behaviour, and correct HUD refresh on the following battle. One item (MERCHANTS_FAVOR's actual discount at purchase time) could not be manually verified in-game because the shop UI is not yet wired into the map — this is documented as a known limitation rather than claimed as tested.
CombatStatsComponent / BattleController / SUNDER / PIERCE / CLEANSE / POISON Lifecycle
What was tested:
The runtime behaviour of SUNDER, PIERCE, CLEANSE and POISON was tested. This included armour reduction, piercing damage that bypasses block and armour, removal of negative status effects, poison damage and expiry, enemy-turn skipping when poison kills an enemy, and victory when poison kills the final enemy.
Test coverage:
Unit tests in ArmorAndStatusEffectTest.java cover valid inputs, non-positive values, excessive armour reduction, repeated cleansing, preservation of positive effects, poison replacement and expiry, piercing-damage health clamping, and death-event handling. Integration tests in BattleControllerTest.java cover poison processing during the enemy-turn lifecycle, defence absorption, skipped enemy actions and battle victory.
Sufficiency:
The tests cover the component-level operations, relevant boundary cases and the BattleController integration paths introduced by this feature. They verify both normal behaviour and edge cases, including dead entities, expired effects, non-positive values and lethal damage.
Status:
Passed. All unit tests, formatting checks, compilation checks, Javadoc checks and four GitHub PR checks completed successfully.
Notes on manual testing:
The game was launched locally after merging the Team 3 battle-flow changes. The battle flow ran without errors. Automated integration tests were used to verify the detailed poison lifecycle and enemy-turn behaviour.
Test files:
source/core/src/test/com/csse3200/game/components/ArmorAndStatusEffectTest.javasource/core/src/test/com/csse3200/game/components/combat/BattleControllerTest.java
What was tested:
- Option building derives one UpgradeOption per deck card that actually defines an
upgradein its CardConfig, each carrying its original deck index plus a resolvedcurrentandpreviewResolvedCard, so the UI can show a before/after comparison - Cards with no upgrade defined produce no options at all (empty list, not an error)
- Unknown card IDs in the deck are skipped without failing, and the surviving options keep their true deck indices rather than being renumbered
- Toggling a card selects it; toggling the same card again deselects it and restores the remaining-selection count
- The selection cap (maxSelections) is enforced: once the cap is reached, new selections are ignored and leave state unchanged
- Already-selected cards stay toggleable at the cap, so a player can revise a choice without having to clear the whole selection first
-
canConfirm()is false with an empty selection and true once at least one card is chosen -
getSelectedDeckIndices()returns indices in descending order, so a committer can apply upgrades by index without earlier removals shifting later ones - The returned selection list is immutable — mutating it throws UnsupportedOperationException
- Constructor rejects a null deck, a null CardService, and a maxSelections below 1
Test coverage: 11 dedicated unit tests in CardUpgradeSelectionTest.java, all
passing (11 tests, 0 failures, 0 errors), verified via
./gradlew core:test --tests "com.csse3200.game.components.cards.CardUpgradeSelectionTest".
Sufficiency: Covers every boundary condition in the selection state machine — option derivation from card configs, unknown and non-upgradable cards, toggle idempotence, cap enforcement in both directions, confirm gating, index ordering, and constructor validation. These are the rules the UI depends on, so testing them at the component level means CardUpgradeDisplay only has to be verified for rendering and click wiring.
Status: Merged to main via PR #256.
Notes on manual testing: CardUpgradeDisplay is a Scene2D UI component (card
tiles, a library overlay, a confirm button) with no meaningful unit-test surface for
its rendering and click behaviour, so it was verified through live builds: opening the
Upgrade overlay from EndBattleScreen, confirming the correct number of tiles appear,
that selecting past the cap is visually rejected, that selected tiles are highlighted,
and that Confirm only enables once a card is selected.
Known limitation: CardUpgradeCommitter currently has exactly one implementation,
LoggingCardUpgradeCommitter, which logs the selected deck indices rather than
mutating the deck. The selection, preview and confirmation flow is complete and
tested, but the chosen upgrades are not yet persisted into the player's deck — that
requires a real committer wired to the deck/card service. This is stated as an
outstanding integration point rather than claimed as working.
Test file:
- `source/core/src/test/com/csse3200/game/components/cards/CardUpgradeSelectionTest.java
Commit:
https://github.com/UQcsse3200/2026-studio-3/commit/efcef20