Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,12 @@ jobs:
with:
username: postgres
password: postgres
- name: Configure PostgreSQL
shell: bash
run: |
PGDATA="$RUNNER_TEMP/pgdata"
echo "max_connections = 500" >> "$PGDATA/postgresql.conf"
pg_ctl restart --pgdata="$PGDATA" --wait
- name: Setup .NET
uses: actions/setup-dotnet@v5
with:
Expand Down Expand Up @@ -104,7 +110,22 @@ jobs:
Logging__LogLevel__Microsoft.Extensions.Hosting.Internal.Host: 'None'
Logging__LogLevel__Microsoft.EntityFrameworkCore.Database.Command: 'None'
Logging__LogLevel__JsonApiDotNetCore: 'None'
JADNC_THROTTLE_DIAG: '1'
run: dotnet test --no-build --configuration Release --collect:"XPlat Code Coverage" --logger "GitHubActions;annotations-title=@test (@framework);annotations-message=@error\n@trace;summary-include-passed=false"
- name: Print throttle diagnostics
if: always()
shell: bash
run: |
shopt -s nullglob
files=("$RUNNER_TEMP"/jadnc-throttle-*.log)
if [[ ${#files[@]} -eq 0 ]]; then
echo "No throttle diagnostic files found."
else
for f in "${files[@]}"; do
echo "=== $f ==="
cat "$f"
done
fi
- name: Upload coverage to codecov.io
if: ${{ matrix.os == 'ubuntu-latest' }}
env:
Expand Down
2 changes: 1 addition & 1 deletion src/Examples/DapperExample/appsettings.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"ConnectionStrings": {
// docker run --rm --detach --name dapper-example-postgresql-db -e POSTGRES_USER=postgres -e POSTGRES_PASSWORD=postgres -p 5432:5432 postgres:latest
// docker run --rm --detach --name dapper-example-postgresql-management --link dapper-example-postgresql-db:db -e PGADMIN_DEFAULT_EMAIL=admin@admin.com -e PGADMIN_DEFAULT_PASSWORD=postgres -p 5050:80 dpage/pgadmin4:latest
"DapperExamplePostgreSql": "Host=localhost;Database=DapperExample;User ID=postgres;Password=postgres;Include Error Detail=true",
"DapperExamplePostgreSql": "Host=localhost;Database=DapperExample;User ID=postgres;Password=postgres;Include Error Detail=true;Command Timeout=120",
// docker run --rm --detach --name dapper-example-mysql-db -e MYSQL_ROOT_PASSWORD=mysql -e MYSQL_DATABASE=DapperExample -e MYSQL_USER=mysql -e MYSQL_PASSWORD=mysql -p 3306:3306 mysql:latest
// docker run --rm --detach --name dapper-example-mysql-management --link dapper-example-mysql-db:db -p 8081:80 phpmyadmin/phpmyadmin
"DapperExampleMySql": "Host=localhost;Database=DapperExample;User ID=mysql;Password=mysql;SSL Mode=None;AllowPublicKeyRetrieval=True",
Expand Down
6 changes: 3 additions & 3 deletions src/Examples/DatabasePerTenantExample/appsettings.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
{
"ConnectionStrings": {
"Default": "Host=localhost;Database=DefaultTenantDb;User ID=postgres;Password=postgres;Include Error Detail=true",
"AdventureWorks": "Host=localhost;Database=AdventureWorks;User ID=postgres;Password=postgres;Include Error Detail=true",
"Contoso": "Host=localhost;Database=Contoso;User ID=postgres;Password=postgres;Include Error Detail=true"
"Default": "Host=localhost;Database=DefaultTenantDb;User ID=postgres;Password=postgres;Include Error Detail=true;Command Timeout=120",
"AdventureWorks": "Host=localhost;Database=AdventureWorks;User ID=postgres;Password=postgres;Include Error Detail=true;Command Timeout=120",
"Contoso": "Host=localhost;Database=Contoso;User ID=postgres;Password=postgres;Include Error Detail=true;Command Timeout=120"
},
"Logging": {
"LogLevel": {
Expand Down
2 changes: 1 addition & 1 deletion src/Examples/JsonApiDotNetCoreExample/appsettings.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"ConnectionStrings": {
"Default": "Host=localhost;Database=JsonApiDotNetCoreExample;User ID=postgres;Password=postgres;Include Error Detail=true"
"Default": "Host=localhost;Database=JsonApiDotNetCoreExample;User ID=postgres;Password=postgres;Include Error Detail=true;Command Timeout=120"
},
"Logging": {
"LogLevel": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ private bool ShouldIncludeTopLevelLink(LinkTypes linkType, ResourceType? resourc

private string GetLinkForTopLevelSelf()
{
// Note: in tests, this does not properly escape special characters due to WebApplicationFactory short-circuiting.
// Note: in tests, this does not properly escape special characters due to TestServer short-circuiting.
return _options.UseRelativeLinks ? HttpContext.Request.GetEncodedPathAndQuery() : HttpContext.Request.GetEncodedUrl();
}

Expand Down
14 changes: 10 additions & 4 deletions test/DapperTests/IntegrationTests/DapperTestContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,13 @@
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Logging;
using TestBuildingBlocks;
using Xunit;
using Xunit.Abstractions;

namespace DapperTests.IntegrationTests;

[PublicAPI]
public sealed class DapperTestContext : IntegrationTest
public sealed class DapperTestContext : IntegrationTest, IAsyncLifetime
{
private const string SqlServerClearAllTablesScript = """
EXEC sp_MSForEachTable 'ALTER TABLE ? NOCHECK CONSTRAINT ALL';
Expand Down Expand Up @@ -48,6 +49,11 @@ public DapperTestContext()
_lazyFactory = new Lazy<WebApplicationFactory<TodoItem>>(CreateFactory);
}

public async Task InitializeAsync()
{
await AcquireDbThrottleAsync();
}

private WebApplicationFactory<TodoItem> CreateFactory()
{
#pragma warning disable CA2000 // Dispose objects before losing scope
Expand All @@ -56,7 +62,7 @@ private WebApplicationFactory<TodoItem> CreateFactory()
#pragma warning restore CA2000 // Dispose objects before losing scope
{
builder.UseSetting("ConnectionStrings:DapperExamplePostgreSql",
$"Host=localhost;Database=DapperExample-{Guid.NewGuid():N};User ID=postgres;Password=postgres;Include Error Detail=true");
$"Host=localhost;Database=DapperExample-{Guid.NewGuid():N};User ID=postgres;Password=postgres;Include Error Detail=true;Command Timeout=120");

builder.UseSetting("ConnectionStrings:DapperExampleMySql",
$"Host=localhost;Database=DapperExample-{Guid.NewGuid():N};User ID=root;Password=mysql;SSL Mode=None;AllowPublicKeyRetrieval=True");
Expand Down Expand Up @@ -141,7 +147,7 @@ protected override HttpClient CreateClient()
return Factory.CreateClient();
}

public override async Task DisposeAsync()
public async Task DisposeAsync()
{
try
{
Expand All @@ -159,7 +165,7 @@ public override async Task DisposeAsync()
}
finally
{
await base.DisposeAsync();
ReleaseDbThrottle();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ public AtomicTransactionConsistencyTests(IntegrationTestContext<TestableStartup<
services.AddResourceRepository<LyricRepository>();

string dbConnectionString =
$"Host=localhost;Database=JsonApiTest-Extra-{Guid.NewGuid():N};User ID=postgres;Password=postgres;Include Error Detail=true";
$"Host=localhost;Database=JsonApiTest-Extra-{Guid.NewGuid():N};User ID=postgres;Password=postgres;Include Error Detail=true;Command Timeout=120";

services.AddDbContext<ExtraDbContext>(options => options.UseNpgsql(dbConnectionString));
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -432,7 +432,7 @@ public async Task Denies_JsonApi_ContentType_header_with_CharSet()
};

const string route = "/policies";
string contentType = $"{JsonApiMediaType.Default}; charset=ISO-8859-4";
string contentType = $"{JsonApiMediaType.Default}; charset=utf-8";

// Act
(HttpResponseMessage httpResponse, Document responseDocument) = await _testContext.ExecutePostAsync<Document>(route, requestBody, contentType);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Net;
using System.Runtime.CompilerServices;
using System.Text.Json;
using FluentAssertions;
using JetBrains.Annotations;
Expand Down Expand Up @@ -91,6 +92,7 @@ private sealed class ThrowingResourceObjectConverter(IResourceGraph resourceGrap
{
private readonly string? _relativeSourcePointer = relativeSourcePointer;

[MethodImpl(MethodImplOptions.NoInlining)]
private protected override void ValidateExtensionInAttributes(string extensionNamespace, string extensionName, ResourceType resourceType,
Utf8JsonReader reader)
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using JsonApiDotNetCore.OpenApi.Client.Kiota;
using Microsoft.AspNetCore.Mvc.Testing;
using Microsoft.Kiota.Abstractions.Authentication;
using Microsoft.Kiota.Http.HttpClientLibrary;
using Microsoft.Kiota.Http.HttpClientLibrary.Middleware;
Expand All @@ -21,10 +20,9 @@ public TestableHttpClientRequestAdapterFactory(ITestOutputHelper testOutputHelpe
_logHttpMessageHandler = new XUnitLogHttpMessageHandler(testOutputHelper);
}

public HttpClientRequestAdapter CreateAdapter<TStartup>(WebApplicationFactory<TStartup> webApplicationFactory)
where TStartup : class
public HttpClientRequestAdapter CreateAdapter(FactoryBridge bridge)
{
ArgumentNullException.ThrowIfNull(webApplicationFactory);
ArgumentNullException.ThrowIfNull(bridge);

DelegatingHandler[] handlers =
[
Expand All @@ -33,7 +31,7 @@ public HttpClientRequestAdapter CreateAdapter<TStartup>(WebApplicationFactory<TS
_logHttpMessageHandler
];

HttpClient httpClient = webApplicationFactory.CreateDefaultClient(handlers);
HttpClient httpClient = bridge.GetTestClient(handlers);
return new HttpClientRequestAdapter(new AnonymousAuthenticationProvider(), httpClient: httpClient);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using FluentAssertions;
using FluentAssertions;
using JsonApiDotNetCore.Errors;
using Xunit;
using Xunit.Abstractions;
Expand Down
5 changes: 3 additions & 2 deletions test/OpenApiTests/OpenApiTestContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ namespace OpenApiTests;

[UsedImplicitly(ImplicitUseKindFlags.InstantiatedNoFixedConstructorSignature)]
public class OpenApiTestContext<TStartup, TDbContext> : IntegrationTestContext<TStartup, TDbContext>
where TStartup : class
where TStartup : IStartup, new()
where TDbContext : TestableDbContext
{
private readonly Lazy<Task<JsonElement>> _lazyDocument;
Expand All @@ -19,6 +19,7 @@ public class OpenApiTestContext<TStartup, TDbContext> : IntegrationTestContext<T

public OpenApiTestContext()
{
CaptureHttpTraffic = false;
_lazyDocument = new Lazy<Task<JsonElement>>(CreateOpenApiDocumentAsync, LazyThreadSafetyMode.ExecutionAndPublication);
}

Expand All @@ -39,7 +40,7 @@ internal async Task<JsonElement> CreateOpenApiDocumentAsync()
return rootElement;
}

internal void SetTestOutputHelper(ITestOutputHelper testOutputHelper)
internal void SetTestOutputHelper(ITestOutputHelper? testOutputHelper)
{
ArgumentNullException.ThrowIfNull(testOutputHelper);

Expand Down
70 changes: 70 additions & 0 deletions test/TestBuildingBlocks/FactoryBridge.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.TestHost;
using Microsoft.Extensions.Hosting;

namespace TestBuildingBlocks;

/// <summary>
/// A temporary bridge to prevent adapting all existing tests.
/// </summary>
public sealed class FactoryBridge
{
private readonly WebApplication _app;
private readonly bool _captureHttpTraffic;
private bool _hasStartedApp;

public IServiceProvider Services => _app.Services;

internal FactoryBridge(WebApplication app, bool captureHttpTraffic)
{
ArgumentNullException.ThrowIfNull(app);

_app = app;
_captureHttpTraffic = captureHttpTraffic;
}

public HttpClient CreateClient()
{
return GetTestClient();
}

public HttpClient CreateDefaultClient(params DelegatingHandler[] handlers)
{
return GetTestClient(handlers);
}

public HttpClient GetTestClient(params DelegatingHandler[] handlers)
{
if (!_hasStartedApp)
{
_hasStartedApp = true;
_app.Start();
}

_ = _captureHttpTraffic;

if (handlers.Length == 0)
{
return _app.GetTestClient();
}

TestServer testServer = _app.GetTestServer();
HttpMessageHandler serverHandler = testServer.CreateHandler();
HttpClient httpClient = CreateHttpClient(serverHandler, handlers);

httpClient.BaseAddress ??= new Uri("http://localhost");

return httpClient;
}

private static HttpClient CreateHttpClient(HttpMessageHandler serverHandler, params DelegatingHandler[] handlers)
{
for (int i = handlers.Length - 1; i > 0; i--)
{
handlers[i - 1].InnerHandler = handlers[i];
}

handlers[^1].InnerHandler = serverHandler;
return new HttpClient(handlers[0]);
}
}
Loading
Loading