From f1c34aa6f9d64b1104c56d65c8e37179b2bf6f8b Mon Sep 17 00:00:00 2001 From: Chase Banna Date: Sun, 2 Feb 2025 20:33:52 -0800 Subject: [PATCH 1/2] Add unit tests --- .../CommentAggregate/Comment.cs | 28 +-- .../Extensions/DateParseExtensions.cs | 9 +- src/Setlistbot.Domain/PostAggregate/Post.cs | 81 ++------ .../Extensions/PostExtensions.cs | 2 +- .../CommentAggregate/CommentTests.cs | 161 ++++++++++++++- .../Extensions/DateParseExtensionsTests.cs | 60 ++++++ .../PostAggregate/PostTests.cs | 186 ++++++++++++++++++ 7 files changed, 435 insertions(+), 92 deletions(-) create mode 100644 test/Setlistbot.Domain.UnitTests/Extensions/DateParseExtensionsTests.cs create mode 100644 test/Setlistbot.Domain.UnitTests/PostAggregate/PostTests.cs diff --git a/src/Setlistbot.Domain/CommentAggregate/Comment.cs b/src/Setlistbot.Domain/CommentAggregate/Comment.cs index d9a7222..0281461 100644 --- a/src/Setlistbot.Domain/CommentAggregate/Comment.cs +++ b/src/Setlistbot.Domain/CommentAggregate/Comment.cs @@ -32,7 +32,6 @@ string reply Ensure.String.IsNotNullOrWhiteSpace(permalink, nameof(permalink)); Ensure.String.IsNotNullOrWhiteSpace(author, nameof(author)); Ensure.String.IsNotEmptyOrWhiteSpace(artistId, nameof(artistId)); - Ensure.String.IsNotNullOrWhiteSpace(reply, nameof(reply)); Id = id; Author = author; @@ -48,24 +47,7 @@ public static Comment NewComment( string body, string permalink, string artistId - ) - { - Ensure.String.IsNotNullOrWhiteSpace(id, nameof(id)); - Ensure.String.IsNotNullOrEmpty(body, nameof(body)); - Ensure.String.IsNotNullOrWhiteSpace(permalink, nameof(permalink)); - Ensure.String.IsNotNullOrWhiteSpace(author, nameof(author)); - Ensure.String.IsNotEmptyOrWhiteSpace(artistId, nameof(artistId)); - - return new Comment() - { - Id = id, - Author = author, - Body = body, - Permalink = permalink, - ArtistId = artistId, - Reply = string.Empty, - }; - } + ) => new(id, author, body, permalink, artistId, string.Empty); /// /// Returns true if text is mentioned in the comment @@ -81,11 +63,7 @@ public bool HasMentionOf(string text) /// Sets the reply value /// /// The raw string value that was used in the reply - public void SetReply(string reply) - { - Ensure.That(reply, nameof(reply)).IsNotNullOrWhiteSpace(); - - Reply = reply; - } + public void SetReply(string reply) => + Reply = Ensure.String.IsNotNullOrWhiteSpace(reply, nameof(reply)); } } diff --git a/src/Setlistbot.Domain/Extensions/DateParseExtensions.cs b/src/Setlistbot.Domain/Extensions/DateParseExtensions.cs index 38d74b2..e59d38a 100644 --- a/src/Setlistbot.Domain/Extensions/DateParseExtensions.cs +++ b/src/Setlistbot.Domain/Extensions/DateParseExtensions.cs @@ -2,18 +2,18 @@ namespace Setlistbot.Domain.Extensions { - public static class DateParseExtensions + public static partial class DateParseExtensions { public static IEnumerable ParseDates(this string input) { if (string.IsNullOrWhiteSpace(input)) { - return Enumerable.Empty(); + return []; } var dates = new List(); - var dateRegex = new Regex(@"\d{1,4}[- /.]\d{1,2}[- /.]\d{1,4}"); + var dateRegex = DatesRegex(); foreach (Match match in dateRegex.Matches(input)) { if (DateOnly.TryParse(match.Value, out var date)) @@ -27,5 +27,8 @@ public static IEnumerable ParseDates(this string input) return dates; } + + [GeneratedRegex(@"\d{1,4}[- /.]\d{1,2}[- /.]\d{1,4}")] + private static partial Regex DatesRegex(); } } diff --git a/src/Setlistbot.Domain/PostAggregate/Post.cs b/src/Setlistbot.Domain/PostAggregate/Post.cs index bceea65..90cfe2e 100644 --- a/src/Setlistbot.Domain/PostAggregate/Post.cs +++ b/src/Setlistbot.Domain/PostAggregate/Post.cs @@ -5,8 +5,6 @@ namespace Setlistbot.Domain.PostAggregate { public sealed class Post { - private List _dates = null!; - public string Id { get; private set; } = string.Empty; public string Author { get; private set; } = string.Empty; public string Title { get; private set; } = string.Empty; @@ -17,87 +15,46 @@ public sealed class Post public string ParentId => $"t3_{Id}"; - public IReadOnlyCollection Dates - { - get - { - _dates ??= Title.ParseDates().Concat(SelfText.ParseDates()).ToList(); - return _dates.AsReadOnly(); - } - } + public IReadOnlyCollection Dates => + Title.ParseDates().Concat(SelfText.ParseDates()).ToList().AsReadOnly(); private Post() { } - public static Post NewPost( + public Post( string id, string author, string title, string selfText, string permalink, - string artistId + string artistId, + string reply ) { - Ensure.String.IsNotNullOrWhiteSpace(id, nameof(id)); - Ensure.String.IsNotNullOrWhiteSpace(author, nameof(author)); - Ensure.String.IsNotNullOrWhiteSpace(title, nameof(title)); - Ensure.String.IsNotNullOrWhiteSpace(permalink, nameof(permalink)); - Ensure.String.IsNotEmptyOrWhiteSpace(artistId, nameof(artistId)); - - return new Post() - { - Id = id, - Author = author, - Title = title, - SelfText = selfText, - Permalink = permalink, - ArtistId = artistId, - Reply = string.Empty, - }; + Id = Ensure.String.IsNotNullOrWhiteSpace(id, nameof(id)); + Author = Ensure.String.IsNotNullOrWhiteSpace(author, nameof(author)); + Title = Ensure.String.IsNotNullOrWhiteSpace(title, nameof(title)); + SelfText = selfText; + Permalink = Ensure.String.IsNotNullOrWhiteSpace(permalink, nameof(permalink)); + ArtistId = Ensure.String.IsNotNullOrWhiteSpace(artistId, nameof(artistId)); + Reply = reply; } - public static Post Hydrate( + public static Post NewPost( string id, string author, string title, string selfText, string permalink, - string artistId, - string reply - ) - { - Ensure.String.IsNotNullOrWhiteSpace(id, nameof(id)); - Ensure.String.IsNotNullOrWhiteSpace(author, nameof(author)); - Ensure.String.IsNotNullOrWhiteSpace(title, nameof(title)); - Ensure.String.IsNotNullOrWhiteSpace(permalink, nameof(permalink)); - Ensure.String.IsNotNullOrWhiteSpace(artistId, nameof(artistId)); - Ensure.String.IsNotNullOrWhiteSpace(reply, nameof(reply)); - - return new Post() - { - Id = id, - Author = author, - Title = title, - SelfText = selfText, - Permalink = permalink, - ArtistId = artistId, - Reply = reply, - }; - } + string artistId + ) => new(id, author, title, selfText, permalink, artistId, string.Empty); public bool HasMentionOf(string text) { - return text != null - && ( - Title.Contains(text, StringComparison.CurrentCultureIgnoreCase) - || SelfText.Contains(text, StringComparison.CurrentCultureIgnoreCase) - ); + return Title.Contains(text, StringComparison.CurrentCultureIgnoreCase) + || SelfText.Contains(text, StringComparison.CurrentCultureIgnoreCase); } - public void SetReply(string reply) - { - Ensure.That(reply, nameof(reply)).IsNotNullOrWhiteSpace(); - - Reply = reply; - } + public void SetReply(string reply) => + Reply = Ensure.String.IsNotNullOrWhiteSpace(reply, nameof(reply)); } } diff --git a/src/Setlistbot.Infrastructure/Extensions/PostExtensions.cs b/src/Setlistbot.Infrastructure/Extensions/PostExtensions.cs index 7453403..805b97e 100644 --- a/src/Setlistbot.Infrastructure/Extensions/PostExtensions.cs +++ b/src/Setlistbot.Infrastructure/Extensions/PostExtensions.cs @@ -27,7 +27,7 @@ public static PostEntity ToTableEntity(this Post post, string partitionKey) { return entity == null ? null - : Post.Hydrate( + : new Post( entity.RowKey, entity.Author, entity.Title, diff --git a/test/Setlistbot.Domain.UnitTests/CommentAggregate/CommentTests.cs b/test/Setlistbot.Domain.UnitTests/CommentAggregate/CommentTests.cs index c440834..1c17dbf 100644 --- a/test/Setlistbot.Domain.UnitTests/CommentAggregate/CommentTests.cs +++ b/test/Setlistbot.Domain.UnitTests/CommentAggregate/CommentTests.cs @@ -1,4 +1,163 @@ +using Setlistbot.Domain.CommentAggregate; + namespace Setlistbot.Domain.UnitTests.CommentAggregate { - public sealed class CommentTests { } + public sealed class CommentTests + { + [Fact] + public void NewComment_ValidParameters_CreatesComment() + { + // Arrange + var id = "1"; + var author = "Author"; + var body = "This is a comment."; + var permalink = "http://example.com"; + var artistId = "ArtistId"; + + // Act + var comment = Comment.NewComment(id, author, body, permalink, artistId); + + // Assert + Assert.Equal(id, comment.Id); + Assert.Equal(author, comment.Author); + Assert.Equal(body, comment.Body); + Assert.Equal(permalink, comment.Permalink); + Assert.Equal(artistId, comment.ArtistId); + Assert.Equal(string.Empty, comment.Reply); + } + + [Fact] + public void HasMentionOf_ContainsText_ReturnsTrue() + { + // Arrange + var comment = Comment.NewComment( + "1", + "Author", + "This is a comment mentioning something.", + "http://example.com", + "ArtistId" + ); + + // Act + var result = comment.HasMentionOf("mentioning"); + + // Assert + Assert.True(result); + } + + [Fact] + public void HasMentionOf_DoesNotContainText_ReturnsFalse() + { + // Arrange + var comment = Comment.NewComment( + "1", + "Author", + "This is a comment.", + "http://example.com", + "ArtistId" + ); + + // Act + var result = comment.HasMentionOf("mentioning"); + + // Assert + Assert.False(result); + } + + [Fact] + public void SetReply_ValidReply_SetsReply() + { + // Arrange + var comment = Comment.NewComment( + "1", + "Author", + "This is a comment.", + "http://example.com", + "ArtistId" + ); + var reply = "This is a reply."; + + // Act + comment.SetReply(reply); + + // Assert + Assert.Equal(reply, comment.Reply); + } + + [Fact] + public void SetReply_EmptyReply_ExpectException() + { + // Arrange + var comment = Comment.NewComment( + "1", + "Author", + "This is a comment.", + "http://example.com", + "ArtistId" + ); + var reply = string.Empty; + + // Act & Assert + Assert.Throws(() => comment.SetReply(reply)); + } + + [Fact] + public void Dates_ValidBody_ReturnsDates() + { + // Arrange + var comment = Comment.NewComment( + "1", + "Author", + "This is a comment mentioning 2023-10-01 and 2023-10-02.", + "http://example.com", + "ArtistId" + ); + + // Act + var result = comment.Dates; + + // Assert + Assert.Equal(2, result.Count); + Assert.Contains(new DateOnly(2023, 10, 1), result); + Assert.Contains(new DateOnly(2023, 10, 2), result); + } + + [Fact] + public void Dates_NoDatesInBody_ReturnsEmptyList() + { + // Arrange + var comment = Comment.NewComment( + "1", + "Author", + "This is a comment without dates.", + "http://example.com", + "ArtistId" + ); + + // Act + var result = comment.Dates; + + // Assert + Assert.Empty(result); + } + + [Fact] + public void ParentId_ValidId_ReturnsParentId() + { + // Arrange + var comment = Comment.NewComment( + "1", + "Author", + "This is a comment.", + "http://example.com", + "ArtistId" + ); + + // Act + var result = comment.ParentId; + + // Assert + Assert.Equal("t1_1", result); + } + } } diff --git a/test/Setlistbot.Domain.UnitTests/Extensions/DateParseExtensionsTests.cs b/test/Setlistbot.Domain.UnitTests/Extensions/DateParseExtensionsTests.cs new file mode 100644 index 0000000..c1597db --- /dev/null +++ b/test/Setlistbot.Domain.UnitTests/Extensions/DateParseExtensionsTests.cs @@ -0,0 +1,60 @@ +using Setlistbot.Domain.Extensions; + +namespace Setlistbot.Domain.UnitTests.Extensions +{ + public class DateParseExtensionsTests + { + [Theory] + [InlineData("This is a date: 2023-10-01")] + [InlineData("Another date: 2023/10/01")] + [InlineData("Date with dots: 10.1.2023")] + [InlineData("Date with mixed delimiters: 2023-10/01")] + [InlineData("Date with spaces: 2023 10 01")] + [InlineData("Date with 2 digit year: 10-1-23")] + public void ParseDates_ValidDate_ReturnsDateOnly(string input) + { + // Arrange + var expectedDate = new DateOnly(2023, 10, 1); + + // Act + var result = input.ParseDates(); + + // Assert + Assert.Single(result); + Assert.Contains(expectedDate, result); + } + + [Fact] + public void ParseDates_MultipleDates_ReturnsUniqueDates() + { + // Arrange + var input = "Dates: 2023-10-01, 2023-10-01, 2023-10-02"; + var expectedDates = new List + { + new DateOnly(2023, 10, 1), + new DateOnly(2023, 10, 2), + }; + + // Act + var result = input.ParseDates(); + + // Assert + Assert.Equal(expectedDates.Count, result.Count()); + Assert.Contains(expectedDates[0], result); + Assert.Contains(expectedDates[1], result); + } + + [Fact] + public void ParseDates_NoDates_ReturnsEmptyCollection() + { + // Arrange + var input = "No dates here."; + + // Act + var result = input.ParseDates(); + + // Assert + Assert.Empty(result); + } + } +} diff --git a/test/Setlistbot.Domain.UnitTests/PostAggregate/PostTests.cs b/test/Setlistbot.Domain.UnitTests/PostAggregate/PostTests.cs new file mode 100644 index 0000000..22213e7 --- /dev/null +++ b/test/Setlistbot.Domain.UnitTests/PostAggregate/PostTests.cs @@ -0,0 +1,186 @@ +using Setlistbot.Domain.PostAggregate; + +namespace Setlistbot.Domain.UnitTests.PostAggregate +{ + public class PostTests + { + [Fact] + public void NewPost_ValidParameters_CreatesPost() + { + // Arrange + var id = "1"; + var author = "Author"; + var title = "This is a post."; + var selfText = "This is the self text."; + var permalink = "http://example.com"; + var artistId = "ArtistId"; + + // Act + var post = Post.NewPost(id, author, title, selfText, permalink, artistId); + + // Assert + Assert.Equal(id, post.Id); + Assert.Equal(author, post.Author); + Assert.Equal(title, post.Title); + Assert.Equal(selfText, post.SelfText); + Assert.Equal(permalink, post.Permalink); + Assert.Equal(artistId, post.ArtistId); + } + + [Fact] + public void HasMentionOf_ContainsText_ReturnsTrue() + { + // Arrange + var post = Post.NewPost( + "1", + "Author", + "This is a post mentioning something.", + "This is the self text.", + "http://example.com", + "ArtistId" + ); + + // Act + var result = post.HasMentionOf("mentioning"); + + // Assert + Assert.True(result); + } + + [Fact] + public void HasMentionOf_DoesNotContainText_ReturnsFalse() + { + // Arrange + var post = Post.NewPost( + "1", + "Author", + "This is a post.", + "This is the self text.", + "http://example.com", + "ArtistId" + ); + + // Act + var result = post.HasMentionOf("mentioning"); + + // Assert + Assert.False(result); + } + + [Fact] + public void SetReply_ValidParameters_SetsReply() + { + // Arrange + var post = Post.NewPost( + "1", + "Author", + "This is a post.", + "This is the self text.", + "http://example.com", + "ArtistId" + ); + var reply = "This is a reply."; + + // Act + post.SetReply(reply); + + // Assert + Assert.Equal(reply, post.Reply); + } + + [Fact] + public void SetReply_EmptyReply_ThrowsArgumentException() + { + // Arrange + var post = Post.NewPost( + "1", + "Author", + "This is a post.", + "This is the self text.", + "http://example.com", + "ArtistId" + ); + + // Act & Assert + Assert.Throws(() => post.SetReply(string.Empty)); + } + + [Fact] + public void SetReply_NullReply_ThrowsArgumentException() + { + // Arrange + var post = Post.NewPost( + "1", + "Author", + "This is a post.", + "This is the self text.", + "http://example.com", + "ArtistId" + ); + + // Act & Assert + Assert.Throws(() => post.SetReply(null!)); + } + + [Fact] + public void Dates_ValidPost_ReturnsDates() + { + // Arrange + var post = Post.NewPost( + "1", + "Author", + "This is a post mentioning 2023-01-01.", + "This is the self text mentioning 2023-02-02.", + "http://example.com", + "ArtistId" + ); + + // Act + var result = post.Dates; + + // Assert + Assert.Contains(new DateOnly(2023, 1, 1), result); + Assert.Contains(new DateOnly(2023, 2, 2), result); + } + + [Fact] + public void Dates_NoDates_ReturnsEmptyCollection() + { + // Arrange + var post = Post.NewPost( + "1", + "Author", + "This is a post.", + "This is the self text.", + "http://example.com", + "ArtistId" + ); + + // Act + var result = post.Dates; + + // Assert + Assert.Empty(result); + } + + [Fact] + public void ParentId_ValidPost_ReturnsParentId() + { + // Arrange + var post = Post.NewPost( + "1", + "Author", + "This is a post.", + "This is the self text.", + "http://example.com", + "ArtistId" + ); + + // Act + var result = post.ParentId; + + // Assert + Assert.Equal("t3_1", result); + } + } +} From 57e537e872df541405100c4812633078db634940 Mon Sep 17 00:00:00 2001 From: Chase Banna Date: Mon, 3 Feb 2025 23:10:51 -0800 Subject: [PATCH 2/2] Add test for ParseDates method to handle empty input --- .../Extensions/DateParseExtensionsTests.cs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/test/Setlistbot.Domain.UnitTests/Extensions/DateParseExtensionsTests.cs b/test/Setlistbot.Domain.UnitTests/Extensions/DateParseExtensionsTests.cs index c1597db..7444a52 100644 --- a/test/Setlistbot.Domain.UnitTests/Extensions/DateParseExtensionsTests.cs +++ b/test/Setlistbot.Domain.UnitTests/Extensions/DateParseExtensionsTests.cs @@ -4,6 +4,18 @@ namespace Setlistbot.Domain.UnitTests.Extensions { public class DateParseExtensionsTests { + [Theory] + [InlineData("")] + [InlineData(" ")] + public void ParseDates_Empty_ReturnsEmptyCollection(string input) + { + // Act + var result = input.ParseDates(); + + // Assert + Assert.Empty(result); + } + [Theory] [InlineData("This is a date: 2023-10-01")] [InlineData("Another date: 2023/10/01")]