diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index 8b10e80..4560f06 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -62,7 +62,7 @@ jobs: failed=0 for tfm in "${frameworks[@]}"; do echo "::group::Testing $tfm" - if ! dotnet test src/ByteAether.Ulid.slnx --configuration CI-Debug -p:ImplTargetFramework=$tfm --verbosity normal --logger "trx;LogFileName=results-$tfm.trx"; then + if ! (cd src && dotnet test ByteAether.Ulid.slnx --configuration CI-Debug -p:ImplTargetFramework=$tfm --report-trx --report-trx-filename "results-$tfm.trx"); then echo "❌ Tests failed for $tfm" failed=1 fi diff --git a/src/Dapper.IntegrationTests/Dapper.IntegrationTests.csproj b/src/Dapper.IntegrationTests/Dapper.IntegrationTests.csproj index 5473d3c..9decdd7 100644 --- a/src/Dapper.IntegrationTests/Dapper.IntegrationTests.csproj +++ b/src/Dapper.IntegrationTests/Dapper.IntegrationTests.csproj @@ -11,7 +11,7 @@ - + diff --git a/src/Dapper/Dapper.Impl.csproj b/src/Dapper/Dapper.Impl.csproj index e87c102..f1a58c2 100644 --- a/src/Dapper/Dapper.Impl.csproj +++ b/src/Dapper/Dapper.Impl.csproj @@ -30,11 +30,11 @@ - + - + diff --git a/src/Directory.Build.props b/src/Directory.Build.props index 4eda8cb..61464e5 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -50,13 +50,14 @@ - - + + + all runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/src/Directory.Packages.props b/src/Directory.Packages.props new file mode 100644 index 0000000..deffec2 --- /dev/null +++ b/src/Directory.Packages.props @@ -0,0 +1,19 @@ + + + true + + + + + + + + + + + + + + + + diff --git a/src/EFCore.IntegrationTests/EFCore.IntegrationTests.csproj b/src/EFCore.IntegrationTests/EFCore.IntegrationTests.csproj index 7e87269..4e74a9d 100644 --- a/src/EFCore.IntegrationTests/EFCore.IntegrationTests.csproj +++ b/src/EFCore.IntegrationTests/EFCore.IntegrationTests.csproj @@ -11,11 +11,11 @@ - - - - - + + + + + diff --git a/src/EFCore.IntegrationTests/UlidEntityFrameworkIntegrationTests.cs b/src/EFCore.IntegrationTests/UlidEntityFrameworkIntegrationTests.cs index cd65c01..1cdd31b 100644 --- a/src/EFCore.IntegrationTests/UlidEntityFrameworkIntegrationTests.cs +++ b/src/EFCore.IntegrationTests/UlidEntityFrameworkIntegrationTests.cs @@ -51,16 +51,16 @@ public async Task EFCore_ShouldSuccessfullyRoundTrip_BothNonNullAndNullableUlids await using (var writeContext = CreateContext(format)) { writeContext.TestEntities.Add(entity); - await writeContext.SaveChangesAsync(); + await writeContext.SaveChangesAsync(TestContext.Current.CancellationToken); } // Act - Step 2: Read back from an isolated context instance & Query via Null comparison await using (var readContext = CreateContext(format)) { - var dbEntity = await readContext.TestEntities.FirstOrDefaultAsync(e => e.SystemUlid == originalUlid); + var dbEntity = await readContext.TestEntities.FirstOrDefaultAsync(e => e.SystemUlid == originalUlid, cancellationToken: TestContext.Current.CancellationToken); // Verify querying directly by matching a null column works flawlessly - var nullQueryEntity = await readContext.TestEntities.FirstOrDefaultAsync(e => e.NullableUlid == null); + var nullQueryEntity = await readContext.TestEntities.FirstOrDefaultAsync(e => e.NullableUlid == null, cancellationToken: TestContext.Current.CancellationToken); // Assert Assert.NotNull(dbEntity); @@ -72,13 +72,13 @@ public async Task EFCore_ShouldSuccessfullyRoundTrip_BothNonNullAndNullableUlids var updatedUlid = Ulid.New(); dbEntity.NullableUlid = updatedUlid; readContext.TestEntities.Update(dbEntity); - await readContext.SaveChangesAsync(); + await readContext.SaveChangesAsync(TestContext.Current.CancellationToken); } // Act - Step 4: Validate update retrieval await using (var verifyContext = CreateContext(format)) { - var dbEntity = await verifyContext.TestEntities.FirstOrDefaultAsync(); + var dbEntity = await verifyContext.TestEntities.FirstOrDefaultAsync(cancellationToken: TestContext.Current.CancellationToken); Assert.NotNull(dbEntity); Assert.Equal(originalUlid, dbEntity.SystemUlid); Assert.NotNull(dbEntity.NullableUlid); @@ -86,13 +86,13 @@ public async Task EFCore_ShouldSuccessfullyRoundTrip_BothNonNullAndNullableUlids // Step 5: Test reversing a value back to null (Null-to-Null round trip) dbEntity.NullableUlid = null; verifyContext.TestEntities.Update(dbEntity); - await verifyContext.SaveChangesAsync(); + await verifyContext.SaveChangesAsync(TestContext.Current.CancellationToken); } // Act - Step 6: Final check that reverting to null persisted properly await using (var finalVerifyContext = CreateContext(format)) { - var dbEntity = await finalVerifyContext.TestEntities.FirstOrDefaultAsync(); + var dbEntity = await finalVerifyContext.TestEntities.FirstOrDefaultAsync(cancellationToken: TestContext.Current.CancellationToken); Assert.NotNull(dbEntity); Assert.Null(dbEntity.NullableUlid); } @@ -111,12 +111,12 @@ public async Task EFCore_ShouldTranslateLINQRangeQueries_ProperlyWithParameters( var maxUlid = Ulid.MaxAt(DateTimeOffset.UtcNow.AddDays(1)); context.TestEntities.Add(new(){ SystemUlid = targetUlid }); - await context.SaveChangesAsync(); + await context.SaveChangesAsync(TestContext.Current.CancellationToken); // Act - Evaluate if EF translation properly maps parameter types to database lookups var results = await context.TestEntities .Where(e => e.SystemUlid >= minUlid && e.SystemUlid <= maxUlid) - .ToListAsync(); + .ToListAsync(cancellationToken: TestContext.Current.CancellationToken); // Assert Assert.Single(results); @@ -135,18 +135,18 @@ public async Task EFCore_ShouldSuccessfullyProject_UlidToAnonymousAndDtoTypes(Ul var targetUlid = Ulid.New(); context.TestEntities.Add(new() { SystemUlid = targetUlid, NullableUlid = Ulid.New() }); - await context.SaveChangesAsync(); + await context.SaveChangesAsync(TestContext.Current.CancellationToken); context.ChangeTracker.Clear(); // Act - Step 1: Project into an anonymous type var anonymousResult = await context.TestEntities .Select(e => new { e.Id, e.SystemUlid, e.NullableUlid }) - .FirstOrDefaultAsync(e => e.SystemUlid == targetUlid); + .FirstOrDefaultAsync(e => e.SystemUlid == targetUlid, cancellationToken: TestContext.Current.CancellationToken); // Act - Step 2: Project directly into a raw primitive/value type collection var rawUlidList = await context.TestEntities .Select(e => e.SystemUlid) - .ToListAsync(); + .ToListAsync(cancellationToken: TestContext.Current.CancellationToken); // Assert Assert.NotNull(anonymousResult); @@ -172,7 +172,7 @@ public async Task EFCore_ShouldTranslateContainsQuery_WhenUsingUlidCollections(U new TestEntity { SystemUlid = Ulid.New(), NullableUlid = targetUlid }, new TestEntity { SystemUlid = Ulid.New(), NullableUlid = null } ); - await context.SaveChangesAsync(); + await context.SaveChangesAsync(TestContext.Current.CancellationToken); context.ChangeTracker.Clear(); // Strategy: Include null directly inside the searchable target criteria collection @@ -181,7 +181,7 @@ public async Task EFCore_ShouldTranslateContainsQuery_WhenUsingUlidCollections(U // Act var results = await context.TestEntities .Where(e => searchCriteria.Contains(e.NullableUlid)) - .ToListAsync(); + .ToListAsync(cancellationToken: TestContext.Current.CancellationToken); // Assert Assert.Equal(2, results.Count); @@ -231,10 +231,10 @@ public async Task SchemaCreation_ShouldGenerateCorrectDatabaseColumnTypes(UlidSt await using var command = _connection.CreateCommand(); command.CommandText = "PRAGMA table_info(TestEntities);"; - await using var reader = await command.ExecuteReaderAsync(); + await using var reader = await command.ExecuteReaderAsync(TestContext.Current.CancellationToken); var foundSystemUlid = false; - while (await reader.ReadAsync()) + while (await reader.ReadAsync(TestContext.Current.CancellationToken)) { var columnName = reader.GetString(reader.GetOrdinal("name")); if (columnName != nameof(TestEntity.SystemUlid)) @@ -261,9 +261,9 @@ public async Task EFCore_ShouldMaintainChronologicalOrder_WhenOrderingByUlid(Uli await using var context = CreateContext(format); var first = Ulid.New(); - await Task.Delay(10); // Ensure timestamp progression if relying on machine clock + await Task.Delay(10, TestContext.Current.CancellationToken); // Ensure timestamp progression if relying on machine clock var second = Ulid.New(); - await Task.Delay(10); + await Task.Delay(10, TestContext.Current.CancellationToken); var third = Ulid.New(); // Add them completely out of order @@ -272,10 +272,10 @@ public async Task EFCore_ShouldMaintainChronologicalOrder_WhenOrderingByUlid(Uli new TestEntity { SystemUlid = third }, new TestEntity { SystemUlid = first } ); - await context.SaveChangesAsync(); + await context.SaveChangesAsync(TestContext.Current.CancellationToken); // Act - var orderedList = await context.TestEntities.OrderBy(e => e.SystemUlid).ToListAsync(); + var orderedList = await context.TestEntities.OrderBy(e => e.SystemUlid).ToListAsync(cancellationToken: TestContext.Current.CancellationToken); // Assert Assert.Equal(3, orderedList.Count); @@ -305,7 +305,7 @@ public async Task EFCore_ShouldSuccessfullyExecuteJoins_OnUlidProperties(UlidSto context.TestEntities.Add(parentEntity); context.Set().Add(childEntity); - await context.SaveChangesAsync(); + await context.SaveChangesAsync(TestContext.Current.CancellationToken); context.ChangeTracker.Clear(); // Act - Explicit inner join execution over ULID properties @@ -316,7 +316,7 @@ public async Task EFCore_ShouldSuccessfullyExecuteJoins_OnUlidProperties(UlidSto child => child.ParentSystemUlid, (parent, child) => new { parent.Id, parent.SystemUlid, child.Description } ) - .FirstOrDefaultAsync(x => x.SystemUlid == parentUlid); + .FirstOrDefaultAsync(x => x.SystemUlid == parentUlid, cancellationToken: TestContext.Current.CancellationToken); // Assert Assert.NotNull(joinResult); @@ -336,18 +336,18 @@ public async Task EFCore_ShouldNotDetectFalseChanges_WhenEntityIsLoadedButUnmodi await using (var setupContext = CreateContext(format)) { setupContext.TestEntities.Add(new() { SystemUlid = originalUlid, NullableUlid = Ulid.New() }); - await setupContext.SaveChangesAsync(); + await setupContext.SaveChangesAsync(TestContext.Current.CancellationToken); } // Act & Assert await using (var trackingContext = CreateContext(format)) { // Load the entity completely into memory state tracking - var dbEntity = await trackingContext.TestEntities.FirstOrDefaultAsync(e => e.SystemUlid == originalUlid); + var dbEntity = await trackingContext.TestEntities.FirstOrDefaultAsync(e => e.SystemUlid == originalUlid, cancellationToken: TestContext.Current.CancellationToken); Assert.NotNull(dbEntity); // Attempt a Save without modifying any structural data properties - var affectedRows = await trackingContext.SaveChangesAsync(); + var affectedRows = await trackingContext.SaveChangesAsync(TestContext.Current.CancellationToken); // Verify that EF Core understands the model is completely clean // (Returns 0 updates executed to the underlying data provider) diff --git a/src/EFCore/EFCore.Impl.csproj b/src/EFCore/EFCore.Impl.csproj index 63cbda9..115bc6f 100644 --- a/src/EFCore/EFCore.Impl.csproj +++ b/src/EFCore/EFCore.Impl.csproj @@ -30,13 +30,13 @@ - + build;analyzers - + diff --git a/src/LinqToDB.IntegrationTests/LinqToDB.IntegrationTests.csproj b/src/LinqToDB.IntegrationTests/LinqToDB.IntegrationTests.csproj index cc419a1..d73a488 100644 --- a/src/LinqToDB.IntegrationTests/LinqToDB.IntegrationTests.csproj +++ b/src/LinqToDB.IntegrationTests/LinqToDB.IntegrationTests.csproj @@ -11,8 +11,8 @@ - - + + diff --git a/src/LinqToDB.IntegrationTests/UlidLinqToDbIntegrationTests.cs b/src/LinqToDB.IntegrationTests/UlidLinqToDbIntegrationTests.cs index ea164a2..71576af 100644 --- a/src/LinqToDB.IntegrationTests/UlidLinqToDbIntegrationTests.cs +++ b/src/LinqToDB.IntegrationTests/UlidLinqToDbIntegrationTests.cs @@ -75,14 +75,14 @@ public async Task LinqToDB_ShouldSuccessfullyRoundTrip_BothNonNullAndNullableUli // Act - Step 1: Write to database await using (var writeContext = CreateConnection(format)) { - await writeContext.InsertAsync(entity); + await writeContext.InsertAsync(entity, token: TestContext.Current.CancellationToken); } // Act - Step 2: Read back via an isolated, stateless connection await using (var readContext = CreateConnection(format)) { var dbEntity = await readContext.GetTable() - .FirstOrDefaultAsync(e => e.SystemUlid == originalUlid); + .FirstOrDefaultAsync(e => e.SystemUlid == originalUlid, token: TestContext.Current.CancellationToken); // Assert Assert.NotNull(dbEntity); @@ -92,26 +92,26 @@ public async Task LinqToDB_ShouldSuccessfullyRoundTrip_BothNonNullAndNullableUli // Step 3: Test update conversion behavior on nullable property types var updatedUlid = Ulid.New(); dbEntity.NullableUlid = updatedUlid; - await readContext.UpdateAsync(dbEntity); + await readContext.UpdateAsync(dbEntity, token: TestContext.Current.CancellationToken); } // Act - Step 4: Validate update persistence await using (var verifyContext = CreateConnection(format)) { - var dbEntity = await verifyContext.GetTable().FirstOrDefaultAsync(); + var dbEntity = await verifyContext.GetTable().FirstOrDefaultAsync(token: TestContext.Current.CancellationToken); Assert.NotNull(dbEntity); Assert.Equal(originalUlid, dbEntity.SystemUlid); Assert.NotNull(dbEntity.NullableUlid); // Step 5: Test reversing a value back to null (Null-to-Null round trip) dbEntity.NullableUlid = null; - await verifyContext.UpdateAsync(dbEntity); + await verifyContext.UpdateAsync(dbEntity, token: TestContext.Current.CancellationToken); } // Act - Step 6: Final check that reverting to null persisted properly await using (var finalVerifyContext = CreateConnection(format)) { - var dbEntity = await finalVerifyContext.GetTable().FirstOrDefaultAsync(); + var dbEntity = await finalVerifyContext.GetTable().FirstOrDefaultAsync(token: TestContext.Current.CancellationToken); Assert.NotNull(dbEntity); Assert.Null(dbEntity.NullableUlid); } @@ -131,12 +131,12 @@ public async Task LinqToDB_ShouldTranslateLINQRangeQueries_ProperlyWithParameter var targetUlid = Ulid.New(); var maxUlid = Ulid.MaxAt(DateTimeOffset.UtcNow.AddDays(1)); - await context.InsertAsync(new TestEntity { SystemUlid = targetUlid }); + await context.InsertAsync(new TestEntity { SystemUlid = targetUlid }, token: TestContext.Current.CancellationToken); // Act - Validate that LinqToDB command tree translates logical bounds matching type conversions var results = await context.GetTable() .Where(e => e.SystemUlid >= minUlid && e.SystemUlid <= maxUlid) - .ToListAsync(); + .ToListAsync(token: TestContext.Current.CancellationToken); // Assert Assert.Single(results); @@ -154,17 +154,17 @@ public async Task LinqToDB_ShouldSuccessfullyProject_UlidToAnonymousAndDtoTypes( await using var context = CreateConnection(format); var targetUlid = Ulid.New(); - await context.InsertAsync(new TestEntity { SystemUlid = targetUlid, NullableUlid = Ulid.New() }); + await context.InsertAsync(new TestEntity { SystemUlid = targetUlid, NullableUlid = Ulid.New() }, token: TestContext.Current.CancellationToken); // Act - Step 1: Query compilation evaluation over anonymous shape allocations var anonymousResult = await context.GetTable() .Select(e => new { e.Id, e.SystemUlid, e.NullableUlid }) - .FirstOrDefaultAsync(e => e.SystemUlid == targetUlid); + .FirstOrDefaultAsync(e => e.SystemUlid == targetUlid, token: TestContext.Current.CancellationToken); // Act - Step 2: Extract scalar data projections var rawUlidList = await context.GetTable() .Select(e => e.SystemUlid) - .ToListAsync(); + .ToListAsync(token: TestContext.Current.CancellationToken); // Assert Assert.NotNull(anonymousResult); @@ -186,8 +186,8 @@ public async Task LinqToDB_ShouldTranslateContainsQuery_WhenUsingUlidCollections var targetUlid = Ulid.New(); - await context.InsertAsync(new TestEntity { NullableUlid = targetUlid }); - await context.InsertAsync(new TestEntity { NullableUlid = null }); + await context.InsertAsync(new TestEntity { NullableUlid = targetUlid }, token: TestContext.Current.CancellationToken); + await context.InsertAsync(new TestEntity { NullableUlid = null }, token: TestContext.Current.CancellationToken); // Strategy: Include null directly inside the searchable target criteria collection var searchCriteria = new Ulid?[] { targetUlid, null }; @@ -195,7 +195,7 @@ public async Task LinqToDB_ShouldTranslateContainsQuery_WhenUsingUlidCollections // Act var results = await context.GetTable() .Where(e => searchCriteria.Contains(e.NullableUlid)) - .ToListAsync(); + .ToListAsync(token: TestContext.Current.CancellationToken); // Assert Assert.Equal(2, results.Count); @@ -214,20 +214,20 @@ public async Task LinqToDB_ShouldMaintainChronologicalOrder_WhenOrderingByUlid(U await using var context = CreateConnection(format); var first = Ulid.New(); - await Task.Delay(10); // Enforce clear hardware timestamp increments + await Task.Delay(10, TestContext.Current.CancellationToken); // Enforce clear hardware timestamp increments var second = Ulid.New(); - await Task.Delay(10); + await Task.Delay(10, TestContext.Current.CancellationToken); var third = Ulid.New(); // Insert scrambled chronological payloads - await context.InsertAsync(new TestEntity { SystemUlid = second }); - await context.InsertAsync(new TestEntity { SystemUlid = third }); - await context.InsertAsync(new TestEntity { SystemUlid = first }); + await context.InsertAsync(new TestEntity { SystemUlid = second }, token: TestContext.Current.CancellationToken); + await context.InsertAsync(new TestEntity { SystemUlid = third }, token: TestContext.Current.CancellationToken); + await context.InsertAsync(new TestEntity { SystemUlid = first }, token: TestContext.Current.CancellationToken); // Act var orderedList = await context.GetTable() .OrderBy(e => e.SystemUlid) - .ToListAsync(); + .ToListAsync(token: TestContext.Current.CancellationToken); // Assert Assert.Equal(3, orderedList.Count); @@ -247,8 +247,8 @@ public async Task LinqToDB_ShouldSuccessfullyExecuteJoins_OnUlidProperties(UlidS await using var context = CreateConnection(format); var parentUlid = Ulid.New(); - await context.InsertAsync(new TestEntity { SystemUlid = parentUlid }); - await context.InsertAsync(new RelatedChildEntity { ParentSystemUlid = parentUlid, Description = "Child linked via ULID" }); + await context.InsertAsync(new TestEntity { SystemUlid = parentUlid }, token: TestContext.Current.CancellationToken); + await context.InsertAsync(new RelatedChildEntity { ParentSystemUlid = parentUlid, Description = "Child linked via ULID" }, token: TestContext.Current.CancellationToken); // Act - Enforce expression evaluation trees across relational predicates var joinResult = await context.GetTable() @@ -258,7 +258,7 @@ public async Task LinqToDB_ShouldSuccessfullyExecuteJoins_OnUlidProperties(UlidS child => child.ParentSystemUlid, (parent, child) => new { parent.Id, parent.SystemUlid, child.Description } ) - .FirstOrDefaultAsync(x => x.SystemUlid == parentUlid); + .FirstOrDefaultAsync(x => x.SystemUlid == parentUlid, token: TestContext.Current.CancellationToken); // Assert Assert.NotNull(joinResult); diff --git a/src/LinqToDB/LinqToDB.Impl.csproj b/src/LinqToDB/LinqToDB.Impl.csproj index 51d09d7..52a3cb7 100644 --- a/src/LinqToDB/LinqToDB.Impl.csproj +++ b/src/LinqToDB/LinqToDB.Impl.csproj @@ -29,13 +29,13 @@ - + build;analyzers - + diff --git a/src/Ulid.Benchmarks/Benchmarks.csproj b/src/Ulid.Benchmarks/Benchmarks.csproj index 7a49738..817172a 100644 --- a/src/Ulid.Benchmarks/Benchmarks.csproj +++ b/src/Ulid.Benchmarks/Benchmarks.csproj @@ -9,10 +9,10 @@ - - - - + + + + diff --git a/src/Ulid.Tests/Tests.csproj b/src/Ulid.Tests/Tests.csproj index a9feaca..dea037a 100644 --- a/src/Ulid.Tests/Tests.csproj +++ b/src/Ulid.Tests/Tests.csproj @@ -13,7 +13,7 @@ - + diff --git a/src/Ulid/Impl.csproj b/src/Ulid/Impl.csproj index 179c56a..e042ca1 100644 --- a/src/Ulid/Impl.csproj +++ b/src/Ulid/Impl.csproj @@ -26,12 +26,12 @@ - - + + - + diff --git a/src/global.json b/src/global.json new file mode 100644 index 0000000..3140116 --- /dev/null +++ b/src/global.json @@ -0,0 +1,5 @@ +{ + "test": { + "runner": "Microsoft.Testing.Platform" + } +}