// // Licensed under the MIT License. See LICENSE file in the project root for full license information. // namespace MUnique.OpenMU.Tests; using Microsoft.Extensions.Logging.Abstractions; using MUnique.OpenMU.DataModel.Configuration; using MUnique.OpenMU.GameLogic; using MUnique.OpenMU.Persistence.InMemory; using MUnique.OpenMU.PlugIns; /// /// Tests for . /// [TestFixture] public class PartyManagerTest { private const byte MaxPartySize = 5; private PartyManager _partyManager = null!; /// /// Sets up a fresh before each test. /// [SetUp] public void SetUp() { this._partyManager = new PartyManager(MaxPartySize, new NullLogger()); } /// /// Tests that returns a party with the configured max size. /// [Test] public void CreateParty_RespectsMaxPartySize() { var party = this._partyManager.CreateParty(); Assert.That(party.MaxPartySize, Is.EqualTo(MaxPartySize)); } /// /// Tests that a member added to the party is present in the party list. /// [Test] public async ValueTask CreateParty_AddedMemberIsInPartyList() { var member = await this.CreatePartyMemberAsync().ConfigureAwait(false); var party = this._partyManager.CreateParty(); await party.AddAsync(member).ConfigureAwait(false); Assert.That(party.PartyList, Contains.Item(member)); } /// /// Tests that does nothing /// when the member has no cached party. /// [Test] public async ValueTask OnMemberReconnected_DoesNothingWhenNoCachedParty() { var member = await this.CreatePartyMemberAsync().ConfigureAwait(false); await this._partyManager.OnMemberReconnectedAsync(member).ConfigureAwait(false); Assert.That(member.Party, Is.Null); } /// /// Tests that when a member is kicked the party list no longer contains them. /// [Test] public async ValueTask KickedMember_IsRemovedFromPartyList() { var (party, _, member2) = await this.CreatePartyWithTwoMembersAsync().ConfigureAwait(false); await party.KickPlayerAsync(GetPartyMemberIndex(party, member2)).ConfigureAwait(false); Assert.That(party.PartyList, Is.Not.Contains(member2)); Assert.That(member2.Party, Is.Null); } /// /// Tests that replaces a live member /// with an snapshot. /// [Test] public async ValueTask LeaveTemporarily_ReplacesWithOfflineSnapshot() { var (party, member1, _) = await this.CreatePartyWithTwoMembersAsync().ConfigureAwait(false); await party.LeaveTemporarilyAsync(member1).ConfigureAwait(false); Assert.That(party.PartyList[0], Is.InstanceOf()); Assert.That(party.PartyList[0].Name, Is.EqualTo(member1.Name)); } /// /// Tests that restores the live member /// from an snapshot. /// [Test] public async ValueTask OnMemberReconnected_RestoresLiveMember() { var (party, member1, _) = await this.CreatePartyWithTwoMembersAsync().ConfigureAwait(false); var originalName = member1.Name; // Member disconnects - replaced with offline snapshot await party.LeaveTemporarilyAsync(member1).ConfigureAwait(false); // Member reconnects - restored from snapshot await this._partyManager.OnMemberReconnectedAsync(member1).ConfigureAwait(false); Assert.That(party.PartyList[0], Is.SameAs(member1)); Assert.That(party.PartyList[0].Name, Is.EqualTo(originalName)); } /// /// Tests that after reconnect, the live member's Party reference is restored. /// [Test] public async ValueTask OnMemberReconnected_RestoresPartyReference() { var (party, member1, _) = await this.CreatePartyWithTwoMembersAsync().ConfigureAwait(false); await party.LeaveTemporarilyAsync(member1).ConfigureAwait(false); await this._partyManager.OnMemberReconnectedAsync(member1).ConfigureAwait(false); Assert.That(member1.Party, Is.SameAs(party)); } /// /// Tests that the offline snapshot preserves the disconnected member's name. /// [Test] public async ValueTask LeaveTemporarily_SnapshotPreservesName() { var (party, member1, _) = await this.CreatePartyWithTwoMembersAsync().ConfigureAwait(false); await party.LeaveTemporarilyAsync(member1).ConfigureAwait(false); Assert.That(party.PartyList[0].Name, Is.EqualTo(member1.Name)); } /// /// Tests that preserves the party master status /// when replacing the master. /// [Test] public async ValueTask ReplaceMember_PreservesMasterStatus() { var (party, member1, _) = await this.CreatePartyWithTwoMembersAsync().ConfigureAwait(false); // member1 is master (first member added) var snapshot = new OfflinePartyMember(member1); await party.ReplaceMemberAsync(member1, snapshot).ConfigureAwait(false); Assert.That(party.PartyMaster, Is.SameAs(snapshot)); } /// /// Tests that clears the old member's party reference. /// [Test] public async ValueTask ReplaceMember_ClearsOldMemberPartyReference() { var (party, member1, _) = await this.CreatePartyWithTwoMembersAsync().ConfigureAwait(false); var snapshot = new OfflinePartyMember(member1); await party.ReplaceMemberAsync(member1, snapshot).ConfigureAwait(false); Assert.That(member1.Party, Is.Null); } private async ValueTask<(Party Party, Player Member1, Player Member2)> CreatePartyWithTwoMembersAsync() { var member1 = await this.CreatePartyMemberAsync().ConfigureAwait(false); var member2 = await this.CreatePartyMemberAsync().ConfigureAwait(false); var party = this._partyManager.CreateParty(); await party.AddAsync(member1).ConfigureAwait(false); await party.AddAsync(member2).ConfigureAwait(false); return (party, member1, member2); } private static byte GetPartyMemberIndex(Party party, IPartyMember member) { for (byte index = 0; index < party.PartyList.Count; index++) { if (party.PartyList[index] == member) { return index; } } throw new ArgumentException("The member is not part of the party.", nameof(member)); } private async ValueTask CreatePartyMemberAsync() { var result = await PlayerTestHelper.CreatePlayerAsync(GameContextTestHelper.CreateGameContext()).ConfigureAwait(false); await result.PlayerState.TryAdvanceToAsync(PlayerState.EnteredWorld).ConfigureAwait(false); return result; } }