diff --git a/src/RecurringThings.MongoDB/Configuration/RecurringThingsBuilderExtensions.cs b/src/RecurringThings.MongoDB/Configuration/RecurringThingsBuilderExtensions.cs index 970e98d..11738cf 100644 --- a/src/RecurringThings.MongoDB/Configuration/RecurringThingsBuilderExtensions.cs +++ b/src/RecurringThings.MongoDB/Configuration/RecurringThingsBuilderExtensions.cs @@ -1,6 +1,7 @@ namespace RecurringThings.MongoDB.Configuration; using System; +using global::MongoDB.Bson.Serialization.Conventions; using global::MongoDB.Driver; using Microsoft.Extensions.DependencyInjection; using RecurringThings.Configuration; @@ -43,6 +44,13 @@ public static RecurringThingsBuilder UseMongoDb( configure(options); options.Validate(); + // Register camelCase naming convention for RecurringThings documents + var conventionPack = new ConventionPack { new CamelCaseElementNameConvention() }; + ConventionRegistry.Register( + "RecurringThingsCamelCase", + conventionPack, + t => t.FullName?.StartsWith("RecurringThings", StringComparison.Ordinal) == true); + // Mark MongoDB as configured builder.MongoDbConfigured = true; diff --git a/src/RecurringThings.MongoDB/Documents/RecurringThingDocument.cs b/src/RecurringThings.MongoDB/Documents/RecurringThingDocument.cs index 67ea962..22f8b44 100644 --- a/src/RecurringThings.MongoDB/Documents/RecurringThingDocument.cs +++ b/src/RecurringThings.MongoDB/Documents/RecurringThingDocument.cs @@ -37,19 +37,16 @@ public sealed class RecurringThingDocument /// /// Valid values: "recurrence", "occurrence", "exception", "override" /// - [BsonElement("documentType")] public required string DocumentType { get; set; } /// /// Gets or sets the tenant identifier for multi-tenant isolation. /// - [BsonElement("organization")] public required string Organization { get; set; } /// /// Gets or sets the hierarchical resource scope. /// - [BsonElement("resourcePath")] public required string ResourcePath { get; set; } /// @@ -58,7 +55,6 @@ public sealed class RecurringThingDocument /// /// Present on recurrences and occurrences. Null for exceptions and overrides. /// - [BsonElement("type")] [BsonIgnoreIfNull] public string? Type { get; set; } @@ -68,7 +64,6 @@ public sealed class RecurringThingDocument /// /// Present on recurrences, occurrences, and overrides. Null for exceptions. /// - [BsonElement("startTime")] [BsonIgnoreIfNull] public DateTime? StartTime { get; set; } @@ -79,7 +74,6 @@ public sealed class RecurringThingDocument /// Present on occurrences and overrides. Null for recurrences and exceptions. /// Computed as StartTime + Duration. /// - [BsonElement("endTime")] [BsonIgnoreIfNull] public DateTime? EndTime { get; set; } @@ -90,7 +84,6 @@ public sealed class RecurringThingDocument /// Stored as milliseconds for efficient MongoDB operations. /// Present on recurrences, occurrences, and overrides. Null for exceptions. /// - [BsonElement("durationMs")] [BsonIgnoreIfNull] public long? DurationMs { get; set; } @@ -100,7 +93,6 @@ public sealed class RecurringThingDocument /// /// Present on recurrences and occurrences. Null for exceptions and overrides. /// - [BsonElement("timeZone")] [BsonIgnoreIfNull] public string? TimeZone { get; set; } @@ -110,7 +102,6 @@ public sealed class RecurringThingDocument /// /// Present only on recurrences. /// - [BsonElement("recurrenceEndTime")] [BsonIgnoreIfNull] public DateTime? RecurrenceEndTime { get; set; } @@ -120,7 +111,6 @@ public sealed class RecurringThingDocument /// /// Present only on recurrences. /// - [BsonElement("rrule")] [BsonIgnoreIfNull] public string? RRule { get; set; } @@ -136,7 +126,6 @@ public sealed class RecurringThingDocument /// monthly patterns with day <= 28, or patterns where no months are affected). /// /// - [BsonElement("monthDayBehavior")] [BsonIgnoreIfNull] public string? MonthDayBehavior { get; set; } @@ -146,7 +135,6 @@ public sealed class RecurringThingDocument /// /// Present on exceptions and overrides. /// - [BsonElement("recurrenceId")] [BsonRepresentation(BsonType.String)] [BsonIgnoreIfNull] public Guid? RecurrenceId { get; set; } @@ -157,7 +145,6 @@ public sealed class RecurringThingDocument /// /// Present on exceptions and overrides. /// - [BsonElement("originalTimeUtc")] [BsonIgnoreIfNull] public DateTime? OriginalTimeUtc { get; set; } @@ -168,7 +155,6 @@ public sealed class RecurringThingDocument /// Denormalized from the parent recurrence at creation time. /// Present only on overrides. /// - [BsonElement("originalDurationMs")] [BsonIgnoreIfNull] public long? OriginalDurationMs { get; set; } @@ -179,14 +165,12 @@ public sealed class RecurringThingDocument /// Denormalized from the parent recurrence at creation time. /// Present only on overrides. /// - [BsonElement("originalExtensions")] [BsonIgnoreIfNull] public Dictionary? OriginalExtensions { get; set; } /// /// Gets or sets the user-defined key-value metadata. /// - [BsonElement("extensions")] [BsonIgnoreIfNull] public Dictionary? Extensions { get; set; } } diff --git a/src/RecurringThings/RecurringThings.csproj b/src/RecurringThings/RecurringThings.csproj index 7301a8a..0c6570c 100644 --- a/src/RecurringThings/RecurringThings.csproj +++ b/src/RecurringThings/RecurringThings.csproj @@ -15,12 +15,12 @@ https://github.com/ChuckNovice/RecurringThings Apache-2.0 README.md - logo_gray.png + logo_red.png - + diff --git a/tests/RecurringThings.MongoDB.Tests/MongoDbIntegrationTests.cs b/tests/RecurringThings.MongoDB.Tests/MongoDbIntegrationTests.cs index cf1d053..cfbf9cf 100644 --- a/tests/RecurringThings.MongoDB.Tests/MongoDbIntegrationTests.cs +++ b/tests/RecurringThings.MongoDB.Tests/MongoDbIntegrationTests.cs @@ -4,7 +4,6 @@ namespace RecurringThings.MongoDB.Tests; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; -using FluentAssertions; using global::MongoDB.Driver; using RecurringThings.Domain; using RecurringThings.MongoDB.Documents; @@ -86,13 +85,13 @@ public async Task RecurrenceRepository_CreateAndGetById_ReturnsCreatedRecurrence var result = await repo.GetByIdAsync(recurrence.Id, TestOrganization, TestResourcePath); // Assert - result.Should().NotBeNull(); - result!.Id.Should().Be(recurrence.Id); - result.Organization.Should().Be(TestOrganization); - result.ResourcePath.Should().Be(TestResourcePath); - result.Type.Should().Be(TestType); - result.Duration.Should().Be(recurrence.Duration); - result.Extensions.Should().BeEquivalentTo(recurrence.Extensions); + Assert.NotNull(result); + Assert.Equal(recurrence.Id, result!.Id); + Assert.Equal(TestOrganization, result.Organization); + Assert.Equal(TestResourcePath, result.ResourcePath); + Assert.Equal(TestType, result.Type); + Assert.Equal(recurrence.Duration, result.Duration); + Assert.Equivalent(recurrence.Extensions, result.Extensions); } [SkippableFact] @@ -113,9 +112,9 @@ public async Task RecurrenceRepository_Update_UpdatesDurationAndExtensions() var result = await repo.GetByIdAsync(recurrence.Id, TestOrganization, TestResourcePath); // Assert - result.Should().NotBeNull(); - result!.Duration.Should().Be(TimeSpan.FromHours(2)); - result.Extensions.Should().ContainKey("updated"); + Assert.NotNull(result); + Assert.Equal(TimeSpan.FromHours(2), result!.Duration); + Assert.True(result.Extensions?.ContainsKey("updated") == true); } [SkippableFact] @@ -133,7 +132,7 @@ public async Task RecurrenceRepository_Delete_RemovesRecurrence() var result = await repo.GetByIdAsync(recurrence.Id, TestOrganization, TestResourcePath); // Assert - result.Should().BeNull(); + Assert.Null(result); } [SkippableFact] @@ -154,8 +153,8 @@ public async Task RecurrenceRepository_GetInRange_ReturnsRecurrencesInRange() TestOrganization, TestResourcePath, queryStart, queryEnd, null).ToListAsync(); // Assert - results.Should().HaveCount(1); - results[0].Id.Should().Be(recurrence.Id); + Assert.Single(results); + Assert.Equal(recurrence.Id, results[0].Id); } [SkippableFact] @@ -177,7 +176,7 @@ public async Task RecurrenceRepository_GetInRange_FiltersOutOfRange() TestOrganization, TestResourcePath, queryStart, queryEnd, null).ToListAsync(); // Assert - results.Should().BeEmpty(); + Assert.Empty(results); } [SkippableFact] @@ -216,8 +215,8 @@ public async Task RecurrenceRepository_GetInRange_FiltersByType() TestOrganization, TestResourcePath, queryStart, queryEnd, [TestType]).ToListAsync(); // Assert - results.Should().HaveCount(1); - results[0].Type.Should().Be(TestType); + Assert.Single(results); + Assert.Equal(TestType, results[0].Type); } #endregion @@ -238,11 +237,11 @@ public async Task OccurrenceRepository_CreateAndGetById_ReturnsCreatedOccurrence var result = await repo.GetByIdAsync(occurrence.Id, TestOrganization, TestResourcePath); // Assert - result.Should().NotBeNull(); - result!.Id.Should().Be(occurrence.Id); - result.StartTime.Should().Be(occurrence.StartTime); - result.EndTime.Should().Be(occurrence.EndTime); - result.Duration.Should().Be(occurrence.Duration); + Assert.NotNull(result); + Assert.Equal(occurrence.Id, result!.Id); + Assert.Equal(occurrence.StartTime, result.StartTime); + Assert.Equal(occurrence.EndTime, result.EndTime); + Assert.Equal(occurrence.Duration, result.Duration); } [SkippableFact] @@ -265,9 +264,9 @@ public async Task OccurrenceRepository_Update_UpdatesFields() var result = await repo.GetByIdAsync(occurrence.Id, TestOrganization, TestResourcePath); // Assert - result.Should().NotBeNull(); - result!.Duration.Should().Be(TimeSpan.FromMinutes(45)); - result.Extensions.Should().ContainKey("updated"); + Assert.NotNull(result); + Assert.Equal(TimeSpan.FromMinutes(45), result!.Duration); + Assert.True(result.Extensions?.ContainsKey("updated") == true); } #endregion @@ -294,9 +293,9 @@ public async Task ExceptionRepository_CreateAndGetById_ReturnsCreatedException() exception.Id, TestOrganization, TestResourcePath); // Assert - result.Should().NotBeNull(); - result!.RecurrenceId.Should().Be(recurrence.Id); - result.OriginalTimeUtc.Should().Be(exception.OriginalTimeUtc); + Assert.NotNull(result); + Assert.Equal(recurrence.Id, result!.RecurrenceId); + Assert.Equal(exception.OriginalTimeUtc, result.OriginalTimeUtc); } [SkippableFact] @@ -319,8 +318,8 @@ public async Task ExceptionRepository_GetByRecurrenceIds_ReturnsExceptionsForRec TestOrganization, TestResourcePath, [recurrence.Id]).ToListAsync(); // Assert - results.Should().HaveCount(1); - results[0].RecurrenceId.Should().Be(recurrence.Id); + Assert.Single(results); + Assert.Equal(recurrence.Id, results[0].RecurrenceId); } #endregion @@ -346,11 +345,11 @@ public async Task OverrideRepository_CreateAndGetById_ReturnsCreatedOverride() var result = await overrideRepo.GetByIdAsync(@override.Id, TestOrganization, TestResourcePath); // Assert - result.Should().NotBeNull(); - result!.RecurrenceId.Should().Be(recurrence.Id); - result.OriginalTimeUtc.Should().Be(@override.OriginalTimeUtc); - result.StartTime.Should().Be(@override.StartTime); - result.OriginalDuration.Should().Be(@override.OriginalDuration); + Assert.NotNull(result); + Assert.Equal(recurrence.Id, result!.RecurrenceId); + Assert.Equal(@override.OriginalTimeUtc, result.OriginalTimeUtc); + Assert.Equal(@override.StartTime, result.StartTime); + Assert.Equal(@override.OriginalDuration, result.OriginalDuration); } [SkippableFact] @@ -376,8 +375,8 @@ public async Task OverrideRepository_GetInRange_ReturnsOverridesWithOriginalTime TestOrganization, TestResourcePath, [recurrence.Id], queryStart, queryEnd).ToListAsync(); // Assert - results.Should().HaveCount(1); - results[0].RecurrenceId.Should().Be(recurrence.Id); + Assert.Single(results); + Assert.Equal(recurrence.Id, results[0].RecurrenceId); } #endregion @@ -409,15 +408,15 @@ public async Task RecurrenceRepository_Delete_CascadesDeleteToExceptionsAndOverr // Assert - All related documents should be deleted var recurrenceResult = await recurrenceRepo.GetByIdAsync( recurrence.Id, TestOrganization, TestResourcePath); - recurrenceResult.Should().BeNull(); + Assert.Null(recurrenceResult); var exceptionResult = await exceptionRepo.GetByIdAsync( exception.Id, TestOrganization, TestResourcePath); - exceptionResult.Should().BeNull(); + Assert.Null(exceptionResult); var overrideResult = await overrideRepo.GetByIdAsync( @override.Id, TestOrganization, TestResourcePath); - overrideResult.Should().BeNull(); + Assert.Null(overrideResult); } #endregion @@ -437,13 +436,13 @@ public async Task IndexManager_EnsureIndexes_CreatesExpectedIndexes() var indexList = await indexes.ToListAsync(); // Assert - should have at least 5 indexes (_id + 4 custom) - indexList.Should().HaveCountGreaterThanOrEqualTo(5); + Assert.True(indexList.Count >= 5); var indexNames = indexList.Select(i => i["name"].AsString).ToList(); - indexNames.Should().Contain("idx_recurring_query"); - indexNames.Should().Contain("idx_original_time"); - indexNames.Should().Contain("idx_override_time_range"); - indexNames.Should().Contain("idx_cascade_delete"); + Assert.Contains("idx_recurring_query", indexNames); + Assert.Contains("idx_original_time", indexNames); + Assert.Contains("idx_override_time_range", indexNames); + Assert.Contains("idx_cascade_delete", indexNames); } #endregion @@ -470,12 +469,12 @@ public async Task RecurrenceRepository_Extensions_StoredAndRetrievedCorrectly() var result = await repo.GetByIdAsync(recurrence.Id, TestOrganization, TestResourcePath); // Assert - result.Should().NotBeNull(); - result!.Extensions.Should().NotBeNull(); - result.Extensions.Should().HaveCount(3); - result.Extensions!["title"].Should().Be("Test Meeting"); - result.Extensions["location"].Should().Be("Conference Room A"); - result.Extensions["unicode"].Should().Be("Hello \u4e16\u754c"); + Assert.NotNull(result); + Assert.NotNull(result!.Extensions); + Assert.Equal(3, result.Extensions!.Count); + Assert.Equal("Test Meeting", result.Extensions["title"]); + Assert.Equal("Conference Room A", result.Extensions["location"]); + Assert.Equal("Hello \u4e16\u754c", result.Extensions["unicode"]); } #endregion @@ -497,8 +496,8 @@ public async Task Duration_StoredAsMillisecondsAndRetrievedCorrectly() var result = await repo.GetByIdAsync(recurrence.Id, TestOrganization, TestResourcePath); // Assert - result.Should().NotBeNull(); - result!.Duration.Should().Be(TimeSpan.FromHours(2) + TimeSpan.FromMinutes(30) + TimeSpan.FromSeconds(15)); + Assert.NotNull(result); + Assert.Equal(TimeSpan.FromHours(2) + TimeSpan.FromMinutes(30) + TimeSpan.FromSeconds(15), result!.Duration); } #endregion diff --git a/tests/RecurringThings.MongoDB.Tests/RecurringThings.MongoDB.Tests.csproj b/tests/RecurringThings.MongoDB.Tests/RecurringThings.MongoDB.Tests.csproj index 302ea71..a450c39 100644 --- a/tests/RecurringThings.MongoDB.Tests/RecurringThings.MongoDB.Tests.csproj +++ b/tests/RecurringThings.MongoDB.Tests/RecurringThings.MongoDB.Tests.csproj @@ -9,7 +9,6 @@ - diff --git a/tests/RecurringThings.PostgreSQL.Tests/PostgreSqlIntegrationTests.cs b/tests/RecurringThings.PostgreSQL.Tests/PostgreSqlIntegrationTests.cs index 2eea334..0c99d64 100644 --- a/tests/RecurringThings.PostgreSQL.Tests/PostgreSqlIntegrationTests.cs +++ b/tests/RecurringThings.PostgreSQL.Tests/PostgreSqlIntegrationTests.cs @@ -4,7 +4,6 @@ namespace RecurringThings.PostgreSQL.Tests; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; -using FluentAssertions; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; using Npgsql; @@ -147,13 +146,13 @@ public async Task RecurrenceRepository_CreateAndGetById_ReturnsCreatedRecurrence var result = await repo.GetByIdAsync(recurrence.Id, TestOrganization, TestResourcePath); // Assert - result.Should().NotBeNull(); - result!.Id.Should().Be(recurrence.Id); - result.Organization.Should().Be(TestOrganization); - result.ResourcePath.Should().Be(TestResourcePath); - result.Type.Should().Be(TestType); - result.Duration.Should().Be(recurrence.Duration); - result.Extensions.Should().BeEquivalentTo(recurrence.Extensions); + Assert.NotNull(result); + Assert.Equal(recurrence.Id, result!.Id); + Assert.Equal(TestOrganization, result.Organization); + Assert.Equal(TestResourcePath, result.ResourcePath); + Assert.Equal(TestType, result.Type); + Assert.Equal(recurrence.Duration, result.Duration); + Assert.Equivalent(recurrence.Extensions, result.Extensions); } [SkippableFact] @@ -174,9 +173,9 @@ public async Task RecurrenceRepository_Update_UpdatesDurationAndExtensions() var result = await repo.GetByIdAsync(recurrence.Id, TestOrganization, TestResourcePath); // Assert - result.Should().NotBeNull(); - result!.Duration.Should().Be(TimeSpan.FromHours(2)); - result.Extensions.Should().ContainKey("updated"); + Assert.NotNull(result); + Assert.Equal(TimeSpan.FromHours(2), result!.Duration); + Assert.True(result.Extensions?.ContainsKey("updated") == true); } [SkippableFact] @@ -194,7 +193,7 @@ public async Task RecurrenceRepository_Delete_RemovesRecurrence() var result = await repo.GetByIdAsync(recurrence.Id, TestOrganization, TestResourcePath); // Assert - result.Should().BeNull(); + Assert.Null(result); } [SkippableFact] @@ -215,8 +214,8 @@ public async Task RecurrenceRepository_GetInRange_ReturnsRecurrencesInRange() TestOrganization, TestResourcePath, queryStart, queryEnd, null).ToListAsync(); // Assert - results.Should().HaveCount(1); - results[0].Id.Should().Be(recurrence.Id); + Assert.Single(results); + Assert.Equal(recurrence.Id, results[0].Id); } [SkippableFact] @@ -238,7 +237,7 @@ public async Task RecurrenceRepository_GetInRange_FiltersOutOfRange() TestOrganization, TestResourcePath, queryStart, queryEnd, null).ToListAsync(); // Assert - results.Should().BeEmpty(); + Assert.Empty(results); } [SkippableFact] @@ -274,8 +273,8 @@ public async Task RecurrenceRepository_GetInRange_FiltersByType() TestOrganization, TestResourcePath, queryStart, queryEnd, [TestType]).ToListAsync(); // Assert - results.Should().HaveCount(1); - results[0].Type.Should().Be(TestType); + Assert.Single(results); + Assert.Equal(TestType, results[0].Type); } #endregion @@ -296,11 +295,11 @@ public async Task OccurrenceRepository_CreateAndGetById_ReturnsCreatedOccurrence var result = await repo.GetByIdAsync(occurrence.Id, TestOrganization, TestResourcePath); // Assert - result.Should().NotBeNull(); - result!.Id.Should().Be(occurrence.Id); - result.StartTime.Should().Be(occurrence.StartTime); - result.EndTime.Should().Be(occurrence.EndTime); - result.Duration.Should().Be(occurrence.Duration); + Assert.NotNull(result); + Assert.Equal(occurrence.Id, result!.Id); + Assert.Equal(occurrence.StartTime, result.StartTime); + Assert.Equal(occurrence.EndTime, result.EndTime); + Assert.Equal(occurrence.Duration, result.Duration); } [SkippableFact] @@ -323,9 +322,9 @@ public async Task OccurrenceRepository_Update_UpdatesFields() var result = await repo.GetByIdAsync(occurrence.Id, TestOrganization, TestResourcePath); // Assert - result.Should().NotBeNull(); - result!.Duration.Should().Be(TimeSpan.FromMinutes(45)); - result.Extensions.Should().ContainKey("updated"); + Assert.NotNull(result); + Assert.Equal(TimeSpan.FromMinutes(45), result!.Duration); + Assert.True(result.Extensions?.ContainsKey("updated") == true); } [SkippableFact] @@ -343,7 +342,7 @@ public async Task OccurrenceRepository_Delete_RemovesOccurrence() var result = await repo.GetByIdAsync(occurrence.Id, TestOrganization, TestResourcePath); // Assert - result.Should().BeNull(); + Assert.Null(result); } [SkippableFact] @@ -364,8 +363,8 @@ public async Task OccurrenceRepository_GetInRange_ReturnsOccurrencesInRange() TestOrganization, TestResourcePath, queryStart, queryEnd, null).ToListAsync(); // Assert - results.Should().HaveCount(1); - results[0].Id.Should().Be(occurrence.Id); + Assert.Single(results); + Assert.Equal(occurrence.Id, results[0].Id); } #endregion @@ -392,9 +391,9 @@ public async Task ExceptionRepository_CreateAndGetById_ReturnsCreatedException() exception.Id, TestOrganization, TestResourcePath); // Assert - result.Should().NotBeNull(); - result!.RecurrenceId.Should().Be(recurrence.Id); - result.OriginalTimeUtc.Should().Be(exception.OriginalTimeUtc); + Assert.NotNull(result); + Assert.Equal(recurrence.Id, result!.RecurrenceId); + Assert.Equal(exception.OriginalTimeUtc, result.OriginalTimeUtc); } [SkippableFact] @@ -417,8 +416,8 @@ public async Task ExceptionRepository_GetByRecurrenceIds_ReturnsExceptionsForRec TestOrganization, TestResourcePath, [recurrence.Id]).ToListAsync(); // Assert - results.Should().HaveCount(1); - results[0].RecurrenceId.Should().Be(recurrence.Id); + Assert.Single(results); + Assert.Equal(recurrence.Id, results[0].RecurrenceId); } [SkippableFact] @@ -442,7 +441,7 @@ public async Task ExceptionRepository_Delete_RemovesException() exception.Id, TestOrganization, TestResourcePath); // Assert - result.Should().BeNull(); + Assert.Null(result); } #endregion @@ -468,11 +467,11 @@ public async Task OverrideRepository_CreateAndGetById_ReturnsCreatedOverride() var result = await overrideRepo.GetByIdAsync(@override.Id, TestOrganization, TestResourcePath); // Assert - result.Should().NotBeNull(); - result!.RecurrenceId.Should().Be(recurrence.Id); - result.OriginalTimeUtc.Should().Be(@override.OriginalTimeUtc); - result.StartTime.Should().Be(@override.StartTime); - result.OriginalDuration.Should().Be(@override.OriginalDuration); + Assert.NotNull(result); + Assert.Equal(recurrence.Id, result!.RecurrenceId); + Assert.Equal(@override.OriginalTimeUtc, result.OriginalTimeUtc); + Assert.Equal(@override.StartTime, result.StartTime); + Assert.Equal(@override.OriginalDuration, result.OriginalDuration); } [SkippableFact] @@ -498,8 +497,8 @@ public async Task OverrideRepository_GetInRange_ReturnsOverridesWithOriginalTime TestOrganization, TestResourcePath, [recurrence.Id], queryStart, queryEnd).ToListAsync(); // Assert - results.Should().HaveCount(1); - results[0].RecurrenceId.Should().Be(recurrence.Id); + Assert.Single(results); + Assert.Equal(recurrence.Id, results[0].RecurrenceId); } [SkippableFact] @@ -525,9 +524,9 @@ public async Task OverrideRepository_Update_UpdatesFields() var result = await overrideRepo.GetByIdAsync(@override.Id, TestOrganization, TestResourcePath); // Assert - result.Should().NotBeNull(); - result!.Duration.Should().Be(TimeSpan.FromMinutes(90)); - result.Extensions.Should().ContainKey("updated"); + Assert.NotNull(result); + Assert.Equal(TimeSpan.FromMinutes(90), result!.Duration); + Assert.True(result.Extensions?.ContainsKey("updated") == true); } #endregion @@ -559,15 +558,15 @@ public async Task RecurrenceRepository_Delete_CascadesDeleteToExceptionsAndOverr // Assert - All related records should be deleted via CASCADE var recurrenceResult = await recurrenceRepo.GetByIdAsync( recurrence.Id, TestOrganization, TestResourcePath); - recurrenceResult.Should().BeNull(); + Assert.Null(recurrenceResult); var exceptionResult = await exceptionRepo.GetByIdAsync( exception.Id, TestOrganization, TestResourcePath); - exceptionResult.Should().BeNull(); + Assert.Null(exceptionResult); var overrideResult = await overrideRepo.GetByIdAsync( @override.Id, TestOrganization, TestResourcePath); - overrideResult.Should().BeNull(); + Assert.Null(overrideResult); } #endregion @@ -594,12 +593,12 @@ public async Task RecurrenceRepository_Extensions_StoredAndRetrievedCorrectly() var result = await repo.GetByIdAsync(recurrence.Id, TestOrganization, TestResourcePath); // Assert - result.Should().NotBeNull(); - result!.Extensions.Should().NotBeNull(); - result.Extensions.Should().HaveCount(3); - result.Extensions!["title"].Should().Be("Test Meeting"); - result.Extensions["location"].Should().Be("Conference Room A"); - result.Extensions["unicode"].Should().Be("Hello \u4e16\u754c"); + Assert.NotNull(result); + Assert.NotNull(result!.Extensions); + Assert.Equal(3, result.Extensions!.Count); + Assert.Equal("Test Meeting", result.Extensions["title"]); + Assert.Equal("Conference Room A", result.Extensions["location"]); + Assert.Equal("Hello \u4e16\u754c", result.Extensions["unicode"]); } #endregion @@ -621,8 +620,8 @@ public async Task Duration_StoredAsIntervalAndRetrievedCorrectly() var result = await repo.GetByIdAsync(recurrence.Id, TestOrganization, TestResourcePath); // Assert - result.Should().NotBeNull(); - result!.Duration.Should().Be(TimeSpan.FromHours(2) + TimeSpan.FromMinutes(30) + TimeSpan.FromSeconds(15)); + Assert.NotNull(result); + Assert.Equal(TimeSpan.FromHours(2) + TimeSpan.FromMinutes(30) + TimeSpan.FromSeconds(15), result!.Duration); } #endregion @@ -655,13 +654,13 @@ AND tablename IN ('recurrences', 'occurrences', 'occurrence_exceptions', 'occurr } // Assert - Check for key indexes - indexNames.Should().Contain("idx_recurrences_query"); - indexNames.Should().Contain("idx_occurrences_query"); - indexNames.Should().Contain("idx_exceptions_recurrence"); - indexNames.Should().Contain("idx_exceptions_query"); - indexNames.Should().Contain("idx_overrides_recurrence"); - indexNames.Should().Contain("idx_overrides_original"); - indexNames.Should().Contain("idx_overrides_start"); + Assert.Contains("idx_recurrences_query", indexNames); + Assert.Contains("idx_occurrences_query", indexNames); + Assert.Contains("idx_exceptions_recurrence", indexNames); + Assert.Contains("idx_exceptions_query", indexNames); + Assert.Contains("idx_overrides_recurrence", indexNames); + Assert.Contains("idx_overrides_original", indexNames); + Assert.Contains("idx_overrides_start", indexNames); } #endregion diff --git a/tests/RecurringThings.PostgreSQL.Tests/RecurringThings.PostgreSQL.Tests.csproj b/tests/RecurringThings.PostgreSQL.Tests/RecurringThings.PostgreSQL.Tests.csproj index f57436f..fe8f3ce 100644 --- a/tests/RecurringThings.PostgreSQL.Tests/RecurringThings.PostgreSQL.Tests.csproj +++ b/tests/RecurringThings.PostgreSQL.Tests/RecurringThings.PostgreSQL.Tests.csproj @@ -9,7 +9,6 @@ - diff --git a/tests/RecurringThings.Tests/Domain/OccurrenceExceptionTests.cs b/tests/RecurringThings.Tests/Domain/OccurrenceExceptionTests.cs index 01b6d2c..c474056 100644 --- a/tests/RecurringThings.Tests/Domain/OccurrenceExceptionTests.cs +++ b/tests/RecurringThings.Tests/Domain/OccurrenceExceptionTests.cs @@ -2,7 +2,6 @@ namespace RecurringThings.Tests.Domain; using System; using System.Collections.Generic; -using FluentAssertions; using RecurringThings.Domain; using Xunit; @@ -27,11 +26,11 @@ public void OccurrenceException_WhenCreated_ShouldHaveAllRequiredProperties() }; // Assert - exception.Id.Should().Be(id); - exception.Organization.Should().Be("org1"); - exception.ResourcePath.Should().Be("user/calendar"); - exception.RecurrenceId.Should().Be(recurrenceId); - exception.OriginalTimeUtc.Should().Be(originalTime); + Assert.Equal(id, exception.Id); + Assert.Equal("org1", exception.Organization); + Assert.Equal("user/calendar", exception.ResourcePath); + Assert.Equal(recurrenceId, exception.RecurrenceId); + Assert.Equal(originalTime, exception.OriginalTimeUtc); } [Fact] @@ -48,7 +47,7 @@ public void OccurrenceException_Extensions_ShouldBeNullByDefault() }; // Assert - exception.Extensions.Should().BeNull(); + Assert.Null(exception.Extensions); } [Fact] @@ -73,9 +72,9 @@ public void OccurrenceException_Extensions_ShouldBeMutable() }; // Assert - exception.Extensions.Should().HaveCount(2); - exception.Extensions["reason"].Should().Be("Vacation"); - exception.Extensions["cancelledBy"].Should().Be("user123"); + Assert.Equal(2, exception.Extensions.Count); + Assert.Equal("Vacation", exception.Extensions["reason"]); + Assert.Equal("user123", exception.Extensions["cancelledBy"]); } [Fact] @@ -92,7 +91,7 @@ public void OccurrenceException_WhenCreatedWithEmptyOrganization_ShouldBeValid() }; // Assert - exception.Organization.Should().BeEmpty(); + Assert.Empty(exception.Organization); } [Fact] @@ -109,7 +108,7 @@ public void OccurrenceException_WhenCreatedWithEmptyResourcePath_ShouldBeValid() }; // Assert - exception.ResourcePath.Should().BeEmpty(); + Assert.Empty(exception.ResourcePath); } [Fact] @@ -135,7 +134,7 @@ public void OccurrenceException_WhenCreatedWithExtensions_ShouldContainAllPairs( }; // Assert - exception.Extensions.Should().BeEquivalentTo(extensions); + Assert.Equivalent(extensions, exception.Extensions); } [Fact] @@ -156,7 +155,7 @@ public void OccurrenceException_Extensions_CanBeSetToNull() exception.Extensions = null; // Assert - exception.Extensions.Should().BeNull(); + Assert.Null(exception.Extensions); } [Fact] @@ -176,7 +175,7 @@ public void OccurrenceException_Id_ShouldBeInitOnly() }; // Assert - exception.Id.Should().Be(id); + Assert.Equal(id, exception.Id); } [Fact] @@ -196,7 +195,7 @@ public void OccurrenceException_RecurrenceId_ShouldBeInitOnly() }; // Assert - exception.RecurrenceId.Should().Be(recurrenceId); + Assert.Equal(recurrenceId, exception.RecurrenceId); } [Fact] @@ -216,6 +215,6 @@ public void OccurrenceException_OriginalTimeUtc_ShouldBeInitOnly() }; // Assert - exception.OriginalTimeUtc.Should().Be(originalTime); + Assert.Equal(originalTime, exception.OriginalTimeUtc); } } diff --git a/tests/RecurringThings.Tests/Domain/OccurrenceOverrideTests.cs b/tests/RecurringThings.Tests/Domain/OccurrenceOverrideTests.cs index e65ce28..ae09228 100644 --- a/tests/RecurringThings.Tests/Domain/OccurrenceOverrideTests.cs +++ b/tests/RecurringThings.Tests/Domain/OccurrenceOverrideTests.cs @@ -2,7 +2,6 @@ namespace RecurringThings.Tests.Domain; using System; using System.Collections.Generic; -using FluentAssertions; using RecurringThings.Domain; using Xunit; @@ -30,9 +29,9 @@ public void OccurrenceOverride_WhenInitialized_ShouldComputeEndTime() @override.Initialize(startTime, duration); // Assert - @override.StartTime.Should().Be(startTime); - @override.Duration.Should().Be(duration); - @override.EndTime.Should().Be(startTime.Add(duration)); + Assert.Equal(startTime, @override.StartTime); + Assert.Equal(duration, @override.Duration); + Assert.Equal(startTime.Add(duration), @override.EndTime); } [Fact] @@ -58,8 +57,8 @@ public void OccurrenceOverride_WhenStartTimeChanges_ShouldRecomputeEndTime() @override.StartTime = newStartTime; // Assert - @override.StartTime.Should().Be(newStartTime); - @override.EndTime.Should().Be(newStartTime.Add(duration)); + Assert.Equal(newStartTime, @override.StartTime); + Assert.Equal(newStartTime.Add(duration), @override.EndTime); } [Fact] @@ -85,8 +84,8 @@ public void OccurrenceOverride_WhenDurationChanges_ShouldRecomputeEndTime() @override.Duration = newDuration; // Assert - @override.Duration.Should().Be(newDuration); - @override.EndTime.Should().Be(startTime.Add(newDuration)); + Assert.Equal(newDuration, @override.Duration); + Assert.Equal(startTime.Add(newDuration), @override.EndTime); } [Fact] @@ -114,9 +113,9 @@ public void OccurrenceOverride_WhenBothStartTimeAndDurationChange_ShouldRecomput @override.Duration = newDuration; // Assert - @override.StartTime.Should().Be(newStartTime); - @override.Duration.Should().Be(newDuration); - @override.EndTime.Should().Be(newStartTime.Add(newDuration)); + Assert.Equal(newStartTime, @override.StartTime); + Assert.Equal(newDuration, @override.Duration); + Assert.Equal(newStartTime.Add(newDuration), @override.EndTime); } [Fact] @@ -138,8 +137,8 @@ public void OccurrenceOverride_OriginalDuration_ShouldBeDenormalized() @override.Initialize(DateTime.UtcNow, TimeSpan.FromHours(2)); // Assert - OriginalDuration should remain unchanged - @override.OriginalDuration.Should().Be(originalDuration); - @override.Duration.Should().NotBe(originalDuration); + Assert.Equal(originalDuration, @override.OriginalDuration); + Assert.NotEqual(originalDuration, @override.Duration); } [Fact] @@ -167,8 +166,8 @@ public void OccurrenceOverride_OriginalExtensions_ShouldBeDenormalized() @override.Initialize(DateTime.UtcNow, TimeSpan.FromHours(1)); // Assert - OriginalExtensions should remain unchanged - @override.OriginalExtensions.Should().BeEquivalentTo(originalExtensions); - @override.Extensions.Should().NotBeEquivalentTo(originalExtensions); + Assert.Equivalent(originalExtensions, @override.OriginalExtensions); + Assert.NotEqual(originalExtensions, @override.Extensions); } [Fact] @@ -187,7 +186,7 @@ public void OccurrenceOverride_Extensions_ShouldBeNullByDefault() @override.Initialize(DateTime.UtcNow, TimeSpan.FromHours(1)); // Assert - @override.Extensions.Should().BeNull(); + Assert.Null(@override.Extensions); } [Fact] @@ -213,9 +212,9 @@ public void OccurrenceOverride_Extensions_ShouldBeMutable() }; // Assert - @override.Extensions.Should().HaveCount(1); - @override.Extensions.Should().ContainKey("key2"); - @override.Extensions.Should().NotContainKey("key1"); + Assert.Single(@override.Extensions); + Assert.True(@override.Extensions.ContainsKey("key2")); + Assert.False(@override.Extensions.ContainsKey("key1")); } [Fact] @@ -234,7 +233,7 @@ public void OccurrenceOverride_OriginalExtensions_CanBeNull() }; // Assert - @override.OriginalExtensions.Should().BeNull(); + Assert.Null(@override.OriginalExtensions); } [Fact] @@ -253,7 +252,7 @@ public void OccurrenceOverride_WhenCreatedWithEmptyOrganization_ShouldBeValid() @override.Initialize(DateTime.UtcNow, TimeSpan.FromHours(1)); // Assert - @override.Organization.Should().BeEmpty(); + Assert.Empty(@override.Organization); } [Fact] @@ -272,7 +271,7 @@ public void OccurrenceOverride_WhenCreatedWithEmptyResourcePath_ShouldBeValid() @override.Initialize(DateTime.UtcNow, TimeSpan.FromHours(1)); // Assert - @override.ResourcePath.Should().BeEmpty(); + Assert.Empty(@override.ResourcePath); } [Fact] @@ -296,9 +295,9 @@ public void OccurrenceOverride_Initialize_ShouldOverridePreviousValues() @override.Initialize(newStartTime, newDuration); // Assert - @override.StartTime.Should().Be(newStartTime); - @override.Duration.Should().Be(newDuration); - @override.EndTime.Should().Be(newStartTime.Add(newDuration)); + Assert.Equal(newStartTime, @override.StartTime); + Assert.Equal(newDuration, @override.Duration); + Assert.Equal(newStartTime.Add(newDuration), @override.EndTime); } [Fact] @@ -321,9 +320,9 @@ public void OccurrenceOverride_OriginalTimeUtc_CanDifferFromStartTime() @override.Initialize(newStartTime, TimeSpan.FromHours(1)); // Assert - The occurrence was "moved" from 10:00 to 14:00 - @override.OriginalTimeUtc.Should().Be(originalTime); - @override.StartTime.Should().Be(newStartTime); - @override.StartTime.Should().NotBe(@override.OriginalTimeUtc); + Assert.Equal(originalTime, @override.OriginalTimeUtc); + Assert.Equal(newStartTime, @override.StartTime); + Assert.NotEqual(@override.OriginalTimeUtc, @override.StartTime); } [Fact] @@ -354,16 +353,16 @@ public void OccurrenceOverride_WhenCreatedWithAllProperties_ShouldHaveCorrectVal @override.Initialize(newStartTime, newDuration); // Assert - @override.Id.Should().Be(id); - @override.Organization.Should().Be("company"); - @override.ResourcePath.Should().Be("meetings/team-a"); - @override.RecurrenceId.Should().Be(recurrenceId); - @override.OriginalTimeUtc.Should().Be(originalTime); - @override.OriginalDuration.Should().Be(originalDuration); - @override.OriginalExtensions.Should().BeEquivalentTo(originalExtensions); - @override.StartTime.Should().Be(newStartTime); - @override.Duration.Should().Be(newDuration); - @override.EndTime.Should().Be(new DateTime(2026, 6, 15, 12, 0, 0, DateTimeKind.Utc)); - @override.Extensions.Should().BeEquivalentTo(newExtensions); + Assert.Equal(id, @override.Id); + Assert.Equal("company", @override.Organization); + Assert.Equal("meetings/team-a", @override.ResourcePath); + Assert.Equal(recurrenceId, @override.RecurrenceId); + Assert.Equal(originalTime, @override.OriginalTimeUtc); + Assert.Equal(originalDuration, @override.OriginalDuration); + Assert.Equivalent(originalExtensions, @override.OriginalExtensions); + Assert.Equal(newStartTime, @override.StartTime); + Assert.Equal(newDuration, @override.Duration); + Assert.Equal(new DateTime(2026, 6, 15, 12, 0, 0, DateTimeKind.Utc), @override.EndTime); + Assert.Equivalent(newExtensions, @override.Extensions); } } diff --git a/tests/RecurringThings.Tests/Domain/OccurrenceTests.cs b/tests/RecurringThings.Tests/Domain/OccurrenceTests.cs index 2abd142..30fe78a 100644 --- a/tests/RecurringThings.Tests/Domain/OccurrenceTests.cs +++ b/tests/RecurringThings.Tests/Domain/OccurrenceTests.cs @@ -2,7 +2,6 @@ namespace RecurringThings.Tests.Domain; using System; using System.Collections.Generic; -using FluentAssertions; using RecurringThings.Domain; using Xunit; @@ -28,9 +27,9 @@ public void Occurrence_WhenInitialized_ShouldComputeEndTime() occurrence.Initialize(startTime, duration); // Assert - occurrence.StartTime.Should().Be(startTime); - occurrence.Duration.Should().Be(duration); - occurrence.EndTime.Should().Be(startTime.Add(duration)); + Assert.Equal(startTime, occurrence.StartTime); + Assert.Equal(duration, occurrence.Duration); + Assert.Equal(startTime.Add(duration), occurrence.EndTime); } [Fact] @@ -55,8 +54,8 @@ public void Occurrence_WhenStartTimeChanges_ShouldRecomputeEndTime() occurrence.StartTime = newStartTime; // Assert - occurrence.StartTime.Should().Be(newStartTime); - occurrence.EndTime.Should().Be(newStartTime.Add(duration)); + Assert.Equal(newStartTime, occurrence.StartTime); + Assert.Equal(newStartTime.Add(duration), occurrence.EndTime); } [Fact] @@ -81,8 +80,8 @@ public void Occurrence_WhenDurationChanges_ShouldRecomputeEndTime() occurrence.Duration = newDuration; // Assert - occurrence.Duration.Should().Be(newDuration); - occurrence.EndTime.Should().Be(startTime.Add(newDuration)); + Assert.Equal(newDuration, occurrence.Duration); + Assert.Equal(startTime.Add(newDuration), occurrence.EndTime); } [Fact] @@ -109,9 +108,9 @@ public void Occurrence_WhenBothStartTimeAndDurationChange_ShouldRecomputeEndTime occurrence.Duration = newDuration; // Assert - occurrence.StartTime.Should().Be(newStartTime); - occurrence.Duration.Should().Be(newDuration); - occurrence.EndTime.Should().Be(newStartTime.Add(newDuration)); + Assert.Equal(newStartTime, occurrence.StartTime); + Assert.Equal(newDuration, occurrence.Duration); + Assert.Equal(newStartTime.Add(newDuration), occurrence.EndTime); } [Fact] @@ -134,7 +133,7 @@ public void Occurrence_WithZeroDuration_ShouldHaveEqualStartAndEndTime() occurrence.Initialize(startTime, duration); // Assert - occurrence.EndTime.Should().Be(occurrence.StartTime); + Assert.Equal(occurrence.StartTime, occurrence.EndTime); } [Fact] @@ -152,7 +151,7 @@ public void Occurrence_Extensions_ShouldBeNullByDefault() occurrence.Initialize(DateTime.UtcNow, TimeSpan.FromHours(1)); // Assert - occurrence.Extensions.Should().BeNull(); + Assert.Null(occurrence.Extensions); } [Fact] @@ -177,9 +176,9 @@ public void Occurrence_Extensions_ShouldBeMutable() }; // Assert - occurrence.Extensions.Should().HaveCount(1); - occurrence.Extensions.Should().ContainKey("key2"); - occurrence.Extensions.Should().NotContainKey("key1"); + Assert.Single(occurrence.Extensions); + Assert.True(occurrence.Extensions.ContainsKey("key2")); + Assert.False(occurrence.Extensions.ContainsKey("key1")); } [Fact] @@ -208,15 +207,15 @@ public void Occurrence_WhenCreatedWithAllProperties_ShouldHaveCorrectValues() occurrence.Initialize(startTime, duration); // Assert - occurrence.Id.Should().Be(id); - occurrence.Organization.Should().Be("acme-corp"); - occurrence.ResourcePath.Should().Be("team/engineering"); - occurrence.Type.Should().Be("standup"); - occurrence.TimeZone.Should().Be("Europe/London"); - occurrence.StartTime.Should().Be(startTime); - occurrence.Duration.Should().Be(duration); - occurrence.EndTime.Should().Be(new DateTime(2026, 6, 15, 15, 15, 0, DateTimeKind.Utc)); - occurrence.Extensions.Should().BeEquivalentTo(extensions); + Assert.Equal(id, occurrence.Id); + Assert.Equal("acme-corp", occurrence.Organization); + Assert.Equal("team/engineering", occurrence.ResourcePath); + Assert.Equal("standup", occurrence.Type); + Assert.Equal("Europe/London", occurrence.TimeZone); + Assert.Equal(startTime, occurrence.StartTime); + Assert.Equal(duration, occurrence.Duration); + Assert.Equal(new DateTime(2026, 6, 15, 15, 15, 0, DateTimeKind.Utc), occurrence.EndTime); + Assert.Equivalent(extensions, occurrence.Extensions); } [Fact] @@ -234,7 +233,7 @@ public void Occurrence_WhenCreatedWithEmptyOrganization_ShouldBeValid() occurrence.Initialize(DateTime.UtcNow, TimeSpan.FromHours(1)); // Assert - occurrence.Organization.Should().BeEmpty(); + Assert.Empty(occurrence.Organization); } [Fact] @@ -252,7 +251,7 @@ public void Occurrence_WhenCreatedWithEmptyResourcePath_ShouldBeValid() occurrence.Initialize(DateTime.UtcNow, TimeSpan.FromHours(1)); // Assert - occurrence.ResourcePath.Should().BeEmpty(); + Assert.Empty(occurrence.ResourcePath); } [Fact] @@ -275,8 +274,8 @@ public void Occurrence_Initialize_ShouldOverridePreviousValues() occurrence.Initialize(newStartTime, newDuration); // Assert - occurrence.StartTime.Should().Be(newStartTime); - occurrence.Duration.Should().Be(newDuration); - occurrence.EndTime.Should().Be(newStartTime.Add(newDuration)); + Assert.Equal(newStartTime, occurrence.StartTime); + Assert.Equal(newDuration, occurrence.Duration); + Assert.Equal(newStartTime.Add(newDuration), occurrence.EndTime); } } diff --git a/tests/RecurringThings.Tests/Domain/RecurrenceTests.cs b/tests/RecurringThings.Tests/Domain/RecurrenceTests.cs index dee5c22..0176c74 100644 --- a/tests/RecurringThings.Tests/Domain/RecurrenceTests.cs +++ b/tests/RecurringThings.Tests/Domain/RecurrenceTests.cs @@ -2,7 +2,6 @@ namespace RecurringThings.Tests.Domain; using System; using System.Collections.Generic; -using FluentAssertions; using RecurringThings.Domain; using Xunit; @@ -32,15 +31,15 @@ public void Recurrence_WhenCreated_ShouldHaveAllRequiredProperties() }; // Assert - recurrence.Id.Should().Be(id); - recurrence.Organization.Should().Be("org1"); - recurrence.ResourcePath.Should().Be("user/calendar"); - recurrence.Type.Should().Be("appointment"); - recurrence.StartTime.Should().Be(startTime); - recurrence.Duration.Should().Be(duration); - recurrence.RecurrenceEndTime.Should().Be(endTime); - recurrence.RRule.Should().Be("FREQ=DAILY;UNTIL=20261231T235959Z"); - recurrence.TimeZone.Should().Be("America/New_York"); + Assert.Equal(id, recurrence.Id); + Assert.Equal("org1", recurrence.Organization); + Assert.Equal("user/calendar", recurrence.ResourcePath); + Assert.Equal("appointment", recurrence.Type); + Assert.Equal(startTime, recurrence.StartTime); + Assert.Equal(duration, recurrence.Duration); + Assert.Equal(endTime, recurrence.RecurrenceEndTime); + Assert.Equal("FREQ=DAILY;UNTIL=20261231T235959Z", recurrence.RRule); + Assert.Equal("America/New_York", recurrence.TimeZone); } [Fact] @@ -61,7 +60,7 @@ public void Recurrence_WhenCreatedWithEmptyOrganization_ShouldBeValid() }; // Assert - recurrence.Organization.Should().BeEmpty(); + Assert.Empty(recurrence.Organization); } [Fact] @@ -82,7 +81,7 @@ public void Recurrence_WhenCreatedWithEmptyResourcePath_ShouldBeValid() }; // Assert - recurrence.ResourcePath.Should().BeEmpty(); + Assert.Empty(recurrence.ResourcePath); } [Fact] @@ -106,7 +105,7 @@ public void Recurrence_Duration_ShouldBeMutable() recurrence.Duration = TimeSpan.FromHours(2); // Assert - recurrence.Duration.Should().Be(TimeSpan.FromHours(2)); + Assert.Equal(TimeSpan.FromHours(2), recurrence.Duration); } [Fact] @@ -127,7 +126,7 @@ public void Recurrence_Extensions_ShouldBeNullByDefault() }; // Assert - recurrence.Extensions.Should().BeNull(); + Assert.Null(recurrence.Extensions); } [Fact] @@ -156,9 +155,9 @@ public void Recurrence_Extensions_ShouldBeMutable() }; // Assert - recurrence.Extensions.Should().HaveCount(2); - recurrence.Extensions.Should().ContainKey("key2"); - recurrence.Extensions.Should().ContainKey("key3"); + Assert.Equal(2, recurrence.Extensions.Count); + Assert.True(recurrence.Extensions.ContainsKey("key2")); + Assert.True(recurrence.Extensions.ContainsKey("key3")); } [Fact] @@ -183,7 +182,7 @@ public void Recurrence_Extensions_CanBeSetToNull() recurrence.Extensions = null; // Assert - recurrence.Extensions.Should().BeNull(); + Assert.Null(recurrence.Extensions); } [Fact] @@ -213,7 +212,7 @@ public void Recurrence_WhenCreatedWithExtensions_ShouldContainAllPairs() }; // Assert - recurrence.Extensions.Should().BeEquivalentTo(extensions); + Assert.Equivalent(extensions, recurrence.Extensions); } [Fact] @@ -235,6 +234,6 @@ public void Recurrence_Id_ShouldBeInitOnly() }; // Assert - Id is init-only, so we just verify it was set correctly - recurrence.Id.Should().Be(id); + Assert.Equal(id, recurrence.Id); } } diff --git a/tests/RecurringThings.Tests/Engine/RecurrenceEngineCrudTests.cs b/tests/RecurringThings.Tests/Engine/RecurrenceEngineCrudTests.cs index 2fce028..3fc1b2b 100644 --- a/tests/RecurringThings.Tests/Engine/RecurrenceEngineCrudTests.cs +++ b/tests/RecurringThings.Tests/Engine/RecurrenceEngineCrudTests.cs @@ -4,7 +4,6 @@ namespace RecurringThings.Tests.Engine; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; -using FluentAssertions; using Moq; using RecurringThings.Domain; using RecurringThings.Engine; @@ -67,95 +66,83 @@ public async Task CreateRecurrenceAsync_WithValidParameters_ReturnsRecurrenceWit startTime, duration, TestRRule, TestTimeZone, extensions); // Assert - result.Should().NotBeNull(); - result.RecurrenceId.Should().NotBe(Guid.Empty); - result.EntryType.Should().Be(CalendarEntryType.Recurrence); - result.Organization.Should().Be(TestOrganization); - result.ResourcePath.Should().Be(TestResourcePath); - result.Type.Should().Be(TestType); - result.StartTime.Should().Be(startTime); - result.Duration.Should().Be(duration); - result.RecurrenceDetails.Should().NotBeNull(); - result.RecurrenceDetails!.RRule.Should().Be(TestRRule); - result.TimeZone.Should().Be(TestTimeZone); - result.Extensions.Should().BeEquivalentTo(extensions); + Assert.NotNull(result); + Assert.NotEqual(Guid.Empty, result.RecurrenceId); + Assert.Equal(CalendarEntryType.Recurrence, result.EntryType); + Assert.Equal(TestOrganization, result.Organization); + Assert.Equal(TestResourcePath, result.ResourcePath); + Assert.Equal(TestType, result.Type); + Assert.Equal(startTime, result.StartTime); + Assert.Equal(duration, result.Duration); + Assert.NotNull(result.RecurrenceDetails); + Assert.Equal(TestRRule, result.RecurrenceDetails!.RRule); + Assert.Equal(TestTimeZone, result.TimeZone); + Assert.Equivalent(extensions, result.Extensions); } [Fact] public async Task CreateRecurrenceAsync_WithNullOrganization_ThrowsArgumentNullException() { - // Act - var act = async () => await _engine.CreateRecurrenceAsync( + // Act & Assert + await Assert.ThrowsAsync(async () => await _engine.CreateRecurrenceAsync( null!, TestResourcePath, TestType, - DateTime.UtcNow, TimeSpan.FromHours(1), TestRRule, TestTimeZone); - - // Assert - await act.Should().ThrowAsync(); + DateTime.UtcNow, TimeSpan.FromHours(1), TestRRule, TestTimeZone)); } [Fact] public async Task CreateRecurrenceAsync_WithEmptyType_ThrowsArgumentException() { - // Act - var act = async () => await _engine.CreateRecurrenceAsync( + // Act & Assert + var ex = await Assert.ThrowsAsync(async () => await _engine.CreateRecurrenceAsync( TestOrganization, TestResourcePath, "", - DateTime.UtcNow, TimeSpan.FromHours(1), TestRRule, TestTimeZone); + DateTime.UtcNow, TimeSpan.FromHours(1), TestRRule, TestTimeZone)); - // Assert - await act.Should().ThrowAsync() - .WithParameterName("type"); + Assert.Equal("type", ex.ParamName); } [Fact] public async Task CreateRecurrenceAsync_WithRRuleContainingCount_ThrowsArgumentException() { - // Act - var act = async () => await _engine.CreateRecurrenceAsync( + // Act & Assert + var ex = await Assert.ThrowsAsync(async () => await _engine.CreateRecurrenceAsync( TestOrganization, TestResourcePath, TestType, - DateTime.UtcNow, TimeSpan.FromHours(1), "FREQ=DAILY;COUNT=10", TestTimeZone); + DateTime.UtcNow, TimeSpan.FromHours(1), "FREQ=DAILY;COUNT=10", TestTimeZone)); - // Assert - await act.Should().ThrowAsync() - .WithMessage("*COUNT*not supported*"); + Assert.Contains("COUNT", ex.Message); + Assert.Contains("not supported", ex.Message); } [Fact] public async Task CreateRecurrenceAsync_WithRRuleMissingUntil_ThrowsArgumentException() { - // Act - var act = async () => await _engine.CreateRecurrenceAsync( + // Act & Assert + var ex = await Assert.ThrowsAsync(async () => await _engine.CreateRecurrenceAsync( TestOrganization, TestResourcePath, TestType, - DateTime.UtcNow, TimeSpan.FromHours(1), "FREQ=DAILY;BYDAY=MO,TU,WE", TestTimeZone); + DateTime.UtcNow, TimeSpan.FromHours(1), "FREQ=DAILY;BYDAY=MO,TU,WE", TestTimeZone)); - // Assert - await act.Should().ThrowAsync() - .WithMessage("*UNTIL*"); + Assert.Contains("UNTIL", ex.Message); } [Fact] public async Task CreateRecurrenceAsync_WithNonUtcUntil_ThrowsArgumentException() { // Act - Missing Z suffix means non-UTC - var act = async () => await _engine.CreateRecurrenceAsync( + var ex = await Assert.ThrowsAsync(async () => await _engine.CreateRecurrenceAsync( TestOrganization, TestResourcePath, TestType, - DateTime.UtcNow, TimeSpan.FromHours(1), "FREQ=DAILY;UNTIL=20251231T235959", TestTimeZone); + DateTime.UtcNow, TimeSpan.FromHours(1), "FREQ=DAILY;UNTIL=20251231T235959", TestTimeZone)); - // Assert - await act.Should().ThrowAsync() - .WithMessage("*UTC*"); + Assert.Contains("UTC", ex.Message); } [Fact] public async Task CreateRecurrenceAsync_WithInvalidTimeZone_ThrowsArgumentException() { - // Act - var act = async () => await _engine.CreateRecurrenceAsync( + // Act & Assert + var ex = await Assert.ThrowsAsync(async () => await _engine.CreateRecurrenceAsync( TestOrganization, TestResourcePath, TestType, - DateTime.UtcNow, TimeSpan.FromHours(1), TestRRule, "Invalid/TimeZone"); + DateTime.UtcNow, TimeSpan.FromHours(1), TestRRule, "Invalid/TimeZone")); - // Assert - await act.Should().ThrowAsync() - .WithParameterName("timeZone"); + Assert.Equal("timeZone", ex.ParamName); } [Fact] @@ -209,38 +196,33 @@ public async Task CreateOccurrenceAsync_WithValidParameters_ReturnsOccurrenceWit startTime, duration, TestTimeZone); // Assert - result.Should().NotBeNull(); - result.OccurrenceId.Should().NotBe(Guid.Empty); - result.EntryType.Should().Be(CalendarEntryType.Standalone); - result.Organization.Should().Be(TestOrganization); - result.StartTime.Should().Be(startTime); - result.Duration.Should().Be(duration); - result.EndTime.Should().Be(expectedEndTime); + Assert.NotNull(result); + Assert.NotEqual(Guid.Empty, result.OccurrenceId); + Assert.Equal(CalendarEntryType.Standalone, result.EntryType); + Assert.Equal(TestOrganization, result.Organization); + Assert.Equal(startTime, result.StartTime); + Assert.Equal(duration, result.Duration); + Assert.Equal(expectedEndTime, result.EndTime); } [Fact] public async Task CreateOccurrenceAsync_WithNullOrganization_ThrowsArgumentNullException() { - // Act - var act = async () => await _engine.CreateOccurrenceAsync( + // Act & Assert + await Assert.ThrowsAsync(async () => await _engine.CreateOccurrenceAsync( null!, TestResourcePath, TestType, - DateTime.UtcNow, TimeSpan.FromMinutes(30), TestTimeZone); - - // Assert - await act.Should().ThrowAsync(); + DateTime.UtcNow, TimeSpan.FromMinutes(30), TestTimeZone)); } [Fact] public async Task CreateOccurrenceAsync_WithZeroDuration_ThrowsArgumentException() { - // Act - var act = async () => await _engine.CreateOccurrenceAsync( + // Act & Assert + var ex = await Assert.ThrowsAsync(async () => await _engine.CreateOccurrenceAsync( TestOrganization, TestResourcePath, TestType, - DateTime.UtcNow, TimeSpan.Zero, TestTimeZone); + DateTime.UtcNow, TimeSpan.Zero, TestTimeZone)); - // Assert - await act.Should().ThrowAsync() - .WithParameterName("duration"); + Assert.Equal("duration", ex.ParamName); } [Fact] @@ -289,12 +271,10 @@ public async Task UpdateOccurrenceAsync_Recurrence_ThrowsInvalidOperationExcepti EntryType = CalendarEntryType.Recurrence }; - // Act - var act = async () => await _engine.UpdateOccurrenceAsync(entry); + // Act & Assert + var ex = await Assert.ThrowsAsync(async () => await _engine.UpdateOccurrenceAsync(entry)); - // Assert - await act.Should().ThrowAsync() - .WithMessage("*Cannot update a recurrence pattern*"); + Assert.Contains("Cannot update a recurrence pattern", ex.Message); } #endregion @@ -333,9 +313,9 @@ public async Task UpdateAsync_StandaloneOccurrence_UpdatesStartTimeDurationExten var result = await _engine.UpdateOccurrenceAsync(entry); // Assert - result.StartTime.Should().Be(newStartTime); - result.Duration.Should().Be(newDuration); - result.EndTime.Should().Be(newStartTime + newDuration); + Assert.Equal(newStartTime, result.StartTime); + Assert.Equal(newDuration, result.Duration); + Assert.Equal(newStartTime + newDuration, result.EndTime); } [Fact] @@ -369,7 +349,7 @@ public async Task UpdateAsync_StandaloneOccurrenceWithTypeChange_UpdatesTypeSucc var result = await _engine.UpdateOccurrenceAsync(entry); // Assert - result.Type.Should().Be(newType); + Assert.Equal(newType, result.Type); _occurrenceRepo.Verify(r => r.UpdateAsync( It.Is(o => o.Type == newType), null, @@ -421,12 +401,12 @@ public async Task UpdateAsync_VirtualizedOccurrenceWithoutOverride_CreatesOverri var result = await _engine.UpdateOccurrenceAsync(entry); // Assert - capturedOverride.Should().NotBeNull(); - capturedOverride!.RecurrenceId.Should().Be(recurrenceId); - capturedOverride.OriginalTimeUtc.Should().Be(originalStartTime); - capturedOverride.OriginalDuration.Should().Be(recurrence.Duration); - capturedOverride.Duration.Should().Be(newDuration); - result.OverrideId.Should().NotBeNull(); + Assert.NotNull(capturedOverride); + Assert.Equal(recurrenceId, capturedOverride!.RecurrenceId); + Assert.Equal(originalStartTime, capturedOverride.OriginalTimeUtc); + Assert.Equal(recurrence.Duration, capturedOverride.OriginalDuration); + Assert.Equal(newDuration, capturedOverride.Duration); + Assert.NotNull(result.OverrideId); } [Fact] @@ -477,7 +457,7 @@ public async Task UpdateAsync_VirtualizedOccurrenceWithOverride_UpdatesExistingO _overrideRepo.Verify(r => r.UpdateAsync( It.Is(o => o.Duration == newDuration), null, default), Times.Once); - result.Duration.Should().Be(newDuration); + Assert.Equal(newDuration, result.Duration); } #endregion @@ -487,11 +467,8 @@ public async Task UpdateAsync_VirtualizedOccurrenceWithOverride_UpdatesExistingO [Fact] public async Task UpdateAsync_WithNullEntry_ThrowsArgumentNullException() { - // Act - var act = () => _engine.UpdateOccurrenceAsync(null!); - - // Assert - await act.Should().ThrowAsync(); + // Act & Assert + await Assert.ThrowsAsync(() => _engine.UpdateOccurrenceAsync(null!)); } [Fact] @@ -507,12 +484,10 @@ public async Task UpdateAsync_WithIndeterminateEntryType_ThrowsInvalidOperationE // No IDs set }; - // Act - var act = () => _engine.UpdateOccurrenceAsync(entry); + // Act & Assert + var ex = await Assert.ThrowsAsync(() => _engine.UpdateOccurrenceAsync(entry)); - // Assert - await act.Should().ThrowAsync() - .WithMessage("*Cannot determine entry type*"); + Assert.Contains("Cannot determine entry type", ex.Message); } #endregion @@ -534,12 +509,10 @@ public async Task DeleteOccurrenceAsync_Recurrence_ThrowsInvalidOperationExcepti EntryType = CalendarEntryType.Recurrence }; - // Act - var act = async () => await _engine.DeleteOccurrenceAsync(entry); + // Act & Assert + var ex = await Assert.ThrowsAsync(async () => await _engine.DeleteOccurrenceAsync(entry)); - // Assert - await act.Should().ThrowAsync() - .WithMessage("*Use DeleteRecurrenceAsync*"); + Assert.Contains("Use DeleteRecurrenceAsync", ex.Message); } [Fact] @@ -598,9 +571,9 @@ public async Task DeleteAsync_VirtualizedOccurrenceWithoutOverride_CreatesExcept await _engine.DeleteOccurrenceAsync(entry); // Assert - capturedExc.Should().NotBeNull(); - capturedExc!.RecurrenceId.Should().Be(recurrenceId); - capturedExc.OriginalTimeUtc.Should().Be(originalTime); + Assert.NotNull(capturedExc); + Assert.Equal(recurrenceId, capturedExc!.RecurrenceId); + Assert.Equal(originalTime, capturedExc.OriginalTimeUtc); } [Fact] @@ -641,18 +614,15 @@ public async Task DeleteAsync_VirtualizedOccurrenceWithOverride_DeletesOverrideA // Assert _overrideRepo.Verify(r => r.DeleteAsync( overrideId, TestOrganization, TestResourcePath, null, default), Times.Once); - capturedExc.Should().NotBeNull(); - capturedExc!.OriginalTimeUtc.Should().Be(originalTime); // Exception at original time, not moved time + Assert.NotNull(capturedExc); + Assert.Equal(originalTime, capturedExc!.OriginalTimeUtc); // Exception at original time, not moved time } [Fact] public async Task DeleteAsync_WithNullEntry_ThrowsArgumentNullException() { - // Act - var act = () => _engine.DeleteOccurrenceAsync(null!); - - // Assert - await act.Should().ThrowAsync(); + // Act & Assert + await Assert.ThrowsAsync(() => _engine.DeleteOccurrenceAsync(null!)); } #endregion @@ -707,12 +677,10 @@ public async Task RestoreAsync_Recurrence_ThrowsInvalidOperationException() EntryType = CalendarEntryType.Recurrence }; - // Act - var act = async () => await _engine.RestoreAsync(entry); + // Act & Assert + var ex = await Assert.ThrowsAsync(async () => await _engine.RestoreAsync(entry)); - // Assert - await act.Should().ThrowAsync() - .WithMessage("*recurrence pattern*"); + Assert.Contains("recurrence pattern", ex.Message); } [Fact] @@ -730,12 +698,10 @@ public async Task RestoreAsync_StandaloneOccurrence_ThrowsInvalidOperationExcept EntryType = CalendarEntryType.Standalone }; - // Act - var act = async () => await _engine.RestoreAsync(entry); + // Act & Assert + var ex = await Assert.ThrowsAsync(async () => await _engine.RestoreAsync(entry)); - // Assert - await act.Should().ThrowAsync() - .WithMessage("*standalone occurrence*"); + Assert.Contains("standalone occurrence", ex.Message); } [Fact] @@ -760,22 +726,17 @@ public async Task RestoreAsync_VirtualizedOccurrenceWithoutOverride_ThrowsInvali } }; - // Act - var act = () => _engine.RestoreAsync(entry); + // Act & Assert + var ex = await Assert.ThrowsAsync(() => _engine.RestoreAsync(entry)); - // Assert - await act.Should().ThrowAsync() - .WithMessage("*without an override*"); + Assert.Contains("without an override", ex.Message); } [Fact] public async Task RestoreAsync_WithNullEntry_ThrowsArgumentNullException() { - // Act - var act = () => _engine.RestoreAsync(null!); - - // Assert - await act.Should().ThrowAsync(); + // Act & Assert + await Assert.ThrowsAsync(() => _engine.RestoreAsync(null!)); } #endregion diff --git a/tests/RecurringThings.Tests/Engine/RecurrenceEngineTests.cs b/tests/RecurringThings.Tests/Engine/RecurrenceEngineTests.cs index 5a27106..d2c3ced 100644 --- a/tests/RecurringThings.Tests/Engine/RecurrenceEngineTests.cs +++ b/tests/RecurringThings.Tests/Engine/RecurrenceEngineTests.cs @@ -5,7 +5,6 @@ namespace RecurringThings.Tests.Engine; using System.Linq; using System.Threading; using System.Threading.Tasks; -using FluentAssertions; using Moq; using RecurringThings.Domain; using RecurringThings.Engine; @@ -49,53 +48,49 @@ public RecurrenceEngineTests() [Fact] public void Constructor_WithNullRecurrenceRepository_ThrowsArgumentNullException() { - var act = () => new RecurrenceEngine( + var ex = Assert.Throws(() => new RecurrenceEngine( null!, _occurrenceRepo.Object, _exceptionRepo.Object, - _overrideRepo.Object); + _overrideRepo.Object)); - act.Should().Throw() - .WithParameterName("recurrenceRepository"); + Assert.Equal("recurrenceRepository", ex.ParamName); } [Fact] public void Constructor_WithNullOccurrenceRepository_ThrowsArgumentNullException() { - var act = () => new RecurrenceEngine( + var ex = Assert.Throws(() => new RecurrenceEngine( _recurrenceRepo.Object, null!, _exceptionRepo.Object, - _overrideRepo.Object); + _overrideRepo.Object)); - act.Should().Throw() - .WithParameterName("occurrenceRepository"); + Assert.Equal("occurrenceRepository", ex.ParamName); } [Fact] public void Constructor_WithNullExceptionRepository_ThrowsArgumentNullException() { - var act = () => new RecurrenceEngine( + var ex = Assert.Throws(() => new RecurrenceEngine( _recurrenceRepo.Object, _occurrenceRepo.Object, null!, - _overrideRepo.Object); + _overrideRepo.Object)); - act.Should().Throw() - .WithParameterName("exceptionRepository"); + Assert.Equal("exceptionRepository", ex.ParamName); } [Fact] public void Constructor_WithNullOverrideRepository_ThrowsArgumentNullException() { - var act = () => new RecurrenceEngine( + var ex = Assert.Throws(() => new RecurrenceEngine( _recurrenceRepo.Object, _occurrenceRepo.Object, _exceptionRepo.Object, - null!); + null!)); - act.Should().Throw() - .WithParameterName("overrideRepository"); + Assert.Equal("overrideRepository", ex.ParamName); } #endregion @@ -126,15 +121,15 @@ public async Task GetAsync_DailyRecurrence_ReturnsCorrectOccurrences() var results = await GetResultsAsync(queryStart, queryEnd); // Assert - results.Should().HaveCount(5); - results.Should().AllSatisfy(r => + Assert.Equal(5, results.Count); + Assert.All(results, r => { - r.RecurrenceId.Should().Be(recurrenceId); - r.Duration.Should().Be(duration); - r.Type.Should().Be(TestType); - r.TimeZone.Should().Be(TestTimeZone); - r.EntryType.Should().Be(CalendarEntryType.Virtualized); - r.Original.Should().NotBeNull(); + Assert.Equal(recurrenceId, r.RecurrenceId); + Assert.Equal(duration, r.Duration); + Assert.Equal(TestType, r.Type); + Assert.Equal(TestTimeZone, r.TimeZone); + Assert.Equal(CalendarEntryType.Virtualized, r.EntryType); + Assert.NotNull(r.Original); }); } @@ -162,12 +157,14 @@ public async Task GetAsync_WeeklyRecurrence_ReturnsCorrectOccurrences() var results = await GetResultsAsync(queryStart, queryEnd); // Assert - results.Should().HaveCountGreaterThan(0); - results.Should().AllSatisfy(r => + Assert.NotEmpty(results); + Assert.All(results, r => { - r.RecurrenceId.Should().Be(recurrenceId); + Assert.Equal(recurrenceId, r.RecurrenceId); var dayOfWeek = r.StartTime.DayOfWeek; - dayOfWeek.Should().BeOneOf(DayOfWeek.Monday, DayOfWeek.Wednesday, DayOfWeek.Friday); + Assert.True( + dayOfWeek == DayOfWeek.Monday || dayOfWeek == DayOfWeek.Wednesday || dayOfWeek == DayOfWeek.Friday, + $"Expected Monday, Wednesday, or Friday but got {dayOfWeek}"); }); } @@ -195,8 +192,8 @@ public async Task GetAsync_MonthlyRecurrence_ReturnsCorrectOccurrences() var results = await GetResultsAsync(queryStart, queryEnd); // Assert - results.Should().HaveCount(4); // Jan 15, Feb 15, Mar 15, Apr 15 - results.Should().AllSatisfy(r => r.StartTime.Day.Should().Be(15)); + Assert.Equal(4, results.Count); // Jan 15, Feb 15, Mar 15, Apr 15 + Assert.All(results, r => Assert.Equal(15, r.StartTime.Day)); } [Fact] @@ -223,11 +220,11 @@ public async Task GetAsync_YearlyRecurrence_ReturnsCorrectOccurrences() var results = await GetResultsAsync(queryStart, queryEnd); // Assert - results.Should().HaveCount(3); // 2024, 2025, 2026 - results.Should().AllSatisfy(r => + Assert.Equal(3, results.Count); // 2024, 2025, 2026 + Assert.All(results, r => { - r.StartTime.Month.Should().Be(3); - r.StartTime.Day.Should().Be(20); + Assert.Equal(3, r.StartTime.Month); + Assert.Equal(20, r.StartTime.Day); }); } @@ -270,8 +267,8 @@ public async Task GetAsync_WithException_ExcludesExceptedOccurrence() var results = await GetResultsAsync(queryStart, queryEnd); // Assert - results.Should().HaveCount(4); // 5 days - 1 exception = 4 - results.Should().NotContain(r => r.StartTime.Day == 3); + Assert.Equal(4, results.Count); // 5 days - 1 exception = 4 + Assert.DoesNotContain(results, r => r.StartTime.Day == 3); } [Fact] @@ -319,9 +316,9 @@ public async Task GetAsync_WithMultipleExceptions_ExcludesAllExceptedOccurrences var results = await GetResultsAsync(queryStart, queryEnd); // Assert - results.Should().HaveCount(5); // 7 - 2 = 5 - results.Should().NotContain(r => r.StartTime.Day == 2); - results.Should().NotContain(r => r.StartTime.Day == 5); + Assert.Equal(5, results.Count); // 7 - 2 = 5 + Assert.DoesNotContain(results, r => r.StartTime.Day == 2); + Assert.DoesNotContain(results, r => r.StartTime.Day == 5); } #endregion @@ -369,15 +366,15 @@ public async Task GetAsync_WithOverride_ReturnsOverriddenValues() var results = await GetResultsAsync(queryStart, queryEnd); // Assert - results.Should().HaveCount(5); + Assert.Equal(5, results.Count); var overriddenEntry = results.Single(r => r.OverrideId == overrideId); - overriddenEntry.StartTime.Hour.Should().Be(14); - overriddenEntry.Duration.Should().Be(TimeSpan.FromHours(2)); - overriddenEntry.Extensions.Should().ContainKey("modified"); - overriddenEntry.Original.Should().NotBeNull(); - overriddenEntry.Original!.StartTime.Should().Be(new DateTime(2024, 1, 3, 9, 0, 0, DateTimeKind.Utc)); - overriddenEntry.Original!.Duration.Should().Be(duration); + Assert.Equal(14, overriddenEntry.StartTime.Hour); + Assert.Equal(TimeSpan.FromHours(2), overriddenEntry.Duration); + Assert.True(overriddenEntry.Extensions?.ContainsKey("modified") == true); + Assert.NotNull(overriddenEntry.Original); + Assert.Equal(new DateTime(2024, 1, 3, 9, 0, 0, DateTimeKind.Utc), overriddenEntry.Original!.StartTime); + Assert.Equal(duration, overriddenEntry.Original!.Duration); } [Fact] @@ -421,8 +418,8 @@ public async Task GetAsync_WithOverrideMovedOutsideRange_ExcludesOverriddenOccur // Assert // Jan 1-5 = 5 occurrences, but Jan 3 was moved out, so 4 remain - results.Should().HaveCount(4); - results.Should().NotContain(r => r.StartTime.Day == 3); + Assert.Equal(4, results.Count); + Assert.DoesNotContain(results, r => r.StartTime.Day == 3); } [Fact] @@ -466,8 +463,8 @@ public async Task GetAsync_WithOverrideMovedIntoRange_IncludesOverriddenOccurren // Assert // Jan 1-5 = 5 regular occurrences + 1 moved-in override = 6 total - results.Should().HaveCount(6); - results.Should().Contain(r => r.OverrideId == overrideId); + Assert.Equal(6, results.Count); + Assert.Contains(results, r => r.OverrideId == overrideId); } #endregion @@ -512,12 +509,12 @@ public async Task GetAsync_WithStandaloneOccurrences_MergesWithVirtualized() var results = await GetResultsAsync(queryStart, queryEnd); // Assert - results.Should().HaveCount(4); // 3 virtualized + 1 standalone + Assert.Equal(4, results.Count); // 3 virtualized + 1 standalone var standaloneEntry = results.Single(r => r.OccurrenceId == occurrenceId); - standaloneEntry.RecurrenceId.Should().BeNull(); - standaloneEntry.EntryType.Should().Be(CalendarEntryType.Standalone); - standaloneEntry.StartTime.Hour.Should().Be(15); + Assert.Null(standaloneEntry.RecurrenceId); + Assert.Equal(CalendarEntryType.Standalone, standaloneEntry.EntryType); + Assert.Equal(15, standaloneEntry.StartTime.Hour); } #endregion @@ -555,7 +552,7 @@ public async Task GetAsync_WithTypeFilter_ReturnsOnlyMatchingTypes() var results = await GetResultsAsync(queryStart, queryEnd, types: ["appointment"]); // Assert - results.Should().AllSatisfy(r => r.Type.Should().Be("appointment")); + Assert.All(results, r => Assert.Equal("appointment", r.Type)); } [Fact] @@ -587,8 +584,8 @@ public async Task GetAsync_WithNullTypeFilter_ReturnsAllTypes() var results = await GetResultsAsync(queryStart, queryEnd, types: null); // Assert - results.Should().Contain(r => r.Type == "appointment"); - results.Should().Contain(r => r.Type == "meeting"); + Assert.Contains(results, r => r.Type == "appointment"); + Assert.Contains(results, r => r.Type == "meeting"); } [Fact] @@ -598,11 +595,8 @@ public async Task GetAsync_WithEmptyTypeFilter_ThrowsArgumentException() var queryStart = new DateTime(2024, 1, 1, 0, 0, 0, DateTimeKind.Utc); var queryEnd = new DateTime(2024, 1, 5, 23, 59, 59, DateTimeKind.Utc); - // Act - var act = async () => await GetResultsAsync(queryStart, queryEnd, types: []); - - // Assert - await act.Should().ThrowAsync(); + // Act & Assert + await Assert.ThrowsAsync(async () => await GetResultsAsync(queryStart, queryEnd, types: [])); } #endregion @@ -634,8 +628,8 @@ public async Task GetAsync_OccurrenceAtExactQueryStart_IsIncluded() var results = await GetResultsAsync(queryStart, queryEnd); // Assert - results.Should().HaveCount(1); - results[0].StartTime.Should().Be(queryStart); + Assert.Single(results); + Assert.Equal(queryStart, results[0].StartTime); } [Fact] @@ -662,8 +656,8 @@ public async Task GetAsync_OccurrenceAtExactQueryEnd_IsIncluded() var results = await GetResultsAsync(queryStart, queryEnd); // Assert - results.Should().HaveCount(1); - results[0].StartTime.Should().Be(startTime); // Occurrence at 23:59:00 is within the query range + Assert.Single(results); + Assert.Equal(startTime, results[0].StartTime); // Occurrence at 23:59:00 is within the query range } [Fact] @@ -691,7 +685,7 @@ public async Task GetAsync_RecurrenceEndTimeFiltering_ExcludesOccurrencesBeyondE var results = await GetResultsAsync(queryStart, queryEnd); // Assert - results.Should().HaveCount(3); // Only Jan 1-3 due to RecurrenceEndTime + Assert.Equal(3, results.Count); // Only Jan 1-3 due to RecurrenceEndTime } #endregion @@ -709,7 +703,7 @@ public async Task GetAsync_NoRecurrencesOrOccurrences_ReturnsEmpty() var results = await GetResultsAsync(queryStart, queryEnd); // Assert - results.Should().BeEmpty(); + Assert.Empty(results); } [Fact] @@ -732,7 +726,7 @@ public async Task GetAsync_RecurrenceOutsideRange_ReturnsEmpty() var results = await GetResultsAsync(queryStart, queryEnd); // Assert - results.Should().BeEmpty(); + Assert.Empty(results); } #endregion @@ -765,28 +759,28 @@ public async Task GetAsync_VirtualizedOccurrence_HasCorrectStructure() var results = await GetResultsAsync(queryStart, queryEnd); // Assert - results.Should().HaveCount(1); + Assert.Single(results); var entry = results[0]; - entry.Organization.Should().Be(TestOrganization); - entry.ResourcePath.Should().Be(TestResourcePath); - entry.Type.Should().Be(TestType); - entry.StartTime.Should().Be(startTime); - entry.EndTime.Should().Be(startTime + duration); - entry.Duration.Should().Be(duration); - entry.TimeZone.Should().Be(TestTimeZone); - entry.Extensions.Should().BeEquivalentTo(extensions); - entry.RecurrenceId.Should().Be(recurrenceId); - entry.OccurrenceId.Should().BeNull(); - entry.OverrideId.Should().BeNull(); - entry.ExceptionId.Should().BeNull(); - entry.EntryType.Should().Be(CalendarEntryType.Virtualized); - entry.RecurrenceDetails.Should().NotBeNull(); - entry.RecurrenceDetails!.RRule.Should().NotBeNullOrEmpty(); - entry.Original.Should().NotBeNull(); - entry.Original!.StartTime.Should().Be(startTime); - entry.Original.Duration.Should().Be(duration); - entry.Original.Extensions.Should().BeEquivalentTo(extensions); + Assert.Equal(TestOrganization, entry.Organization); + Assert.Equal(TestResourcePath, entry.ResourcePath); + Assert.Equal(TestType, entry.Type); + Assert.Equal(startTime, entry.StartTime); + Assert.Equal(startTime + duration, entry.EndTime); + Assert.Equal(duration, entry.Duration); + Assert.Equal(TestTimeZone, entry.TimeZone); + Assert.Equivalent(extensions, entry.Extensions); + Assert.Equal(recurrenceId, entry.RecurrenceId); + Assert.Null(entry.OccurrenceId); + Assert.Null(entry.OverrideId); + Assert.Null(entry.ExceptionId); + Assert.Equal(CalendarEntryType.Virtualized, entry.EntryType); + Assert.NotNull(entry.RecurrenceDetails); + Assert.False(string.IsNullOrEmpty(entry.RecurrenceDetails!.RRule)); + Assert.NotNull(entry.Original); + Assert.Equal(startTime, entry.Original!.StartTime); + Assert.Equal(duration, entry.Original.Duration); + Assert.Equivalent(extensions, entry.Original.Extensions); } #endregion diff --git a/tests/RecurringThings.Tests/RecurringThings.Tests.csproj b/tests/RecurringThings.Tests/RecurringThings.Tests.csproj index 96976cc..0a381cc 100644 --- a/tests/RecurringThings.Tests/RecurringThings.Tests.csproj +++ b/tests/RecurringThings.Tests/RecurringThings.Tests.csproj @@ -9,7 +9,6 @@ - diff --git a/tests/RecurringThings.Tests/Validation/ValidatorTests.cs b/tests/RecurringThings.Tests/Validation/ValidatorTests.cs index d547124..1b5a45c 100644 --- a/tests/RecurringThings.Tests/Validation/ValidatorTests.cs +++ b/tests/RecurringThings.Tests/Validation/ValidatorTests.cs @@ -1,7 +1,6 @@ namespace RecurringThings.Tests.Validation; using System; -using FluentAssertions; using RecurringThings.Domain; using RecurringThings.Validation; using Xunit; @@ -20,9 +19,8 @@ public class ValidatorTests [Fact] public void ValidateTypesFilter_WhenNull_ShouldNotThrow() { - // Act & Assert - var action = () => Validator.ValidateTypesFilter(null); - action.Should().NotThrow(); + // Act & Assert - no exception means success + Validator.ValidateTypesFilter(null); } [Fact] @@ -31,9 +29,8 @@ public void ValidateTypesFilter_WhenPopulated_ShouldNotThrow() // Arrange var types = new[] { "appointment", "meeting" }; - // Act & Assert - var action = () => Validator.ValidateTypesFilter(types); - action.Should().NotThrow(); + // Act & Assert - no exception means success + Validator.ValidateTypesFilter(types); } [Fact] @@ -43,9 +40,9 @@ public void ValidateTypesFilter_WhenEmptyArray_ShouldThrowArgumentException() var types = Array.Empty(); // Act & Assert - var action = () => Validator.ValidateTypesFilter(types); - action.Should().Throw() - .WithMessage("*cannot be an empty array*Use null to include all types*"); + var ex = Assert.Throws(() => Validator.ValidateTypesFilter(types)); + Assert.Contains("cannot be an empty array", ex.Message); + Assert.Contains("Use null to include all types", ex.Message); } #endregion @@ -69,9 +66,8 @@ public void ValidateTenantScope_WhenMatching_ShouldNotThrow() TimeZone = "UTC" }; - // Act & Assert - var action = () => Validator.ValidateTenantScope(recurrence, "org1", "path1"); - action.Should().NotThrow(); + // Act & Assert - no exception means success + Validator.ValidateTenantScope(recurrence, "org1", "path1"); } [Fact] @@ -92,9 +88,10 @@ public void ValidateTenantScope_WhenOrganizationMismatch_ShouldThrowInvalidOpera }; // Act & Assert - var action = () => Validator.ValidateTenantScope(recurrence, "org2", "path1"); - action.Should().Throw() - .WithMessage("*Organization mismatch*must match parent recurrence*"); + var ex = Assert.Throws(() => + Validator.ValidateTenantScope(recurrence, "org2", "path1")); + Assert.Contains("Organization mismatch", ex.Message); + Assert.Contains("must match parent recurrence", ex.Message); } [Fact] @@ -115,17 +112,18 @@ public void ValidateTenantScope_WhenResourcePathMismatch_ShouldThrowInvalidOpera }; // Act & Assert - var action = () => Validator.ValidateTenantScope(recurrence, "org1", "path2"); - action.Should().Throw() - .WithMessage("*ResourcePath mismatch*must match parent recurrence*"); + var ex = Assert.Throws(() => + Validator.ValidateTenantScope(recurrence, "org1", "path2")); + Assert.Contains("ResourcePath mismatch", ex.Message); + Assert.Contains("must match parent recurrence", ex.Message); } [Fact] public void ValidateTenantScope_WhenParentRecurrenceNull_ShouldThrowArgumentNullException() { // Act & Assert - var action = () => Validator.ValidateTenantScope(null!, "org", "path"); - action.Should().Throw(); + Assert.Throws(() => + Validator.ValidateTenantScope(null!, "org", "path")); } #endregion diff --git a/tests/RecurringThings.Tests/Validation/Validators/OccurrenceExceptionValidatorTests.cs b/tests/RecurringThings.Tests/Validation/Validators/OccurrenceExceptionValidatorTests.cs index 7de0daa..e94215f 100644 --- a/tests/RecurringThings.Tests/Validation/Validators/OccurrenceExceptionValidatorTests.cs +++ b/tests/RecurringThings.Tests/Validation/Validators/OccurrenceExceptionValidatorTests.cs @@ -2,7 +2,6 @@ namespace RecurringThings.Tests.Validation.Validators; using System; using System.Collections.Generic; -using FluentAssertions; using FluentValidation.TestHelper; using RecurringThings.Domain; using RecurringThings.Validation.Validators; @@ -75,7 +74,7 @@ public void Validate_WhenOrganizationExceedsMaxLength_ShouldHaveError() // Assert result.ShouldHaveValidationErrorFor(x => x.Organization); - result.Errors.Should().Contain(e => e.PropertyName == "Organization" && e.ErrorMessage.Contains("must not exceed 100 characters")); + Assert.Contains(result.Errors, e => e.PropertyName == "Organization" && e.ErrorMessage.Contains("must not exceed 100 characters")); } [Fact] @@ -96,7 +95,7 @@ public void Validate_WhenOriginalTimeUtcNotUtc_ShouldHaveError() // Assert result.ShouldHaveValidationErrorFor(x => x.OriginalTimeUtc); - result.Errors.Should().Contain(e => e.PropertyName == "OriginalTimeUtc" && e.ErrorMessage.Contains("must be in UTC")); + Assert.Contains(result.Errors, e => e.PropertyName == "OriginalTimeUtc" && e.ErrorMessage.Contains("must be in UTC")); } [Fact] diff --git a/tests/RecurringThings.Tests/Validation/Validators/OccurrenceOverrideValidatorTests.cs b/tests/RecurringThings.Tests/Validation/Validators/OccurrenceOverrideValidatorTests.cs index a3b23d4..d208d42 100644 --- a/tests/RecurringThings.Tests/Validation/Validators/OccurrenceOverrideValidatorTests.cs +++ b/tests/RecurringThings.Tests/Validation/Validators/OccurrenceOverrideValidatorTests.cs @@ -2,7 +2,6 @@ namespace RecurringThings.Tests.Validation.Validators; using System; using System.Collections.Generic; -using FluentAssertions; using FluentValidation.TestHelper; using RecurringThings.Domain; using RecurringThings.Validation.Validators; @@ -143,7 +142,7 @@ public void Validate_WhenOrganizationExceedsMaxLength_ShouldHaveError() // Assert result.ShouldHaveValidationErrorFor(x => x.Organization); - result.Errors.Should().Contain(e => e.PropertyName == "Organization" && e.ErrorMessage.Contains("must not exceed 100 characters")); + Assert.Contains(result.Errors, e => e.PropertyName == "Organization" && e.ErrorMessage.Contains("must not exceed 100 characters")); } #endregion @@ -214,7 +213,7 @@ public void Validate_WhenResourcePathExceedsMaxLength_ShouldHaveError() // Assert result.ShouldHaveValidationErrorFor(x => x.ResourcePath); - result.Errors.Should().Contain(e => e.PropertyName == "ResourcePath" && e.ErrorMessage.Contains("must not exceed 100 characters")); + Assert.Contains(result.Errors, e => e.PropertyName == "ResourcePath" && e.ErrorMessage.Contains("must not exceed 100 characters")); } #endregion @@ -241,7 +240,7 @@ public void Validate_WhenOriginalTimeUtcNotUtc_ShouldHaveError() // Assert result.ShouldHaveValidationErrorFor(x => x.OriginalTimeUtc); - result.Errors.Should().Contain(e => e.PropertyName == "OriginalTimeUtc" && e.ErrorMessage.Contains("must be in UTC")); + Assert.Contains(result.Errors, e => e.PropertyName == "OriginalTimeUtc" && e.ErrorMessage.Contains("must be in UTC")); } [Fact] @@ -281,7 +280,7 @@ public void Validate_WhenStartTimeNotUtc_ShouldHaveError() // Assert result.ShouldHaveValidationErrorFor(x => x.StartTime); - result.Errors.Should().Contain(e => e.PropertyName == "StartTime" && e.ErrorMessage.Contains("must be in UTC")); + Assert.Contains(result.Errors, e => e.PropertyName == "StartTime" && e.ErrorMessage.Contains("must be in UTC")); } [Fact] @@ -321,7 +320,7 @@ public void Validate_WhenDurationZero_ShouldHaveError() // Assert result.ShouldHaveValidationErrorFor(x => x.Duration); - result.Errors.Should().Contain(e => e.PropertyName == "Duration" && e.ErrorMessage.Contains("must be positive")); + Assert.Contains(result.Errors, e => e.PropertyName == "Duration" && e.ErrorMessage.Contains("must be positive")); } [Fact] @@ -344,7 +343,7 @@ public void Validate_WhenDurationNegative_ShouldHaveError() // Assert result.ShouldHaveValidationErrorFor(x => x.Duration); - result.Errors.Should().Contain(e => e.PropertyName == "Duration" && e.ErrorMessage.Contains("must be positive")); + Assert.Contains(result.Errors, e => e.PropertyName == "Duration" && e.ErrorMessage.Contains("must be positive")); } [Fact] @@ -411,7 +410,7 @@ public void Validate_WhenExtensionKeyEmpty_ShouldHaveError() // Assert result.ShouldHaveValidationErrorFor(x => x.Extensions); - result.Errors.Should().Contain(e => e.PropertyName == "Extensions" && e.ErrorMessage.Contains("must be at least 1 character")); + Assert.Contains(result.Errors, e => e.PropertyName == "Extensions" && e.ErrorMessage.Contains("must be at least 1 character")); } [Fact] @@ -438,7 +437,7 @@ [new string('a', 101)] = "value" // Assert result.ShouldHaveValidationErrorFor(x => x.Extensions); - result.Errors.Should().Contain(e => e.PropertyName == "Extensions" && e.ErrorMessage.Contains("must not exceed 100 characters")); + Assert.Contains(result.Errors, e => e.PropertyName == "Extensions" && e.ErrorMessage.Contains("must not exceed 100 characters")); } [Fact] @@ -465,7 +464,7 @@ public void Validate_WhenExtensionValueExceedsMaxLength_ShouldHaveError() // Assert result.ShouldHaveValidationErrorFor(x => x.Extensions); - result.Errors.Should().Contain(e => e.PropertyName == "Extensions" && e.ErrorMessage.Contains("must not exceed 1024 characters")); + Assert.Contains(result.Errors, e => e.PropertyName == "Extensions" && e.ErrorMessage.Contains("must not exceed 1024 characters")); } #endregion @@ -519,7 +518,7 @@ public void Validate_WhenOriginalExtensionKeyEmpty_ShouldHaveError() // Assert result.ShouldHaveValidationErrorFor(x => x.OriginalExtensions); - result.Errors.Should().Contain(e => e.PropertyName == "OriginalExtensions" && e.ErrorMessage.Contains("must be at least 1 character")); + Assert.Contains(result.Errors, e => e.PropertyName == "OriginalExtensions" && e.ErrorMessage.Contains("must be at least 1 character")); } [Fact] @@ -546,7 +545,7 @@ [new string('a', 101)] = "value" // Assert result.ShouldHaveValidationErrorFor(x => x.OriginalExtensions); - result.Errors.Should().Contain(e => e.PropertyName == "OriginalExtensions" && e.ErrorMessage.Contains("must not exceed 100 characters")); + Assert.Contains(result.Errors, e => e.PropertyName == "OriginalExtensions" && e.ErrorMessage.Contains("must not exceed 100 characters")); } [Fact] @@ -573,7 +572,7 @@ public void Validate_WhenOriginalExtensionValueExceedsMaxLength_ShouldHaveError( // Assert result.ShouldHaveValidationErrorFor(x => x.OriginalExtensions); - result.Errors.Should().Contain(e => e.PropertyName == "OriginalExtensions" && e.ErrorMessage.Contains("must not exceed 1024 characters")); + Assert.Contains(result.Errors, e => e.PropertyName == "OriginalExtensions" && e.ErrorMessage.Contains("must not exceed 1024 characters")); } #endregion