diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index d914fc369d..f8589a73a7 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -3,13 +3,13 @@ "isRoot": true, "tools": { "jetbrains.resharper.globaltools": { - "version": "2022.2.3", + "version": "2022.2.4", "commands": [ "jb" ] }, "regitlint": { - "version": "6.1.1", + "version": "6.2.1", "commands": [ "regitlint" ] @@ -21,7 +21,7 @@ ] }, "dotnet-reportgenerator-globaltool": { - "version": "5.1.3", + "version": "5.1.11", "commands": [ "reportgenerator" ] diff --git a/Build.ps1 b/Build.ps1 index d8be7da5e6..9b076bc4d8 100644 --- a/Build.ps1 +++ b/Build.ps1 @@ -51,7 +51,7 @@ function RunCleanupCode { if ($baseCommitHash -ne $headCommitHash) { Write-Output "Running code cleanup on commit range $baseCommitHash..$headCommitHash in pull request." - dotnet regitlint -s JsonApiDotNetCore.sln --print-command --skip-tool-check --jb-profile="JADNC Full Cleanup" --jb --properties:Configuration=Release --jb --verbosity=WARN -f commits -a $headCommitHash -b $baseCommitHash --fail-on-diff --print-diff + dotnet regitlint -s JsonApiDotNetCore.sln --print-command --skip-tool-check --max-runs=5 --jb-profile="JADNC Full Cleanup" --jb --properties:Configuration=Release --jb --verbosity=WARN -f commits -a $headCommitHash -b $baseCommitHash --fail-on-diff --print-diff CheckLastExitCode } } @@ -105,7 +105,7 @@ dotnet build -c Release CheckLastExitCode # https://youtrack.jetbrains.com/issue/RSRP-488628/Breaking-InspectCode-fails-with-Roslyn-Worker-process-exited-unexpectedly-after-update -if ($env:APPVEYOR_BUILD_WORKER_IMAGE -ne 'Ubuntu') { +if ($IsWindows) { RunInspectCode RunCleanupCode } diff --git a/Directory.Build.props b/Directory.Build.props index 99319a64e6..1810b9cd46 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -6,7 +6,7 @@ 6.0.* 4.3.* 2.14.1 - 5.1.0 + 5.1.1 $(MSBuildThisFileDirectory)CodingGuidelines.ruleset 9999 enable @@ -16,7 +16,7 @@ - + @@ -33,8 +33,8 @@ - 3.1.2 + 3.2.0 4.18.2 - 17.3.1 + 17.4.0 diff --git a/appveyor.yml b/appveyor.yml index 61feec2ab8..81ba53020c 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,10 +1,10 @@ image: - - Ubuntu + - Ubuntu2004 - Visual Studio 2022 version: '{build}' -stack: postgresql 13.4 +stack: postgresql 15 environment: PGUSER: postgres @@ -34,7 +34,10 @@ for: only: - image: Visual Studio 2022 services: - - postgresql13 + - postgresql15 + install: + # Temporary workaround for https://help.appveyor.com/discussions/questions/60488-postgresql-version + - net start postgresql-x64-15 # REF: https://github.com/docascode/docfx-seed/blob/master/appveyor.yml before_build: - pwsh: | @@ -101,7 +104,7 @@ build_script: Write-Output "PostgreSQL version:" if ($IsWindows) { - . "${env:ProgramFiles}\PostgreSQL\13\bin\psql" --version + . "${env:ProgramFiles}\PostgreSQL\15\bin\psql" --version } else { psql --version diff --git a/cleanupcode.ps1 b/cleanupcode.ps1 index bab8b82af1..5740ab5a90 100644 --- a/cleanupcode.ps1 +++ b/cleanupcode.ps1 @@ -28,17 +28,17 @@ if ($revision) { if ($baseCommitHash -eq $headCommitHash) { Write-Output "Running code cleanup on staged/unstaged files." - dotnet regitlint -s JsonApiDotNetCore.sln --print-command --skip-tool-check --jb-profile="JADNC Full Cleanup" --jb --properties:Configuration=Release --jb --verbosity=WARN -f staged,modified + dotnet regitlint -s JsonApiDotNetCore.sln --print-command --skip-tool-check --max-runs=5 --jb-profile="JADNC Full Cleanup" --jb --properties:Configuration=Release --jb --verbosity=WARN -f staged,modified VerifySuccessExitCode } else { Write-Output "Running code cleanup on commit range $baseCommitHash..$headCommitHash, including staged/unstaged files." - dotnet regitlint -s JsonApiDotNetCore.sln --print-command --skip-tool-check --jb-profile="JADNC Full Cleanup" --jb --properties:Configuration=Release --jb --verbosity=WARN -f staged,modified,commits -a $headCommitHash -b $baseCommitHash + dotnet regitlint -s JsonApiDotNetCore.sln --print-command --skip-tool-check --max-runs=5 --jb-profile="JADNC Full Cleanup" --jb --properties:Configuration=Release --jb --verbosity=WARN -f staged,modified,commits -a $headCommitHash -b $baseCommitHash VerifySuccessExitCode } } else { Write-Output "Running code cleanup on all files." - dotnet regitlint -s JsonApiDotNetCore.sln --print-command --skip-tool-check --jb-profile="JADNC Full Cleanup" --jb --properties:Configuration=Release --jb --verbosity=WARN + dotnet regitlint -s JsonApiDotNetCore.sln --print-command --skip-tool-check --max-runs=5 --jb-profile="JADNC Full Cleanup" --jb --properties:Configuration=Release --jb --verbosity=WARN VerifySuccessExitCode } diff --git a/docs/usage/reading/filtering.md b/docs/usage/reading/filtering.md index a1c1215ccd..b99a14c3b2 100644 --- a/docs/usage/reading/filtering.md +++ b/docs/usage/reading/filtering.md @@ -196,7 +196,7 @@ matchTextExpression: ( 'contains' | 'startsWith' | 'endsWith' ) LPAREN fieldChain COMMA literalConstant RPAREN; anyExpression: - 'any' LPAREN fieldChain COMMA literalConstant ( COMMA literalConstant )+ RPAREN; + 'any' LPAREN fieldChain ( COMMA literalConstant )+ RPAREN; hasExpression: 'has' LPAREN fieldChain ( COMMA filterExpression )? RPAREN; diff --git a/run-docker-postgres.ps1 b/run-docker-postgres.ps1 index 89a325e3b5..153b93a846 100644 --- a/run-docker-postgres.ps1 +++ b/run-docker-postgres.ps1 @@ -9,4 +9,4 @@ docker run --rm --name jsonapi-dotnet-core-testing ` -e POSTGRES_USER=postgres ` -e POSTGRES_PASSWORD=postgres ` -p 5432:5432 ` - postgres:13.4 + postgres:15 diff --git a/src/Examples/DatabasePerTenantExample/Data/AppDbContext.cs b/src/Examples/DatabasePerTenantExample/Data/AppDbContext.cs index c70fc8655f..ba73b8bf3a 100644 --- a/src/Examples/DatabasePerTenantExample/Data/AppDbContext.cs +++ b/src/Examples/DatabasePerTenantExample/Data/AppDbContext.cs @@ -36,7 +36,7 @@ protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) private string GetConnectionString() { string? tenantName = GetTenantName(); - string connectionString = _configuration[$"Data:{tenantName ?? "Default"}Connection"]; + string? connectionString = _configuration[$"Data:{tenantName ?? "Default"}Connection"]; if (connectionString == null) { diff --git a/src/Examples/NoEntityFrameworkExample/Services/WorkItemService.cs b/src/Examples/NoEntityFrameworkExample/Services/WorkItemService.cs index 6df109e5ba..34a40755cb 100644 --- a/src/Examples/NoEntityFrameworkExample/Services/WorkItemService.cs +++ b/src/Examples/NoEntityFrameworkExample/Services/WorkItemService.cs @@ -11,12 +11,12 @@ namespace NoEntityFrameworkExample.Services; [UsedImplicitly(ImplicitUseKindFlags.InstantiatedNoFixedConstructorSignature)] public sealed class WorkItemService : IResourceService { - private readonly string _connectionString; + private readonly string? _connectionString; public WorkItemService(IConfiguration configuration) { string postgresPassword = Environment.GetEnvironmentVariable("PGPASSWORD") ?? "postgres"; - _connectionString = configuration["Data:DefaultConnection"].Replace("###", postgresPassword); + _connectionString = configuration["Data:DefaultConnection"]?.Replace("###", postgresPassword); } public async Task> GetAsync(CancellationToken cancellationToken) diff --git a/src/JsonApiDotNetCore/CollectionExtensions.cs b/src/JsonApiDotNetCore/CollectionExtensions.cs index 133231eb23..6a17fed7b9 100644 --- a/src/JsonApiDotNetCore/CollectionExtensions.cs +++ b/src/JsonApiDotNetCore/CollectionExtensions.cs @@ -79,15 +79,4 @@ public static IEnumerable WhereNotNull(this IEnumerable source) return source.Where(element => element is not null)!; #pragma warning restore AV1250 // Evaluate LINQ query before returning it } - - public static void AddRange(this ICollection source, IEnumerable itemsToAdd) - { - ArgumentGuard.NotNull(source); - ArgumentGuard.NotNull(itemsToAdd); - - foreach (T item in itemsToAdd) - { - source.Add(item); - } - } } diff --git a/src/JsonApiDotNetCore/Queries/Expressions/AnyExpression.cs b/src/JsonApiDotNetCore/Queries/Expressions/AnyExpression.cs index 2b855b1bdb..ecacc41c7c 100644 --- a/src/JsonApiDotNetCore/Queries/Expressions/AnyExpression.cs +++ b/src/JsonApiDotNetCore/Queries/Expressions/AnyExpression.cs @@ -17,12 +17,7 @@ public class AnyExpression : FilterExpression public AnyExpression(ResourceFieldChainExpression targetAttribute, IImmutableSet constants) { ArgumentGuard.NotNull(targetAttribute); - ArgumentGuard.NotNull(constants); - - if (constants.Count < 2) - { - throw new ArgumentException("At least two constants are required.", nameof(constants)); - } + ArgumentGuard.NotNullNorEmpty(constants); TargetAttribute = targetAttribute; Constants = constants; diff --git a/src/JsonApiDotNetCore/Queries/Internal/Parsing/FilterParser.cs b/src/JsonApiDotNetCore/Queries/Internal/Parsing/FilterParser.cs index c68e0f77f7..b768eb15b1 100644 --- a/src/JsonApiDotNetCore/Queries/Internal/Parsing/FilterParser.cs +++ b/src/JsonApiDotNetCore/Queries/Internal/Parsing/FilterParser.cs @@ -199,11 +199,6 @@ protected AnyExpression ParseAny() LiteralConstantExpression constant = ParseConstant(); constantsBuilder.Add(constant); - EatSingleCharacterToken(TokenKind.Comma); - - constant = ParseConstant(); - constantsBuilder.Add(constant); - while (TokenStack.TryPeek(out Token? nextToken) && nextToken.Kind == TokenKind.Comma) { EatSingleCharacterToken(TokenKind.Comma); diff --git a/src/JsonApiDotNetCore/Queries/Internal/Parsing/IncludeParser.cs b/src/JsonApiDotNetCore/Queries/Internal/Parsing/IncludeParser.cs index 3c8be88e46..1250e36312 100644 --- a/src/JsonApiDotNetCore/Queries/Internal/Parsing/IncludeParser.cs +++ b/src/JsonApiDotNetCore/Queries/Internal/Parsing/IncludeParser.cs @@ -110,7 +110,7 @@ private ICollection LookupRelationshipName(string relationshipN if (relationships.Any()) { - relationshipsFound.AddRange(relationships); + relationshipsFound.UnionWith(relationships); RelationshipAttribute[] relationshipsToInclude = relationships.Where(relationship => !relationship.IsIncludeBlocked()).ToArray(); ICollection affectedChildren = parent.EnsureChildren(relationshipsToInclude); diff --git a/src/JsonApiDotNetCore/Queries/Internal/QueryLayerComposer.cs b/src/JsonApiDotNetCore/Queries/Internal/QueryLayerComposer.cs index 29e0935954..4661a5bdda 100644 --- a/src/JsonApiDotNetCore/Queries/Internal/QueryLayerComposer.cs +++ b/src/JsonApiDotNetCore/Queries/Internal/QueryLayerComposer.cs @@ -254,7 +254,7 @@ private static IImmutableSet ApplyIncludeElementUpdate IDictionary> updatesInChildren) { ImmutableHashSet.Builder newElementsBuilder = ImmutableHashSet.CreateBuilder(); - newElementsBuilder.AddRange(includeElements); + newElementsBuilder.UnionWith(includeElements); foreach ((IncludeElementExpression existingElement, IImmutableSet updatedChildren) in updatesInChildren) { diff --git a/src/JsonApiDotNetCore/QueryStrings/Internal/FilterQueryStringParameterReader.cs b/src/JsonApiDotNetCore/QueryStrings/Internal/FilterQueryStringParameterReader.cs index 60c7f6b75c..dace5b8ca4 100644 --- a/src/JsonApiDotNetCore/QueryStrings/Internal/FilterQueryStringParameterReader.cs +++ b/src/JsonApiDotNetCore/QueryStrings/Internal/FilterQueryStringParameterReader.cs @@ -77,18 +77,21 @@ public virtual void Read(string parameterName, StringValues parameterValue) } } - private IEnumerable ExtractParameterValue(string parameterValue) + private IEnumerable ExtractParameterValue(string? parameterValue) { - if (_options.EnableLegacyFilterNotation) + if (parameterValue != null) { - foreach (string condition in LegacyConverter.ExtractConditions(parameterValue)) + if (_options.EnableLegacyFilterNotation) { - yield return condition; + foreach (string condition in LegacyConverter.ExtractConditions(parameterValue)) + { + yield return condition; + } + } + else + { + yield return parameterValue; } - } - else - { - yield return parameterValue; } } diff --git a/src/JsonApiDotNetCore/QueryStrings/Internal/IncludeQueryStringParameterReader.cs b/src/JsonApiDotNetCore/QueryStrings/Internal/IncludeQueryStringParameterReader.cs index a4db6ebd4a..7db9a9a7d7 100644 --- a/src/JsonApiDotNetCore/QueryStrings/Internal/IncludeQueryStringParameterReader.cs +++ b/src/JsonApiDotNetCore/QueryStrings/Internal/IncludeQueryStringParameterReader.cs @@ -48,7 +48,7 @@ public virtual void Read(string parameterName, StringValues parameterValue) { try { - _includeExpression = GetInclude(parameterValue); + _includeExpression = GetInclude(parameterValue.ToString()); } catch (QueryParseException exception) { diff --git a/src/JsonApiDotNetCore/QueryStrings/Internal/PaginationQueryStringParameterReader.cs b/src/JsonApiDotNetCore/QueryStrings/Internal/PaginationQueryStringParameterReader.cs index 416b48f4de..3e4293c5e0 100644 --- a/src/JsonApiDotNetCore/QueryStrings/Internal/PaginationQueryStringParameterReader.cs +++ b/src/JsonApiDotNetCore/QueryStrings/Internal/PaginationQueryStringParameterReader.cs @@ -53,7 +53,7 @@ public virtual void Read(string parameterName, StringValues parameterValue) { try { - PaginationQueryStringValueExpression constraint = GetPageConstraint(parameterValue); + PaginationQueryStringValueExpression constraint = GetPageConstraint(parameterValue.ToString()); if (constraint.Elements.Any(element => element.Scope == null)) { diff --git a/src/JsonApiDotNetCore/QueryStrings/Internal/SortQueryStringParameterReader.cs b/src/JsonApiDotNetCore/QueryStrings/Internal/SortQueryStringParameterReader.cs index 060d0c9986..5e5842c960 100644 --- a/src/JsonApiDotNetCore/QueryStrings/Internal/SortQueryStringParameterReader.cs +++ b/src/JsonApiDotNetCore/QueryStrings/Internal/SortQueryStringParameterReader.cs @@ -62,7 +62,7 @@ public virtual void Read(string parameterName, StringValues parameterValue) try { ResourceFieldChainExpression? scope = GetScope(parameterName); - SortExpression sort = GetSort(parameterValue, scope); + SortExpression sort = GetSort(parameterValue.ToString(), scope); var expressionInScope = new ExpressionInScope(scope, sort); _constraints.Add(expressionInScope); diff --git a/src/JsonApiDotNetCore/QueryStrings/Internal/SparseFieldSetQueryStringParameterReader.cs b/src/JsonApiDotNetCore/QueryStrings/Internal/SparseFieldSetQueryStringParameterReader.cs index fb4f665873..09c3c0ede8 100644 --- a/src/JsonApiDotNetCore/QueryStrings/Internal/SparseFieldSetQueryStringParameterReader.cs +++ b/src/JsonApiDotNetCore/QueryStrings/Internal/SparseFieldSetQueryStringParameterReader.cs @@ -69,7 +69,7 @@ public virtual void Read(string parameterName, StringValues parameterValue) try { ResourceType targetResourceType = GetSparseFieldType(parameterName); - SparseFieldSetExpression sparseFieldSet = GetSparseFieldSet(parameterValue, targetResourceType); + SparseFieldSetExpression sparseFieldSet = GetSparseFieldSet(parameterValue.ToString(), targetResourceType); _sparseFieldTableBuilder[targetResourceType] = sparseFieldSet; } diff --git a/src/JsonApiDotNetCore/Repositories/EntityFrameworkCoreRepository.cs b/src/JsonApiDotNetCore/Repositories/EntityFrameworkCoreRepository.cs index 653db6129a..7cdd114301 100644 --- a/src/JsonApiDotNetCore/Repositories/EntityFrameworkCoreRepository.cs +++ b/src/JsonApiDotNetCore/Repositories/EntityFrameworkCoreRepository.cs @@ -471,7 +471,7 @@ private IEnumerable GetRightValueToStoreForAddToToMany(TResource leftResource, H if (rightResourceIdsStored.Any()) { - rightResourceIdsStored.AddRange(rightResourceIdsToAdd); + rightResourceIdsStored.UnionWith(rightResourceIdsToAdd); return rightResourceIdsStored; } diff --git a/src/JsonApiDotNetCore/Resources/OperationContainer.cs b/src/JsonApiDotNetCore/Resources/OperationContainer.cs index a066943c82..88ea29ecdc 100644 --- a/src/JsonApiDotNetCore/Resources/OperationContainer.cs +++ b/src/JsonApiDotNetCore/Resources/OperationContainer.cs @@ -58,6 +58,6 @@ private void AddSecondaryResources(RelationshipAttribute relationship, HashSet rightResources = CollectionConverter.ExtractResources(rightValue); - secondaryResources.AddRange(rightResources); + secondaryResources.UnionWith(rightResources); } } diff --git a/src/JsonApiDotNetCore/Resources/TargetedFields.cs b/src/JsonApiDotNetCore/Resources/TargetedFields.cs index fe4701c61e..4d40fc240d 100644 --- a/src/JsonApiDotNetCore/Resources/TargetedFields.cs +++ b/src/JsonApiDotNetCore/Resources/TargetedFields.cs @@ -18,8 +18,8 @@ public void CopyFrom(ITargetedFields other) { Clear(); - Attributes.AddRange(other.Attributes); - Relationships.AddRange(other.Relationships); + Attributes.UnionWith(other.Attributes); + Relationships.UnionWith(other.Relationships); } public void Clear() diff --git a/src/JsonApiDotNetCore/Serialization/Request/Adapters/RelationshipDataAdapter.cs b/src/JsonApiDotNetCore/Serialization/Request/Adapters/RelationshipDataAdapter.cs index ac1e25746b..0e90f7df07 100644 --- a/src/JsonApiDotNetCore/Serialization/Request/Adapters/RelationshipDataAdapter.cs +++ b/src/JsonApiDotNetCore/Serialization/Request/Adapters/RelationshipDataAdapter.cs @@ -111,7 +111,7 @@ private IEnumerable ConvertToManyRelationshipData(SingleOrManyData(IdentifiableComparer.Instance); - resourceSet.AddRange(rightResources); + resourceSet.UnionWith(rightResources); return resourceSet; } } diff --git a/src/JsonApiDotNetCore/Serialization/Response/LinkBuilder.cs b/src/JsonApiDotNetCore/Serialization/Response/LinkBuilder.cs index ea0eb197df..fb86eb084c 100644 --- a/src/JsonApiDotNetCore/Serialization/Response/LinkBuilder.cs +++ b/src/JsonApiDotNetCore/Serialization/Response/LinkBuilder.cs @@ -140,13 +140,13 @@ private void SetPaginationInTopLevelLinks(ResourceType resourceType, TopLevelLin private string? CalculatePageSizeValue(PageSize? topPageSize, ResourceType resourceType) { - string pageSizeParameterValue = HttpContext.Request.Query[PageSizeParameterName]; + string? pageSizeParameterValue = HttpContext.Request.Query[PageSizeParameterName]; PageSize? newTopPageSize = Equals(topPageSize, _options.DefaultPageSize) ? null : topPageSize; return ChangeTopPageSize(pageSizeParameterValue, newTopPageSize, resourceType); } - private string? ChangeTopPageSize(string pageSizeParameterValue, PageSize? topPageSize, ResourceType resourceType) + private string? ChangeTopPageSize(string? pageSizeParameterValue, PageSize? topPageSize, ResourceType resourceType) { IImmutableList elements = ParsePageSizeExpression(pageSizeParameterValue, resourceType); int elementInTopScopeIndex = elements.FindIndex(expression => expression.Scope == null); diff --git a/test/AnnotationTests/AnnotationTests.csproj b/test/AnnotationTests/AnnotationTests.csproj index 51df20d735..7b221a9a42 100644 --- a/test/AnnotationTests/AnnotationTests.csproj +++ b/test/AnnotationTests/AnnotationTests.csproj @@ -4,12 +4,6 @@ latest - - - PreserveNewest - - - diff --git a/test/DiscoveryTests/DiscoveryTests.csproj b/test/DiscoveryTests/DiscoveryTests.csproj index 2f1048de3f..abbec3ed98 100644 --- a/test/DiscoveryTests/DiscoveryTests.csproj +++ b/test/DiscoveryTests/DiscoveryTests.csproj @@ -3,12 +3,6 @@ $(TargetFrameworkName) - - - PreserveNewest - - - diff --git a/test/DiscoveryTests/xunit.runner.json b/test/DiscoveryTests/xunit.runner.json deleted file mode 100644 index 9db029ba52..0000000000 --- a/test/DiscoveryTests/xunit.runner.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "parallelizeAssembly": false, - "parallelizeTestCollections": false -} diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/AtomicOperations/OperationsDbContext.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/AtomicOperations/OperationsDbContext.cs index 26dd815521..6fd7817ba7 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/AtomicOperations/OperationsDbContext.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/AtomicOperations/OperationsDbContext.cs @@ -31,5 +31,7 @@ protected override void OnModelCreating(ModelBuilder builder) builder.Entity() .HasMany(musicTrack => musicTrack.OccursIn) .WithMany(playlist => playlist.Tracks); + + base.OnModelCreating(builder); } } diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/AtomicOperations/QueryStrings/MusicTrackReleaseDefinition.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/AtomicOperations/QueryStrings/MusicTrackReleaseDefinition.cs index 41aa048ecb..65ab4a4344 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/AtomicOperations/QueryStrings/MusicTrackReleaseDefinition.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/AtomicOperations/QueryStrings/MusicTrackReleaseDefinition.cs @@ -32,7 +32,7 @@ private IQueryable FilterOnRecentlyReleased(IQueryable s { IQueryable tracks = source; - if (bool.Parse(parameterValue)) + if (bool.Parse(parameterValue.ToString())) { tracks = tracks.Where(musicTrack => musicTrack.ReleasedAt < _systemClock.UtcNow && musicTrack.ReleasedAt > _systemClock.UtcNow.AddMonths(-3)); } diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/AtomicOperations/Transactions/AtomicTransactionConsistencyTests.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/AtomicOperations/Transactions/AtomicTransactionConsistencyTests.cs index 97f0e08ff5..46ef0c4784 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/AtomicOperations/Transactions/AtomicTransactionConsistencyTests.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/AtomicOperations/Transactions/AtomicTransactionConsistencyTests.cs @@ -9,7 +9,8 @@ namespace JsonApiDotNetCoreTests.IntegrationTests.AtomicOperations.Transactions; -public sealed class AtomicTransactionConsistencyTests : IClassFixture, OperationsDbContext>> +public sealed class AtomicTransactionConsistencyTests + : IClassFixture, OperationsDbContext>>, IAsyncLifetime { private readonly IntegrationTestContext, OperationsDbContext> _testContext; private readonly OperationsFakers _fakers = new(); @@ -27,7 +28,7 @@ public AtomicTransactionConsistencyTests(IntegrationTestContext(); string postgresPassword = Environment.GetEnvironmentVariable("PGPASSWORD") ?? "postgres"; - string dbConnectionString = $"Host=localhost;Port=5432;Database=JsonApiTest-{Guid.NewGuid():N};User ID=postgres;Password={postgresPassword}"; + string dbConnectionString = $"Host=localhost;Port=5432;Database=JsonApiTest-Extra-{Guid.NewGuid():N};User ID=postgres;Password={postgresPassword}"; services.AddDbContext(options => options.UseNpgsql(dbConnectionString)); }); @@ -158,4 +159,22 @@ public async Task Cannot_use_distributed_transaction() error.Source.ShouldNotBeNull(); error.Source.Pointer.Should().Be("/atomic:operations[0]"); } + + public Task InitializeAsync() + { + return Task.CompletedTask; + } + + Task IAsyncLifetime.DisposeAsync() + { + return DeleteExtraDatabaseAsync(); + } + + private async Task DeleteExtraDatabaseAsync() + { + await using AsyncServiceScope scope = _testContext.Factory.Services.CreateAsyncScope(); + var dbContext = scope.ServiceProvider.GetRequiredService(); + + await dbContext.Database.EnsureDeletedAsync(); + } } diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/CompositeKeys/CompositeDbContext.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/CompositeKeys/CompositeDbContext.cs index b745208cae..d4850ad428 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/CompositeKeys/CompositeDbContext.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/CompositeKeys/CompositeDbContext.cs @@ -39,5 +39,7 @@ protected override void OnModelCreating(ModelBuilder builder) builder.Entity() .HasMany(car => car.PreviousDealerships) .WithMany(dealership => dealership.SoldCars); + + base.OnModelCreating(builder); } } diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/CustomRoutes/CustomRouteTests.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/CustomRoutes/CustomRouteTests.cs index 572f2baeed..e89641470d 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/CustomRoutes/CustomRouteTests.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/CustomRoutes/CustomRouteTests.cs @@ -67,12 +67,12 @@ await _testContext.RunOnDatabaseAsync(async dbContext => public async Task Can_get_resources_at_custom_action_method() { // Arrange - List town = _fakers.Town.Generate(7); + List towns = _fakers.Town.Generate(7); await _testContext.RunOnDatabaseAsync(async dbContext => { await dbContext.ClearTableAsync(); - dbContext.Towns.AddRange(town); + dbContext.Towns.AddRange(towns); await dbContext.SaveChangesAsync(); }); diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/EagerLoading/EagerLoadingDbContext.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/EagerLoading/EagerLoadingDbContext.cs index 030a1a447b..a31deab9a8 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/EagerLoading/EagerLoadingDbContext.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/EagerLoading/EagerLoadingDbContext.cs @@ -34,5 +34,7 @@ protected override void OnModelCreating(ModelBuilder builder) .HasOne(building => building.SecondaryDoor) .WithOne() .HasForeignKey("SecondaryDoorId"); + + base.OnModelCreating(builder); } } diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/InputValidation/ModelState/ModelStateDbContext.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/InputValidation/ModelState/ModelStateDbContext.cs index 73f3241f28..1bfdb1a28e 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/InputValidation/ModelState/ModelStateDbContext.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/InputValidation/ModelState/ModelStateDbContext.cs @@ -37,5 +37,7 @@ protected override void OnModelCreating(ModelBuilder builder) builder.Entity() .HasOne(systemDirectory => systemDirectory.AlsoSelf) .WithOne(); + + base.OnModelCreating(builder); } } diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/Links/LinksDbContext.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/Links/LinksDbContext.cs index ffd2333fbe..149b29b785 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/Links/LinksDbContext.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/Links/LinksDbContext.cs @@ -24,5 +24,7 @@ protected override void OnModelCreating(ModelBuilder builder) .HasOne(photo => photo.Location) .WithOne(location => location.Photo) .HasForeignKey("LocationId"); + + base.OnModelCreating(builder); } } diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/MultiTenancy/MultiTenancyDbContext.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/MultiTenancy/MultiTenancyDbContext.cs index 8e1fcd8350..69a6459303 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/MultiTenancy/MultiTenancyDbContext.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/MultiTenancy/MultiTenancyDbContext.cs @@ -31,5 +31,7 @@ protected override void OnModelCreating(ModelBuilder builder) builder.Entity() .HasQueryFilter(webProduct => webProduct.Shop.TenantId == _tenantProvider.TenantId); + + base.OnModelCreating(builder); } } diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/QueryStrings/Filtering/FilterDbContext.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/QueryStrings/Filtering/FilterDbContext.cs index 1b939ee9a1..35e7b4e51e 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/QueryStrings/Filtering/FilterDbContext.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/QueryStrings/Filtering/FilterDbContext.cs @@ -21,5 +21,7 @@ protected override void OnModelCreating(ModelBuilder builder) builder.Entity() .Property(resource => resource.SomeDateTimeInLocalZone) .HasColumnType("timestamp without time zone"); + + base.OnModelCreating(builder); } } diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/QueryStrings/Filtering/FilterOperatorTests.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/QueryStrings/Filtering/FilterOperatorTests.cs index 0abad96024..6d000f6433 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/QueryStrings/Filtering/FilterOperatorTests.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/QueryStrings/Filtering/FilterOperatorTests.cs @@ -466,6 +466,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => } [Theory] + [InlineData("yes", "no", "'yes'")] [InlineData("two", "one two", "'one','two','three'")] [InlineData("two", "nine", "'one','two','three','four','five'")] public async Task Can_filter_in_set(string matchingText, string nonMatchingText, string filterText) diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/QueryStrings/QueryStringDbContext.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/QueryStrings/QueryStringDbContext.cs index 131bfe19fe..473a7428ba 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/QueryStrings/QueryStringDbContext.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/QueryStrings/QueryStringDbContext.cs @@ -37,5 +37,7 @@ protected override void OnModelCreating(ModelBuilder builder) .HasOne(man => man.Wife) .WithOne(woman => woman.Husband) .HasForeignKey(); + + base.OnModelCreating(builder); } } diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/ReadWrite/ReadWriteDbContext.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/ReadWrite/ReadWriteDbContext.cs index d25acf8a06..4f3d1080cf 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/ReadWrite/ReadWriteDbContext.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/ReadWrite/ReadWriteDbContext.cs @@ -49,5 +49,7 @@ protected override void OnModelCreating(ModelBuilder builder) left => left .HasOne(joinEntity => joinEntity.ToItem) .WithMany()); + + base.OnModelCreating(builder); } } diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/ReadWrite/Updating/Relationships/RemoveFromToManyRelationshipTests.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/ReadWrite/Updating/Relationships/RemoveFromToManyRelationshipTests.cs index eef972dd1f..ed9e4e7936 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/ReadWrite/Updating/Relationships/RemoveFromToManyRelationshipTests.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/ReadWrite/Updating/Relationships/RemoveFromToManyRelationshipTests.cs @@ -1142,7 +1142,7 @@ private void RemoveFromSubscribers(WorkItem workItem, ISet rightR { if (!workItem.Subscribers.IsNullOrEmpty()) { - PreloadedSubscribers.AddRange(workItem.Subscribers); + PreloadedSubscribers.UnionWith(workItem.Subscribers); } foreach (long subscriberId in ExtraSubscribersIdsToRemove) @@ -1158,7 +1158,7 @@ private void RemoveFromTags(WorkItem workItem, ISet rightResource { if (!workItem.Tags.IsNullOrEmpty()) { - PreloadedTags.AddRange(workItem.Tags); + PreloadedTags.UnionWith(workItem.Tags); } foreach (int tagId in ExtraTagIdsToRemove) diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/ReadWrite/Updating/Relationships/UpdateToOneRelationshipTests.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/ReadWrite/Updating/Relationships/UpdateToOneRelationshipTests.cs index a492cc4826..b96fcd6fff 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/ReadWrite/Updating/Relationships/UpdateToOneRelationshipTests.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/ReadWrite/Updating/Relationships/UpdateToOneRelationshipTests.cs @@ -66,7 +66,7 @@ public async Task Can_clear_OneToOne_relationship() await _testContext.RunOnDatabaseAsync(async dbContext => { - dbContext.Groups.AddRange(existingGroup); + dbContext.Groups.Add(existingGroup); await dbContext.SaveChangesAsync(); }); diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/ReadWrite/Updating/Resources/UpdateToOneRelationshipTests.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/ReadWrite/Updating/Resources/UpdateToOneRelationshipTests.cs index 81faaa8297..f96f4a9efa 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/ReadWrite/Updating/Resources/UpdateToOneRelationshipTests.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/ReadWrite/Updating/Resources/UpdateToOneRelationshipTests.cs @@ -209,7 +209,7 @@ public async Task Can_clear_OneToOne_relationship() await _testContext.RunOnDatabaseAsync(async dbContext => { - dbContext.RgbColors.AddRange(existingColor); + dbContext.RgbColors.Add(existingColor); await dbContext.SaveChangesAsync(); }); diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/RequiredRelationships/DefaultBehaviorDbContext.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/RequiredRelationships/DefaultBehaviorDbContext.cs index 6ed4deaeff..5b0b839c63 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/RequiredRelationships/DefaultBehaviorDbContext.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/RequiredRelationships/DefaultBehaviorDbContext.cs @@ -33,5 +33,7 @@ protected override void OnModelCreating(ModelBuilder builder) .HasOne(order => order.Shipment) .WithOne(shipment => shipment.Order) .HasForeignKey("OrderId"); + + base.OnModelCreating(builder); } } diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/ResourceDefinitions/Reading/MoonDefinition.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/ResourceDefinitions/Reading/MoonDefinition.cs index bdd75a9aff..2b904434ad 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/ResourceDefinitions/Reading/MoonDefinition.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/ResourceDefinitions/Reading/MoonDefinition.cs @@ -51,7 +51,7 @@ public override QueryStringParameterHandlers OnRegisterQueryableHandlersFo private static IQueryable FilterByRadius(IQueryable source, StringValues parameterValue) { - bool isFilterOnLargerThan = bool.Parse(parameterValue); + bool isFilterOnLargerThan = bool.Parse(parameterValue.ToString()); return isFilterOnLargerThan ? source.Where(moon => moon.SolarRadius > 1m) : source.Where(moon => moon.SolarRadius <= 1m); } } diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/ResourceDefinitions/Serialization/SerializationDbContext.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/ResourceDefinitions/Serialization/SerializationDbContext.cs index 7f62a63b73..4bfb7aa709 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/ResourceDefinitions/Serialization/SerializationDbContext.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/ResourceDefinitions/Serialization/SerializationDbContext.cs @@ -22,5 +22,7 @@ protected override void OnModelCreating(ModelBuilder builder) builder.Entity() .HasMany(scholarship => scholarship.Participants) .WithOne(student => student.Scholarship!); + + base.OnModelCreating(builder); } } diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/ResourceInheritance/ResourceInheritanceReadTests.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/ResourceInheritance/ResourceInheritanceReadTests.cs index 3a7b60e93d..ebca28dac9 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/ResourceInheritance/ResourceInheritanceReadTests.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/ResourceInheritance/ResourceInheritanceReadTests.cs @@ -2429,7 +2429,7 @@ public async Task Can_sort_on_derived_attribute_from_resource_definition_using_e await _testContext.RunOnDatabaseAsync(async dbContext => { - await dbContext.ClearTableAsync(); + await dbContext.ClearTableAsync(); dbContext.Wheels.AddRange(chromeWheel1, chromeWheel2, chromeWheel3, carbonWheel1, carbonWheel2); await dbContext.SaveChangesAsync(); }); @@ -2487,7 +2487,7 @@ public async Task Can_sort_on_derived_attribute_from_resource_definition_using_l await _testContext.RunOnDatabaseAsync(async dbContext => { - await dbContext.ClearTableAsync(); + await dbContext.ClearTableAsync(); dbContext.Wheels.AddRange(chromeWheel1, chromeWheel2, chromeWheel3, carbonWheel1, carbonWheel2); await dbContext.SaveChangesAsync(); }); diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/ResourceInheritance/TablePerType/TablePerTypeDbContext.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/ResourceInheritance/TablePerType/TablePerTypeDbContext.cs index a1549e6332..7c50ee5573 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/ResourceInheritance/TablePerType/TablePerTypeDbContext.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/ResourceInheritance/TablePerType/TablePerTypeDbContext.cs @@ -32,5 +32,7 @@ protected override void OnModelCreating(ModelBuilder builder) builder.Entity().ToTable("GenericProperties"); builder.Entity().ToTable("StringProperties"); builder.Entity().ToTable("NumberProperties"); + + base.OnModelCreating(builder); } } diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/SoftDeletion/SoftDeletionDbContext.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/SoftDeletion/SoftDeletionDbContext.cs index 2a19ba74b4..3e98950be0 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/SoftDeletion/SoftDeletionDbContext.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/SoftDeletion/SoftDeletionDbContext.cs @@ -24,5 +24,7 @@ protected override void OnModelCreating(ModelBuilder builder) builder.Entity() .HasQueryFilter(department => department.SoftDeletedAt == null); + + base.OnModelCreating(builder); } } diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/ZeroKeys/ZeroKeyDbContext.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/ZeroKeys/ZeroKeyDbContext.cs index 301a7b6d7b..3e93768683 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/ZeroKeys/ZeroKeyDbContext.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/ZeroKeys/ZeroKeyDbContext.cs @@ -27,5 +27,7 @@ protected override void OnModelCreating(ModelBuilder builder) builder.Entity() .HasOne(player => player.ActiveGame) .WithMany(game => game.ActivePlayers); + + base.OnModelCreating(builder); } } diff --git a/test/JsonApiDotNetCoreTests/JsonApiDotNetCoreTests.csproj b/test/JsonApiDotNetCoreTests/JsonApiDotNetCoreTests.csproj index ddb7550e5e..22d50630ca 100644 --- a/test/JsonApiDotNetCoreTests/JsonApiDotNetCoreTests.csproj +++ b/test/JsonApiDotNetCoreTests/JsonApiDotNetCoreTests.csproj @@ -3,12 +3,6 @@ $(TargetFrameworkName) - - - PreserveNewest - - - { webBuilder.ConfigureServices(services => - services.AddDbContext(options => options.UseInMemoryDatabase("db"))); + services.AddDbContext(options => options.UseInMemoryDatabase(Guid.NewGuid().ToString()))); webBuilder.UseStartup>(); diff --git a/test/JsonApiDotNetCoreTests/UnitTests/QueryStringParameters/FilterParseTests.cs b/test/JsonApiDotNetCoreTests/UnitTests/QueryStringParameters/FilterParseTests.cs index e471218ff6..562270a358 100644 --- a/test/JsonApiDotNetCoreTests/UnitTests/QueryStringParameters/FilterParseTests.cs +++ b/test/JsonApiDotNetCoreTests/UnitTests/QueryStringParameters/FilterParseTests.cs @@ -86,7 +86,7 @@ public void Reader_Is_Enabled(JsonApiQueryStringParameters parametersDisabled, b [InlineData("filter", "any(null,'a','b')", "Attribute 'null' does not exist on resource type 'blogs'.")] [InlineData("filter", "any('a','b','c')", "Field name expected.")] [InlineData("filter", "any(title,'b','c',)", "Value between quotes expected.")] - [InlineData("filter", "any(title,'b')", ", expected.")] + [InlineData("filter", "any(title)", ", expected.")] [InlineData("filter[posts]", "any(author,'a','b')", "Attribute 'author' does not exist on resource type 'blogPosts'.")] [InlineData("filter", "and(", "Filter function expected.")] [InlineData("filter", "or(equals(title,'some'),equals(title,'other')", ") expected.")] @@ -146,6 +146,7 @@ public void Reader_Read_Fails(string parameterName, string parameterValue, strin [InlineData("filter", "contains(title,'this')", null, "contains(title,'this')")] [InlineData("filter", "startsWith(title,'this')", null, "startsWith(title,'this')")] [InlineData("filter", "endsWith(title,'this')", null, "endsWith(title,'this')")] + [InlineData("filter", "any(title,'this')", null, "any(title,'this')")] [InlineData("filter", "any(title,'this','that','there')", null, "any(title,'that','there','this')")] [InlineData("filter", "and(contains(title,'sales'),contains(title,'marketing'),contains(title,'advertising'))", null, "and(contains(title,'sales'),contains(title,'marketing'),contains(title,'advertising'))")] diff --git a/test/JsonApiDotNetCoreTests/UnitTests/Serialization/Response/ResponseModelAdapterTests.cs b/test/JsonApiDotNetCoreTests/UnitTests/Serialization/Response/ResponseModelAdapterTests.cs index d9459f7ec1..39279181dd 100644 --- a/test/JsonApiDotNetCoreTests/UnitTests/Serialization/Response/ResponseModelAdapterTests.cs +++ b/test/JsonApiDotNetCoreTests/UnitTests/Serialization/Response/ResponseModelAdapterTests.cs @@ -40,6 +40,7 @@ public void Resources_in_deeply_nested_circular_chain_are_written_in_relationshi // Assert string text = JsonSerializer.Serialize(document, new JsonSerializerOptions(options.SerializerWriteOptions)); + // ReSharper disable StringLiteralTypo text.Should().BeJson(@"{ ""data"": { ""type"": ""articles"", @@ -145,6 +146,7 @@ public void Resources_in_deeply_nested_circular_chain_are_written_in_relationshi } ] }"); + // ReSharper restore StringLiteralTypo } [Fact] @@ -177,6 +179,7 @@ public void Resources_in_deeply_nested_circular_chains_are_written_in_relationsh // Assert string text = JsonSerializer.Serialize(document, new JsonSerializerOptions(options.SerializerWriteOptions)); + // ReSharper disable StringLiteralTypo text.Should().BeJson(@"{ ""data"": [ { @@ -299,6 +302,7 @@ public void Resources_in_deeply_nested_circular_chains_are_written_in_relationsh } ] }"); + // ReSharper restore StringLiteralTypo } [Fact] @@ -335,6 +339,7 @@ public void Resources_in_overlapping_deeply_nested_circular_chains_are_written_i // Assert string text = JsonSerializer.Serialize(document, new JsonSerializerOptions(options.SerializerWriteOptions)); + // ReSharper disable StringLiteralTypo text.Should().BeJson(@"{ ""data"": { ""type"": ""articles"", @@ -514,6 +519,7 @@ public void Resources_in_overlapping_deeply_nested_circular_chains_are_written_i } ] }"); + // ReSharper restore StringLiteralTypo } [Fact] diff --git a/test/JsonApiDotNetCoreTests/xunit.runner.json b/test/JsonApiDotNetCoreTests/xunit.runner.json deleted file mode 100644 index 8f5f10571b..0000000000 --- a/test/JsonApiDotNetCoreTests/xunit.runner.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "parallelizeAssembly": false, - "parallelizeTestCollections": false, - "maxParallelThreads": 1 -} diff --git a/test/MultiDbContextTests/MultiDbContextTests.csproj b/test/MultiDbContextTests/MultiDbContextTests.csproj index a5d0715a2a..b08c25805f 100644 --- a/test/MultiDbContextTests/MultiDbContextTests.csproj +++ b/test/MultiDbContextTests/MultiDbContextTests.csproj @@ -3,12 +3,6 @@ $(TargetFrameworkName) - - - PreserveNewest - - - diff --git a/test/MultiDbContextTests/xunit.runner.json b/test/MultiDbContextTests/xunit.runner.json deleted file mode 100644 index 8f5f10571b..0000000000 --- a/test/MultiDbContextTests/xunit.runner.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "parallelizeAssembly": false, - "parallelizeTestCollections": false, - "maxParallelThreads": 1 -} diff --git a/test/TestBuildingBlocks/DbContextExtensions.cs b/test/TestBuildingBlocks/DbContextExtensions.cs index b570cbf655..5bb3f81a14 100644 --- a/test/TestBuildingBlocks/DbContextExtensions.cs +++ b/test/TestBuildingBlocks/DbContextExtensions.cs @@ -1,6 +1,5 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Metadata; -using Npgsql; namespace TestBuildingBlocks; @@ -36,17 +35,7 @@ private static async Task ClearTablesAsync(this DbContext dbContext, params Type } string tableName = entityType.GetTableName()!; - - // PERF: We first try to clear the table, which is fast and usually succeeds, unless foreign key constraints are violated. - // In that case, we recursively delete all related data, which is slow. - try - { - await dbContext.Database.ExecuteSqlRawAsync($"delete from \"{tableName}\""); - } - catch (PostgresException) - { - await dbContext.Database.ExecuteSqlRawAsync($"truncate table \"{tableName}\" cascade"); - } + await dbContext.Database.ExecuteSqlRawAsync($"delete from \"{tableName}\""); } } } diff --git a/test/TestBuildingBlocks/IntegrationTest.cs b/test/TestBuildingBlocks/IntegrationTest.cs index 4fb2e5cd26..92f49879fc 100644 --- a/test/TestBuildingBlocks/IntegrationTest.cs +++ b/test/TestBuildingBlocks/IntegrationTest.cs @@ -2,14 +2,18 @@ using System.Text; using System.Text.Json; using JsonApiDotNetCore.Middleware; +using Xunit; namespace TestBuildingBlocks; /// -/// A base class for tests that conveniently enables to execute HTTP requests against JSON:API endpoints. +/// A base class for tests that conveniently enables to execute HTTP requests against JSON:API endpoints. It throttles tests that are running in parallel +/// to avoid exceeding the maximum active database connections. /// -public abstract class IntegrationTest +public abstract class IntegrationTest : IAsyncLifetime { + private static readonly SemaphoreSlim ThrottleSemaphore = new(64); + protected abstract JsonSerializerOptions SerializerOptions { get; } public async Task<(HttpResponseMessage httpResponse, TResponseDocument responseDocument)> ExecuteHeadAsync(string requestUrl, @@ -105,4 +109,15 @@ public abstract class IntegrationTest throw new FormatException($"Failed to deserialize response body to JSON:\n{responseText}", exception); } } + + public async Task InitializeAsync() + { + await ThrottleSemaphore.WaitAsync(); + } + + public virtual Task DisposeAsync() + { + _ = ThrottleSemaphore.Release(); + return Task.CompletedTask; + } } diff --git a/test/TestBuildingBlocks/IntegrationTestContext.cs b/test/TestBuildingBlocks/IntegrationTestContext.cs index bccf7d8bf3..7856ba67f9 100644 --- a/test/TestBuildingBlocks/IntegrationTestContext.cs +++ b/test/TestBuildingBlocks/IntegrationTestContext.cs @@ -24,7 +24,7 @@ namespace TestBuildingBlocks; /// The Entity Framework Core database context, which can be defined in the test project or API project. /// [UsedImplicitly(ImplicitUseKindFlags.InstantiatedNoFixedConstructorSignature)] -public class IntegrationTestContext : IntegrationTest, IDisposable +public class IntegrationTestContext : IntegrationTest where TStartup : class where TDbContext : TestableDbContext { @@ -103,16 +103,6 @@ private WebApplicationFactory CreateFactory() return factoryWithConfiguredContentRoot; } - public void Dispose() - { - if (_lazyFactory.IsValueCreated) - { - RunOnDatabaseAsync(async dbContext => await dbContext.Database.EnsureDeletedAsync()).Wait(); - - _lazyFactory.Value.Dispose(); - } - } - public void ConfigureLogging(Action loggingConfiguration) { _loggingConfiguration = loggingConfiguration; @@ -136,6 +126,22 @@ public async Task RunOnDatabaseAsync(Func asyncAction) await asyncAction(dbContext); } + public override async Task DisposeAsync() + { + try + { + if (_lazyFactory.IsValueCreated) + { + await RunOnDatabaseAsync(async dbContext => await dbContext.Database.EnsureDeletedAsync()); + await _lazyFactory.Value.DisposeAsync(); + } + } + finally + { + await base.DisposeAsync(); + } + } + private sealed class IntegrationTestWebApplicationFactory : WebApplicationFactory { private Action? _loggingConfiguration; diff --git a/test/TestBuildingBlocks/TestBuildingBlocks.csproj b/test/TestBuildingBlocks/TestBuildingBlocks.csproj index 5600104fda..ce8c54ef3b 100644 --- a/test/TestBuildingBlocks/TestBuildingBlocks.csproj +++ b/test/TestBuildingBlocks/TestBuildingBlocks.csproj @@ -10,7 +10,7 @@ - + diff --git a/test/TestBuildingBlocks/TestableDbContext.cs b/test/TestBuildingBlocks/TestableDbContext.cs index d40db11c03..18ef090baa 100644 --- a/test/TestBuildingBlocks/TestableDbContext.cs +++ b/test/TestBuildingBlocks/TestableDbContext.cs @@ -1,6 +1,7 @@ using System.Diagnostics; using JsonApiDotNetCore; using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata; using Microsoft.Extensions.Logging; namespace TestBuildingBlocks; @@ -17,4 +18,15 @@ protected override void OnConfiguring(DbContextOptionsBuilder builder) // Writes SQL statements to the Output Window when debugging. builder.LogTo(message => Debug.WriteLine(message), DbLoggerCategory.Database.Name.AsArray(), LogLevel.Information); } + + protected override void OnModelCreating(ModelBuilder builder) + { + foreach (IMutableForeignKey foreignKey in builder.Model.GetEntityTypes().SelectMany(entityType => entityType.GetForeignKeys())) + { + if (foreignKey.DeleteBehavior == DeleteBehavior.ClientSetNull) + { + foreignKey.DeleteBehavior = DeleteBehavior.SetNull; + } + } + } } diff --git a/test/UnitTests/Extensions/ServiceCollectionExtensionsTests.cs b/test/UnitTests/Extensions/ServiceCollectionExtensionsTests.cs index a90c3851ee..0cb24d8025 100644 --- a/test/UnitTests/Extensions/ServiceCollectionExtensionsTests.cs +++ b/test/UnitTests/Extensions/ServiceCollectionExtensionsTests.cs @@ -25,7 +25,7 @@ public void RegisterResource_DeviatingDbContextPropertyName_RegistersCorrectly() // Arrange var services = new ServiceCollection(); services.AddLogging(); - services.AddDbContext(options => options.UseInMemoryDatabase("UnitTestDb")); + services.AddDbContext(options => options.UseInMemoryDatabase(Guid.NewGuid().ToString())); // Act services.AddJsonApi(); diff --git a/test/UnitTests/UnitTests.csproj b/test/UnitTests/UnitTests.csproj index eb3383fbdf..3166fe27e1 100644 --- a/test/UnitTests/UnitTests.csproj +++ b/test/UnitTests/UnitTests.csproj @@ -3,12 +3,6 @@ $(TargetFrameworkName) - - - PreserveNewest - - - diff --git a/test/UnitTests/xunit.runner.json b/test/UnitTests/xunit.runner.json deleted file mode 100644 index 9db029ba52..0000000000 --- a/test/UnitTests/xunit.runner.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "parallelizeAssembly": false, - "parallelizeTestCollections": false -}