diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json
index c9a67346d..6a5ca6d3f 100644
--- a/.config/dotnet-tools.json
+++ b/.config/dotnet-tools.json
@@ -3,18 +3,18 @@
"isRoot": true,
"tools": {
"csharpier": {
- "version": "1.2.5",
+ "version": "1.3.0",
"commands": [
"csharpier"
],
"rollForward": false
},
"dotnet-coverage": {
- "version": "18.3.1",
+ "version": "18.11.2",
"commands": [
"dotnet-coverage"
],
"rollForward": false
}
}
-}
\ No newline at end of file
+}
diff --git a/.github/workflows/core_build_publish.yml b/.github/workflows/core_build_publish.yml
index b512bfa11..786572ff4 100644
--- a/.github/workflows/core_build_publish.yml
+++ b/.github/workflows/core_build_publish.yml
@@ -8,7 +8,7 @@ on:
workflow_dispatch:
env:
- DOTNET_VERSION: 10.0.100
+ DOTNET_VERSION: 11.0.100-rc.1.26425.128
DOTNET_SKIP_FIRST_TIME_EXPERIENCE: 1
DOTNET_CLI_TELEMETRY_OPTOUT: 1
@@ -121,7 +121,7 @@ jobs:
ports:
- "1433:1433"
postgres:
- image: postgres:15
+ image: postgres:18
env:
POSTGRES_PASSWORD: Passw12#
ports:
@@ -152,7 +152,7 @@ jobs:
CORELIB_TESTS_AZURE_BLOB_STORAGE_CONTAINER_NAME: ${{ secrets.CORELIB_TESTS_AZURE_BLOB_STORAGE_CONTAINER_NAME }}
CORELIB_TESTS_AZURE_TABLE_STORAGE_TABLE_NAME: ${{ secrets.CORELIB_TESTS_AZURE_TABLE_STORAGE_TABLE_NAME }}
- name: Integration Test - SQL Server
- run: dotnet test --no-build --configuration Release -- --explicit on --filter-query '/[(category=integration)&(database!=postgres)]'
+ run: dotnet test --no-build --configuration Release -- --explicit on --filter-trait 'category=integration'
env:
SqlServer__ConnectionStringBase: Server=localhost,1433;User Id=sa;Password=Passw12#;Encrypt=false
LeanCodeIntegrationTests__Database: sqlserver
diff --git a/Directory.Build.props b/Directory.Build.props
index d7d30d30d..2fe03827c 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -5,7 +5,7 @@
preview
library
- net10.0
+ net11.0
portable
true
enable
diff --git a/Directory.Packages.props b/Directory.Packages.props
index a91178548..217437c00 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -2,17 +2,23 @@
true
- 10.0.0
- 10.0.0
- 10.0.0
+ 11.0.0-rc.1.26431.118
+ 11.0.0-rc.1.26425.128
+ 11.0.0-rc.1.26431.118
- 8.5.7
+
+ 8.5.10
+
+ 1.18.0
+ 10.2.3
+ 4.0.1
- 5.0.0
+
+ 5.9.0
- 10.0.0
+ 11.0.0-rc.1.26425.128
@@ -21,41 +27,41 @@
-
+
-
-
+
+
-
-
-
-
-
+
+
+
+
+
-
+
-
+
-
+
-
+
-
-
+
+
-
+
-
+
-
-
-
-
+
+
+
+
@@ -72,7 +78,7 @@
-
+
@@ -80,7 +86,7 @@
-
+
@@ -97,36 +103,36 @@
-
+
-
+
-
-
+
+
-
-
-
+
+
+
-
-
+
+
-
-
-
-
-
-
+
+
+
+
+
+
diff --git a/README.md b/README.md
index 073e56421..d86645bf7 100644
--- a/README.md
+++ b/README.md
@@ -3,7 +3,7 @@

[](https://www.nuget.org/packages/LeanCode.Components)
[](https://feedz.io/org/leancode/repository/public/packages/LeanCode.Components)
-[](https://codecov.io/gh/leancodepl/corelibrary)
+[](https://codecov.io/gh/leancodepl/corelibrary)
[](https://www.apache.org/licenses/LICENSE-2.0)
The LeanCode Core Library is a set of helper libraries developed at [our company](https://leancode.co/) that aids our day-to-day development. Not only does it serve as a facilitator in our day-to-day coding activities, but it also encapsulates comprehensive guidelines, gathers our collective knowledge on application architecture and development best practices.
@@ -50,7 +50,7 @@ All of the libraries that are part of the CoreLib are versioned together and req
## Supported versions
-Here is the list of available major versions of the library (as of 2024-08-16):
+Here is the list of available major versions of the library (as of 2026-09-14):
| CoreLib | .NET Core | Under development | Stable | Notes |
|---------|-----------|-------------------|------------|-------------------|
@@ -65,7 +65,8 @@ Here is the list of available major versions of the library (as of 2024-08-16):
| v8.0 | 8.0 | | | Unmaintained |
| v8.1 | 8.0 | | ✔ | |
| v9.0 | 9.0 | | ✔ | |
-| v10.0 | 10.0 | ✔ | | |
+| v10.0 | 10.0 | | ✔ | |
+| v11.0 | 11.0 | ✔ | | Release candidate |
## Building & Testing
@@ -95,9 +96,9 @@ It has a `docker` folder that contains necessary configuration. You can run the
```sh
# For running tests with SQL Server
-$ DB=sqlserver docker-compose run test
+$ DB=sqlserver docker compose run test
# For running tests with PostgreSQL
-$ DB=postgres docker-compose run test
+$ DB=postgres docker compose run test
```
### Publishing
diff --git a/cloud-tests-infrastructure/postgres.tf b/cloud-tests-infrastructure/postgres.tf
index 6db5a19f4..b9a2694a4 100644
--- a/cloud-tests-infrastructure/postgres.tf
+++ b/cloud-tests-infrastructure/postgres.tf
@@ -3,7 +3,7 @@ resource "azurerm_postgresql_flexible_server" "server" {
location = var.location
name = var.postgres_server_name
- version = "14"
+ version = "18"
sku_name = "B_Standard_B1ms"
storage_mb = "32768"
diff --git a/global.json b/global.json
index 6f5a3e61c..4dc8ca551 100644
--- a/global.json
+++ b/global.json
@@ -1,7 +1,7 @@
{
"sdk": {
- "version": "10.0.100",
- "allowPrerelease": false,
+ "version": "11.0.100-rc.1.26425.128",
+ "allowPrerelease": true,
"rollForward": "latestMinor"
},
"test": {
diff --git a/nuget.config b/nuget.config
new file mode 100644
index 000000000..345b4b2ff
--- /dev/null
+++ b/nuget.config
@@ -0,0 +1,47 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/CQRS/LeanCode.CQRS.Annotations/LeanCode.CQRS.Annotations.csproj b/src/CQRS/LeanCode.CQRS.Annotations/LeanCode.CQRS.Annotations.csproj
index 96871dbad..60ea24f82 100644
--- a/src/CQRS/LeanCode.CQRS.Annotations/LeanCode.CQRS.Annotations.csproj
+++ b/src/CQRS/LeanCode.CQRS.Annotations/LeanCode.CQRS.Annotations.csproj
@@ -1,7 +1,7 @@
- net8.0;net9.0;net10.0
+ net8.0;net9.0;net10.0;net11.0
diff --git a/src/Domain/LeanCode.DomainModels.EF/DbContextOptionsBuilderExtensions.cs b/src/Domain/LeanCode.DomainModels.EF/DbContextOptionsBuilderExtensions.cs
index 753b8c97d..071b71c77 100644
--- a/src/Domain/LeanCode.DomainModels.EF/DbContextOptionsBuilderExtensions.cs
+++ b/src/Domain/LeanCode.DomainModels.EF/DbContextOptionsBuilderExtensions.cs
@@ -35,7 +35,7 @@ public static DbContextOptionsBuilder AddSqlServerTypedIdMappingPlugins(this DbC
}
}
-internal class TypedIdDbContextOptionsExtension : IDbContextOptionsExtension
+internal sealed class TypedIdDbContextOptionsExtension : IDbContextOptionsExtension
{
private readonly ITypedIdStoreTypeProvider storeTypeProvider;
diff --git a/src/Domain/LeanCode.DomainModels.EF/TimestampTzExpressionInterceptor.cs b/src/Domain/LeanCode.DomainModels.EF/TimestampTzExpressionInterceptor.cs
index e555010cf..e1f33892a 100644
--- a/src/Domain/LeanCode.DomainModels.EF/TimestampTzExpressionInterceptor.cs
+++ b/src/Domain/LeanCode.DomainModels.EF/TimestampTzExpressionInterceptor.cs
@@ -9,6 +9,7 @@ public sealed class TimestampTzExpressionRewriter : ExpressionVisitor
{
private static readonly Type TimestampTzType = typeof(TimestampTz);
private static readonly Type TimeZoneInfoType = typeof(TimeZoneInfo);
+ private static readonly Type DateTimeType = typeof(DateTime);
private static readonly Type DateTimeOffsetType = typeof(DateTimeOffset);
public static readonly PropertyInfo LocalTimestampWithoutOffsetProperty =
@@ -27,6 +28,10 @@ public sealed class TimestampTzExpressionRewriter : ExpressionVisitor
DateTimeOffsetType.GetProperty(nameof(DateTimeOffset.UtcDateTime))
?? throw new MissingMemberException(DateTimeOffsetType.FullName, nameof(DateTimeOffset.UtcDateTime));
+ public static readonly MethodInfo ToUniversalTimeMethod =
+ DateTimeType.GetMethod(nameof(DateTime.ToUniversalTime), Type.EmptyTypes)
+ ?? throw new MissingMemberException(DateTimeType.FullName, nameof(DateTime.ToUniversalTime));
+
public static readonly MethodInfo ConvertDateTimeBySystemTimeZoneIdMethod =
TimeZoneInfoType.GetMethod(
nameof(TimeZoneInfo.ConvertTimeBySystemTimeZoneId),
@@ -44,7 +49,15 @@ protected override Expression VisitMember(MemberExpression node)
return Expression.Call(
null,
ConvertDateTimeBySystemTimeZoneIdMethod,
- Expression.Property(Expression.Property(node.Expression, UtcTimestampProperty), UtcDateTimeProperty),
+ // Npgsql 11 translates UtcDateTime as a no-op which retains DateTimeOffset as the SQL
+ // expression's CLR type. ToUniversalTime retags it as DateTime, which the outer translator requires.
+ Expression.Call(
+ Expression.Property(
+ Expression.Property(node.Expression, UtcTimestampProperty),
+ UtcDateTimeProperty
+ ),
+ ToUniversalTimeMethod
+ ),
Expression.Property(node.Expression, TimeZoneIdProperty)
);
}
diff --git a/src/Infrastructure/LeanCode.AzureIdentity/DefaultLeanCodeCredential.cs b/src/Infrastructure/LeanCode.AzureIdentity/DefaultLeanCodeCredential.cs
index a019cc934..db9bb9d73 100644
--- a/src/Infrastructure/LeanCode.AzureIdentity/DefaultLeanCodeCredential.cs
+++ b/src/Infrastructure/LeanCode.AzureIdentity/DefaultLeanCodeCredential.cs
@@ -49,7 +49,11 @@ public static TokenCredential Create(AzureCredentialConfiguration config)
if (config.UseManagedIdentity)
{
- return new ManagedIdentityCredential(config.ClientId);
+ var managedIdentityId = string.IsNullOrEmpty(config.ClientId)
+ ? ManagedIdentityId.SystemAssigned
+ : ManagedIdentityId.FromUserAssignedClientId(config.ClientId);
+
+ return new ManagedIdentityCredential(managedIdentityId);
}
else if (config.UseAzureCLI)
{
diff --git a/src/Infrastructure/LeanCode.ClientCredentialsHandler/ClientCredentialsHandler.cs b/src/Infrastructure/LeanCode.ClientCredentialsHandler/ClientCredentialsHandler.cs
index ea5d5f031..5a2670ae8 100644
--- a/src/Infrastructure/LeanCode.ClientCredentialsHandler/ClientCredentialsHandler.cs
+++ b/src/Infrastructure/LeanCode.ClientCredentialsHandler/ClientCredentialsHandler.cs
@@ -1,6 +1,6 @@
using System.Net;
using System.Net.Http.Headers;
-using IdentityModel.Client;
+using Duende.IdentityModel.Client;
using LeanCode.Logging;
namespace LeanCode.ClientCredentialsHandler;
diff --git a/src/Infrastructure/LeanCode.ClientCredentialsHandler/LeanCode.ClientCredentialsHandler.csproj b/src/Infrastructure/LeanCode.ClientCredentialsHandler/LeanCode.ClientCredentialsHandler.csproj
index 433f62140..e7f6092e9 100644
--- a/src/Infrastructure/LeanCode.ClientCredentialsHandler/LeanCode.ClientCredentialsHandler.csproj
+++ b/src/Infrastructure/LeanCode.ClientCredentialsHandler/LeanCode.ClientCredentialsHandler.csproj
@@ -1,6 +1,6 @@
-
+
diff --git a/src/Infrastructure/LeanCode.Dapper/RawQueryValidator.cs b/src/Infrastructure/LeanCode.Dapper/RawQueryValidator.cs
index e8b646e6c..cdf526a4e 100644
--- a/src/Infrastructure/LeanCode.Dapper/RawQueryValidator.cs
+++ b/src/Infrastructure/LeanCode.Dapper/RawQueryValidator.cs
@@ -6,7 +6,7 @@ namespace LeanCode.Dapper;
// Inspired by QueryValidator.Fody
// https://github.com/kamil-mrzyglod/QueryValidator.Fody/blob/master/QueryValidator.Fody/QueryValidator.Fody/ModuleWeaver.cs
-public static class RawQueryValidator
+public static partial class RawQueryValidator
{
[System.Diagnostics.CodeAnalysis.SuppressMessage("?", "CA2100", Justification = "It does not. :)")]
[System.Diagnostics.CodeAnalysis.SuppressMessage("?", "CA2201", Justification = "Doesn't need to be specific.")]
@@ -29,8 +29,8 @@ public static async Task ValidateQueriesAsync(string connectionString, params As
var sqlText = (string?)sql.GetValue(null) ?? "";
// https://xkcd.com/208/
- sqlText = Regex.Replace(sqlText, "IN @[a-zA-Z]{0,}", "IN (1)", RegexOptions.Compiled);
- sqlText = Regex.Replace(sqlText, "(?i)(? t.Value).ToList();
+ message.Fids = tokens.SelectMany(t => t.Value).ToList();
- if (message.Tokens.Count == 0)
+ if (message.Fids.Count == 0)
{
logger.Information("Cannot send push to users {UserIds} - no tokens", userIds);
}
@@ -99,10 +99,10 @@ public virtual async Task SendToUsersAsync(
logger.Debug(
"Sending push notification to user {Count} users, targeting {Count} devices",
userIds.Count,
- message.Tokens.Count
+ message.Fids.Count
);
var response = await messaging.SendEachForMulticastAsync(message, dryRun, cancellationToken);
- await HandleBatchResponseAsync(response, message.Tokens, cancellationToken);
+ await HandleBatchResponseAsync(response, message.Fids, cancellationToken);
}
}
@@ -116,7 +116,7 @@ public virtual async Task SendAllAsync(
logger.Debug("Sending {Count} push messages", messages.Count());
var response = await messaging.SendEachAsync(messages, dryRun, cancellationToken);
- await HandleBatchResponseAsync(response, messages.Select(m => m.Token), cancellationToken);
+ await HandleBatchResponseAsync(response, messages.Select(m => m.Fid), cancellationToken);
}
public virtual async Task SendMulticastAsync(
@@ -125,10 +125,10 @@ public virtual async Task SendMulticastAsync(
CancellationToken cancellationToken = default
)
{
- logger.Debug("Sending multicast push message to {Count} targets", message.Tokens.Count);
+ logger.Debug("Sending multicast push message to {Count} targets", message.Fids.Count);
var response = await messaging.SendEachForMulticastAsync(message, dryRun, cancellationToken);
- await HandleBatchResponseAsync(response, message.Tokens, cancellationToken);
+ await HandleBatchResponseAsync(response, message.Fids, cancellationToken);
}
private async Task HandleBatchResponseAsync(
diff --git a/src/Infrastructure/LeanCode.Firebase.FCM/PushNotificationTokenStore.cs b/src/Infrastructure/LeanCode.Firebase.FCM/PushNotificationTokenStore.cs
index cd68196ff..90a60af5a 100644
--- a/src/Infrastructure/LeanCode.Firebase.FCM/PushNotificationTokenStore.cs
+++ b/src/Infrastructure/LeanCode.Firebase.FCM/PushNotificationTokenStore.cs
@@ -72,7 +72,7 @@ public async Task AddUserTokenAsync(TUserId userId, string newToken, Cancellatio
var dateCreatedColumn = GetTokensColumnName(nameof(PushNotificationTokenEntity.DateCreated));
// perform an upsert using SQL merge, ensuring that given token is assigned to our user and no-one else
- await dbContext.Database.ExecuteSqlInterpolatedAsync(
+ await dbContext.Database.ExecuteSqlAsync(
FormattableStringFactory.Create(
$$"""
MERGE INTO {{tokensTable}} "pt"
diff --git a/src/Infrastructure/LeanCode.Firebase/FirebaseConfiguration.cs b/src/Infrastructure/LeanCode.Firebase/FirebaseConfiguration.cs
index 4d31f3f09..634573e5b 100644
--- a/src/Infrastructure/LeanCode.Firebase/FirebaseConfiguration.cs
+++ b/src/Infrastructure/LeanCode.Firebase/FirebaseConfiguration.cs
@@ -28,7 +28,7 @@ public static FirebaseApp Prepare(string? cfg, string name)
return FirebaseApp.Create(
new AppOptions
{
- Credential = GoogleCredential.FromJson(cfg),
+ Credential = CredentialFactory.FromJson(cfg).ToGoogleCredential(),
ProjectId = JsonNode.Parse(cfg)?["project_id"]?.ToString(),
},
name
diff --git a/src/Infrastructure/LeanCode.ForceUpdate.Contracts/LeanCode.ForceUpdate.Contracts.csproj b/src/Infrastructure/LeanCode.ForceUpdate.Contracts/LeanCode.ForceUpdate.Contracts.csproj
index 3023b2002..0cbcf095d 100644
--- a/src/Infrastructure/LeanCode.ForceUpdate.Contracts/LeanCode.ForceUpdate.Contracts.csproj
+++ b/src/Infrastructure/LeanCode.ForceUpdate.Contracts/LeanCode.ForceUpdate.Contracts.csproj
@@ -2,7 +2,7 @@
annotations
- net8.0;net9.0;net10.0
+ net8.0;net9.0;net10.0;net11.0
diff --git a/src/Infrastructure/LeanCode.ServiceToService/Incoming/ServiceToServiceAuthenticationHandler.cs b/src/Infrastructure/LeanCode.ServiceToService/Incoming/ServiceToServiceAuthenticationHandler.cs
index 2af34eeb8..d198cd12d 100644
--- a/src/Infrastructure/LeanCode.ServiceToService/Incoming/ServiceToServiceAuthenticationHandler.cs
+++ b/src/Infrastructure/LeanCode.ServiceToService/Incoming/ServiceToServiceAuthenticationHandler.cs
@@ -79,7 +79,11 @@ out var multipleCallerIds
var identity = new ClaimsIdentity(claims, Scheme.Name, Options.NameClaimType, Options.RoleClaimType);
var ticket = new AuthenticationTicket(new(identity), Scheme.Name);
- LogAuthenticatedServiceCaller(Logger, callerId, string.Join(", ", roles));
+ if (Logger.IsEnabled(LogLevel.Debug))
+ {
+ var formattedRoles = string.Join(", ", roles);
+ LogAuthenticatedServiceCaller(Logger, callerId, formattedRoles);
+ }
return Task.FromResult(AuthenticateResult.Success(ticket));
}
diff --git a/src/Testing/LeanCode.IntegrationTestHelpers/LeanCode.IntegrationTestHelpers.csproj b/src/Testing/LeanCode.IntegrationTestHelpers/LeanCode.IntegrationTestHelpers.csproj
index 83be8e630..579fa6b2f 100644
--- a/src/Testing/LeanCode.IntegrationTestHelpers/LeanCode.IntegrationTestHelpers.csproj
+++ b/src/Testing/LeanCode.IntegrationTestHelpers/LeanCode.IntegrationTestHelpers.csproj
@@ -19,8 +19,6 @@
-
-
diff --git a/test-bed/LeanCode.TestBed.csproj b/test-bed/LeanCode.TestBed.csproj
index 33d2f5efe..720fcb253 100644
--- a/test-bed/LeanCode.TestBed.csproj
+++ b/test-bed/LeanCode.TestBed.csproj
@@ -1,6 +1,6 @@
- net10.0
+ net11.0
enable
enable
false
diff --git a/test/Directory.Build.props b/test/Directory.Build.props
index 07f23288a..5ebd9a026 100644
--- a/test/Directory.Build.props
+++ b/test/Directory.Build.props
@@ -13,9 +13,13 @@
true
true
+ $(ArtifactsPath)/test-results
+ $(MSBuildThisFileDirectory)/../TestResults
- $(TestingPlatformCommandLineArguments) --report-ctrf --coverage --ignore-exit-code 8 --results-directory $(MSBuildThisFileDirectory)/../TestResults --coverage-output-format cobertura --coverage-settings $(MSBuildThisFileDirectory)/coverage.xml --report-ctrf-filename $(MSBuildProjectName)-$([System.DateTime]::Now.ToString("HH-mm-ss")).json --coverage-output $(MSBuildProjectName)-$([System.DateTime]::Now.ToString("HH-mm-ss")).xml
+ $(TestingPlatformCommandLineArguments) --report-xunit-ctrf --coverage --ignore-exit-code 8 --results-directory $(LeanCodeTestResultsDirectory) --coverage-output-format cobertura --coverage-settings $(MSBuildThisFileDirectory)/coverage.xml --report-xunit-ctrf-filename $(MSBuildProjectName)-$([System.DateTime]::Now.ToString("HH-mm-ss")).json --coverage-output $(MSBuildProjectName)-$([System.DateTime]::Now.ToString("HH-mm-ss")).xml
diff --git a/test/Domain/LeanCode.DomainModels.Tests/Ids/GuidTests.cs b/test/Domain/LeanCode.DomainModels.Tests/Ids/GuidTests.cs
index 1f290de62..3274b5026 100644
--- a/test/Domain/LeanCode.DomainModels.Tests/Ids/GuidTests.cs
+++ b/test/Domain/LeanCode.DomainModels.Tests/Ids/GuidTests.cs
@@ -20,7 +20,7 @@ public class GuidIdTests
[SuppressMessage("?", "xUnit2007", Justification = "Cannot use `IRawTypedId` as generic parameter.")]
public void Generated_class_implements_ITypedId()
{
- Assert.IsAssignableFrom(typeof(IRawTypedId), new TestGuidId());
+ Assert.IsType(typeof(IRawTypedId), new TestGuidId(), exactMatch: false);
}
[Fact]
diff --git a/test/Domain/LeanCode.DomainModels.Tests/Ids/IntTests.cs b/test/Domain/LeanCode.DomainModels.Tests/Ids/IntTests.cs
index f3f7e9ca6..a4504b43e 100644
--- a/test/Domain/LeanCode.DomainModels.Tests/Ids/IntTests.cs
+++ b/test/Domain/LeanCode.DomainModels.Tests/Ids/IntTests.cs
@@ -15,7 +15,7 @@ public class IntIdTests
[SuppressMessage("?", "xUnit2007", Justification = "Cannot use `IRawTypedId` as generic parameter.")]
public void Generated_class_implements_ITypedId()
{
- Assert.IsAssignableFrom(typeof(IRawTypedId), new TestIntId());
+ Assert.IsType(typeof(IRawTypedId), new TestIntId(), exactMatch: false);
}
[Fact]
diff --git a/test/Domain/LeanCode.DomainModels.Tests/Ids/LongTests.cs b/test/Domain/LeanCode.DomainModels.Tests/Ids/LongTests.cs
index 531c8a74a..f55e4ab5f 100644
--- a/test/Domain/LeanCode.DomainModels.Tests/Ids/LongTests.cs
+++ b/test/Domain/LeanCode.DomainModels.Tests/Ids/LongTests.cs
@@ -15,7 +15,7 @@ public class LongIdTests
[SuppressMessage("?", "xUnit2007", Justification = "Cannot use `IRawTypedId` as generic parameter.")]
public void Generated_class_implements_ITypedId()
{
- Assert.IsAssignableFrom(typeof(IRawTypedId), new TestLongId());
+ Assert.IsType(typeof(IRawTypedId), new TestLongId(), exactMatch: false);
}
[Fact]
diff --git a/test/Domain/LeanCode.DomainModels.Tests/Ids/PrefixedGuidTests.cs b/test/Domain/LeanCode.DomainModels.Tests/Ids/PrefixedGuidTests.cs
index 656e90dc0..6038d830b 100644
--- a/test/Domain/LeanCode.DomainModels.Tests/Ids/PrefixedGuidTests.cs
+++ b/test/Domain/LeanCode.DomainModels.Tests/Ids/PrefixedGuidTests.cs
@@ -22,7 +22,7 @@ public class PrefixedGuidIdTests
[SuppressMessage("?", "xUnit2007", Justification = "Cannot use `IPrefixedTypedId` as generic parameter.")]
public void Generated_class_implements_ITypedId()
{
- Assert.IsAssignableFrom(typeof(IPrefixedTypedId), new TestPrefixedGuidId());
+ Assert.IsType(typeof(IPrefixedTypedId), new TestPrefixedGuidId(), exactMatch: false);
}
[Fact]
diff --git a/test/Domain/LeanCode.DomainModels.Tests/Ids/PrefixedStringTests.cs b/test/Domain/LeanCode.DomainModels.Tests/Ids/PrefixedStringTests.cs
index d2d135c93..cf495d872 100644
--- a/test/Domain/LeanCode.DomainModels.Tests/Ids/PrefixedStringTests.cs
+++ b/test/Domain/LeanCode.DomainModels.Tests/Ids/PrefixedStringTests.cs
@@ -21,7 +21,7 @@ public class PrefixedStringIdTests
[SuppressMessage("?", "xUnit2007", Justification = "Cannot use `IPrefixedTypedId` as generic parameter.")]
public void Generated_class_implements_ITypedId()
{
- Assert.IsAssignableFrom(typeof(IPrefixedTypedId), new TestPrefixedStringId());
+ Assert.IsType(typeof(IPrefixedTypedId), new TestPrefixedStringId(), exactMatch: false);
}
[Fact]
diff --git a/test/Domain/LeanCode.DomainModels.Tests/Ids/RawStringTests.cs b/test/Domain/LeanCode.DomainModels.Tests/Ids/RawStringTests.cs
index 60e5f7908..ec07464fb 100644
--- a/test/Domain/LeanCode.DomainModels.Tests/Ids/RawStringTests.cs
+++ b/test/Domain/LeanCode.DomainModels.Tests/Ids/RawStringTests.cs
@@ -20,7 +20,7 @@ public class RawStringIdTests
[SuppressMessage("?", "xUnit2007", Justification = "Cannot use `IRawStringTypedId` as generic parameter.")]
public void Generated_class_implements_ITypedId()
{
- Assert.IsAssignableFrom(typeof(IRawStringTypedId), new TestRawStringId());
+ Assert.IsType(typeof(IRawStringTypedId), new TestRawStringId(), exactMatch: false);
}
[Fact]
diff --git a/test/Infrastructure/LeanCode.Azure.Tests/AzureStorageAuditLogIntegrationTests.cs b/test/Infrastructure/LeanCode.Azure.Tests/AzureStorageAuditLogIntegrationTests.cs
index 4a40906d0..829ddc6b9 100644
--- a/test/Infrastructure/LeanCode.Azure.Tests/AzureStorageAuditLogIntegrationTests.cs
+++ b/test/Infrastructure/LeanCode.Azure.Tests/AzureStorageAuditLogIntegrationTests.cs
@@ -200,7 +200,14 @@ await storage.StoreEventAsync(
var blobsFound = 0;
var linesFound = 0;
- await foreach (var b in containerClient.GetBlobsAsync(BlobTraits.All, BlobStates.All, $"{type}/{id}"))
+ await foreach (
+ var b in containerClient.GetBlobsAsync(
+ BlobTraits.All,
+ BlobStates.All,
+ $"{type}/{id}",
+ CancellationToken.None
+ )
+ )
{
var blockBlob = containerClient.GetBlobClient(b.Name);
using var stream = new MemoryStream();
diff --git a/test/Infrastructure/LeanCode.AzureIdentity.Tests/Properties.cs b/test/Infrastructure/LeanCode.AzureIdentity.Tests/Properties.cs
index e03b09461..1176b0c4a 100644
--- a/test/Infrastructure/LeanCode.AzureIdentity.Tests/Properties.cs
+++ b/test/Infrastructure/LeanCode.AzureIdentity.Tests/Properties.cs
@@ -1,4 +1,2 @@
-using Xunit;
-
// This is necessary, because some tests rely on setting environmental variables, which might be overwritten by tests.
-[assembly: CollectionBehavior(CollectionBehavior.CollectionPerAssembly, DisableTestParallelization = true)]
+[assembly: Xunit.v3.Parallelization(Mode = Xunit.Sdk.ParallelMode.None)]
diff --git a/test/Infrastructure/LeanCode.Firebase.FCM.Tests/FCMClientTests.cs b/test/Infrastructure/LeanCode.Firebase.FCM.Tests/FCMClientTests.cs
index 1a955a0c8..765747f84 100644
--- a/test/Infrastructure/LeanCode.Firebase.FCM.Tests/FCMClientTests.cs
+++ b/test/Infrastructure/LeanCode.Firebase.FCM.Tests/FCMClientTests.cs
@@ -73,7 +73,7 @@ public async Task Gets_user_tokens_when_sending_the_message()
await client.SendToUserAsync(UserId, message);
- var token = Assert.Single(message.Tokens);
+ var token = Assert.Single(message.Fids);
Assert.Equal(Token, token);
}
}
diff --git a/test/LeanCode.IntegrationTests/PostgresOnlyFactAttribute.cs b/test/LeanCode.IntegrationTests/PostgresOnlyFactAttribute.cs
index 4d3da9f28..727aa56e2 100644
--- a/test/LeanCode.IntegrationTests/PostgresOnlyFactAttribute.cs
+++ b/test/LeanCode.IntegrationTests/PostgresOnlyFactAttribute.cs
@@ -3,11 +3,21 @@
namespace LeanCode.IntegrationTests;
-public sealed class PostgresOnlyFactAttribute(
- [CallerFilePath] string? sourceFilePath = null,
- [CallerLineNumber] int sourceLineNumber = 0
-) : IntegrationFactAttribute(sourceFilePath, sourceLineNumber)
+public sealed class PostgresOnlyFactAttribute : IntegrationFactAttribute
{
+ public static bool IsPostgres => Environment.GetEnvironmentVariable(TestDatabaseConfig.ConfigEnvName) == "postgres";
+
+ public PostgresOnlyFactAttribute(
+ [CallerFilePath] string? sourceFilePath = null,
+ [CallerLineNumber] int sourceLineNumber = 0
+ )
+ : base(sourceFilePath, sourceLineNumber)
+ {
+ Skip = "Test requires PostgreSQL.";
+ SkipType = typeof(PostgresOnlyFactAttribute);
+ SkipUnless = nameof(IsPostgres);
+ }
+
public override IReadOnlyCollection> GetTraits() =>
base.GetTraits().Append(new("database", "postgres")).ToList();
}
diff --git a/test/LeanCode.IntegrationTests/TestApp.cs b/test/LeanCode.IntegrationTests/TestApp.cs
index 87e649e9c..1a91b3853 100644
--- a/test/LeanCode.IntegrationTests/TestApp.cs
+++ b/test/LeanCode.IntegrationTests/TestApp.cs
@@ -37,7 +37,7 @@ protected override IEnumerable GetTestAssemblies()
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
base.ConfigureWebHost(builder);
- builder.UseSolutionRelativeContentRoot("test/LeanCode.IntegrationTests");
+ builder.UseContentRoot(AppContext.BaseDirectory);
}
protected override IHostBuilder CreateHostBuilder()
diff --git a/test/LeanCode.IntegrationTests/docker/Dockerfile b/test/LeanCode.IntegrationTests/docker/Dockerfile
index 6b7a54a42..7d9370194 100644
--- a/test/LeanCode.IntegrationTests/docker/Dockerfile
+++ b/test/LeanCode.IntegrationTests/docker/Dockerfile
@@ -1,4 +1,4 @@
-FROM mcr.microsoft.com/dotnet/sdk:10.0
+FROM mcr.microsoft.com/dotnet/sdk:11.0
WORKDIR /app/code/test/LeanCode.IntegrationTests
-ENTRYPOINT dotnet run -- --explicit on
+ENTRYPOINT ["dotnet", "run", "--", "--explicit", "on"]
diff --git a/test/LeanCode.IntegrationTests/docker/Dockerfile.watch b/test/LeanCode.IntegrationTests/docker/Dockerfile.watch
index c8c3dfedb..b3892bea9 100644
--- a/test/LeanCode.IntegrationTests/docker/Dockerfile.watch
+++ b/test/LeanCode.IntegrationTests/docker/Dockerfile.watch
@@ -1,5 +1,5 @@
-FROM mcr.microsoft.com/dotnet/sdk:10.0
+FROM mcr.microsoft.com/dotnet/sdk:11.0
-# Wait for SQL Server first
WORKDIR /app/code/test/LeanCode.IntegrationTests
-ENTRYPOINT dotnet restore && dotnet watch test -- --explicit on
+ENTRYPOINT dotnet restore && \
+ dotnet watch test -- --explicit on
diff --git a/test/LeanCode.IntegrationTests/docker/docker-compose.yml b/test/LeanCode.IntegrationTests/docker/docker-compose.yml
index 9ad141e03..04811d403 100755
--- a/test/LeanCode.IntegrationTests/docker/docker-compose.yml
+++ b/test/LeanCode.IntegrationTests/docker/docker-compose.yml
@@ -9,6 +9,9 @@ services:
- ../../..:/app/code
- ~/.nuget:/home/dotnet/.nuget
environment:
+ UseArtifactsOutput: "true"
+ ArtifactsPath: /tmp/corelibrary-artifacts
+ ASPNETCORE_TEST_CONTENTROOT_LEANCODE_INTEGRATIONTESTS: /app/code/test/LeanCode.IntegrationTests
LeanCodeIntegrationTests__Database: ${DB:?}
SqlServer__ConnectionStringBase: Server=sqlserver;User Id=sa;Password=Passw12#;Encrypt=false
Postgres__ConnectionStringBase: Host=postgres;Username=postgres;Password=Passw12#;SSL Mode=Disable
@@ -24,6 +27,9 @@ services:
- ../../..:/app/code
- ~/.nuget:/home/dotnet/.nuget
environment:
+ UseArtifactsOutput: "true"
+ ArtifactsPath: /tmp/corelibrary-artifacts
+ ASPNETCORE_TEST_CONTENTROOT_LEANCODE_INTEGRATIONTESTS: /app/code/test/LeanCode.IntegrationTests
LeanCodeIntegrationTests__Database: ${DB:?}
SqlServer__ConnectionStringBase: Server=sqlserver;User Id=sa;Password=Passw12#;Encrypt=false
Postgres__ConnectionStringBase: Host=postgres;Username=postgres;Password=Passw12#;SSL Mode=Disable
@@ -41,7 +47,7 @@ services:
- "1433:1433"
postgres:
- image: postgres:17
+ image: postgres:18
environment:
POSTGRES_PASSWORD: Passw12#
ports: