diff --git a/CentCom.API/CentCom.API.csproj b/CentCom.API/CentCom.API.csproj index 71d0f58..10d437f 100644 --- a/CentCom.API/CentCom.API.csproj +++ b/CentCom.API/CentCom.API.csproj @@ -2,8 +2,8 @@ netcoreapp3.1 - 1.0.5 - 1.0.5.0 + 1.0.6 + 1.0.6.0 diff --git a/CentCom.Common/CentCom.Common.csproj b/CentCom.Common/CentCom.Common.csproj index 68d7a66..f0a4370 100644 --- a/CentCom.Common/CentCom.Common.csproj +++ b/CentCom.Common/CentCom.Common.csproj @@ -2,7 +2,8 @@ netstandard2.0 - 1.0.5 + 1.0.6 + 1.0.6.0 diff --git a/CentCom.Common/Models/Ban.cs b/CentCom.Common/Models/Ban.cs index 76a05c7..eafb582 100644 --- a/CentCom.Common/Models/Ban.cs +++ b/CentCom.Common/Models/Ban.cs @@ -1,16 +1,13 @@ -using System; +using CentCom.Common.Models.Equality; +using System; using System.Collections.Generic; -using System.Linq; using System.Net; using System.Text.Json.Serialization; -using System.Text.RegularExpressions; namespace CentCom.Common.Models { - public class Ban :IEquatable + public class Ban { - private static Regex _keyReplacePattern = new Regex(@"[^a-z0-9]"); - public int Id { get; set; } public int Source { get; set; } [JsonIgnore] @@ -28,101 +25,9 @@ public class Ban :IEquatable public string BanID { get; set; } public HashSet JobBans { get; set; } - /// - /// Determines if two bans are equal, using their values - /// - /// The ban to compare against - /// If the two bans are equal by their values - /// - /// Note that these equals are really just used for determining if - /// the contents of two bans are the same, at which point they're equal. - /// - public bool Equals(Ban other) - { - if (other == null) return false; - else if (BanID != null || other.BanID != null) - { - return Source == other.Source - && BanID == other.BanID; - } - else - { - return Source == other.Source - && BannedOn == other.BannedOn - && BanType == other.BanType - && CKey == other.CKey - && BannedBy == other.BannedBy - && Reason == other.Reason - && Expires == other.Expires - && UnbannedBy == other.UnbannedBy - && (IP == null || IP.Equals(other.IP)) - && CID == other.CID - && (BanType == BanType.Server - || (JobBans != null && other.JobBans != null && JobBans.SetEquals(other.JobBans))); - } - } - - public static bool operator ==(Ban a, Ban b) - { - return (a is null && b is null) || a.Equals(b); - } - - public static bool operator !=(Ban a, Ban b) - { - return !(a == b); - } - - public override bool Equals(object obj) + public Ban() { - return (obj is Ban ban) && Equals(ban); - } - - public override int GetHashCode() - { - var hash = new HashCode(); - if (BanID != null) - { - hash.Add(BanID); - hash.Add(Source); - } - else - { - hash.Add(Source); - hash.Add(BannedOn); - hash.Add(BanType); - hash.Add(CKey); - hash.Add(BannedBy); - hash.Add(Reason); - hash.Add(Expires); - hash.Add(UnbannedBy); - hash.Add(IP); - hash.Add(CID); - if (JobBans != null) - { - foreach (var job in JobBans.OrderBy(x => x.Job)) - { - hash.Add(job.Job, StringComparer.OrdinalIgnoreCase); - } - } - } - return hash.ToHashCode(); - } - - public void MakeKeysCanonical() - { - CKey = CKey == null ? null : GetCanonicalKey(CKey); - BannedBy = BannedBy == null ? null : GetCanonicalKey(BannedBy); - UnbannedBy = UnbannedBy == null ? null : GetCanonicalKey(UnbannedBy); - } - - public static string GetCanonicalKey(string raw) - { - if (raw == null) - { - throw new ArgumentNullException(nameof(raw)); - } - - return _keyReplacePattern.Replace(raw.ToLower(), ""); + JobBans = new HashSet(JobBanEqualityComparer.Instance); } } } diff --git a/CentCom.Common/Models/Equality/BanEqualityComparer.cs b/CentCom.Common/Models/Equality/BanEqualityComparer.cs new file mode 100644 index 0000000..349dfd2 --- /dev/null +++ b/CentCom.Common/Models/Equality/BanEqualityComparer.cs @@ -0,0 +1,68 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace CentCom.Common.Models.Equality +{ + public class BanEqualityComparer : IEqualityComparer + { + public static readonly BanEqualityComparer Instance = new BanEqualityComparer(); + + public bool Equals(Ban x, Ban y) + { + if (x is null || y is null) return x == y; + else if (x.BanID != null || y.BanID != null) + { + return x.Source == y.Source + && x.BanID == y.BanID; + } + else + { + return x.Source == y.Source + && x.BannedOn == y.BannedOn + && x.BanType == y.BanType + && x.CKey == y.CKey + && x.BannedBy == y.BannedBy + && x.Reason == y.Reason + && x.Expires == y.Expires + && x.UnbannedBy == y.UnbannedBy + && (x.IP == null || x.IP.Equals(y.IP)) + && x.CID == y.CID + && (x.BanType == BanType.Server + || (x.JobBans != null && y.JobBans != null && x.JobBans.SetEquals(y.JobBans))); + } + } + + public int GetHashCode(Ban obj) + { + var hash = new HashCode(); + if (obj.BanID != null) + { + hash.Add(obj.BanID); + hash.Add(obj.Source); + } + else + { + hash.Add(obj.Source); + hash.Add(obj.BannedOn); + hash.Add(obj.BanType); + hash.Add(obj.CKey); + hash.Add(obj.BannedBy); + hash.Add(obj.Reason); + hash.Add(obj.Expires); + hash.Add(obj.UnbannedBy); + hash.Add(obj.IP); + hash.Add(obj.CID); + if (obj.JobBans != null) + { + foreach (var job in obj.JobBans.OrderBy(x => x.Job)) + { + hash.Add(job.Job); + } + } + } + return hash.ToHashCode(); + } + } +} diff --git a/CentCom.Common/Models/Equality/JobBanEqualityComparer.cs b/CentCom.Common/Models/Equality/JobBanEqualityComparer.cs new file mode 100644 index 0000000..c931651 --- /dev/null +++ b/CentCom.Common/Models/Equality/JobBanEqualityComparer.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace CentCom.Common.Models.Equality +{ + public class JobBanEqualityComparer : IEqualityComparer + { + public static readonly JobBanEqualityComparer Instance = new JobBanEqualityComparer(); + + public bool Equals(JobBan x, JobBan y) + { + return (x is null || y is null) ? x == y : x.Job == y.Job; + } + + public int GetHashCode(JobBan obj) + { + return HashCode.Combine(obj.Job); + } + } +} diff --git a/CentCom.Common/Models/JobBan.cs b/CentCom.Common/Models/JobBan.cs index f74995f..0a5bc16 100644 --- a/CentCom.Common/Models/JobBan.cs +++ b/CentCom.Common/Models/JobBan.cs @@ -3,38 +3,11 @@ namespace CentCom.Common.Models { - public class JobBan : IEquatable + public class JobBan { public int BanId { get; set; } [JsonIgnore] public virtual Ban BanNavigation { get; set; } public string Job { get; set; } - - public bool Equals(JobBan other) - { - return !(other is null) && Job == other.Job; - } - - public static bool operator ==(JobBan a, JobBan b) - { - return (a is null && b is null) || a.Equals(b); - } - - public static bool operator !=(JobBan a, JobBan b) - { - return !(a == b); - } - - public override bool Equals(object obj) - { - return (obj is JobBan jb) && Equals(jb); - } - - public override int GetHashCode() - { - var hash = new HashCode(); - hash.Add(Job, StringComparer.OrdinalIgnoreCase); - return hash.ToHashCode(); - } } } diff --git a/CentCom.Server/BanSources/BanParser.cs b/CentCom.Server/BanSources/BanParser.cs index c61ef30..faf17b1 100644 --- a/CentCom.Server/BanSources/BanParser.cs +++ b/CentCom.Server/BanSources/BanParser.cs @@ -8,6 +8,8 @@ using System.Threading.Tasks; using CentCom.Common.Data; using CentCom.Server.Exceptions; +using CentCom.Server.Extensions; +using CentCom.Common.Models.Equality; namespace CentCom.Server.BanSources { @@ -128,12 +130,13 @@ public virtual async Task ParseBans(IJobExecutionContext context) && b.CKey == x.CKey && b.BannedBy == x.BannedBy && (b.BanType == BanType.Server - || (b.JobBans != null && x.JobBans != null && b.JobBans.SetEquals(x.JobBans)))); + || b.JobBans.SetEquals(x.JobBans))); } // Update ban if an existing one is found if (matchedBan != null) { + // Check for a difference in date time, unbans, or reason if (matchedBan.Reason != b.Reason || matchedBan.Expires != b.Expires || matchedBan.UnbannedBy != b.UnbannedBy) { matchedBan.Reason = b.Reason; @@ -141,6 +144,14 @@ public virtual async Task ParseBans(IJobExecutionContext context) matchedBan.UnbannedBy = b.UnbannedBy; updated++; } + + // Check for a difference in recorded jobbans + if (b.BanType == BanType.Job && !b.JobBans.SetEquals(matchedBan.JobBans)) + { + matchedBan.JobBans = new HashSet(JobBanEqualityComparer.Instance); + matchedBan.AddJobRange(b.JobBans.Select(x => x.Job)); + updated++; + } } // Otherwise add insert a new ban else @@ -157,8 +168,14 @@ public virtual async Task ParseBans(IJobExecutionContext context) // Delete any missing bans if we're doing a complete refresh if (isCompleteRefresh) { - var bansHashed = new HashSet(bans); - var missingBans = storedBans.Except(bansHashed).ToHashSet(); + var bansHashed = new HashSet(bans, BanEqualityComparer.Instance); + var missingBans = storedBans.Except(bansHashed, BanEqualityComparer.Instance).ToList(); + + if (bansHashed.Count == 0 && missingBans.Count > 1) + { + throw new Exception("Failed to find any bans for source, aborting removal phase of ban " + + "parsing to avoid dumping entire set of bans"); + } // Apply deletions _logger.LogInformation(missingBans.Count > 0 ? $"Removing {missingBans.Count} deleted bans..." diff --git a/CentCom.Server/BanSources/TGMCBanParser.cs b/CentCom.Server/BanSources/TGMCBanParser.cs new file mode 100644 index 0000000..c44c21f --- /dev/null +++ b/CentCom.Server/BanSources/TGMCBanParser.cs @@ -0,0 +1,66 @@ +using CentCom.Common.Data; +using CentCom.Common.Models; +using CentCom.Server.Services; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Logging; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace CentCom.Server.BanSources +{ + public class TGMCBanParser : BanParser + { + public override Dictionary Sources => new Dictionary() + { + { "tgmc", new BanSource() + { + Display = "TGMC", + Name = "tgmc", + RoleplayLevel = RoleplayLevel.Medium + } } + }; + public override bool SourceSupportsBanIDs => true; + private TGMCBanService _banService; + private const int PAGES_PER_BATCH = 3; + + public TGMCBanParser(DatabaseContext dbContext, TGMCBanService banService, ILogger logger) : base(dbContext, logger) + { + _banService = banService; + } + + public override async Task> FetchAllBansAsync() + { + _logger.LogInformation("Getting all bans for TGMC..."); + return await _banService.GetBansBatchedAsync(); + } + + public override async Task> FetchNewBansAsync() + { + _logger.LogInformation("Getting new bans for TGMC..."); + var recent = await _dbContext.Bans + .Where(x => Sources.Keys.Contains(x.SourceNavigation.Name)) + .OrderByDescending(x => x.BannedOn) + .Take(5) + .Include(x => x.JobBans) + .Include(x => x.SourceNavigation) + .ToListAsync(); + var foundBans = new List(); + var page = 1; + + while (true) + { + var batch = await _banService.GetBansBatchedAsync(page, PAGES_PER_BATCH); + foundBans.AddRange(batch); + if (batch.Count() == 0 || batch.Any(x => recent.Any(y => y.BannedOn == x.BannedOn && y.CKey == y.CKey))) + { + break; + } + } + + return foundBans; + } + } +} diff --git a/CentCom.Server/CentCom.Server.csproj b/CentCom.Server/CentCom.Server.csproj index bf06107..e6d5e94 100644 --- a/CentCom.Server/CentCom.Server.csproj +++ b/CentCom.Server/CentCom.Server.csproj @@ -3,9 +3,9 @@ Exe netcoreapp3.1 - 1.0.5.0 - 1.0.5.0 - 1.0.5 + 1.0.6.0 + 1.0.6.0 + 1.0.6 diff --git a/CentCom.Server/Extensions/BanExtensions.cs b/CentCom.Server/Extensions/BanExtensions.cs new file mode 100644 index 0000000..0ec21c1 --- /dev/null +++ b/CentCom.Server/Extensions/BanExtensions.cs @@ -0,0 +1,48 @@ +using CentCom.Common.Models; +using System; +using System.Collections; +using System.Collections.Generic; +using System.Text.RegularExpressions; + +namespace CentCom.Server.Extensions +{ + public static class BanExtensions + { + private static Regex _keyReplacePattern = new Regex(@"[^a-z0-9]"); + + public static void MakeKeysCanonical(this Ban ban) + { + ban.CKey = ban.CKey == null ? null : GetCanonicalKey(ban.CKey); + ban.BannedBy = ban.BannedBy == null ? null : GetCanonicalKey(ban.BannedBy); + ban.UnbannedBy = ban.UnbannedBy == null ? null : GetCanonicalKey(ban.UnbannedBy); + } + + public static string GetCanonicalKey(string raw) + { + if (raw == null) + { + throw new ArgumentNullException(nameof(raw)); + } + + return _keyReplacePattern.Replace(raw.ToLower(), ""); + } + + public static string CleanJob(string job) + { + return job.ToLower(); + } + + public static bool AddJob(this Ban ban, string job) + { + return ban.JobBans.Add(new JobBan() { Job = CleanJob(job), BanId = ban.Id, BanNavigation = ban }); + } + + public static void AddJobRange(this Ban ban, IEnumerable jobs) + { + foreach (var job in jobs) + { + ban.AddJob(job); + } + } + } +} diff --git a/CentCom.Server/Program.cs b/CentCom.Server/Program.cs index 54ed2ba..899834b 100644 --- a/CentCom.Server/Program.cs +++ b/CentCom.Server/Program.cs @@ -157,6 +157,7 @@ public static void RegisterServices() services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); + services.AddSingleton(); // Add ban parsers var parsers = AppDomain.CurrentDomain.GetAssemblies().Aggregate(new List(), (curr, next) => diff --git a/CentCom.Server/Services/BeeBanService.cs b/CentCom.Server/Services/BeeBanService.cs index 570ca97..a46f879 100644 --- a/CentCom.Server/Services/BeeBanService.cs +++ b/CentCom.Server/Services/BeeBanService.cs @@ -1,5 +1,6 @@ using CentCom.Common.Models; using CentCom.Server.Exceptions; +using CentCom.Server.Extensions; using Extensions; using Microsoft.Extensions.Logging; using RestSharp; @@ -7,7 +8,6 @@ using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; -using System.Text; using System.Text.Json; using System.Text.RegularExpressions; using System.Threading.Tasks; @@ -58,10 +58,7 @@ public async Task> GetBansAsync(int page = 1) if (toAdd.BanType == BanType.Job) { - toAdd.JobBans = b["job"].EnumerateArray() - .Select(x => x.GetString().ToLower()) - .Select(x => new JobBan() { Job = x }) - .ToHashSet(); + toAdd.AddJobRange(b["job"].EnumerateArray().Select(x => x.GetString())); } toReturn.Add(toAdd); diff --git a/CentCom.Server/Services/FulpBanService.cs b/CentCom.Server/Services/FulpBanService.cs index f909fb5..4327289 100644 --- a/CentCom.Server/Services/FulpBanService.cs +++ b/CentCom.Server/Services/FulpBanService.cs @@ -1,5 +1,6 @@ using CentCom.Common.Models; using CentCom.Server.Exceptions; +using CentCom.Server.Extensions; using Extensions; using Microsoft.Extensions.Logging; using RestSharp; @@ -57,10 +58,7 @@ public async Task> GetBansAsync(int page = 1) // Add jobs if relevant if (toAdd.BanType == BanType.Job) { - toAdd.JobBans = ban.GetProperty("role").EnumerateArray() - .Select(x => x.GetString().ToLower()) - .Select(x => new JobBan() { Job = x }) - .ToHashSet(); + toAdd.AddJobRange(ban.GetProperty("role").EnumerateArray().Select(x => x.GetString())); } // Specify UTC diff --git a/CentCom.Server/Services/TGMCBanService.cs b/CentCom.Server/Services/TGMCBanService.cs new file mode 100644 index 0000000..f4758fb --- /dev/null +++ b/CentCom.Server/Services/TGMCBanService.cs @@ -0,0 +1,163 @@ +using CentCom.Common.Models; +using CentCom.Server.Exceptions; +using CentCom.Server.Extensions; +using Extensions; +using Microsoft.Extensions.Logging; +using RestSharp; +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using System.Text.Json; +using System.Threading.Tasks; + +namespace CentCom.Server.Services +{ + /// + /// TGMC Ban Service for getting bans from the API + /// + /// + /// Note that the data is provided in a flat tg format, meaning + /// that each role in a jobban has its own ban ID. Thus, our strategy + /// for using the paging must account for the possibility of a job ban + /// spanning two seperate pages. + /// + public class TGMCBanService + { + private IRestClient _client; + ILogger _logger; + private const string BASE_URL = "http://statbus.psykzz.com:8080/api/"; + private const int RECORDS_PER_PAGE = 100; + private static BanSource _banSource = new BanSource() { Name = "tgmc" }; + + public TGMCBanService(ILogger logger) + { + _logger = logger; + _client = new RestClient(BASE_URL); + } + + public async Task> GetBansAsync(int page = 1) + { + var request = new RestRequest($"bans/{page}", Method.GET, DataFormat.Json).AddQueryParameter("limit", RECORDS_PER_PAGE.ToString()); + var response = await _client.ExecuteAsync(request); + + if (response.StatusCode != System.Net.HttpStatusCode.OK) + { + _logger.LogError($"TGMC website returned a non-200 HTTP response code: {response.StatusCode}, aborting parse."); + throw new BanSourceUnavailableException($"TGMC website returned a non-200 HTTP response code: {response.StatusCode}, aborting parse."); + } + + var toReturn = new List(); + var dirtyBans = new List(); + var content = JsonSerializer.Deserialize>(response.Content); + foreach (var bh in content["bans"].EnumerateObject()) + { + var ban = bh.Value; + + // Get ban + var toAdd = new Ban() + { + BanID = bh.Name, + BannedBy = ban.GetProperty("admin").GetString(), + BannedOn = DateTime.Parse(ban.GetProperty("bantime").ToString()), + CKey = ban.GetProperty("ckey").GetString(), + Expires = ban.GetProperty("expiration_time").GetString() == null ? (DateTime?)null + : DateTime.Parse(ban.GetProperty("expiration_time").GetString()), + Reason = ban.GetProperty("reason").ToString(), + BanType = ban.GetProperty("role").GetString().ToLower() == "server" ? BanType.Server : BanType.Job, + SourceNavigation = _banSource + }; + + // Specify UTC + toAdd.BannedOn = DateTime.SpecifyKind(toAdd.BannedOn, DateTimeKind.Utc); + if (toAdd.Expires.HasValue) + { + toAdd.Expires = DateTime.SpecifyKind(toAdd.Expires.Value, DateTimeKind.Utc); + } + + // Add jobban if relevant + if (toAdd.BanType == BanType.Job) + { + toAdd.AddJob(ban.GetProperty("role").GetString()); + dirtyBans.Add(toAdd); + } + else + { + toReturn.Add(toAdd); + } + } + + // Group jobbans + foreach (var group in dirtyBans.GroupBy(x => new { x.BannedOn, x.CKey, x.Reason })) + { + var firstBan = group.OrderBy(x => x.BanID).First(); + firstBan.AddJobRange(group.SelectMany(x => x.JobBans).Select(x => x.Job)); + toReturn.Add(firstBan); + } + + return toReturn; + } + + public async Task> GetBansBatchedAsync(int startPage = 1, int pages = -1) + { + var maxPages = await GetNumberOfPagesAsync(); + var range = Enumerable.Range(startPage, pages != -1 ? Math.Min(startPage + pages, maxPages) : maxPages); + var dirtyBans = new ConcurrentBag(); + await range.AsyncParallelForEach(async page => + { + foreach (var b in await GetBansAsync(page)) + { + dirtyBans.Add(b); + } + }, 12); + + // We have to ensure that our jobs are correctly grouped due to possible errors with paging + var cleanBans = new List(dirtyBans.Where(x => x.BanType == BanType.Server)); + foreach (var group in dirtyBans.Where(x => x.BanType == BanType.Job).GroupBy(x => new { x.BannedOn, x.CKey, x.Reason })) + { + var firstBan = group.OrderBy(x => x.BanID).First(); + firstBan.AddJobRange(group.SelectMany(x => x.JobBans).Select(x => x.Job)); + cleanBans.Add(firstBan); + } + + // Check for the possibility of a job ban spanning multiple pages + cleanBans = cleanBans.OrderBy(x => int.Parse(x.BanID)).ToList(); + if (startPage != 1 && cleanBans.First().BanType == BanType.Job) + { + // Discard the first ban if it is a job ban, as it may be incomplete. + // The alternate would be walking backwards in the page list, but that + // is not an optimal solution + cleanBans.RemoveAt(0); + } + if (pages != -1 && startPage + pages < maxPages && cleanBans.LastOrDefault()?.BanType == BanType.Job) + { + // Discard the last ban if it is a job ban as it may be incomplete. + // Same reasoning above, except it would require a pagewalk forward. + cleanBans.RemoveAt(cleanBans.Count() - 1); + } + + return cleanBans; + } + + public async Task GetNumberOfPagesAsync() + { + var request = new RestRequest($"bans/1", Method.GET, DataFormat.Json).AddQueryParameter("limit", RECORDS_PER_PAGE.ToString()); + var result = await _client.ExecuteAsync(request); + + if (result.StatusCode != System.Net.HttpStatusCode.OK) + { + throw new Exception($"Unexpected non-200 status code [{result.StatusCode}] when trying to retrieve number of ban pages for TGMC"); + } + + var content = JsonSerializer.Deserialize>(result.Content); + if (content["page"].TryGetProperty("total", out var lastpage)) + { + return lastpage.GetInt32(); + } + else + { + throw new Exception("Failed to find the last page number in the response from TGMC's API"); + } + } + } +} diff --git a/CentCom.Server/Services/VgBanService.cs b/CentCom.Server/Services/VgBanService.cs index 577c782..a9068b2 100644 --- a/CentCom.Server/Services/VgBanService.cs +++ b/CentCom.Server/Services/VgBanService.cs @@ -1,12 +1,12 @@ using AngleSharp; using CentCom.Common.Models; using CentCom.Server.Exceptions; +using CentCom.Server.Extensions; using Microsoft.Extensions.Logging; using System; using System.Collections.Generic; using System.Linq; using System.Net; -using System.Text; using System.Threading.Tasks; namespace CentCom.Server.Services @@ -98,11 +98,10 @@ public async Task> GetBansAsync() expires = DateTime.SpecifyKind(d, DateTimeKind.Utc); } - toReturn.Add(new Ban() + var toAdd = new Ban() { CKey = ckey, BanType = BanType.Job, - JobBans = jobs.Select(x => x.ToLower()).Select(x => new JobBan() { Job = x }).ToHashSet(), Reason = reason, BannedBy = bannedBy, BannedOn = date.UtcDateTime, @@ -110,7 +109,10 @@ public async Task> GetBansAsync() IP = ip, CID = cid, SourceNavigation = _source - }); + }; + + toAdd.AddJobRange(jobs); + toReturn.Add(toAdd); } return toReturn; diff --git a/CentCom.Test/Ban_EqualsShould.cs b/CentCom.Test/Ban_EqualsShould.cs index 23e19d5..c39c5ae 100644 --- a/CentCom.Test/Ban_EqualsShould.cs +++ b/CentCom.Test/Ban_EqualsShould.cs @@ -1,4 +1,6 @@ using CentCom.Common.Models; +using CentCom.Common.Models.Equality; +using CentCom.Server.Extensions; using System; using System.Collections.Generic; using System.Net; @@ -43,8 +45,9 @@ public void Equals_SameBanDifferentID_ReturnTrue() SourceNavigation = source }; - Assert.True(banA == banB, "Two bans equal by internal values should be equal"); - Assert.True(banA.GetHashCode() == banB.GetHashCode(), "Two bans equal by internal values should have equal hashcodes"); + var comparer = BanEqualityComparer.Instance; + Assert.True(comparer.Equals(banA, banB), "Two bans equal by internal values should be equal"); + Assert.True(comparer.GetHashCode(banA) == comparer.GetHashCode(banB), "Two bans equal by internal values should have equal hashcodes"); } [Fact] @@ -90,8 +93,9 @@ public void Equals_SameBanDifferentIDDifferentSource_ReturnFalse() SourceNavigation = sourceB }; - Assert.False(banA == banB, "Two bans from different sources should not be equal by internal values"); - Assert.False(banA.GetHashCode() == banB.GetHashCode(), "Two bans from different sources should not have equal hashcodes"); + var comparer = BanEqualityComparer.Instance; + Assert.False(comparer.Equals(banA, banB), "Two bans from different sources should not be equal by internal values"); + Assert.False(comparer.GetHashCode(banA) == comparer.GetHashCode(banB), "Two bans from different sources should not have equal hashcodes"); } [Fact] @@ -109,8 +113,9 @@ public void Equals_SameBanByBanID_ReturnTrue() CKey = "different" }; - Assert.True(banA == banB, "Two bans with BanIDs should be checked for equality by ID"); - Assert.True(banA.GetHashCode() == banB.GetHashCode(), "Two bans with BanIDs that are equal should have equal hashcodes"); + var comparer = BanEqualityComparer.Instance; + Assert.True(comparer.Equals(banA, banB), "Two bans with BanIDs should be checked for equality by ID"); + Assert.True(comparer.GetHashCode(banA) == comparer.GetHashCode(banB), "Two bans with BanIDs that are equal should have equal hashcodes"); } [Fact] @@ -148,8 +153,9 @@ public void Equals_SameBanIDDifferentSource_ReturnFalse() SourceNavigation = sourceB }; - Assert.False(banA == banB, "Two bans from different sources should not be equal by BanID"); - Assert.False(banA.GetHashCode() == banB.GetHashCode(), "Two bans from different sources should not have equal hashcodes"); + var comparer = BanEqualityComparer.Instance; + Assert.False(comparer.Equals(banA, banB), "Two bans from different sources should not be equal by BanID"); + Assert.False(comparer.GetHashCode(banA) == comparer.GetHashCode(banB), "Two bans from different sources should not have equal hashcodes"); } [Fact] @@ -158,25 +164,20 @@ public void Equals_SameBanDifferentJobOrder_ReturnTrue() var banA = new Ban() { Id = 12, - JobBans = new HashSet() - { - new JobBan() { Job = "captain" }, - new JobBan() { Job = "assistant" } - } + BanType = BanType.Job }; + banA.AddJobRange(new[] { "detective", "head of security", "security officer", "warden" }); var banB = new Ban() { Id = 0, - JobBans = new HashSet() - { - new JobBan() { Job = "assistant" }, - new JobBan() { Job = "captain" } - } + BanType = BanType.Job }; + banB.AddJobRange(new[] { "head of security", "warden", "detective", "security officer" }); - Assert.True(banA == banB, "Two bans with the same jobbans in different orders should be equal"); - Assert.True(banA.GetHashCode() == banB.GetHashCode(), "Two bans with the same jobbans in different orders should be equal"); + var comparer = BanEqualityComparer.Instance; + Assert.True(comparer.Equals(banA, banB), "Two bans with the same jobbans in different orders should be equal"); + Assert.True(comparer.GetHashCode(banA) == comparer.GetHashCode(banB), "Two bans with the same jobbans in different orders should be equal"); } [Fact] @@ -194,12 +195,12 @@ public void Equals_SameBanNullVsEmptyJobBans_ReturnTrue() { Id = 0, Source = 15, - BanType = BanType.Server, - JobBans = new HashSet() + BanType = BanType.Server }; - Assert.True(banA == banB, "Bans should be equal if the jobbans only differ by null and an empty set"); - Assert.True(banA.GetHashCode() == banB.GetHashCode(), "Bans should have the same hashcode if the jobbans only differ by null and an empty set"); + var comparer = BanEqualityComparer.Instance; + Assert.True(comparer.Equals(banA, banB), "Bans should be equal if the jobbans only differ by null and an empty set"); + Assert.True(comparer.GetHashCode(banA) == comparer.GetHashCode(banB), "Bans should have the same hashcode if the jobbans only differ by null and an empty set"); } [Fact] @@ -229,8 +230,9 @@ public void Equals_SameBansSameIP_ReturnsTrue() IP = IPAddress.Parse("108.20.220.45") }; - Assert.True(banA == banB, "Bans should be equal if all values are equal, including IP"); - Assert.True(banA.GetHashCode() == banB.GetHashCode(), "Bans should have the same hashcode if all values are equal, including IP"); + var comparer = BanEqualityComparer.Instance; + Assert.True(comparer.Equals(banA, banB), "Bans should be equal if all values are equal, including IP"); + Assert.True(comparer.GetHashCode(banA) == comparer.GetHashCode(banB), "Bans should have the same hashcode if all values are equal, including IP"); } } } diff --git a/CentCom.Test/Ban_GetCanonicalKeyShould.cs b/CentCom.Test/Ban_GetCanonicalKeyShould.cs index 35a3493..403bbdb 100644 --- a/CentCom.Test/Ban_GetCanonicalKeyShould.cs +++ b/CentCom.Test/Ban_GetCanonicalKeyShould.cs @@ -1,4 +1,5 @@ using CentCom.Common.Models; +using CentCom.Server.Extensions; using System; using Xunit; @@ -10,13 +11,13 @@ public class Ban_GetCanonicalKeyShould public void GetCanonicalKey_FromRaw_ReturnTrue() { var rawKey = "B o bbahbrown"; - Assert.True("bobbahbrown" == Ban.GetCanonicalKey(rawKey)); + Assert.True("bobbahbrown" == BanExtensions.GetCanonicalKey(rawKey)); } [Fact] public void GetCanonicalKey_NullArgument_ThrowsException() { - Assert.Throws(() => Ban.GetCanonicalKey(null)); + Assert.Throws(() => BanExtensions.GetCanonicalKey(null)); } } } diff --git a/CentCom.Test/CentCom.Test.csproj b/CentCom.Test/CentCom.Test.csproj index 184bf7e..61ace9b 100644 --- a/CentCom.Test/CentCom.Test.csproj +++ b/CentCom.Test/CentCom.Test.csproj @@ -21,6 +21,7 @@ + diff --git a/CentCom.Test/JobBan_EqualsShould.cs b/CentCom.Test/JobBan_EqualsShould.cs index 9c88e1a..9442eb9 100644 --- a/CentCom.Test/JobBan_EqualsShould.cs +++ b/CentCom.Test/JobBan_EqualsShould.cs @@ -1,4 +1,5 @@ using CentCom.Common.Models; +using CentCom.Common.Models.Equality; using Xunit; namespace CentCom.Test @@ -20,8 +21,9 @@ public void Equals_SameJobBan_ReturnTrue() Job = "ooc" }; - Assert.True(jobA == jobB, "Two jobs equal by internal values should be equal"); - Assert.True(jobA.GetHashCode() == jobB.GetHashCode(), "Two jobs equal by internal values should have the same hashcode"); + var comparer = JobBanEqualityComparer.Instance; + Assert.True(comparer.Equals(jobA, jobB), "Two jobs equal by internal values should be equal"); + Assert.True(comparer.GetHashCode(jobA) == comparer.GetHashCode(jobB), "Two jobs equal by internal values should have the same hashcode"); } /// @@ -44,8 +46,9 @@ public void Equals_SameJobBan_DifferentID_ReturnTrue() Job = "ooc" }; - Assert.True(jobA == jobB, "Two jobs equal by job, even with differing IDs, should be equal"); - Assert.True(jobA.GetHashCode() == jobB.GetHashCode(), "Two jobs equal by job, even with differing ids, should have the same hashcode"); + var comparer = JobBanEqualityComparer.Instance; + Assert.True(comparer.Equals(jobA, jobB), "Two jobs equal by job, even with differing IDs, should be equal"); + Assert.True(comparer.GetHashCode(jobA) == comparer.GetHashCode(jobB), "Two jobs equal by job, even with differing ids, should have the same hashcode"); } } }