diff --git a/Sources/App/Program.cs b/Sources/App/Program.cs index b2f3ffb..f2ad730 100644 --- a/Sources/App/Program.cs +++ b/Sources/App/Program.cs @@ -54,7 +54,7 @@ namespace App // persist them as models ! await masterOfCeremonies.GlobalPlayerManager.Add(entity.ToModel()); } - catch (Exception ex) { Console.WriteLine($"{ex.Message}\n... Never mind"); } + catch (Exception ex) { Debug.WriteLine($"{ex.Message}\n... Never mind"); } } } } @@ -194,7 +194,7 @@ namespace App await playerDbManager.Add(entity); } // what if there's already a player with that name? Exception (see PlayerEntity's annotations) - catch (ArgumentException ex) { Console.WriteLine($"{ex.Message}\n... Never mind"); } + catch (ArgumentException ex) { Debug.WriteLine($"{ex.Message}\n... Never mind"); } } } // flushing and closing NLog before quitting completely diff --git a/Sources/Data/EF/DiceAppDbContextWithStub.cs b/Sources/Data/EF/DiceAppDbContextWithStub.cs index 852d7fb..dec0a54 100644 --- a/Sources/Data/EF/DiceAppDbContextWithStub.cs +++ b/Sources/Data/EF/DiceAppDbContextWithStub.cs @@ -5,6 +5,7 @@ using Data.EF.Joins; using Data.EF.Players; using Microsoft.EntityFrameworkCore; using Model.Games; +using System.Drawing; using System.Linq.Expressions; using System.Security.Cryptography.X509Certificates; @@ -37,19 +38,10 @@ namespace Data.EF Guid turnID_1 = Guid.NewGuid(); Guid turnID_2 = Guid.NewGuid(); - TurnEntity turn_1 = new() - { - ID = turnID_1, - When = new DateTime(2017, 1, 6, 17, 30, 0, DateTimeKind.Utc), - PlayerEntityID = playerID_1 - }; + DateTime datetime_1 = new(2017, 1, 6, 17, 30, 0, DateTimeKind.Utc); - TurnEntity turn_2 = new() - { - ID = turnID_2, - When = DateTime.UtcNow, - PlayerEntityID = playerID_2 - }; + TurnEntity turn_1 = new() { ID = turnID_1, When = datetime_1, PlayerEntityID = playerID_1 }; + TurnEntity turn_2 = new() { ID = turnID_2, When = DateTime.UtcNow, PlayerEntityID = playerID_2 }; Guid dieID_1 = Guid.NewGuid(); Guid dieID_2 = Guid.NewGuid(); @@ -66,65 +58,28 @@ namespace Data.EF Guid faceID_5 = Guid.NewGuid(); Guid faceID_6 = Guid.NewGuid(); - NumberFaceEntity face_1 = new() - { - ID = faceID_1, - Value = 1, - NumberDieEntityID = dieID_1 - }; - NumberFaceEntity face_2 = new() - { - ID = faceID_2, - Value = 2, - NumberDieEntityID = dieID_1 - }; - - ImageFaceEntity face_3 = new() - { - ID = faceID_3, - Value = "https://1", - ImageDieEntityID = dieID_2 - }; - ImageFaceEntity face_4 = new() - { - ID = faceID_4, - Value = "https://2", - ImageDieEntityID = dieID_2 - }; - - ColorFaceEntity face_5 = new() - { - ID = faceID_5, - A = 255, - R = 255, - G = 0, - B = 0, - ColorDieEntityID = dieID_3 - }; - ColorFaceEntity face_6 = new() - { - ID = faceID_6, - A = 255, - R = 0, - G = 255, - B = 0, - ColorDieEntityID = dieID_3 - }; + NumberFaceEntity face_1 = new() { ID = faceID_1, Value = 1, NumberDieEntityID = dieID_1 }; + NumberFaceEntity face_2 = new() { ID = faceID_2, Value = 2, NumberDieEntityID = dieID_1 }; + + ImageFaceEntity face_3 = new() { ID = faceID_3, Value = "https://1", ImageDieEntityID = dieID_2 }; + ImageFaceEntity face_4 = new() { ID = faceID_4, Value = "https://2", ImageDieEntityID = dieID_2 }; + + ColorFaceEntity face_5 = new() { ID = faceID_5, ColorDieEntityID = dieID_3 }; + face_5.SetValue(Color.FromArgb(255, 255, 0, 0)); + ColorFaceEntity face_6 = new() { ID = faceID_6, ColorDieEntityID = dieID_3 }; + face_6.SetValue(Color.FromName("green")); modelBuilder.Entity().HasData(player_1, player_2, player_3, player_4); modelBuilder.Entity().HasData(turn_1, turn_2); modelBuilder.Entity().HasData(die_1); - modelBuilder.Entity().HasData(face_1, face_2); modelBuilder.Entity().HasData(die_2); - modelBuilder.Entity().HasData(face_3, face_4); modelBuilder.Entity().HasData(die_3); - modelBuilder.Entity().HasData(face_5, face_6); // die 1 die 2 die 3 @@ -135,71 +90,23 @@ namespace Data.EF .Entity() .HasMany(turn => turn.Faces) .WithMany(face => face.Turns) - .UsingEntity( - join => join.HasData( - new - { - TurnEntityID = turnID_1, - FaceEntityID = faceID_2 - }, - new - { - TurnEntityID = turnID_1, - FaceEntityID = faceID_4 - }, - new - { - TurnEntityID = turnID_1, - FaceEntityID = faceID_5 - }, - - new - { - TurnEntityID = turnID_2, - FaceEntityID = faceID_1 - }, - new - { - TurnEntityID = turnID_2, - FaceEntityID = faceID_6 - } - ) - ); + .UsingEntity(join => join.HasData( + new { TurnEntityID = turnID_1, FaceEntityID = faceID_2 }, + new { TurnEntityID = turnID_1, FaceEntityID = faceID_4 }, + new { TurnEntityID = turnID_1, FaceEntityID = faceID_5 }, + new { TurnEntityID = turnID_2, FaceEntityID = faceID_1 }, + new { TurnEntityID = turnID_2, FaceEntityID = faceID_6 })); modelBuilder .Entity() .HasMany(turn => turn.Dice) .WithMany(die => die.Turns) - .UsingEntity( - join => join.HasData( - new - { - TurnEntityID = turnID_1, - DieEntityID = dieID_1 - }, - new - { - TurnEntityID = turnID_1, - DieEntityID = dieID_2 - }, - new - { - TurnEntityID = turnID_1, - DieEntityID = dieID_3 - }, - - new - { - TurnEntityID = turnID_2, - DieEntityID = dieID_1 - }, - new - { - TurnEntityID = turnID_2, - DieEntityID = dieID_3 - } - ) - ); + .UsingEntity(join => join.HasData( + new { TurnEntityID = turnID_1, DieEntityID = dieID_1 }, + new { TurnEntityID = turnID_1, DieEntityID = dieID_2 }, + new { TurnEntityID = turnID_1, DieEntityID = dieID_3 }, + new { TurnEntityID = turnID_2, DieEntityID = dieID_1 }, + new { TurnEntityID = turnID_2, DieEntityID = dieID_3 })); } } } diff --git a/Sources/Data/EF/Games/TurnEntity.cs b/Sources/Data/EF/Games/TurnEntity.cs index 21ff016..711e195 100644 --- a/Sources/Data/EF/Games/TurnEntity.cs +++ b/Sources/Data/EF/Games/TurnEntity.cs @@ -2,10 +2,11 @@ using Data.EF.Dice.Faces; using Data.EF.Joins; using Data.EF.Players; +using Model.Players; namespace Data.EF.Games { - public sealed class TurnEntity: IEquatable + public sealed class TurnEntity : IEquatable { public Guid ID { get; set; } public DateTime When { get; set; } @@ -27,12 +28,41 @@ namespace Data.EF.Games public bool Equals(TurnEntity other) { - return other is not null - && this.ID.Equals(other.ID) - && this.When.Equals(other.When) - && this.PlayerEntity.Equals(other.PlayerEntity) - && this.Dice.SequenceEqual(other.Dice) - && this.Faces.SequenceEqual(other.Faces); + if (other is null + || + !(PlayerEntity.Equals(other.PlayerEntity) + && When.Equals(other.When) + && ID.Equals(other.ID) + && Dice.Count == other.Dice.Count + && Faces.Count == other.Faces.Count)) + { + return false; + } + + for (int i = 0; i < Faces.Count; i++) + { + if (Dice.ElementAt(i).Faces.Count + != other.Dice.ElementAt(i).Faces.Count) + { + return false; + } + + if (!other.Faces.ElementAt(i).ID + .Equals(Faces.ElementAt(i).ID)) + { + return false; + } + + for (int j = 0; j < Dice.ElementAt(i).Faces.Count; j++) + { + if (!other.Dice.ElementAt(i).Faces.ElementAt(j).ID + .Equals(Dice.ElementAt(i).Faces.ElementAt(j).ID)) + { + return false; + } + } + } + return true; } public override int GetHashCode() diff --git a/Sources/Data/EF/Games/TurnExtensions.cs b/Sources/Data/EF/Games/TurnExtensions.cs index 469fd64..5380a1f 100644 --- a/Sources/Data/EF/Games/TurnExtensions.cs +++ b/Sources/Data/EF/Games/TurnExtensions.cs @@ -10,7 +10,7 @@ namespace Data.EF.Games public static class TurnExtensions { - private static (List, List) ToModels(ICollection diceEntities, ICollection faceEntities) + private static (List, List) ToModelsByTypes(ICollection diceEntities, ICollection faceEntities) { List dice = new(); List faces = new(); @@ -38,7 +38,7 @@ namespace Data.EF.Games List keysList; List valuesList; - (keysList, valuesList) = ToModels(entity.Dice, entity.Faces); + (keysList, valuesList) = ToModelsByTypes(entity.Dice, entity.Faces); DiceNFaces = Utils.Enumerables.FeedListsToDict(DiceNFaces, keysList, valuesList); diff --git a/Sources/Data/EF/Players/PlayerExtensions.cs b/Sources/Data/EF/Players/PlayerExtensions.cs index a487010..c058fce 100644 --- a/Sources/Data/EF/Players/PlayerExtensions.cs +++ b/Sources/Data/EF/Players/PlayerExtensions.cs @@ -4,11 +4,11 @@ namespace Data.EF.Players { public static class PlayerExtensions { - public static Player ToModel(this PlayerEntity entity) => new Player(name: entity.Name); + public static Player ToModel(this PlayerEntity entity) => new(name: entity.Name); public static IEnumerable ToModels(this IEnumerable entities) => entities.Select(entity => entity.ToModel()); - public static PlayerEntity ToEntity(this Player model) => new PlayerEntity() { Name = model.Name }; + public static PlayerEntity ToEntity(this Player model) => new() { Name = model.Name }; public static IEnumerable ToEntities(this IEnumerable models) => models.Select(model => model.ToEntity()); } diff --git a/Sources/Model/Dice/Faces/Face.cs b/Sources/Model/Dice/Faces/Face.cs index 815d603..e05ab6f 100644 --- a/Sources/Model/Dice/Faces/Face.cs +++ b/Sources/Model/Dice/Faces/Face.cs @@ -3,12 +3,8 @@ public abstract class Face { public string StringValue { get; protected set; } - - public override string ToString() - { - return StringValue; - } } + public abstract class Face : Face { public T Value { get; protected set; } diff --git a/Sources/Model/Games/Turn.cs b/Sources/Model/Games/Turn.cs index c6a118f..7709e0c 100644 --- a/Sources/Model/Games/Turn.cs +++ b/Sources/Model/Games/Turn.cs @@ -89,10 +89,39 @@ namespace Model.Games public bool Equals(Turn other) { - return Player.Equals(other.Player) + if (other is null + || + !(Player.Equals(other.Player) && When.Equals(other.When) - && DiceNFaces.SequenceEqual(other.DiceNFaces); + && DiceNFaces.Count() == other.DiceNFaces.Count())) + { + return false; + } + + for (int i = 0; i < DiceNFaces.Count(); i++) + { + if (DiceNFaces.ElementAt(i).Key.Faces.Count() + != other.DiceNFaces.ElementAt(i).Key.Faces.Count()) + { + return false; + } + if (!other.DiceNFaces.ElementAt(i).Value.StringValue + .Equals(DiceNFaces.ElementAt(i).Value.StringValue)) + { + return false; + } + + for (int j = 0; j < DiceNFaces.ElementAt(i).Key.Faces.Count(); j++) + { + if (!other.DiceNFaces.ElementAt(i).Key.Faces.ElementAt(j).StringValue + .Equals(DiceNFaces.ElementAt(i).Key.Faces.ElementAt(j).StringValue)) + { + return false; + } + } + } + return true; } public override bool Equals(object obj) @@ -106,7 +135,16 @@ namespace Model.Games public override int GetHashCode() { - return HashCode.Combine(Player, When, DiceNFaces); + int hash = Player.GetHashCode() + When.GetHashCode(); + foreach (KeyValuePair kvp in DiceNFaces) + { + hash = hash * 31 + kvp.Value.StringValue.GetHashCode(); + foreach (Face face in kvp.Key.Faces) + { + hash = hash * 19 + face.StringValue.GetHashCode(); + } + } + return hash; } } } diff --git a/Sources/Tests/Data_UTs/Games/TurnEntityTest.cs b/Sources/Tests/Data_UTs/Games/TurnEntityTest.cs index 0afb061..31df762 100644 --- a/Sources/Tests/Data_UTs/Games/TurnEntityTest.cs +++ b/Sources/Tests/Data_UTs/Games/TurnEntityTest.cs @@ -25,6 +25,12 @@ namespace Tests.Data_UTs.Games private readonly FaceEntity imgFace1 = new ImageFaceEntity() { ID = Guid.NewGuid(), Value = "https://a" }; private readonly FaceEntity imgFace2 = new ImageFaceEntity() { ID = Guid.NewGuid(), Value = "https://b" }; + private readonly PlayerEntity player1 = new() { ID = Guid.NewGuid(), Name = "Marvin" }; + private readonly PlayerEntity player2 = new() { ID = Guid.NewGuid(), Name = "Barbara" }; + + private readonly DateTime datetime1 = new(2020, 6, 15, 12, 15, 3, DateTimeKind.Utc); + private readonly DateTime datetime2 = new(2016, 12, 13, 14, 15, 16, DateTimeKind.Utc); + private readonly DieTurn dieTurn1; private readonly DieTurn dieTurn2; private readonly DieTurn dieTurn3; @@ -147,14 +153,6 @@ namespace Tests.Data_UTs.Games }; } - - - private readonly PlayerEntity player1 = new() { ID = Guid.NewGuid(), Name = "Marvin" }; - private readonly PlayerEntity player2 = new() { ID = Guid.NewGuid(), Name = "Barbara" }; - - private readonly DateTime datetime1 = new(2020, 6, 15, 12, 15, 3, DateTimeKind.Utc); - private readonly DateTime datetime2 = new(2016, 12, 13, 14, 15, 16, DateTimeKind.Utc); - [Fact] public void TestGetSetID() { diff --git a/Sources/Tests/Data_UTs/Games/TurnExtensionsTest.cs b/Sources/Tests/Data_UTs/Games/TurnExtensionsTest.cs index 7d13e86..c3f0b2b 100644 --- a/Sources/Tests/Data_UTs/Games/TurnExtensionsTest.cs +++ b/Sources/Tests/Data_UTs/Games/TurnExtensionsTest.cs @@ -1,12 +1,273 @@ -using System; +using Data.EF.Dice.Faces; +using Data.EF.Dice; +using Data.EF.Players; +using Model.Dice; +using Model.Players; +using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; +using Xunit; +using Data.EF.Games; +using System.Drawing; +using Model.Games; +using Newtonsoft.Json.Linq; +using Model.Dice.Faces; +using System.Diagnostics; namespace Tests.Data_UTs.Games { public class TurnExtensionsTest { + private readonly DateTime datetime1 = new(2013, 2, 1, 1, 19, 4, DateTimeKind.Utc); + private readonly DateTime datetime2 = new(2014, 8, 1, 23, 12, 4, DateTimeKind.Utc); + + private readonly PlayerEntity playerEntity = new() { Name = "Paula" }; + + private readonly DieEntity numDieEntity; + private readonly FaceEntity numFace1Entity = new NumberFaceEntity() { Value = 7 }; + private readonly FaceEntity numFace2Entity = new NumberFaceEntity() { Value = 8 }; + + private readonly DieEntity clrDieEntity; + private readonly FaceEntity clrFace1Entity = new ColorFaceEntity() { A = 255, R = 255, G = 255, B = 255 }; + private readonly FaceEntity clrFace2Entity = new ColorFaceEntity() { A = 255, R = 0, G = 0, B = 128 }; + + private readonly DieEntity imgDieEntity; + private readonly FaceEntity imgFace1Entity = new ImageFaceEntity() { Value = "https://a" }; + private readonly FaceEntity imgFace2Entity = new ImageFaceEntity() { Value = "https://b" }; + + public TurnExtensionsTest() + { + numDieEntity = new NumberDieEntity() { Faces = new List() { numFace1Entity as NumberFaceEntity, numFace2Entity as NumberFaceEntity } }; + (numFace1Entity as NumberFaceEntity).NumberDieEntity = (NumberDieEntity)numDieEntity; + (numFace2Entity as NumberFaceEntity).NumberDieEntity = (NumberDieEntity)numDieEntity; + + clrDieEntity = new ColorDieEntity() { Faces = new List() { clrFace1Entity as ColorFaceEntity, clrFace2Entity as ColorFaceEntity } }; + (clrFace1Entity as ColorFaceEntity).ColorDieEntity = (ColorDieEntity)clrDieEntity; + (clrFace2Entity as ColorFaceEntity).ColorDieEntity = (ColorDieEntity)clrDieEntity; + + imgDieEntity = new ImageDieEntity() { Faces = new List() { imgFace1Entity as ImageFaceEntity, imgFace2Entity as ImageFaceEntity } }; + (imgFace1Entity as ImageFaceEntity).ImageDieEntity = (ImageDieEntity)imgDieEntity; + (imgFace2Entity as ImageFaceEntity).ImageDieEntity = (ImageDieEntity)imgDieEntity; + } + + [Fact] + public void TestToModel() + { + // Arrange + + TurnEntity entity = new() + { + When = datetime1, + PlayerEntity = playerEntity, + Dice = new List + { + numDieEntity, + clrDieEntity, + imgDieEntity + }, + Faces = new List + { + numFace1Entity, + clrFace2Entity, + imgFace2Entity + } + }; + + Turn expected = Turn.CreateWithSpecifiedTime( + datetime1, + playerEntity.ToModel(), + new() + { + {(numDieEntity as NumberDieEntity).ToModel(), (numFace1Entity as NumberFaceEntity).ToModel() }, + {(clrDieEntity as ColorDieEntity).ToModel(), (clrFace2Entity as ColorFaceEntity).ToModel() }, + {(imgDieEntity as ImageDieEntity).ToModel(), (imgFace2Entity as ImageFaceEntity).ToModel() } + }); + + // Act + Turn actual = entity.ToModel(); + + // Assert + Assert.True(expected.Equals(actual)); + } + + [Fact] + public void TestToModels() + { + // Arrange + + TurnEntity[] entities = new TurnEntity[] + { + new TurnEntity() + { + When = datetime1, + PlayerEntity = new PlayerEntity() {Name = "Aardvark"}, + Dice = new List + { + numDieEntity, + clrDieEntity + + }, + Faces = new List + { + numFace2Entity, + clrFace2Entity + } + }, + + new TurnEntity() + { + When = datetime2, + PlayerEntity = new PlayerEntity() {Name = "Chloe"}, + Dice = new List + { + clrDieEntity, + imgDieEntity + }, + Faces = new List + { + clrFace1Entity, + imgFace1Entity + } + } + }; + + IEnumerable expected = new Turn[] + { + Turn.CreateWithSpecifiedTime( + datetime1, + new("Aardvark"), + new() + { + {(numDieEntity as NumberDieEntity).ToModel(), (numFace2Entity as NumberFaceEntity).ToModel() }, + {(clrDieEntity as ColorDieEntity).ToModel(), (clrFace2Entity as ColorFaceEntity).ToModel() }, + }), + + Turn.CreateWithSpecifiedTime( + datetime2, + new("Chloe"), + new() + { + {(clrDieEntity as ColorDieEntity).ToModel(), (clrFace1Entity as ColorFaceEntity).ToModel() }, + {(imgDieEntity as ImageDieEntity).ToModel(), (imgFace1Entity as ImageFaceEntity).ToModel() } + }) + }.AsEnumerable(); + + // Act + IEnumerable actual = entities.ToModels(); + + // Assert + Assert.Equal(expected, actual); + } + + [Fact] + public void TestToEntity() + { + // Arrange + + Turn model = Turn.CreateWithSpecifiedTime( + datetime1, + playerEntity.ToModel(), + new() + { + {(numDieEntity as NumberDieEntity).ToModel(), (numFace2Entity as NumberFaceEntity).ToModel() }, + {(clrDieEntity as ColorDieEntity).ToModel(), (clrFace2Entity as ColorFaceEntity).ToModel() }, + {(imgDieEntity as ImageDieEntity).ToModel(), (imgFace1Entity as ImageFaceEntity).ToModel() } + }); + + TurnEntity expected = new() + { + When = datetime1, + PlayerEntity = playerEntity, + Dice = new List + { + numDieEntity, + clrDieEntity, + imgDieEntity + }, + Faces = new List + { + numFace2Entity, + clrFace2Entity, + imgFace1Entity + } + }; + + // Act + TurnEntity actual = model.ToEntity(); + + // Assert + Assert.True(expected.Equals(actual)); + } + + [Fact] + public void TestToEntities() + { + // Arrange + + Turn[] models = new Turn[] + { + Turn.CreateWithSpecifiedTime( + datetime2, + new("Mimi"), + new() + { + {(numDieEntity as NumberDieEntity).ToModel(), (numFace2Entity as NumberFaceEntity).ToModel() }, + {(clrDieEntity as ColorDieEntity).ToModel(), (clrFace2Entity as ColorFaceEntity).ToModel() }, + }), + + Turn.CreateWithSpecifiedTime( + datetime1, + new("blaaargh"), + new() + { + {(clrDieEntity as ColorDieEntity).ToModel(), (clrFace1Entity as ColorFaceEntity).ToModel() }, + {(imgDieEntity as ImageDieEntity).ToModel(), (imgFace1Entity as ImageFaceEntity).ToModel() } + }) + }; + + IEnumerable expected = new TurnEntity[] + { + new TurnEntity() + { + When = datetime2, + PlayerEntity = new PlayerEntity() {Name = "Mimi"}, + Dice = new List + { + numDieEntity, + clrDieEntity + + }, + Faces = new List + { + numFace2Entity, + clrFace2Entity + } + }, + + new TurnEntity() + { + When = datetime1, + PlayerEntity = new PlayerEntity() {Name = "blaaargh"}, + Dice = new List + { + clrDieEntity, + imgDieEntity + }, + Faces = new List + { + clrFace1Entity, + imgFace1Entity + } + } + }.AsEnumerable(); + + // Act + IEnumerable actual = models.ToEntities(); + + // Assert + Assert.Equal(expected, actual); + } } } diff --git a/Sources/Tests/Model_UTs/Games/GameTest.cs b/Sources/Tests/Model_UTs/Games/GameTest.cs index 95cb95c..d602512 100644 --- a/Sources/Tests/Model_UTs/Games/GameTest.cs +++ b/Sources/Tests/Model_UTs/Games/GameTest.cs @@ -78,7 +78,7 @@ namespace Tests.Model_UTs.Games } [Fact] - public async void TestGetHistory() + public async Task TestGetHistory() { // Arrange Dictionary diceNFaces = @@ -87,8 +87,8 @@ namespace Tests.Model_UTs.Games .GetHistory() .First().DiceNFaces; - Turn turn1 = Turn.CreateWithDefaultTime(PLAYER_1, diceNFaces); - Turn turn2 = Turn.CreateWithDefaultTime(PLAYER_2, diceNFaces); // yeah they rolled the same + Turn turn1 = Turn.CreateWithSpecifiedTime(new(1, 2, 3), PLAYER_1, diceNFaces); + Turn turn2 = Turn.CreateWithSpecifiedTime(new(1, 2, 3), PLAYER_2, diceNFaces); // yeah they rolled the same IEnumerable expected = new List() { turn1, turn2 };