a web server fixture shared state
This commit is contained in:
parent
b1a7a47f69
commit
5b6caa72b5
2 changed files with 140 additions and 111 deletions
|
|
@ -23,7 +23,7 @@ namespace yavscTests
|
||||||
var serverUrl = _serverFixture.Addresses.FirstOrDefault(u => u.StartsWith("https:"));
|
var serverUrl = _serverFixture.Addresses.FirstOrDefault(u => u.StartsWith("https:"));
|
||||||
if (string.IsNullOrEmpty(serverUrl))
|
if (string.IsNullOrEmpty(serverUrl))
|
||||||
throw new InvalidOperationException("No HTTPS server address found");
|
throw new InvalidOperationException("No HTTPS server address found");
|
||||||
|
|
||||||
HttpClient client = NewHttpClient();
|
HttpClient client = NewHttpClient();
|
||||||
var disco = await client.GetDiscoveryDocumentAsync(serverUrl);
|
var disco = await client.GetDiscoveryDocumentAsync(serverUrl);
|
||||||
if (disco.IsError) throw new Exception(disco.Error);
|
if (disco.IsError) throw new Exception(disco.Error);
|
||||||
|
|
@ -39,10 +39,10 @@ namespace yavscTests
|
||||||
if (response.IsError) throw new Exception(response.Error);
|
if (response.IsError) throw new Exception(response.Error);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static HttpClient NewHttpClient()
|
private static HttpClient NewHttpClient()
|
||||||
{
|
{
|
||||||
return new HttpClient(new BypassSslValidationHandler());
|
return new HttpClient(new BypassSslValidationHandler());
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task ObtainResourceOwnerPasswordToken()
|
public async Task ObtainResourceOwnerPasswordToken()
|
||||||
|
|
@ -50,7 +50,7 @@ namespace yavscTests
|
||||||
var serverUrl = _serverFixture.Addresses.FirstOrDefault(u => u.StartsWith("https:"));
|
var serverUrl = _serverFixture.Addresses.FirstOrDefault(u => u.StartsWith("https:"));
|
||||||
if (string.IsNullOrEmpty(serverUrl))
|
if (string.IsNullOrEmpty(serverUrl))
|
||||||
throw new InvalidOperationException("No HTTPS server address found");
|
throw new InvalidOperationException("No HTTPS server address found");
|
||||||
|
|
||||||
var client = NewHttpClient();
|
var client = NewHttpClient();
|
||||||
var disco = await client.GetDiscoveryDocumentAsync(serverUrl);
|
var disco = await client.GetDiscoveryDocumentAsync(serverUrl);
|
||||||
if (disco.IsError) throw new Exception(disco.Error);
|
if (disco.IsError) throw new Exception(disco.Error);
|
||||||
|
|
@ -80,22 +80,22 @@ namespace yavscTests
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal class BypassSslValidationHandler : HttpClientHandler
|
internal class BypassSslValidationHandler : HttpClientHandler
|
||||||
{
|
|
||||||
public BypassSslValidationHandler()
|
|
||||||
{
|
{
|
||||||
// Override validation for this handler only
|
public BypassSslValidationHandler()
|
||||||
ServerCertificateCustomValidationCallback = ValidateCertificate;
|
{
|
||||||
}
|
// Override validation for this handler only
|
||||||
|
ServerCertificateCustomValidationCallback = ValidateCertificate;
|
||||||
private bool ValidateCertificate(
|
}
|
||||||
HttpRequestMessage request,
|
|
||||||
X509Certificate2? certificate,
|
private bool ValidateCertificate(
|
||||||
X509Chain? chain,
|
HttpRequestMessage request,
|
||||||
SslPolicyErrors errors)
|
X509Certificate2? certificate,
|
||||||
{
|
X509Chain? chain,
|
||||||
// Accept all certificates (bypass validation)
|
SslPolicyErrors errors)
|
||||||
return true;
|
{
|
||||||
|
// Accept all certificates (bypass validation)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -29,9 +29,20 @@ namespace isnd.tests
|
||||||
public class WebServerFixture : IDisposable
|
public class WebServerFixture : IDisposable
|
||||||
{
|
{
|
||||||
private static readonly Lazy<X509Certificate2> _selfSignedCertificate = new Lazy<X509Certificate2>(CreateSelfSignedCertificate);
|
private static readonly Lazy<X509Certificate2> _selfSignedCertificate = new Lazy<X509Certificate2>(CreateSelfSignedCertificate);
|
||||||
|
private static readonly object _sync = new object();
|
||||||
private static WebApplication? _app;
|
private static WebApplication? _app;
|
||||||
private static bool _isInitialized = false;
|
private static bool _isInitialized = false;
|
||||||
private static int _instanceCount = 0;
|
private static int _instanceCount = 0;
|
||||||
|
private static readonly List<string> _sharedAddresses = new List<string>();
|
||||||
|
private static string? _sharedTestClientId;
|
||||||
|
private static string? _sharedTestClientSecret;
|
||||||
|
private static string? _sharedTestingUserName;
|
||||||
|
private static string? _sharedTestingUserPassword;
|
||||||
|
private static string? _sharedTestingUserEmail;
|
||||||
|
private static IServiceProvider? _sharedServices;
|
||||||
|
private static IConfiguration? _sharedConfiguration;
|
||||||
|
private static SiteSettings? _sharedSiteSettings;
|
||||||
|
private static Microsoft.Extensions.Logging.ILogger? _sharedLogger;
|
||||||
|
|
||||||
public List<string> Addresses { get; private set; } = new List<string>();
|
public List<string> Addresses { get; private set; } = new List<string>();
|
||||||
public Microsoft.Extensions.Logging.ILogger? Logger { get; internal set; }
|
public Microsoft.Extensions.Logging.ILogger? Logger { get; internal set; }
|
||||||
|
|
@ -40,7 +51,6 @@ namespace isnd.tests
|
||||||
|
|
||||||
public IConfiguration? Configuration { get; private set; }
|
public IConfiguration? Configuration { get; private set; }
|
||||||
|
|
||||||
private WebApplication? app;
|
|
||||||
public string? TestClientId { get; private set; }
|
public string? TestClientId { get; private set; }
|
||||||
|
|
||||||
public IServiceProvider? Services { get; private set; }
|
public IServiceProvider? Services { get; private set; }
|
||||||
|
|
@ -52,10 +62,10 @@ namespace isnd.tests
|
||||||
public bool DbCreated { get; internal set; }
|
public bool DbCreated { get; internal set; }
|
||||||
public SiteSettings? SiteSettings { get => siteSettings; set => siteSettings = value; }
|
public SiteSettings? SiteSettings { get => siteSettings; set => siteSettings = value; }
|
||||||
public string? TestClientSecret { get; set; }
|
public string? TestClientSecret { get; set; }
|
||||||
|
public string? TestingUserEmail { get; set; }
|
||||||
public WebServerFixture()
|
public WebServerFixture()
|
||||||
{
|
{
|
||||||
lock (this)
|
lock (_sync)
|
||||||
{
|
{
|
||||||
_instanceCount++;
|
_instanceCount++;
|
||||||
if (!_isInitialized)
|
if (!_isInitialized)
|
||||||
|
|
@ -63,25 +73,14 @@ namespace isnd.tests
|
||||||
SetupHost().Wait();
|
SetupHost().Wait();
|
||||||
_isInitialized = true;
|
_isInitialized = true;
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
CopySharedState();
|
||||||
// Get addresses from existing app
|
|
||||||
var server = _app!.Services.GetRequiredService<IServer>();
|
|
||||||
var addressFeatures = server.Features.Get<IServerAddressesFeature>();
|
|
||||||
if (addressFeatures?.Addresses != null)
|
|
||||||
{
|
|
||||||
foreach (var address in addressFeatures.Addresses)
|
|
||||||
{
|
|
||||||
Addresses.Add(address);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
lock (this)
|
lock (_sync)
|
||||||
{
|
{
|
||||||
_instanceCount--;
|
_instanceCount--;
|
||||||
if (_instanceCount == 0 && _app != null)
|
if (_instanceCount == 0 && _app != null)
|
||||||
|
|
@ -89,9 +88,33 @@ namespace isnd.tests
|
||||||
_app.StopAsync().Wait();
|
_app.StopAsync().Wait();
|
||||||
_app = null;
|
_app = null;
|
||||||
_isInitialized = false;
|
_isInitialized = false;
|
||||||
|
_sharedAddresses.Clear();
|
||||||
|
_sharedServices = null;
|
||||||
|
_sharedConfiguration = null;
|
||||||
|
_sharedSiteSettings = null;
|
||||||
|
_sharedLogger = null;
|
||||||
|
_sharedTestClientId = null;
|
||||||
|
_sharedTestClientSecret = null;
|
||||||
|
_sharedTestingUserName = null;
|
||||||
|
_sharedTestingUserPassword = null;
|
||||||
|
_sharedTestingUserEmail = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void CopySharedState()
|
||||||
|
{
|
||||||
|
Addresses = new List<string>(_sharedAddresses);
|
||||||
|
Logger = _sharedLogger;
|
||||||
|
Configuration = _sharedConfiguration;
|
||||||
|
Services = _sharedServices;
|
||||||
|
SiteSettings = _sharedSiteSettings;
|
||||||
|
TestClientId = _sharedTestClientId;
|
||||||
|
TestClientSecret = _sharedTestClientSecret;
|
||||||
|
TestingUserName = _sharedTestingUserName;
|
||||||
|
TestingUserPassword = _sharedTestingUserPassword;
|
||||||
|
TestingUserEmail = _sharedTestingUserEmail;
|
||||||
|
}
|
||||||
void ConfigureLogger() => Log.Logger = new LoggerConfiguration()
|
void ConfigureLogger() => Log.Logger = new LoggerConfiguration()
|
||||||
.MinimumLevel.Debug()
|
.MinimumLevel.Debug()
|
||||||
.MinimumLevel.Override("Microsoft", LogEventLevel.Warning)
|
.MinimumLevel.Override("Microsoft", LogEventLevel.Warning)
|
||||||
|
|
@ -128,10 +151,10 @@ namespace isnd.tests
|
||||||
["Smtp:SenderEmail"] = "test@example.com"
|
["Smtp:SenderEmail"] = "test@example.com"
|
||||||
});
|
});
|
||||||
|
|
||||||
// Configure Kestrel for HTTPS with self-signed certificate
|
// Configure Kestrel for HTTPS with self-signed certificate on a dynamic port
|
||||||
builder.WebHost.ConfigureKestrel(options =>
|
builder.WebHost.ConfigureKestrel(options =>
|
||||||
{
|
{
|
||||||
options.Listen(IPAddress.Loopback, 5001, listenOptions =>
|
options.Listen(IPAddress.Loopback, 0, listenOptions =>
|
||||||
{
|
{
|
||||||
listenOptions.UseHttps(_selfSignedCertificate.Value);
|
listenOptions.UseHttps(_selfSignedCertificate.Value);
|
||||||
});
|
});
|
||||||
|
|
@ -142,7 +165,7 @@ namespace isnd.tests
|
||||||
_app = builder.ConfigureWebAppServices();
|
_app = builder.ConfigureWebAppServices();
|
||||||
Services = _app.Services;
|
Services = _app.Services;
|
||||||
SiteSettings = _app.Services.GetRequiredService<IOptions<SiteSettings>>().Value;
|
SiteSettings = _app.Services.GetRequiredService<IOptions<SiteSettings>>().Value;
|
||||||
|
|
||||||
using (var migrationScope = _app.Services.CreateScope())
|
using (var migrationScope = _app.Services.CreateScope())
|
||||||
{
|
{
|
||||||
var db = migrationScope.ServiceProvider.GetRequiredService<ApplicationDbContext>();
|
var db = migrationScope.ServiceProvider.GetRequiredService<ApplicationDbContext>();
|
||||||
|
|
@ -151,104 +174,110 @@ namespace isnd.tests
|
||||||
TestingUserName = "Tester";
|
TestingUserName = "Tester";
|
||||||
TestingUserPassword = "tesT456+*";
|
TestingUserPassword = "tesT456+*";
|
||||||
TestClientId = "testClientId";
|
TestClientId = "testClientId";
|
||||||
|
TestingUserEmail = "test@no-reply.com";
|
||||||
|
TestingUser = null;
|
||||||
TestClientSecret = Guid.CreateVersion7().ToString();
|
TestClientSecret = Guid.CreateVersion7().ToString();
|
||||||
EnsureUser(TestingUserName, TestingUserPassword);
|
EnsureUser(TestingUserName, TestingUserPassword, TestingUserEmail, migrationScope);
|
||||||
AddAuthorizedClient(TestClientId, TestClientSecret);
|
AddAuthorizedClient(migrationScope, TestClientId, TestClientSecret);
|
||||||
TestingUser = await db.Users.FirstOrDefaultAsync(u => u.UserName == TestingUserName);
|
TestingUser = await db.Users.FirstOrDefaultAsync(u => u.UserName == TestingUserName);
|
||||||
}
|
}
|
||||||
await _app!.ConfigurePipeline();
|
await _app!.ConfigurePipeline();
|
||||||
_app.UseSession();
|
_app.UseSession();
|
||||||
await _app.StartAsync();
|
await _app.StartAsync();
|
||||||
|
|
||||||
|
_sharedServices = _app.Services;
|
||||||
|
_sharedConfiguration = Configuration;
|
||||||
|
_sharedSiteSettings = SiteSettings;
|
||||||
|
_sharedTestClientId = TestClientId;
|
||||||
|
_sharedTestClientSecret = TestClientSecret;
|
||||||
|
_sharedTestingUserName = TestingUserName;
|
||||||
|
_sharedTestingUserPassword = TestingUserPassword;
|
||||||
|
_sharedTestingUserEmail = TestingUserEmail;
|
||||||
|
_sharedLogger = _app.Services.GetRequiredService<ILoggerFactory>().CreateLogger<WebServerFixture>();
|
||||||
|
Logger = _sharedLogger;
|
||||||
|
|
||||||
|
var server = _app.Services.GetRequiredService<IServer>();
|
||||||
var logFactory = app.Services.GetRequiredService<ILoggerFactory>();
|
|
||||||
Logger = logFactory.CreateLogger<WebServerFixture>();
|
|
||||||
|
|
||||||
var server = app.Services.GetRequiredService<IServer>();
|
|
||||||
|
|
||||||
var addressFeatures = server.Features.Get<IServerAddressesFeature>();
|
var addressFeatures = server.Features.Get<IServerAddressesFeature>();
|
||||||
|
|
||||||
if (addressFeatures?.Addresses != null)
|
if (addressFeatures?.Addresses != null)
|
||||||
{
|
{
|
||||||
|
_sharedAddresses.Clear();
|
||||||
foreach (var address in addressFeatures.Addresses)
|
foreach (var address in addressFeatures.Addresses)
|
||||||
{
|
{
|
||||||
|
_sharedAddresses.Add(address);
|
||||||
Addresses.Add(address);
|
Addresses.Add(address);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void AddAuthorizedClient(string testClientId, string testClientSecret)
|
private void AddAuthorizedClient(IServiceScope scope, string testClientId, string testClientSecret)
|
||||||
{
|
{
|
||||||
using (IServiceScope scope = app!.Services.CreateScope())
|
|
||||||
|
var db = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>();
|
||||||
|
Client testingClient = new Client
|
||||||
{
|
{
|
||||||
var db = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>();
|
ClientId = testClientId,
|
||||||
Client testingClient = new Client
|
AccessTokenLifetime = 3600000,
|
||||||
{
|
AccessTokenType = 1,
|
||||||
ClientId = testClientId,
|
BackChannelLogoutUri = SiteSettings!.Audience,
|
||||||
AccessTokenLifetime = 3600000,
|
ClientName = "Testing client",
|
||||||
AccessTokenType = 1,
|
Enabled = true
|
||||||
BackChannelLogoutUri = SiteSettings!.Audience,
|
};
|
||||||
ClientName = "Testing client",
|
db.Clients.Add(testingClient);
|
||||||
Enabled = true
|
db.SaveChanges();
|
||||||
};
|
ClientSecret secret = new ClientSecret
|
||||||
db.Clients.Add(testingClient);
|
{
|
||||||
db.SaveChanges();
|
Value = testClientSecret.Sha256(),
|
||||||
ClientSecret secret = new ClientSecret
|
ClientId = testingClient.Id
|
||||||
{
|
};
|
||||||
Value = testClientSecret.Sha256(),
|
db.ClientSecrets.Add(secret);
|
||||||
ClientId = testingClient.Id
|
|
||||||
};
|
|
||||||
db.ClientSecrets.Add(secret);
|
|
||||||
|
|
||||||
var testOrigin = new ClientCorsOrigin
|
var testOrigin = new ClientCorsOrigin
|
||||||
{
|
{
|
||||||
ClientId = testingClient.Id,
|
ClientId = testingClient.Id,
|
||||||
Origin = SiteSettings!.Audience
|
Origin = SiteSettings!.Audience
|
||||||
|
|
||||||
};
|
};
|
||||||
db.ClientCorsOrigins.Add(testOrigin);
|
db.ClientCorsOrigins.Add(testOrigin);
|
||||||
db.ClientGrantTypes.Add(new ClientGrantType
|
db.ClientGrantTypes.Add(new ClientGrantType
|
||||||
{
|
{
|
||||||
ClientId = testingClient.Id,
|
ClientId = testingClient.Id,
|
||||||
GrantType = "client_credentials"
|
GrantType = "client_credentials"
|
||||||
});
|
});
|
||||||
db.ClientGrantTypes.Add(new ClientGrantType
|
db.ClientGrantTypes.Add(new ClientGrantType
|
||||||
{
|
{
|
||||||
ClientId = testingClient.Id,
|
ClientId = testingClient.Id,
|
||||||
GrantType = "password"
|
GrantType = "password"
|
||||||
});
|
});
|
||||||
db.ClientGrantTypes.Add(new ClientGrantType
|
db.ClientGrantTypes.Add(new ClientGrantType
|
||||||
{
|
{
|
||||||
ClientId = testingClient.Id,
|
ClientId = testingClient.Id,
|
||||||
GrantType = "code"
|
GrantType = "code"
|
||||||
});
|
});
|
||||||
db.ClientScopes.Add(new ClientScope
|
db.ClientScopes.Add(new ClientScope
|
||||||
{
|
{
|
||||||
ClientId = testingClient.Id,
|
ClientId = testingClient.Id,
|
||||||
Scope = "test"
|
Scope = "test"
|
||||||
});
|
});
|
||||||
db.ApiScopes.Add(new IdentityServer8.EntityFramework.Entities.ApiScope
|
db.ApiScopes.Add(new IdentityServer8.EntityFramework.Entities.ApiScope
|
||||||
{
|
{
|
||||||
Name = "test",
|
Name = "test",
|
||||||
Enabled = true
|
Enabled = true
|
||||||
});
|
});
|
||||||
db.ClientRedirectUris.Add(new ClientRedirectUri
|
db.ClientRedirectUris.Add(new ClientRedirectUri
|
||||||
{
|
{
|
||||||
ClientId = testingClient.Id,
|
ClientId = testingClient.Id,
|
||||||
RedirectUri = SiteSettings!.Audience
|
RedirectUri = SiteSettings!.Audience
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
db.SaveChanges();
|
db.SaveChanges();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void EnsureUser(string testingUserName, string password)
|
public void EnsureUser(string testingUserName, string password, string email, IServiceScope scope)
|
||||||
{
|
{
|
||||||
if (TestingUser == null)
|
if (TestingUser == null)
|
||||||
{
|
{
|
||||||
using IServiceScope scope = app!.Services.CreateScope();
|
|
||||||
|
|
||||||
var userManager =
|
var userManager =
|
||||||
scope.ServiceProvider.GetRequiredService<UserManager<ApplicationUser>>();
|
scope.ServiceProvider.GetRequiredService<UserManager<ApplicationUser>>();
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue