diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index d914fc3..9278758 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": "2023.1.2", "commands": [ "jb" ] }, "regitlint": { - "version": "6.1.1", + "version": "6.3.11", "commands": [ "regitlint" ] @@ -21,7 +21,7 @@ ] }, "dotnet-reportgenerator-globaltool": { - "version": "5.1.3", + "version": "5.1.20", "commands": [ "reportgenerator" ] diff --git a/.editorconfig b/.editorconfig index ca191cf..86cbbc3 100644 --- a/.editorconfig +++ b/.editorconfig @@ -66,15 +66,18 @@ csharp_indent_case_contents_when_block = false csharp_preserve_single_line_statements = false # 'var' usage preferences -csharp_style_var_for_built_in_types = false:suggestion -csharp_style_var_when_type_is_apparent = true:suggestion -csharp_style_var_elsewhere = false:suggestion +csharp_style_var_for_built_in_types = false:none +csharp_style_var_when_type_is_apparent = true:none +csharp_style_var_elsewhere = false:none # Parentheses preferences dotnet_style_parentheses_in_arithmetic_binary_operators = never_if_unnecessary:suggestion dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:suggestion dotnet_style_parentheses_in_relational_binary_operators = never_if_unnecessary:suggestion +# Expression value is never used +dotnet_diagnostic.IDE0058.severity = none + #### Naming Style #### dotnet_diagnostic.IDE1006.severity = warning diff --git a/Build.ps1 b/Build.ps1 index ce11718..623cfa7 100644 --- a/Build.ps1 +++ b/Build.ps1 @@ -23,7 +23,7 @@ function RunInspectCode { $issueType = $xml.report.IssueTypes.SelectSingleNode("IssueType[@Id='$($_.TypeId)']") $severity = $_.Severity ?? $issueType.Severity - Write-Output "[$severity] $($_.File):$($_.Line) $($_.Message)" + Write-Output "[$severity] $($_.File):$($_.Line) $($_.TypeId): $($_.Message)" }) }) } @@ -104,11 +104,8 @@ CheckLastExitCode dotnet build -c Release CheckLastExitCode -# https://youtrack.jetbrains.com/issue/RSRP-488628/Breaking-InspectCode-fails-with-Roslyn-Worker-process-exited-unexpectedly-after-update -if ($IsWindows) { - RunInspectCode - RunCleanupCode -} +RunInspectCode +RunCleanupCode dotnet test -c Release --no-build --collect:"XPlat Code Coverage" CheckLastExitCode diff --git a/Directory.Build.props b/Directory.Build.props index a9d5421..218247a 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -2,9 +2,9 @@ net6.0 6.0.* - 5.1.2 - 2.15.0 - 5.1.2 + 5.3.0 + 2.20.0 + 5.3.0 $(MSBuildThisFileDirectory)CodingGuidelines.ruleset 9999 enable @@ -14,8 +14,8 @@ - - + + @@ -31,8 +31,7 @@ - 3.2.0 - 4.16.1 - 17.4.0 + 6.0.* + 17.6.* diff --git a/JetBrainsInspectCodeTransform.xslt b/JetBrainsInspectCodeTransform.xslt index 098821f..28fa772 100644 --- a/JetBrainsInspectCodeTransform.xslt +++ b/JetBrainsInspectCodeTransform.xslt @@ -25,6 +25,7 @@ File Line Number + Type Message @@ -35,6 +36,9 @@ + + + diff --git a/JsonApiDotNetCore.MongoDb.sln.DotSettings b/JsonApiDotNetCore.MongoDb.sln.DotSettings index 8e80299..8e89021 100644 --- a/JsonApiDotNetCore.MongoDb.sln.DotSettings +++ b/JsonApiDotNetCore.MongoDb.sln.DotSettings @@ -28,6 +28,7 @@ JsonApiDotNetCore.MongoDb.ArgumentGuard.NotNull($EXPR$); SUGGESTION SUGGESTION WARNING + WARNING SUGGESTION SUGGESTION SUGGESTION @@ -54,16 +55,16 @@ JsonApiDotNetCore.MongoDb.ArgumentGuard.NotNull($EXPR$); WARNING WARNING WARNING + SUGGESTION HINT WARNING DO_NOT_SHOW HINT SUGGESTION - WARNING - WARNING + SUGGESTION + SUGGESTION WARNING WARNING - SUGGESTION WARNING SUGGESTION SUGGESTION @@ -76,6 +77,7 @@ JsonApiDotNetCore.MongoDb.ArgumentGuard.NotNull($EXPR$); SUGGESTION SUGGESTION SUGGESTION + WARNING WARNING WARNING WARNING @@ -88,8 +90,10 @@ JsonApiDotNetCore.MongoDb.ArgumentGuard.NotNull($EXPR$); WARNING WARNING WARNING + SUGGESTION + SUGGESTION WARNING - <?xml version="1.0" encoding="utf-16"?><Profile name="JADNC Full Cleanup"><XMLReformatCode>True</XMLReformatCode><CSCodeStyleAttributes ArrangeTypeAccessModifier="True" ArrangeTypeMemberAccessModifier="True" SortModifiers="True" RemoveRedundantParentheses="True" AddMissingParentheses="True" ArrangeBraces="True" ArrangeAttributes="True" ArrangeArgumentsStyle="True" ArrangeCodeBodyStyle="True" ArrangeVarStyle="True" ArrangeTrailingCommas="True" ArrangeObjectCreation="True" ArrangeDefaultValue="True" ArrangeNamespaces="True" /><CssAlphabetizeProperties>True</CssAlphabetizeProperties><JsInsertSemicolon>True</JsInsertSemicolon><FormatAttributeQuoteDescriptor>True</FormatAttributeQuoteDescriptor><CorrectVariableKindsDescriptor>True</CorrectVariableKindsDescriptor><VariablesToInnerScopesDescriptor>True</VariablesToInnerScopesDescriptor><StringToTemplatesDescriptor>True</StringToTemplatesDescriptor><JsReformatCode>True</JsReformatCode><JsFormatDocComments>True</JsFormatDocComments><RemoveRedundantQualifiersTs>True</RemoveRedundantQualifiersTs><OptimizeImportsTs>True</OptimizeImportsTs><OptimizeReferenceCommentsTs>True</OptimizeReferenceCommentsTs><PublicModifierStyleTs>True</PublicModifierStyleTs><ExplicitAnyTs>True</ExplicitAnyTs><TypeAnnotationStyleTs>True</TypeAnnotationStyleTs><RelativePathStyleTs>True</RelativePathStyleTs><AsInsteadOfCastTs>True</AsInsteadOfCastTs><HtmlReformatCode>True</HtmlReformatCode><AspOptimizeRegisterDirectives>True</AspOptimizeRegisterDirectives><RemoveCodeRedundancies>True</RemoveCodeRedundancies><CSUseAutoProperty>True</CSUseAutoProperty><CSMakeFieldReadonly>True</CSMakeFieldReadonly><CSMakeAutoPropertyGetOnly>True</CSMakeAutoPropertyGetOnly><CSArrangeQualifiers>True</CSArrangeQualifiers><CSFixBuiltinTypeReferences>True</CSFixBuiltinTypeReferences><CssReformatCode>True</CssReformatCode><CSOptimizeUsings><OptimizeUsings>True</OptimizeUsings></CSOptimizeUsings><CSShortenReferences>True</CSShortenReferences><CSReformatCode>True</CSReformatCode><CSharpFormatDocComments>True</CSharpFormatDocComments><CSReorderTypeMembers>True</CSReorderTypeMembers><XAMLCollapseEmptyTags>False</XAMLCollapseEmptyTags></Profile> + <?xml version="1.0" encoding="utf-16"?><Profile name="JADNC Full Cleanup"><XMLReformatCode>True</XMLReformatCode><CSCodeStyleAttributes ArrangeTypeAccessModifier="True" ArrangeTypeMemberAccessModifier="True" SortModifiers="True" RemoveRedundantParentheses="True" AddMissingParentheses="True" ArrangeBraces="True" ArrangeAttributes="True" ArrangeArgumentsStyle="True" ArrangeCodeBodyStyle="True" ArrangeVarStyle="True" ArrangeTrailingCommas="True" ArrangeObjectCreation="True" ArrangeDefaultValue="True" ArrangeNamespaces="True" ArrangeNullCheckingPattern="True" /><CssAlphabetizeProperties>True</CssAlphabetizeProperties><JsInsertSemicolon>True</JsInsertSemicolon><FormatAttributeQuoteDescriptor>True</FormatAttributeQuoteDescriptor><CorrectVariableKindsDescriptor>True</CorrectVariableKindsDescriptor><VariablesToInnerScopesDescriptor>True</VariablesToInnerScopesDescriptor><StringToTemplatesDescriptor>True</StringToTemplatesDescriptor><JsReformatCode>True</JsReformatCode><JsFormatDocComments>True</JsFormatDocComments><RemoveRedundantQualifiersTs>True</RemoveRedundantQualifiersTs><OptimizeImportsTs>True</OptimizeImportsTs><OptimizeReferenceCommentsTs>True</OptimizeReferenceCommentsTs><PublicModifierStyleTs>True</PublicModifierStyleTs><ExplicitAnyTs>True</ExplicitAnyTs><TypeAnnotationStyleTs>True</TypeAnnotationStyleTs><RelativePathStyleTs>True</RelativePathStyleTs><AsInsteadOfCastTs>True</AsInsteadOfCastTs><HtmlReformatCode>True</HtmlReformatCode><AspOptimizeRegisterDirectives>True</AspOptimizeRegisterDirectives><RemoveCodeRedundancies>True</RemoveCodeRedundancies><CSUseAutoProperty>True</CSUseAutoProperty><CSMakeFieldReadonly>True</CSMakeFieldReadonly><CSMakeAutoPropertyGetOnly>True</CSMakeAutoPropertyGetOnly><CSArrangeQualifiers>True</CSArrangeQualifiers><CSFixBuiltinTypeReferences>True</CSFixBuiltinTypeReferences><CssReformatCode>True</CssReformatCode><CSOptimizeUsings><OptimizeUsings>True</OptimizeUsings></CSOptimizeUsings><CSShortenReferences>True</CSShortenReferences><CSReformatCode>True</CSReformatCode><CSharpFormatDocComments>True</CSharpFormatDocComments><CSReorderTypeMembers>True</CSReorderTypeMembers><XAMLCollapseEmptyTags>False</XAMLCollapseEmptyTags><CSReformatInactiveBranches>True</CSReformatInactiveBranches></Profile> JADNC Full Cleanup Required Required @@ -116,6 +120,7 @@ JsonApiDotNetCore.MongoDb.ArgumentGuard.NotNull($EXPR$); False False False + False False False False @@ -134,6 +139,7 @@ JsonApiDotNetCore.MongoDb.ArgumentGuard.NotNull($EXPR$); False False CHOP_ALWAYS + False True True True @@ -641,7 +647,7 @@ $left$ = $right$; True CSHARP False - JsonApiDotNetCore.ArgumentGuard.NotNull($argument$); + JsonApiDotNetCore.MongoDb.ArgumentGuard.NotNull($argument$); if ($argument$ is null) throw new ArgumentNullException(nameof($argument$)); WARNING True diff --git a/PackageReadme.md b/PackageReadme.md new file mode 100644 index 0000000..6c9ce84 --- /dev/null +++ b/PackageReadme.md @@ -0,0 +1 @@ +Persistence layer implementation for use of [MongoDB](https://www.mongodb.com/) in APIs using [JsonApiDotNetCore](https://www.jsonapi.net/). diff --git a/WarningSeverities.DotSettings b/WarningSeverities.DotSettings index 0d4eeba..96f358d 100644 --- a/WarningSeverities.DotSettings +++ b/WarningSeverities.DotSettings @@ -13,6 +13,7 @@ WARNING WARNING WARNING + WARNING WARNING WARNING WARNING @@ -70,6 +71,7 @@ WARNING WARNING WARNING + WARNING WARNING WARNING WARNING @@ -82,6 +84,7 @@ WARNING WARNING WARNING + WARNING WARNING WARNING WARNING @@ -97,8 +100,13 @@ WARNING WARNING WARNING + WARNING WARNING WARNING + WARNING + WARNING + WARNING + WARNING WARNING WARNING WARNING @@ -109,12 +117,14 @@ WARNING WARNING WARNING + WARNING WARNING WARNING WARNING WARNING WARNING WARNING + WARNING WARNING WARNING WARNING @@ -135,6 +145,7 @@ WARNING WARNING WARNING + WARNING WARNING WARNING WARNING @@ -152,6 +163,8 @@ WARNING WARNING WARNING + WARNING + WARNING WARNING WARNING WARNING @@ -240,6 +253,7 @@ WARNING WARNING WARNING + WARNING WARNING WARNING WARNING diff --git a/appveyor.yml b/appveyor.yml index 2593740..18aa690 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -32,14 +32,14 @@ for: - provider: NuGet skip_symbols: false api_key: - secure: S9fkLwmhi7w+DGouXYqYq/1PGocnYo8UBUKwv+BGpWHnzE6yHZEYth3j/XJ9Ydsa + secure: hlP/zkfkHzmutSXPYAiINmPdv+QEj3TpAjKewHEkCtQnHnA2tSo+Xey0g6FVM6S5 on: branch: master appveyor_repo_tag: true - provider: NuGet skip_symbols: false api_key: - secure: S9fkLwmhi7w+DGouXYqYq/1PGocnYo8UBUKwv+BGpWHnzE6yHZEYth3j/XJ9Ydsa + secure: hlP/zkfkHzmutSXPYAiINmPdv+QEj3TpAjKewHEkCtQnHnA2tSo+Xey0g6FVM6S5 on: branch: /release\/.+/ appveyor_repo_tag: true diff --git a/src/Examples/GettingStarted/Properties/launchSettings.json b/src/Examples/GettingStarted/Properties/launchSettings.json index ad97b55..b82968b 100644 --- a/src/Examples/GettingStarted/Properties/launchSettings.json +++ b/src/Examples/GettingStarted/Properties/launchSettings.json @@ -10,7 +10,7 @@ "profiles": { "IIS Express": { "commandName": "IISExpress", - "launchBrowser": false, + "launchBrowser": true, "launchUrl": "api/books", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" @@ -18,7 +18,7 @@ }, "Kestrel": { "commandName": "Project", - "launchBrowser": false, + "launchBrowser": true, "launchUrl": "api/books", "applicationUrl": "http://localhost:24141", "environmentVariables": { diff --git a/src/Examples/GettingStarted/appsettings.json b/src/Examples/GettingStarted/appsettings.json index 31455b7..4db298e 100644 --- a/src/Examples/GettingStarted/appsettings.json +++ b/src/Examples/GettingStarted/appsettings.json @@ -6,7 +6,9 @@ "Logging": { "LogLevel": { "Default": "Warning", + // Include server startup and incoming requests. "Microsoft.Hosting.Lifetime": "Information", + "Microsoft.AspNetCore.Hosting.Diagnostics": "Information", "Microsoft.EntityFrameworkCore": "Critical" } }, diff --git a/src/Examples/JsonApiDotNetCoreMongoDbExample/Definitions/TodoItemDefinition.cs b/src/Examples/JsonApiDotNetCoreMongoDbExample/Definitions/TodoItemDefinition.cs index 8079def..61027b4 100644 --- a/src/Examples/JsonApiDotNetCoreMongoDbExample/Definitions/TodoItemDefinition.cs +++ b/src/Examples/JsonApiDotNetCoreMongoDbExample/Definitions/TodoItemDefinition.cs @@ -29,7 +29,7 @@ private SortExpression GetDefaultSortOrder() { return CreateSortExpressionFromLambda(new PropertySortOrder { - (todoItem => todoItem.Priority, ListSortDirection.Descending), + (todoItem => todoItem.Priority, ListSortDirection.Ascending), (todoItem => todoItem.LastModifiedAt, ListSortDirection.Descending) }); } diff --git a/src/Examples/JsonApiDotNetCoreMongoDbExample/Models/TodoItem.cs b/src/Examples/JsonApiDotNetCoreMongoDbExample/Models/TodoItem.cs index 7dc654c..9b0e8d6 100644 --- a/src/Examples/JsonApiDotNetCoreMongoDbExample/Models/TodoItem.cs +++ b/src/Examples/JsonApiDotNetCoreMongoDbExample/Models/TodoItem.cs @@ -16,6 +16,9 @@ public sealed class TodoItem : HexStringMongoIdentifiable [Required] public TodoItemPriority? Priority { get; set; } + [Attr] + public long? DurationInHours { get; set; } + [Attr(Capabilities = AttrCapabilities.AllowFilter | AttrCapabilities.AllowSort | AttrCapabilities.AllowView)] public DateTimeOffset CreatedAt { get; set; } diff --git a/src/Examples/JsonApiDotNetCoreMongoDbExample/Models/TodoItemPriority.cs b/src/Examples/JsonApiDotNetCoreMongoDbExample/Models/TodoItemPriority.cs index a782897..3bb17ed 100644 --- a/src/Examples/JsonApiDotNetCoreMongoDbExample/Models/TodoItemPriority.cs +++ b/src/Examples/JsonApiDotNetCoreMongoDbExample/Models/TodoItemPriority.cs @@ -5,7 +5,7 @@ namespace JsonApiDotNetCoreMongoDbExample.Models; [UsedImplicitly(ImplicitUseTargetFlags.Members)] public enum TodoItemPriority { - Low, - Medium, - High + High = 1, + Medium = 2, + Low = 3 } diff --git a/src/Examples/JsonApiDotNetCoreMongoDbExample/Program.cs b/src/Examples/JsonApiDotNetCoreMongoDbExample/Program.cs index 5f4b0dc..9820cb1 100644 --- a/src/Examples/JsonApiDotNetCoreMongoDbExample/Program.cs +++ b/src/Examples/JsonApiDotNetCoreMongoDbExample/Program.cs @@ -1,3 +1,4 @@ +using System.Diagnostics.CodeAnalysis; using System.Text.Json.Serialization; using JsonApiDotNetCore.Configuration; using JsonApiDotNetCore.MongoDb.Configuration; @@ -6,6 +7,8 @@ using Microsoft.AspNetCore.Authentication; using MongoDB.Driver; +[assembly: ExcludeFromCodeCoverage] + WebApplicationBuilder builder = WebApplication.CreateBuilder(args); // Add services to the container. @@ -37,13 +40,13 @@ static void ConfigureJsonApiOptions(JsonApiOptions options) { - options.Namespace = "api/v1"; + options.Namespace = "api"; options.UseRelativeLinks = true; options.IncludeTotalResourceCount = true; - options.SerializerOptions.WriteIndented = true; options.SerializerOptions.Converters.Add(new JsonStringEnumConverter()); #if DEBUG options.IncludeExceptionStackTraceInErrors = true; options.IncludeRequestBodyInErrors = true; + options.SerializerOptions.WriteIndented = true; #endif } diff --git a/src/Examples/JsonApiDotNetCoreMongoDbExample/Properties/AssemblyInfo.cs b/src/Examples/JsonApiDotNetCoreMongoDbExample/Properties/AssemblyInfo.cs deleted file mode 100644 index 82d1291..0000000 --- a/src/Examples/JsonApiDotNetCoreMongoDbExample/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,4 +0,0 @@ -using System.Diagnostics.CodeAnalysis; - -// https://github.com/coverlet-coverage/coverlet/blob/master/Documentation/MSBuildIntegration.md#excluding-from-coverage -[assembly: ExcludeFromCodeCoverage] diff --git a/src/Examples/JsonApiDotNetCoreMongoDbExample/Properties/launchSettings.json b/src/Examples/JsonApiDotNetCoreMongoDbExample/Properties/launchSettings.json index f155249..c14bdd1 100644 --- a/src/Examples/JsonApiDotNetCoreMongoDbExample/Properties/launchSettings.json +++ b/src/Examples/JsonApiDotNetCoreMongoDbExample/Properties/launchSettings.json @@ -11,16 +11,16 @@ "profiles": { "IIS Express": { "commandName": "IISExpress", - "launchBrowser": false, - "launchUrl": "api/v1/todoItems", + "launchBrowser": true, + "launchUrl": "api/todoItems", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" } }, "Kestrel": { "commandName": "Project", - "launchBrowser": false, - "launchUrl": "api/v1/todoItems", + "launchBrowser": true, + "launchUrl": "api/todoItems", "applicationUrl": "https://localhost:44340;http://localhost:24140", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" diff --git a/src/Examples/JsonApiDotNetCoreMongoDbExample/appsettings.json b/src/Examples/JsonApiDotNetCoreMongoDbExample/appsettings.json index dde4b49..b8ed43e 100644 --- a/src/Examples/JsonApiDotNetCoreMongoDbExample/appsettings.json +++ b/src/Examples/JsonApiDotNetCoreMongoDbExample/appsettings.json @@ -6,7 +6,9 @@ "Logging": { "LogLevel": { "Default": "Warning", + // Include server startup and incoming requests. "Microsoft.Hosting.Lifetime": "Information", + "Microsoft.AspNetCore.Hosting.Diagnostics": "Information", "Microsoft.EntityFrameworkCore": "Critical" } }, diff --git a/src/JsonApiDotNetCore.MongoDb/Configuration/ServiceCollectionExtensions.cs b/src/JsonApiDotNetCore.MongoDb/Configuration/ServiceCollectionExtensions.cs index f914f91..836cf87 100644 --- a/src/JsonApiDotNetCore.MongoDb/Configuration/ServiceCollectionExtensions.cs +++ b/src/JsonApiDotNetCore.MongoDb/Configuration/ServiceCollectionExtensions.cs @@ -3,7 +3,7 @@ using JsonApiDotNetCore.MongoDb.AtomicOperations; using JsonApiDotNetCore.MongoDb.Queries.Internal; using JsonApiDotNetCore.MongoDb.Repositories; -using JsonApiDotNetCore.Queries.Internal; +using JsonApiDotNetCore.Queries; using Microsoft.Extensions.DependencyInjection; namespace JsonApiDotNetCore.MongoDb.Configuration; diff --git a/src/JsonApiDotNetCore.MongoDb/JsonApiDotNetCore.MongoDb.csproj b/src/JsonApiDotNetCore.MongoDb/JsonApiDotNetCore.MongoDb.csproj index 2cc225f..7b9f43a 100644 --- a/src/JsonApiDotNetCore.MongoDb/JsonApiDotNetCore.MongoDb.csproj +++ b/src/JsonApiDotNetCore.MongoDb/JsonApiDotNetCore.MongoDb.csproj @@ -15,16 +15,19 @@ false See https://github.com/json-api-dotnet/JsonApiDotNetCore.MongoDb/releases. logo.png + PackageReadme.md true true embedded + + true + + - - True - - + + diff --git a/src/JsonApiDotNetCore.MongoDb/Queries/Internal/HideRelationshipsSparseFieldSetCache.cs b/src/JsonApiDotNetCore.MongoDb/Queries/Internal/HideRelationshipsSparseFieldSetCache.cs index fcd9d8a..505bf89 100644 --- a/src/JsonApiDotNetCore.MongoDb/Queries/Internal/HideRelationshipsSparseFieldSetCache.cs +++ b/src/JsonApiDotNetCore.MongoDb/Queries/Internal/HideRelationshipsSparseFieldSetCache.cs @@ -2,7 +2,6 @@ using JsonApiDotNetCore.Configuration; using JsonApiDotNetCore.MongoDb.Resources; using JsonApiDotNetCore.Queries; -using JsonApiDotNetCore.Queries.Internal; using JsonApiDotNetCore.Resources; using JsonApiDotNetCore.Resources.Annotations; diff --git a/src/JsonApiDotNetCore.MongoDb/Repositories/MongoQueryExpressionValidator.cs b/src/JsonApiDotNetCore.MongoDb/Repositories/MongoQueryExpressionValidator.cs index 58081c6..5e17cff 100644 --- a/src/JsonApiDotNetCore.MongoDb/Repositories/MongoQueryExpressionValidator.cs +++ b/src/JsonApiDotNetCore.MongoDb/Repositories/MongoQueryExpressionValidator.cs @@ -62,7 +62,7 @@ private void ValidateExpression(QueryExpression? expression) public override QueryExpression? VisitComparison(ComparisonExpression expression, object? argument) { - if (expression.Left is ResourceFieldChainExpression && expression.Right is ResourceFieldChainExpression) + if (expression is { Left: ResourceFieldChainExpression, Right: ResourceFieldChainExpression }) { throw new AttributeComparisonInFilterNotSupportedException(); } diff --git a/src/JsonApiDotNetCore.MongoDb/Repositories/MongoRepository.cs b/src/JsonApiDotNetCore.MongoDb/Repositories/MongoRepository.cs index 6bd64e3..ea673c0 100644 --- a/src/JsonApiDotNetCore.MongoDb/Repositories/MongoRepository.cs +++ b/src/JsonApiDotNetCore.MongoDb/Repositories/MongoRepository.cs @@ -7,7 +7,7 @@ using JsonApiDotNetCore.MongoDb.Resources; using JsonApiDotNetCore.Queries; using JsonApiDotNetCore.Queries.Expressions; -using JsonApiDotNetCore.Queries.Internal.QueryableBuilding; +using JsonApiDotNetCore.Queries.QueryableBuilding; using JsonApiDotNetCore.Repositories; using JsonApiDotNetCore.Resources; using JsonApiDotNetCore.Resources.Annotations; @@ -29,6 +29,7 @@ public class MongoRepository : IResourceRepository _constraintProviders; private readonly IResourceDefinitionAccessor _resourceDefinitionAccessor; + private readonly IQueryableBuilder _queryableBuilder; protected virtual IMongoCollection Collection => _mongoDataAccess.MongoDatabase.GetCollection(typeof(TResource).Name); @@ -36,7 +37,7 @@ public class MongoRepository : IResourceRepository _mongoDataAccess.TransactionId; public MongoRepository(IMongoDataAccess mongoDataAccess, ITargetedFields targetedFields, IResourceGraph resourceGraph, IResourceFactory resourceFactory, - IEnumerable constraintProviders, IResourceDefinitionAccessor resourceDefinitionAccessor) + IEnumerable constraintProviders, IResourceDefinitionAccessor resourceDefinitionAccessor, IQueryableBuilder queryableBuilder) { ArgumentGuard.NotNull(mongoDataAccess); ArgumentGuard.NotNull(targetedFields); @@ -44,6 +45,7 @@ public MongoRepository(IMongoDataAccess mongoDataAccess, ITargetedFields targete ArgumentGuard.NotNull(resourceFactory); ArgumentGuard.NotNull(constraintProviders); ArgumentGuard.NotNull(resourceDefinitionAccessor); + ArgumentGuard.NotNull(queryableBuilder); _mongoDataAccess = mongoDataAccess; _targetedFields = targetedFields; @@ -51,6 +53,7 @@ public MongoRepository(IMongoDataAccess mongoDataAccess, ITargetedFields targete _resourceFactory = resourceFactory; _constraintProviders = constraintProviders; _resourceDefinitionAccessor = resourceDefinitionAccessor; + _queryableBuilder = queryableBuilder; if (!typeof(TResource).IsAssignableTo(typeof(IMongoIdentifiable))) { @@ -112,12 +115,9 @@ protected virtual IMongoQueryable ApplyQueryLayer(QueryLayer queryLay source = queryableHandler.Apply(source); } - var nameFactory = new LambdaParameterNameFactory(); + var context = QueryableBuilderContext.CreateRoot(source, typeof(Queryable), new MongoModel(_resourceGraph), null); + Expression expression = _queryableBuilder.ApplyQuery(queryLayer, context); - var builder = new QueryableBuilder(source.Expression, source.ElementType, typeof(Queryable), nameFactory, _resourceFactory, - new MongoModel(_resourceGraph)); - - Expression expression = builder.ApplyQuery(queryLayer); return (IMongoQueryable)source.Provider.CreateQuery(expression); } diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/AtomicOperationsFixture.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/AtomicOperationsFixture.cs index e10e17d..c9029bb 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/AtomicOperationsFixture.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/AtomicOperationsFixture.cs @@ -1,11 +1,12 @@ using JetBrains.Annotations; using Microsoft.Extensions.DependencyInjection; using TestBuildingBlocks; +using Xunit; namespace JsonApiDotNetCoreMongoDbTests.IntegrationTests.AtomicOperations; [UsedImplicitly(ImplicitUseKindFlags.InstantiatedNoFixedConstructorSignature)] -public sealed class AtomicOperationsFixture : IDisposable +public sealed class AtomicOperationsFixture : IAsyncLifetime { internal IntegrationTestContext TestContext { get; } @@ -21,8 +22,13 @@ public AtomicOperationsFixture() TestContext.ConfigureServicesAfterStartup(services => services.AddSingleton()); } - public void Dispose() + public Task InitializeAsync() { - TestContext.Dispose(); + return Task.CompletedTask; + } + + public async Task DisposeAsync() + { + await TestContext.DisposeAsync(); } } diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Transactions/LyricRepository.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Transactions/LyricRepository.cs index c1275be..93e31b2 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Transactions/LyricRepository.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Transactions/LyricRepository.cs @@ -4,6 +4,7 @@ using JsonApiDotNetCore.MongoDb.AtomicOperations; using JsonApiDotNetCore.MongoDb.Repositories; using JsonApiDotNetCore.Queries; +using JsonApiDotNetCore.Queries.QueryableBuilding; using JsonApiDotNetCore.Resources; namespace JsonApiDotNetCoreMongoDbTests.IntegrationTests.AtomicOperations.Transactions; @@ -19,8 +20,9 @@ public sealed class LyricRepository : MongoRepository, IAsyncDis public override string TransactionId => _transaction.TransactionId; public LyricRepository(IMongoDataAccess mongoDataAccess, ITargetedFields targetedFields, IResourceGraph resourceGraph, IResourceFactory resourceFactory, - IEnumerable constraintProviders, IResourceDefinitionAccessor resourceDefinitionAccessor) - : base(mongoDataAccess, targetedFields, resourceGraph, resourceFactory, constraintProviders, resourceDefinitionAccessor) + IEnumerable constraintProviders, IResourceDefinitionAccessor resourceDefinitionAccessor, + IQueryableBuilder queryableBuilder) + : base(mongoDataAccess, targetedFields, resourceGraph, resourceFactory, constraintProviders, resourceDefinitionAccessor, queryableBuilder) { IMongoDataAccess otherDataAccess = new MongoDataAccess(mongoDataAccess.MongoDatabase); diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Transactions/MusicTrackRepository.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Transactions/MusicTrackRepository.cs index 09164f2..5d40f37 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Transactions/MusicTrackRepository.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Transactions/MusicTrackRepository.cs @@ -2,6 +2,7 @@ using JsonApiDotNetCore.Configuration; using JsonApiDotNetCore.MongoDb.Repositories; using JsonApiDotNetCore.Queries; +using JsonApiDotNetCore.Queries.QueryableBuilding; using JsonApiDotNetCore.Resources; namespace JsonApiDotNetCoreMongoDbTests.IntegrationTests.AtomicOperations.Transactions; @@ -14,8 +15,9 @@ public sealed class MusicTrackRepository : MongoRepository public override string? TransactionId => null; public MusicTrackRepository(IMongoDataAccess mongoDataAccess, ITargetedFields targetedFields, IResourceGraph resourceGraph, - IResourceFactory resourceFactory, IEnumerable constraintProviders, IResourceDefinitionAccessor resourceDefinitionAccessor) - : base(mongoDataAccess, targetedFields, resourceGraph, resourceFactory, constraintProviders, resourceDefinitionAccessor) + IResourceFactory resourceFactory, IEnumerable constraintProviders, IResourceDefinitionAccessor resourceDefinitionAccessor, + IQueryableBuilder queryableBuilder) + : base(mongoDataAccess, targetedFields, resourceGraph, resourceFactory, constraintProviders, resourceDefinitionAccessor, queryableBuilder) { } } diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/Meta/TopLevelCountTests.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/Meta/TopLevelCountTests.cs index 6b1c1c7..c486d0d 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/Meta/TopLevelCountTests.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/Meta/TopLevelCountTests.cs @@ -1,5 +1,4 @@ using System.Net; -using System.Text.Json; using FluentAssertions; using JsonApiDotNetCore.Configuration; using JsonApiDotNetCore.Resources; @@ -54,11 +53,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => responseDocument.Meta.ShouldNotBeNull(); - responseDocument.Meta.ShouldContainKey("total").With(value => - { - JsonElement element = value.Should().BeOfType().Subject; - element.GetInt32().Should().Be(1); - }); + responseDocument.Meta.Should().ContainTotal(1); } [Fact] @@ -80,11 +75,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => responseDocument.Meta.ShouldNotBeNull(); - responseDocument.Meta.ShouldContainKey("total").With(value => - { - JsonElement element = value.Should().BeOfType().Subject; - element.GetInt32().Should().Be(0); - }); + responseDocument.Meta.Should().ContainTotal(0); } [Fact] diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/QueryStrings/AccountPreferences.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/QueryStrings/AccountPreferences.cs index 0cd5aa2..73ea764 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/QueryStrings/AccountPreferences.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/QueryStrings/AccountPreferences.cs @@ -5,6 +5,7 @@ namespace JsonApiDotNetCoreMongoDbTests.IntegrationTests.QueryStrings; [UsedImplicitly(ImplicitUseTargetFlags.Members)] +[Resource(ControllerNamespace = "JsonApiDotNetCoreMongoDbTests.IntegrationTests.QueryStrings")] public sealed class AccountPreferences : HexStringMongoIdentifiable { [Attr] diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/QueryStrings/Filtering/FilterDataTypeTests.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/QueryStrings/Filtering/FilterDataTypeTests.cs index 3e97ce3..5dc1442 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/QueryStrings/Filtering/FilterDataTypeTests.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/QueryStrings/Filtering/FilterDataTypeTests.cs @@ -237,7 +237,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => } [Fact] - public async Task Cannot_filter_equality_on_incompatible_value() + public async Task Cannot_filter_equality_on_incompatible_values() { // Arrange var resource = new FilterableResource @@ -264,9 +264,10 @@ await _testContext.RunOnDatabaseAsync(async dbContext => ErrorObject error = responseDocument.Errors[0]; error.StatusCode.Should().Be(HttpStatusCode.BadRequest); - error.Title.Should().Be("Query creation failed due to incompatible types."); - error.Detail.Should().Be("Failed to convert 'ABC' of type 'String' to type 'Int32'."); - error.Source.Should().BeNull(); + error.Title.Should().Be("The specified filter is invalid."); + error.Detail.Should().StartWith("Failed to convert 'ABC' of type 'String' to type 'Int32'."); + error.Source.ShouldNotBeNull(); + error.Source.Parameter.Should().Be("filter"); } [Theory] diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/QueryStrings/Label.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/QueryStrings/Label.cs index 5c267fb..1cc8ce3 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/QueryStrings/Label.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/QueryStrings/Label.cs @@ -6,6 +6,7 @@ namespace JsonApiDotNetCoreMongoDbTests.IntegrationTests.QueryStrings; [UsedImplicitly(ImplicitUseTargetFlags.Members)] +[Resource(ControllerNamespace = "JsonApiDotNetCoreMongoDbTests.IntegrationTests.QueryStrings")] public sealed class Label : HexStringMongoIdentifiable { [Attr] diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/QueryStrings/LoginAttempt.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/QueryStrings/LoginAttempt.cs index 5e06cea..412b964 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/QueryStrings/LoginAttempt.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/QueryStrings/LoginAttempt.cs @@ -5,6 +5,7 @@ namespace JsonApiDotNetCoreMongoDbTests.IntegrationTests.QueryStrings; [UsedImplicitly(ImplicitUseTargetFlags.Members)] +[Resource(ControllerNamespace = "JsonApiDotNetCoreMongoDbTests.IntegrationTests.QueryStrings")] public sealed class LoginAttempt : HexStringMongoIdentifiable { [Attr] diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/QueryStrings/Pagination/PaginationWithTotalCountTests.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/QueryStrings/Pagination/PaginationWithTotalCountTests.cs index cb54c88..d638555 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/QueryStrings/Pagination/PaginationWithTotalCountTests.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/QueryStrings/Pagination/PaginationWithTotalCountTests.cs @@ -94,7 +94,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => } [Fact] - public async Task Returns_all_resources_when_paging_is_disabled() + public async Task Returns_all_resources_when_pagination_is_disabled() { // Arrange var options = (JsonApiOptions)_testContext.Factory.Services.GetRequiredService(); diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/QueryStrings/SparseFieldSets/ResultCapturingRepository.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/QueryStrings/SparseFieldSets/ResultCapturingRepository.cs index 400ee49..b88cd2e 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/QueryStrings/SparseFieldSets/ResultCapturingRepository.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/QueryStrings/SparseFieldSets/ResultCapturingRepository.cs @@ -2,6 +2,7 @@ using JsonApiDotNetCore.Configuration; using JsonApiDotNetCore.MongoDb.Repositories; using JsonApiDotNetCore.Queries; +using JsonApiDotNetCore.Queries.QueryableBuilding; using JsonApiDotNetCore.Resources; namespace JsonApiDotNetCoreMongoDbTests.IntegrationTests.QueryStrings.SparseFieldSets; @@ -17,8 +18,8 @@ public sealed class ResultCapturingRepository : MongoRepository< public ResultCapturingRepository(IMongoDataAccess mongoDataAccess, ITargetedFields targetedFields, IResourceGraph resourceGraph, IResourceFactory resourceFactory, IEnumerable constraintProviders, IResourceDefinitionAccessor resourceDefinitionAccessor, - ResourceCaptureStore captureStore) - : base(mongoDataAccess, targetedFields, resourceGraph, resourceFactory, constraintProviders, resourceDefinitionAccessor) + IQueryableBuilder queryableBuilder, ResourceCaptureStore captureStore) + : base(mongoDataAccess, targetedFields, resourceGraph, resourceFactory, constraintProviders, resourceDefinitionAccessor, queryableBuilder) { _captureStore = captureStore; } diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/QueryStrings/SparseFieldSets/SparseFieldSetTests.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/QueryStrings/SparseFieldSets/SparseFieldSetTests.cs index b5b2344..dca4609 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/QueryStrings/SparseFieldSets/SparseFieldSetTests.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/QueryStrings/SparseFieldSets/SparseFieldSetTests.cs @@ -300,7 +300,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => } [Fact] - public async Task Retrieves_all_properties_when_fieldset_contains_readonly_attribute() + public async Task Fetches_all_scalar_properties_when_fieldset_contains_readonly_attribute() { // Arrange var store = _testContext.Factory.Services.GetRequiredService(); diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/WorkTag.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/WorkTag.cs index 48de6cf..bbd12e5 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/WorkTag.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/WorkTag.cs @@ -6,6 +6,7 @@ namespace JsonApiDotNetCoreMongoDbTests.IntegrationTests.ReadWrite; [UsedImplicitly(ImplicitUseTargetFlags.Members)] +[Resource(ControllerNamespace = "JsonApiDotNetCoreMongoDbTests.IntegrationTests.ReadWrite")] public sealed class WorkTag : HexStringMongoIdentifiable { [Attr] diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ResourceDefinitions/Reading/MoonDefinition.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ResourceDefinitions/Reading/MoonDefinition.cs index b2b8fa6..26dcdb5 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ResourceDefinitions/Reading/MoonDefinition.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ResourceDefinitions/Reading/MoonDefinition.cs @@ -29,7 +29,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/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ResourceDefinitions/Reading/ResourceDefinitionReadTests.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ResourceDefinitions/Reading/ResourceDefinitionReadTests.cs index 521da09..d9b54c4 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ResourceDefinitions/Reading/ResourceDefinitionReadTests.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ResourceDefinitions/Reading/ResourceDefinitionReadTests.cs @@ -1,5 +1,4 @@ using System.Net; -using System.Text.Json; using FluentAssertions; using JsonApiDotNetCore.Configuration; using JsonApiDotNetCore.Serialization.Objects; @@ -70,11 +69,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => responseDocument.Data.ManyValue[0].Id.Should().Be(planets[1].StringId); responseDocument.Data.ManyValue[1].Id.Should().Be(planets[3].StringId); - responseDocument.Meta.ShouldContainKey("total").With(value => - { - JsonElement element = value.Should().BeOfType().Subject; - element.GetInt32().Should().Be(2); - }); + responseDocument.Meta.Should().ContainTotal(2); hitCounter.HitExtensibilityPoints.Should().BeEquivalentTo(new[] { @@ -129,11 +124,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => responseDocument.Data.ManyValue.ShouldHaveCount(1); responseDocument.Data.ManyValue[0].Id.Should().Be(planets[3].StringId); - responseDocument.Meta.ShouldContainKey("total").With(value => - { - JsonElement element = value.Should().BeOfType().Subject; - element.GetInt32().Should().Be(1); - }); + responseDocument.Meta.Should().ContainTotal(1); hitCounter.HitExtensibilityPoints.Should().BeEquivalentTo(new[] { diff --git a/test/JsonApiDotNetCoreMongoDbTests/JsonApiDotNetCoreMongoDbTests.csproj b/test/JsonApiDotNetCoreMongoDbTests/JsonApiDotNetCoreMongoDbTests.csproj index c202a91..dbfe9fa 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/JsonApiDotNetCoreMongoDbTests.csproj +++ b/test/JsonApiDotNetCoreMongoDbTests/JsonApiDotNetCoreMongoDbTests.csproj @@ -3,12 +3,6 @@ $(TargetFrameworkName) - - - PreserveNewest - - - diff --git a/test/JsonApiDotNetCoreMongoDbTests/xunit.runner.json b/test/JsonApiDotNetCoreMongoDbTests/xunit.runner.json deleted file mode 100644 index 9db029b..0000000 --- a/test/JsonApiDotNetCoreMongoDbTests/xunit.runner.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "parallelizeAssembly": false, - "parallelizeTestCollections": false -} diff --git a/test/TestBuildingBlocks/FakerContainer.cs b/test/TestBuildingBlocks/FakerContainer.cs index 99cce6e..72f9a05 100644 --- a/test/TestBuildingBlocks/FakerContainer.cs +++ b/test/TestBuildingBlocks/FakerContainer.cs @@ -12,7 +12,7 @@ static FakerContainer() { // Setting the system DateTime to kind Utc, so that faker calls like PastOffset() don't depend on the system time zone. // See https://docs.microsoft.com/en-us/dotnet/api/system.datetimeoffset.op_implicit?view=net-6.0#remarks - Date.SystemClock = () => 1.January(2020).AsUtc(); + Date.SystemClock = () => 1.January(2020).At(1, 1, 1).AsUtc(); } protected static int GetFakerSeed() diff --git a/test/TestBuildingBlocks/IntegrationTest.cs b/test/TestBuildingBlocks/IntegrationTest.cs index c66e6c5..a42877a 100644 --- a/test/TestBuildingBlocks/IntegrationTest.cs +++ b/test/TestBuildingBlocks/IntegrationTest.cs @@ -2,16 +2,26 @@ 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; + protected abstract JsonSerializerOptions SerializerOptions { get; } + static IntegrationTest() + { + int maxConcurrentTestRuns = Environment.GetEnvironmentVariable("APPVEYOR") != null ? 32 : 64; + ThrottleSemaphore = new SemaphoreSlim(maxConcurrentTestRuns); + } + public async Task<(HttpResponseMessage httpResponse, TResponseDocument responseDocument)> ExecuteGetAsync(string requestUrl, Action? setRequestHeaders = null) { @@ -99,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 2e3cc72..826447d 100644 --- a/test/TestBuildingBlocks/IntegrationTestContext.cs +++ b/test/TestBuildingBlocks/IntegrationTestContext.cs @@ -28,7 +28,7 @@ namespace TestBuildingBlocks; /// . /// [UsedImplicitly(ImplicitUseKindFlags.InstantiatedNoFixedConstructorSignature)] -public class IntegrationTestContext : IntegrationTest, IDisposable +public class IntegrationTestContext : IntegrationTest where TStartup : class where TMongoDbContextShim : MongoDbContextShim { @@ -125,19 +125,6 @@ private void ConfigureJsonApiOptions(JsonApiOptions options) options.SerializerOptions.WriteIndented = true; } - public void Dispose() - { - if (_lazyFactory.IsValueCreated) - { - _lazyFactory.Value.Dispose(); - } - - if (_runner.IsValueCreated) - { - _runner.Value.Dispose(); - } - } - public void ConfigureServicesAfterStartup(Action servicesConfiguration) { _afterServicesConfiguration = servicesConfiguration; @@ -151,6 +138,26 @@ public async Task RunOnDatabaseAsync(Func asyncAction await asyncAction(mongoDbContextShim); } + public override async Task DisposeAsync() + { + try + { + if (_lazyFactory.IsValueCreated) + { + await _lazyFactory.Value.DisposeAsync(); + } + + if (_runner.IsValueCreated) + { + _runner.Value.Dispose(); + } + } + finally + { + await base.DisposeAsync(); + } + } + private sealed class IntegrationTestWebApplicationFactory : WebApplicationFactory { private Action? _beforeServicesConfiguration; diff --git a/test/TestBuildingBlocks/ObjectAssertionsExtensions.cs b/test/TestBuildingBlocks/ObjectAssertionsExtensions.cs index c78a005..1e833be 100644 --- a/test/TestBuildingBlocks/ObjectAssertionsExtensions.cs +++ b/test/TestBuildingBlocks/ObjectAssertionsExtensions.cs @@ -1,4 +1,6 @@ +using System.Text.Json; using FluentAssertions; +using FluentAssertions.Collections; using FluentAssertions.Numeric; using JetBrains.Annotations; @@ -19,4 +21,13 @@ public static AndConstraint> BeApproximately( { return parent.BeApproximately(expectedValue, NumericPrecision, because, becauseArgs); } + + /// + /// Asserts that a "meta" dictionary contains a single element named "total" with the specified value. + /// + [CustomAssertion] + public static void ContainTotal(this GenericDictionaryAssertions, string, object?> source, int expectedTotal) + { + source.ContainKey("total").WhoseValue.Should().BeOfType().Subject.GetInt32().Should().Be(expectedTotal); + } } diff --git a/test/TestBuildingBlocks/TestBuildingBlocks.csproj b/test/TestBuildingBlocks/TestBuildingBlocks.csproj index db9e8da..629c236 100644 --- a/test/TestBuildingBlocks/TestBuildingBlocks.csproj +++ b/test/TestBuildingBlocks/TestBuildingBlocks.csproj @@ -10,11 +10,11 @@ - + - - + + diff --git a/test/TestBuildingBlocks/appsettings.json b/test/TestBuildingBlocks/appsettings.json index edbd7e4..5c69fe7 100644 --- a/test/TestBuildingBlocks/appsettings.json +++ b/test/TestBuildingBlocks/appsettings.json @@ -2,8 +2,11 @@ "Logging": { "LogLevel": { "Default": "Warning", + // Disable logging to keep the output from C/I build clean. Errors are expected to occur while testing failure handling. + "Microsoft.AspNetCore.Hosting.Diagnostics": "None", "Microsoft.Hosting.Lifetime": "Warning", - "Microsoft.EntityFrameworkCore": "Critical" + "Microsoft.EntityFrameworkCore": "Critical", + "JsonApiDotNetCore": "Critical" } } } pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy