KoogleApp/test/Koogle.Tests/ReducerTests/ThrowPanelStateTests.cs

649 lines
23 KiB
C#

using KoogleApp.Components.Controls;
using KoogleApp.Games.Training;
using KoogleApp.Store.Game.ThrowPanel;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using FluentAssertions;
namespace Koogle.Tests.ReducerTests
{
public class ThrowPanelStateTests
{
[Fact]
public void StartStopAction_StartAGame()
{
// Arrange
var initialState = new ThrowPanelState();
var action = new StartStopAction(new TrainingSetupState());
// Act
var newState = ThrowPanelStateReducer.OnStartStop(initialState, action);
// Assert
newState.IsStated.Should().Be(true);
}
[Fact]
public void StartStopAction_StopAGame()
{
// Arrange
var initialState = new ThrowPanelState();
var action = new StartStopAction(null);
// Act
var newState = ThrowPanelStateReducer.OnStartStop(initialState, action);
// Assert
newState.IsStated.Should().Be(false);
}
[Theory]
[InlineData(1, true, PinStatus.Fallen)]
[InlineData(1, false, PinStatus.Standing)]
[InlineData(5, true, PinStatus.Fallen)]
[InlineData(9, true, PinStatus.Fallen)]
public void TogglePinValueAction_SetsPinState(int pinNumber, bool isHit, PinStatus expectedStatus)
{
// Arrange
var initialState = new ThrowPanelState();
var action = new TogglePinValueAction(isHit, pinNumber);
// Act
var newState = ThrowPanelStateReducer.OnTogglePinValue(initialState, action);
// Assert
var actualStatus = pinNumber switch
{
1 => newState.Pin1State,
2 => newState.Pin2State,
3 => newState.Pin3State,
4 => newState.Pin4State,
5 => newState.Pin5State,
6 => newState.Pin6State,
7 => newState.Pin7State,
8 => newState.Pin8State,
9 => newState.Pin9State,
_ => throw new ArgumentException("Invalid pin number")
};
actualStatus.Should().Be(expectedStatus);
newState.ThrowPanelStateStatus.Should().Be(ThrowPanelStateStatus.BeforeThrow);
}
[Fact]
public void ToggleAllPinsAction_TogglesAllStandingPinsToFallen()
{
// Arrange
var initialState = new ThrowPanelState(); // All pins standing
// Act
var newState = ThrowPanelStateReducer.OnToggleAllPins(initialState, new ToggleAllPinsAction());
// Assert
newState.Pin1State.Should().Be(PinStatus.Fallen);
newState.Pin2State.Should().Be(PinStatus.Fallen);
newState.Pin3State.Should().Be(PinStatus.Fallen);
newState.Pin4State.Should().Be(PinStatus.Fallen);
newState.Pin5State.Should().Be(PinStatus.Fallen);
newState.Pin6State.Should().Be(PinStatus.Fallen);
newState.Pin7State.Should().Be(PinStatus.Fallen);
newState.Pin8State.Should().Be(PinStatus.Fallen);
newState.Pin9State.Should().Be(PinStatus.Fallen);
newState.ThrowPanelStateStatus.Should().Be(ThrowPanelStateStatus.BeforeThrow);
}
[Fact]
public void ToggleAllPinsAction_TogglesAllFallenPinsToStanding()
{
// Arrange
var initialState = new ThrowPanelState() with
{
Pin1State = PinStatus.Fallen,
Pin2State = PinStatus.Fallen,
Pin3State = PinStatus.Fallen,
Pin4State = PinStatus.Fallen,
Pin5State = PinStatus.Fallen,
Pin6State = PinStatus.Fallen,
Pin7State = PinStatus.Fallen,
Pin8State = PinStatus.Fallen,
Pin9State = PinStatus.Fallen
};
// Act
var newState = ThrowPanelStateReducer.OnToggleAllPins(initialState, new ToggleAllPinsAction());
// Assert
newState.Pin1State.Should().Be(PinStatus.Standing);
newState.Pin2State.Should().Be(PinStatus.Standing);
newState.Pin3State.Should().Be(PinStatus.Standing);
newState.Pin4State.Should().Be(PinStatus.Standing);
newState.Pin5State.Should().Be(PinStatus.Standing);
newState.Pin6State.Should().Be(PinStatus.Standing);
newState.Pin7State.Should().Be(PinStatus.Standing);
newState.Pin8State.Should().Be(PinStatus.Standing);
newState.Pin9State.Should().Be(PinStatus.Standing);
}
[Fact]
public void ToggleAllPinsAction_DoesNotToggleDisabledPins()
{
// Arrange
var initialState = new ThrowPanelState() with
{
Pin1State = PinStatus.Disabled,
Pin2State = PinStatus.Standing,
Pin3State = PinStatus.Standing
};
// Act
var newState = ThrowPanelStateReducer.OnToggleAllPins(initialState, new ToggleAllPinsAction());
// Assert
newState.Pin1State.Should().Be(PinStatus.Disabled);
newState.Pin2State.Should().Be(PinStatus.Fallen);
newState.Pin3State.Should().Be(PinStatus.Fallen);
}
[Fact]
public void ToggleBellAction_TogglesBellValue()
{
// Arrange
var initialState = new ThrowPanelState(); // BellValue = false
// Act
var newState = ThrowPanelStateReducer.OnToggleBell(initialState, new ToggleBellAction());
// Assert
newState.BellValue.Should().Be(true);
newState.ThrowPanelStateStatus.Should().Be(ThrowPanelStateStatus.BeforeThrow);
}
[Fact]
public void ToggleBellAction_TogglesBellValueBackToFalse()
{
// Arrange
var initialState = new ThrowPanelState() with { BellValue = true };
// Act
var newState = ThrowPanelStateReducer.OnToggleBell(initialState, new ToggleBellAction());
// Assert
newState.BellValue.Should().Be(false);
}
[Fact]
public void UpdatePinStateByNumberAction_SetFirstNPinsToFallen()
{
// Arrange
var initialState = new ThrowPanelState(); // All pins standing
var action = new UpdatePinStateByNumberAction(3);
// Act
var newState = ThrowPanelStateReducer.OnUpdatePinStateByNumber(initialState, action);
// Assert - pins are set from the back (Pin9, Pin8, Pin7...)
newState.Pin9State.Should().Be(PinStatus.Standing);
newState.Pin8State.Should().Be(PinStatus.Standing);
newState.Pin7State.Should().Be(PinStatus.Standing);
newState.Pin6State.Should().Be(PinStatus.Standing);
newState.Pin5State.Should().Be(PinStatus.Standing);
newState.Pin4State.Should().Be(PinStatus.Standing);
newState.Pin3State.Should().Be(PinStatus.Fallen);
newState.Pin2State.Should().Be(PinStatus.Fallen);
newState.Pin1State.Should().Be(PinStatus.Fallen);
newState.ThrowPanelStateStatus.Should().Be(ThrowPanelStateStatus.BeforeThrow);
}
[Fact]
public void UpdatePinStateByNumberAction_SetAllPinsToFallen()
{
// Arrange
var initialState = new ThrowPanelState();
var action = new UpdatePinStateByNumberAction(9);
// Act
var newState = ThrowPanelStateReducer.OnUpdatePinStateByNumber(initialState, action);
// Assert
newState.Pin1State.Should().Be(PinStatus.Fallen);
newState.Pin2State.Should().Be(PinStatus.Fallen);
newState.Pin3State.Should().Be(PinStatus.Fallen);
newState.Pin4State.Should().Be(PinStatus.Fallen);
newState.Pin5State.Should().Be(PinStatus.Fallen);
newState.Pin6State.Should().Be(PinStatus.Fallen);
newState.Pin7State.Should().Be(PinStatus.Fallen);
newState.Pin8State.Should().Be(PinStatus.Fallen);
newState.Pin9State.Should().Be(PinStatus.Fallen);
}
[Fact]
public void UpdatePinStateByNumberAction_SetZeroPinsToFallen()
{
// Arrange
var initialState = new ThrowPanelState() with
{
Pin1State = PinStatus.Fallen,
Pin2State = PinStatus.Fallen
};
var action = new UpdatePinStateByNumberAction(0);
// Act
var newState = ThrowPanelStateReducer.OnUpdatePinStateByNumber(initialState, action);
// Assert
newState.Pin1State.Should().Be(PinStatus.Standing);
newState.Pin2State.Should().Be(PinStatus.Standing);
newState.Pin3State.Should().Be(PinStatus.Standing);
}
[Fact]
public void EnsureBeforeThrowStatusAction_SetsStatusToBeforeThrow()
{
// Arrange
var initialState = new ThrowPanelState() with
{
ThrowPanelStateStatus = ThrowPanelStateStatus.AfterThrow
};
var action = new EnsureBeforeThrowStatusAction(null!, null!);
// Act
var newState = ThrowPanelStateReducer.OnEnsureBeforeThrowStatusAction(initialState, action);
// Assert
newState.ThrowPanelStateStatus.Should().Be(ThrowPanelStateStatus.BeforeThrow);
}
#region CalculateNextPanelState Extension Tests
[Fact]
public void CalculateNextPanelState_RepositionMode_ResetsPinsAndIncrementsCounter()
{
// Arrange
var state = new ThrowPanelState() with
{
ThrowMode = ThrowMode.Reposition,
ThrowsPerRound = 3,
ThrowCounterPerRound = 1,
ThrowCounter = 0,
Pin1State = PinStatus.Fallen,
Pin2State = PinStatus.Fallen,
Pin3State = PinStatus.Standing,
BellValue = true
};
// Act
var newState = state.CalculateNextPanelState();
// Assert - In Reposition mode, pins are reset after each throw
newState.Pin1State.Should().Be(PinStatus.Standing);
newState.Pin2State.Should().Be(PinStatus.Standing);
newState.Pin3State.Should().Be(PinStatus.Standing);
newState.ThrowCounterPerRound.Should().Be(2);
newState.ThrowCounter.Should().Be(1);
newState.BellValue.Should().Be(false);
newState.ThrowPanelStateStatus.Should().Be(ThrowPanelStateStatus.AfterThrow);
}
[Fact]
public void CalculateNextPanelState_DecreaseMode_DisablesFallenPins()
{
// Arrange
var state = new ThrowPanelState() with
{
ThrowMode = ThrowMode.Decrease,
ThrowsPerRound = 3,
ThrowCounterPerRound = 1,
ThrowCounter = 0,
Pin1State = PinStatus.Fallen,
Pin2State = PinStatus.Fallen,
Pin3State = PinStatus.Standing,
Pin4State = PinStatus.Standing,
Pin5State = PinStatus.Standing,
Pin6State = PinStatus.Standing,
Pin7State = PinStatus.Standing,
Pin8State = PinStatus.Standing,
Pin9State = PinStatus.Standing
};
// Act
var newState = state.CalculateNextPanelState();
// Assert - In Decrease mode, fallen pins become disabled
newState.Pin1State.Should().Be(PinStatus.Disabled);
newState.Pin2State.Should().Be(PinStatus.Disabled);
newState.Pin3State.Should().Be(PinStatus.Standing);
newState.ThrowCounterPerRound.Should().Be(2);
}
[Fact]
public void CalculateNextPanelState_AllPinsFallen_ResetsPinsRegardlessOfMode()
{
// Arrange - All pins fallen (cleared)
var state = new ThrowPanelState() with
{
ThrowMode = ThrowMode.Decrease,
ThrowsPerRound = 3,
ThrowCounterPerRound = 1,
Pin1State = PinStatus.Fallen,
Pin2State = PinStatus.Fallen,
Pin3State = PinStatus.Fallen,
Pin4State = PinStatus.Fallen,
Pin5State = PinStatus.Fallen,
Pin6State = PinStatus.Fallen,
Pin7State = PinStatus.Fallen,
Pin8State = PinStatus.Fallen,
Pin9State = PinStatus.Fallen
};
// Act
var newState = state.CalculateNextPanelState();
// Assert - All pins reset when cleared
newState.Pin1State.Should().Be(PinStatus.Standing);
newState.Pin2State.Should().Be(PinStatus.Standing);
newState.Pin3State.Should().Be(PinStatus.Standing);
newState.Pin4State.Should().Be(PinStatus.Standing);
newState.Pin5State.Should().Be(PinStatus.Standing);
newState.Pin6State.Should().Be(PinStatus.Standing);
newState.Pin7State.Should().Be(PinStatus.Standing);
newState.Pin8State.Should().Be(PinStatus.Standing);
newState.Pin9State.Should().Be(PinStatus.Standing);
}
[Fact]
public void CalculateNextPanelState_LastThrowInRound_ResetsCounterToOne()
{
// Arrange
var state = new ThrowPanelState() with
{
ThrowMode = ThrowMode.Reposition,
ThrowsPerRound = 3,
ThrowCounterPerRound = 3, // Last throw of round
ThrowCounter = 2
};
// Act
var newState = state.CalculateNextPanelState();
// Assert
newState.ThrowCounterPerRound.Should().Be(1);
newState.ThrowCounter.Should().Be(3);
}
#endregion
#region Reducer State Replacement Tests
[Fact]
public void OnReceiveStateFromServer_ReplacesEntireState()
{
// Arrange
var initialState = new ThrowPanelState();
var serverState = new ThrowPanelState() with
{
IsStated = true,
ThrowCounter = 10,
Pin5State = PinStatus.Fallen,
DayId = 42
};
var action = new ReceiveThrowPanelStateFromServerAction(serverState);
// Act
var newState = ThrowPanelStateReducer.OnReceiveStateFromServer(initialState, action);
// Assert
newState.Should().Be(serverState);
newState.IsStated.Should().Be(true);
newState.ThrowCounter.Should().Be(10);
newState.Pin5State.Should().Be(PinStatus.Fallen);
newState.DayId.Should().Be(42);
}
[Fact]
public void OnStateLoaded_ReplacesEntireState()
{
// Arrange
var initialState = new ThrowPanelState();
var loadedState = new ThrowPanelState() with
{
IsStated = true,
ThrowMode = ThrowMode.Decrease,
ThrowsPerRound = 5
};
var action = new ThrowPanelStateLoadedAction(loadedState);
// Act
var newState = ThrowPanelStateReducer.OnStateLoaded(initialState, action);
// Assert
newState.Should().Be(loadedState);
}
[Fact]
public void OnOverwriteThrowPanelStateAction_ReplacesEntireState()
{
// Arrange
var initialState = new ThrowPanelState();
var overwriteState = new ThrowPanelState() with
{
BellValue = true,
ThrowCounterPerRound = 2
};
var action = new OverwriteThrowPanelStateAction(overwriteState);
// Act
var newState = ThrowPanelStateReducer.OnOverwriteThrowPanelStateAction(initialState, action);
// Assert
newState.Should().Be(overwriteState);
}
[Fact]
public void OnUpdateStateAfterUndoRedo_ReplacesThrowPanelState()
{
// Arrange
var initialState = new ThrowPanelState();
var undoRedoState = new ThrowPanelState() with
{
ThrowCounter = 5,
Pin1State = PinStatus.Disabled
};
var action = new UpdateStateAfterUndoRedo(undoRedoState, null!, null!);
// Act
var newState = ThrowPanelStateReducer.OnUpdateStateAfterUndoRedo(initialState, action);
// Assert
newState.Should().Be(undoRedoState);
}
#endregion
#region StartStopAction Parameter Tests
[Fact]
public void StartStopAction_StartGame_SetsThrowModeFromParams()
{
// Arrange
var initialState = new ThrowPanelState();
var setupState = new TrainingSetupState() { ThrowMode = ThrowMode.Decrease };
var action = new StartStopAction(setupState);
// Act
var newState = ThrowPanelStateReducer.OnStartStop(initialState, action);
// Assert
newState.ThrowMode.Should().Be(ThrowMode.Decrease);
}
[Fact]
public void StartStopAction_StartGame_SetsThrowsPerRoundFromParams()
{
// Arrange
var initialState = new ThrowPanelState();
var setupState = new TrainingSetupState() { ThrowsPerRound = 5 };
var action = new StartStopAction(setupState);
// Act
var newState = ThrowPanelStateReducer.OnStartStop(initialState, action);
// Assert
newState.ThrowsPerRound.Should().Be(5);
}
[Fact]
public void StartStopAction_StartGame_SetsDayIdFromParams()
{
// Arrange
var initialState = new ThrowPanelState();
var setupState = new TrainingSetupState() { DayId = 123 };
var action = new StartStopAction(setupState);
// Act
var newState = ThrowPanelStateReducer.OnStartStop(initialState, action);
// Assert
newState.DayId.Should().Be(123);
}
[Fact]
public void StartStopAction_StartGame_SetsStatusToGameStart()
{
// Arrange
var initialState = new ThrowPanelState();
var setupState = new TrainingSetupState();
var action = new StartStopAction(setupState);
// Act
var newState = ThrowPanelStateReducer.OnStartStop(initialState, action);
// Assert
newState.ThrowPanelStateStatus.Should().Be(ThrowPanelStateStatus.GameStart);
}
[Fact]
public void StartStopAction_StopGame_SetsStatusToGameEnd()
{
// Arrange
var initialState = new ThrowPanelState() with { IsStated = true };
var action = new StartStopAction(null);
// Act
var newState = ThrowPanelStateReducer.OnStartStop(initialState, action);
// Assert
newState.ThrowPanelStateStatus.Should().Be(ThrowPanelStateStatus.GameEnd);
newState.IsStated.Should().Be(false);
}
#endregion
#region Selector Tests
[Theory]
[InlineData(ThrowMode.Decrease, "Abräumen")]
[InlineData(ThrowMode.Reposition, "in die Vollen")]
public void GetThrowModeName_ReturnsCorrectName(ThrowMode mode, string expectedName)
{
// Arrange
var state = new ThrowPanelState() with { ThrowMode = mode };
// Act
var name = ThrowPanelSelectors.GetThrowModeName(state);
// Assert
name.Should().Be(expectedName);
}
#endregion
#region Edge Case Tests
[Fact]
public void TogglePinValueAction_InvalidPinNumber_ReturnsUnchangedState()
{
// Arrange
var initialState = new ThrowPanelState();
var action = new TogglePinValueAction(true, 0); // Invalid pin number
// Act
var newState = ThrowPanelStateReducer.OnTogglePinValue(initialState, action);
// Assert
newState.Should().Be(initialState);
}
[Fact]
public void TogglePinValueAction_PinNumber10_ReturnsUnchangedState()
{
// Arrange
var initialState = new ThrowPanelState();
var action = new TogglePinValueAction(true, 10); // Invalid pin number
// Act
var newState = ThrowPanelStateReducer.OnTogglePinValue(initialState, action);
// Assert
newState.Should().Be(initialState);
}
[Fact]
public void ThrowPanelState_DefaultConstructor_HasCorrectDefaults()
{
// Act
var state = new ThrowPanelState();
// Assert
state.IsStated.Should().Be(false);
state.BellValue.Should().Be(false);
state.Pin1State.Should().Be(PinStatus.Standing);
state.Pin2State.Should().Be(PinStatus.Standing);
state.Pin3State.Should().Be(PinStatus.Standing);
state.Pin4State.Should().Be(PinStatus.Standing);
state.Pin5State.Should().Be(PinStatus.Standing);
state.Pin6State.Should().Be(PinStatus.Standing);
state.Pin7State.Should().Be(PinStatus.Standing);
state.Pin8State.Should().Be(PinStatus.Standing);
state.Pin9State.Should().Be(PinStatus.Standing);
state.ThrowsPerRound.Should().Be(3);
state.ThrowCounterPerRound.Should().Be(1);
state.ThrowMode.Should().Be(ThrowMode.Reposition);
state.ThrowPanelStateStatus.Should().Be(ThrowPanelStateStatus.Undefined);
state.ThrowCounter.Should().Be(0);
state.DayId.Should().Be(0);
}
[Fact]
public void UpdatePinStateByNumberAction_DisabledPinsAreSkippedInCount()
{
// Arrange - Pin1 is disabled (last in iteration order)
var initialState = new ThrowPanelState() with
{
Pin1State = PinStatus.Disabled
};
var action = new UpdatePinStateByNumberAction(8);
// Act
var newState = ThrowPanelStateReducer.OnUpdatePinStateByNumber(initialState, action);
// Assert - Disabled pins are skipped when counting
// With 8 fallen pins requested and Pin1 disabled, all other 8 pins become fallen
// Pin1 remains standing (not disabled) because the reducer doesn't preserve disabled state
newState.Pin9State.Should().Be(PinStatus.Fallen);
newState.Pin8State.Should().Be(PinStatus.Fallen);
newState.Pin7State.Should().Be(PinStatus.Fallen);
newState.Pin6State.Should().Be(PinStatus.Fallen);
newState.Pin5State.Should().Be(PinStatus.Fallen);
newState.Pin4State.Should().Be(PinStatus.Fallen);
newState.Pin3State.Should().Be(PinStatus.Fallen);
newState.Pin2State.Should().Be(PinStatus.Fallen);
// Pin1 was disabled, but the reducer skips it in counting, keeping it standing
newState.Pin1State.Should().Be(PinStatus.Disabled);
}
#endregion
}
}