Relocate test project: test/yavscTests -> src/Yavsc.Org.Tests

Move the integration test project from the top-level test/ directory into
src/ alongside the projects it tests. Rename the project (and folder) to
Yavsc.Org.Tests to match .NET conventions and reflect that it tests the
Org runtime primarily.

Path changes:
- test/yavscTests/yavscTests.csproj -> src/Yavsc.Org.Tests/Yavsc.Org.Tests.csproj
- All .cs / .json / .resx files moved to their new location
- PostItViewModelTests moved out to the dedicated src/PostIt.Tests project
  (it was unrelated to Org testing)

Build adjustments:
- <ProjectReference> paths shortened (..\..\src\X -> ..\X)
- PostIt project reference removed (covered by its own test project)
- <OutputType>exe added (required by xunit.v3)
- xunit.v3.common and xunit.v3.extensibility.core added to package versions

Solution + sln:
- yavsc.sln Project Name updated to 'Yavsc.Org.Tests' and path updated
- GUID preserved so existing build configs stay valid

Static web assets:
- The CopyStaticWebAssetsManifest target was hard-coding the destination
  filename to 'testhost.staticwebassets.endpoints.json', which worked
  when the assembly was named 'yavscTests'. Now that the assembly name
  is 'Yavsc.Org.Tests', ASP.NET Core's MapStaticAssets() looks for
  'Yavsc.Org.Tests.staticwebassets.endpoints.json' (entry-assembly-based
  resolution). Use $(MSBuildProjectName) so the copy target stays
  correct under any future rename.
This commit is contained in:
Paul Schneider 2026-06-19 17:52:54 +01:00
commit 22b397ce7e
18 changed files with 16 additions and 22 deletions

View file

@ -0,0 +1,40 @@
using yavscTests.ServerFixtures;
namespace Yavsc.Tests
{
[Collection("Yavsc Server")]
public class DumpHtml : IClassFixture<WebServerFixture>
{
readonly WebServerFixture _server;
public DumpHtml(WebServerFixture server) { _server = server; }
// FIXME [Fact]
[Trait("debug", "html")]
public void DumpHomePageHtml()
{
var settings = _server.SiteSettings;
var handler = new HttpClientHandler
{
ServerCertificateCustomValidationCallback = (_, _, _, _) => true,
};
var httpsUrl = _server.Addresses.FirstOrDefault(u => u.StartsWith("https:"))
?? _server.Addresses.FirstOrDefault() ?? "";
// var httpsUrl = "https://localhost:5001" ;
using var client = new HttpClient(handler) { BaseAddress = new Uri(httpsUrl) };
var paths = new[] { "/Home/About", "/css/site.css", "/lib/bootstrap.quartz.min.css", "/nonexistent" };
var sb = new System.Text.StringBuilder();
sb.AppendLine($"BaseAddress = {httpsUrl}");
foreach (var p in paths)
{
var r = client.GetAsync(p).GetAwaiter().GetResult();
var b = r.Content.ReadAsStringAsync().GetAwaiter().GetResult();
sb.AppendLine($"GET {p} => {r.StatusCode} len={b.Length} ct={r.Content.Headers.ContentType}");
if (b.Length > 0 && b.Length < 500)
sb.AppendLine($" body: {b.Substring(0, Math.Min(300, b.Length))}");
}
Assert.Fail(sb.ToString());
}
}
}

View file

@ -0,0 +1,76 @@
using OpenQA.Selenium;
using OpenQA.Selenium.Chrome;
using OpenQA.Selenium.Firefox;
namespace yavscTests.ServerFixtures;
[Collection("Yavsc Server")]
public class FirstScript : BaseTestContext
{
public readonly WebServerFixture _serverFixture;
readonly ITestOutputHelper _output;
IWebDriver driver = new ChromeDriver();
public FirstScript(ITestOutputHelper output, WebServerFixture fixture) : base(output, fixture)
{
_serverFixture = fixture;
this._output = output;
}
[Fact]
public void DoTestSeleniumWebSite()
{
var firefoxOptions = new FirefoxOptions();
firefoxOptions.AcceptInsecureCertificates = true;
var driver = new FirefoxDriver(firefoxOptions);
driver.Navigate()
.GoToUrl(_serverFixture.Addresses.FirstOrDefault(u => u.StartsWith("http:")));
driver.Quit();
}
// FIXME [Fact]
public async Task DoTestYavscSite()
{
var firefoxOptions = new FirefoxOptions
{
AcceptInsecureCertificates = true
};
var driver = new FirefoxDriver(firefoxOptions);
var url = _serverFixture.Addresses.FirstOrDefault(u => u.StartsWith("http:"));
Assert.NotNull(url);
//driver.Navigate().GoToUrl(url);
driver.Navigate().GoToUrl("http://localhost:5000/Home/About");
var title = driver.Title;
driver.Manage().Timeouts().ImplicitWait = TimeSpan.FromMilliseconds(500);
var navbar = driver.FindElement(By.Id("navbar"));
Assert.NotNull(navbar);
/*
var textBox = driver.FindElement(By.Name("my-text"));
var submitButton = driver.FindElement(By.TagName("button"));
textBox.SendKeys("Selenium");
submitButton.Click();
var message = driver.FindElement(By.Id("message"));
var value = message.Text;
*/
driver.Quit();
}
}

View file

@ -0,0 +1,67 @@
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Yavsc.Models;
using Yavsc.Server.Models.IT.SourceCode;
using Microsoft.EntityFrameworkCore;
using yavscTests.ServerFixtures;
using Yavsc.Server.Models.IT;
namespace yavscTests
{
[Collection("Yavsc Server")]
[Trait("regression", "oui")]
public class BaseTestContext: IClassFixture<WebServerFixture>, IDisposable
{
public readonly WebServerFixture _serverFixture;
private readonly ITestOutputHelper _output;
public BaseTestContext(ITestOutputHelper output, WebServerFixture fixture)
{
this._serverFixture = fixture;
this._output = output;
}
// FIXME write a scenario from an empty database [Fact]
public void GitClone()
{
using var scope = _serverFixture.Services.CreateScope();
var dbContext = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>();
Assert.NotNull(dbContext.Project);
Project yavsc = new Project
{
Name = "Yavsc"
};
dbContext.Project.Add(yavsc);
dbContext.SaveChanges();
var firstProject = dbContext.Project.Include(p => p.Repository).FirstOrDefault(
p => p.Name == "Yavsc"
);
Assert.NotNull (firstProject);
var di = new DirectoryInfo(_serverFixture.SiteSettings.GitRepository);
if (!di.Exists) di.Create();
var clone = new GitClone(_serverFixture.SiteSettings.GitRepository);
clone.Launch(firstProject);
gitRepo = di.FullName;
}
string gitRepo=null;
private IConfigurationRoot configurationRoot;
[Fact]
public void HaveConfigurationRoot()
{
var builder = new ConfigurationBuilder();
configurationRoot = builder.Build();
}
public void Dispose()
{
if (gitRepo!=null)
{
Directory.Delete(Path.Combine(gitRepo,"yavsc"), true);
}
}
}
}

View file

@ -0,0 +1,100 @@
using System.Security.Cryptography.X509Certificates;
using System.Net.Security;
using yavscTests.ServerFixtures;
using IdentityModel.Client;
namespace yavscTests
{
[Collection("Yavsc Server")]
[Trait("regression", "oui")]
public class Remoting : BaseTestContext, IClassFixture<WebServerFixture>
{
public Remoting(WebServerFixture serverFixture, ITestOutputHelper output)
: base(output, serverFixture)
{
}
[Fact]
public async Task ObtainServiceToken()
{
var serverUrl = _serverFixture.Addresses.FirstOrDefault(u => u.StartsWith("https:"));
if (string.IsNullOrEmpty(serverUrl))
throw new InvalidOperationException("No HTTPS server address found");
HttpClient client = NewHttpClient();
var disco = await client.GetDiscoveryDocumentAsync(serverUrl);
if (disco.IsError) throw new Exception(disco.Error);
var response = await client.RequestClientCredentialsTokenAsync(new ClientCredentialsTokenRequest
{
Address = disco.TokenEndpoint,
ClientId = _serverFixture.TestClientId,
ClientSecret = _serverFixture.TestClientSecret,
Scope = "test",
GrantType = "client_credentials"
});
if (response.IsError) throw new Exception(response.Error);
}
private static HttpClient NewHttpClient()
{
return new HttpClient(new BypassSslValidationHandler());
}
[Fact]
public async Task ObtainResourceOwnerPasswordToken()
{
var serverUrl = _serverFixture.Addresses.FirstOrDefault(u => u.StartsWith("https:"));
if (string.IsNullOrEmpty(serverUrl))
throw new InvalidOperationException("No HTTPS server address found");
var client = NewHttpClient();
var disco = await client.GetDiscoveryDocumentAsync(serverUrl);
if (disco.IsError) throw new Exception(disco.Error);
var response = await client.RequestPasswordTokenAsync(new PasswordTokenRequest
{
Address = disco.TokenEndpoint,
ClientId = _serverFixture.TestClientId,
ClientSecret = _serverFixture.TestClientSecret,
UserName = _serverFixture.TestingUserName,
Password = _serverFixture.TestingUserPassword,
Scope = "test",
Parameters =
{
{ "acr_values", "tenant:custom_account_store1 foo bar quux" }
}
});
if (response.IsError) throw new Exception(response.Error);
}
public static IEnumerable<object[]> GetLoginIntentData()
{
return new object[][] { new object[] { "testuser", "test" } };
}
}
internal class BypassSslValidationHandler : HttpClientHandler
{
public BypassSslValidationHandler()
{
// Override validation for this handler only
ServerCertificateCustomValidationCallback = ValidateCertificate;
}
private bool ValidateCertificate(
HttpRequestMessage request,
X509Certificate2? certificate,
X509Chain? chain,
SslPolicyErrors errors)
{
// Accept all certificates (bypass validation)
return true;
}
}
}

View file

@ -0,0 +1,22 @@
namespace yavscTests {
public class ResxResources {
const string resPath = "Resources/Test.TestResources.resx";
public void HaveAResxLoader()
{
System.Resources.ResourceReader loader = new System.Resources.ResourceReader(resPath);
// IDictionary
var etor = loader.GetEnumerator();
while (etor.Current !=null)
{
byte[] data;
string stringdata;
string resName = etor.Key.ToString();
loader.GetResourceData(resName, out stringdata, out data);
}
}
}
}

View file

@ -0,0 +1,15 @@
using yavscTests.ServerFixtures;
namespace yavscTests.Mandatory;
[Collection("Yavsc Server")]
[Trait("regression", "oui")]
public class Services : BaseTestContext, IClassFixture<WebServerFixture>
{
public Services(ITestOutputHelper output, WebServerFixture fixture) : base(output, fixture)
{
}
}

View file

@ -0,0 +1,27 @@
using Yavsc.Server.Helpers;
namespace yavscTests
{
[Collection("Yavsc Abstract tests")]
[Trait("regression", "II")]
public class AbstractTests
{
readonly ITestOutputHelper output;
public AbstractTests(ITestOutputHelper output)
{
this.output = output;
}
[Fact]
public void UniqueFilenameAfterCleaning()
{
var name1 = "content:///scanned_files/2020-06-02/00.11.02.JPG";
var name2 = "content:///scanned_files/2020-06-02/00.11.03.JPG";
var cleanName1 = AbstractFileSystemHelpers.FilterFileName(name1);
var cleanName2 = AbstractFileSystemHelpers.FilterFileName(name2);
output.WriteLine($"{name1} => {cleanName1}");
output.WriteLine($"{name2} => {cleanName2}");
Assert.True(cleanName1 != cleanName2);
}
}
}

View file

@ -0,0 +1,48 @@
using Microsoft.EntityFrameworkCore;
using Xunit;
using Yavsc;
using Yavsc.Abstract.Workflow;
using Yavsc.Helpers;
using Yavsc.Models;
using Yavsc.Models.Billing;
using Yavsc.Models.Haircut;
using Yavsc.Services;
namespace yavscTests
{
[Trait("regression", "II")]
public class BillingServiceTests
{
[Fact]
public void ConfigureBillingService_CanBeCalledTwiceWithoutThrowing()
{
// First initialization should populate the billing registry.
WorkflowHelpers.ConfigureBillingService();
int firstBillingCount = BillingService.Billing.Count;
int firstSettingsCount = BillingService.UserSettings.Count;
int firstProfileTypesCount = Config.ProfileTypes.Count;
// Second call should be idempotent and not throw.
WorkflowHelpers.ConfigureBillingService();
Assert.Equal(firstBillingCount, BillingService.Billing.Count);
Assert.Equal(firstSettingsCount, BillingService.UserSettings.Count);
Assert.Equal(firstProfileTypesCount, Config.ProfileTypes.Count);
}
[Fact]
public void RegisterBilling_DuplicateRegistrationThrowsInvalidOperationException()
{
WorkflowHelpers.ConfigureBillingService();
var firstRegistrar = new Func<ApplicationDbContext, long, IDecidableQuery>((db, id) =>
db.HairCutQueries.Include(q => q.Prestation).Include(q => q.Regularisation).Single(q => q.Id == id));
const string testCode = "Brush";
Assert.Throws<InvalidOperationException>(() =>
WorkflowHelpers.RegisterBilling<HairCutQuery>(testCode, firstRegistrar));
}
}
}

View file

@ -0,0 +1,36 @@
using yavscTests.ServerFixtures;
namespace yavscTests.Mandatory
{
[Collection("Database")]
[Trait("regression", "II")]
[Trait("dev", "wip")]
public class Database: IClassFixture<WebServerFixture>, IDisposable
{
readonly WebServerFixture _serverFixture;
readonly ITestOutputHelper output;
public Database(WebServerFixture serverFixture, ITestOutputHelper output)
{
this.output = output;
_serverFixture = serverFixture;
}
/// <summary>
/// Assuming we're using an account that may create databases,
/// Install all our migrations in a fresh new database.
/// </summary>
public void Dispose()
{
if (_serverFixture!=null)
{
_serverFixture.Dispose();
}
}
}
}

View file

@ -0,0 +1,43 @@
using yavscTests.ServerFixtures;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Yavsc.Interface;
namespace yavscTests
{
[Collection("EMaillingTeststCollection")]
[Trait("regression", "II")]
public class EMaillingTests : IClassFixture<WebServerFixture>
{
readonly WebServerFixture _serverFixture;
readonly ITestOutputHelper output;
readonly ILogger _logger;
public EMaillingTests(WebServerFixture serverFixture, ITestOutputHelper output)
{
this.output = output;
_serverFixture = serverFixture;
_logger = serverFixture.Logger;
}
[Fact]
public void SendEMailSynchrone()
{
using IServiceScope scope = _serverFixture.Services.CreateScope();
ITrueEmailSender mailSender = scope.ServiceProvider.GetRequiredService<ITrueEmailSender>();
output.WriteLine("SendEMailSynchrone ...");
mailSender.SendEmailAsync
(
_serverFixture.SiteSettings.Owner.Name,
_serverFixture.SiteSettings.Owner.EMail,
$"monthly email",
"test boby monthly email").Wait();
}
}
}

View file

@ -0,0 +1,66 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<!--
route name for the api controller used to tag the 'BlogPost' entity
-->
<data name="ErrMessageTooLong"><value>Too Long ({MaxLen} at maximum, {ecart} in excess))</value></data>
<data name="ErrMessageTooShort"><value>Too Short ({MinLen} at minus, {ecart} missing)</value></data>
</root>

View file

@ -0,0 +1,324 @@
using IdentityServer8.EntityFramework.Entities;
using IdentityServer8.Models;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Hosting.Server;
using Microsoft.AspNetCore.Hosting.Server.Features;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using System.Net;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using Yavsc;
using Yavsc.Extensions;
using Yavsc.Models;
using Yavsc.Server.Helpers;
using Client = IdentityServer8.EntityFramework.Entities.Client;
namespace yavscTests.ServerFixtures
{
[CollectionDefinition("Yavsc Server")]
public class WebServerFixture : IDisposable
{
private static readonly Lazy<X509Certificate2> _selfSignedCertificate = new Lazy<X509Certificate2>(CreateSelfSignedCertificate);
private static readonly object _sync = new object();
private static WebApplication? _app;
private static bool _isInitialized = false;
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 Microsoft.Extensions.Logging.ILogger? Logger { get; internal set; }
private SiteSettings? siteSettings;
public IConfiguration? Configuration { get; private set; }
public string? TestClientId { get; private set; }
public IServiceProvider? Services { get; private set; }
public string? TestingUserName { get; private set; }
public string? TestingUserPassword { get; private set; }
public string? ProtectedTestingApiKey { get; internal set; }
public ApplicationUser? TestingUser { get; private set; }
public bool DbCreated { get; internal set; }
public SiteSettings? SiteSettings { get => siteSettings; set => siteSettings = value; }
public string? TestClientSecret { get; set; }
public string? TestingUserEmail { get; set; }
public WebServerFixture()
{
lock (_sync)
{
_instanceCount++;
if (!_isInitialized)
{
SetupHost().Wait();
_isInitialized = true;
}
CopySharedState();
}
}
public void Dispose()
{
lock (_sync)
{
_instanceCount--;
if (_instanceCount == 0 && _app != null)
{
_app.StopAsync().Wait();
_app = null;
_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;
}
public async Task SetupHost()
{
var builder = WebApplication.CreateBuilder();
builder.AddConfiguration("org").AddInMemoryCollection(new Dictionary<string, string?>
{
[$"ConnectionStrings:{YavscConstants.YavscConnectionStringName}"] = "InMemory"
});
// Configure Kestrel for HTTPS with self-signed certificate on a dynamic port
builder.WebHost.ConfigureKestrel(options =>
{
options.Listen(IPAddress.Loopback, 0, listenOptions =>
{
listenOptions.UseHttps(_selfSignedCertificate.Value);
});
});
Configuration = builder.Configuration;
_app = builder.ConfigureWebAppServices();
Services = _app.Services;
SiteSettings = _app.Services.GetRequiredService<IOptions<SiteSettings>>().Value;
using (var migrationScope = _app.Services.CreateScope())
{
var db = migrationScope.ServiceProvider.GetRequiredService<ApplicationDbContext>();
db.Database.EnsureDeleted();
db.Database.EnsureCreated();
TestingUserName = "Tester";
TestingUserPassword = "Test123!";
TestClientId = "testClientId";
TestingUserEmail = "test@no-reply.com";
TestingUser = null;
TestClientSecret = Guid.CreateVersion7().ToString();
EnsureUser(TestingUserName, TestingUserPassword, TestingUserEmail, migrationScope);
AddAuthorizedClient(migrationScope, TestClientId, TestClientSecret);
TestingUser = await db.Users.FirstOrDefaultAsync(u => u.UserName == TestingUserName);
// Add test API scope if it doesn't exist
var testScope = db.ApiScopes.FirstOrDefault(s => s.Name == "test");
if (testScope == null)
{
db.ApiScopes.Add(new IdentityServer8.EntityFramework.Entities.ApiScope
{
Name = "test",
Enabled = true,
DisplayName = "Test API Scope",
Description = "Scope for testing purposes",
UserClaims = new List<IdentityServer8.EntityFramework.Entities.ApiScopeClaim>
{
new IdentityServer8.EntityFramework.Entities.ApiScopeClaim { Type = "role" },
new IdentityServer8.EntityFramework.Entities.ApiScopeClaim { Type = "email" }
}
});
// Add a basic API resource for the test scope
var apiResource = new IdentityServer8.EntityFramework.Entities.ApiResource
{
Name = "testapi",
DisplayName = "Test API",
Enabled = true,
Scopes = new List<IdentityServer8.EntityFramework.Entities.ApiResourceScope>
{
new IdentityServer8.EntityFramework.Entities.ApiResourceScope
{
Scope = "test"
}
}
};
db.ApiResources.Add(apiResource);
db.SaveChanges();
}
}
_app = await _app.ConfigurePipeline();
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 addressFeatures = server.Features.Get<IServerAddressesFeature>();
if (addressFeatures?.Addresses != null)
{
_sharedAddresses.Clear();
foreach (var address in addressFeatures.Addresses)
{
_sharedAddresses.Add(address);
Addresses.Add(address);
}
}
}
private void AddAuthorizedClient(IServiceScope scope, string testClientId, string testClientSecret)
{
var configDb = scope.ServiceProvider.GetRequiredService<IdentityServer8.EntityFramework.DbContexts.ConfigurationDbContext>();
if (configDb == null)
throw new InvalidOperationException("ConfigurationDbContext is not available for IdentityServer client seeding.");
Client testingClient = new Client
{
ClientId = testClientId,
AccessTokenLifetime = 3600000,
AccessTokenType = 1,
ClientName = "Testing client",
Enabled = true,
RequireClientSecret = true
};
configDb.Set<Client>().Add(testingClient);
configDb.SaveChanges();
var apiScope = new IdentityServer8.EntityFramework.Entities.ApiScope
{
Name = "test",
DisplayName = "Test Scope",
Description = "Scope for testing",
Enabled = true,
Required = false,
ShowInDiscoveryDocument = true,
Emphasize = false
};
configDb.Set<IdentityServer8.EntityFramework.Entities.ApiScope>().Add(apiScope);
ClientSecret secret = new ClientSecret
{
Value = testClientSecret.Sha256(),
Type = IdentityServer8.IdentityServerConstants.SecretTypes.SharedSecret,
ClientId = testingClient.Id
};
configDb.Set<ClientSecret>().Add(secret);
configDb.Set<ClientGrantType>().Add(new ClientGrantType
{
ClientId = testingClient.Id,
GrantType = "client_credentials"
});
configDb.Set<ClientGrantType>().Add(new ClientGrantType
{
ClientId = testingClient.Id,
GrantType = "password"
});
configDb.Set<ClientScope>().Add(new ClientScope
{
ClientId = testingClient.Id,
Scope = "test"
});
configDb.SaveChanges();
}
public void EnsureUser(string testingUserName, string password, string email, IServiceScope scope)
{
if (TestingUser == null)
{
var userManager =
scope.ServiceProvider.GetRequiredService<UserManager<ApplicationUser>>();
TestingUser = new ApplicationUser
{
UserName = testingUserName,
Email = testingUserName + "@example.com",
EmailConfirmed = true
};
var result = userManager.CreateAsync(TestingUser, password).Result;
Assert.True(result.Succeeded);
ApplicationDbContext dbContext =
scope.ServiceProvider.GetRequiredService<ApplicationDbContext>();
TestingUser = dbContext.Users.FirstOrDefault(u => u.UserName == testingUserName);
}
}
private static X509Certificate2 CreateSelfSignedCertificate()
{
var rsa = RSA.Create(2048);
var certRequest = new CertificateRequest("CN=localhost", rsa, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
certRequest.CertificateExtensions.Add(
new X509KeyUsageExtension(X509KeyUsageFlags.DataEncipherment | X509KeyUsageFlags.KeyEncipherment | X509KeyUsageFlags.DigitalSignature, false));
certRequest.CertificateExtensions.Add(
new X509EnhancedKeyUsageExtension(
new OidCollection { new Oid("1.3.6.1.5.5.7.3.1") }, false));
var certificate = certRequest.CreateSelfSigned(new DateTimeOffset(DateTime.UtcNow.AddDays(-1)), new DateTimeOffset(DateTime.UtcNow.AddDays(3650)));
return certificate;
}
}
}

View file

@ -0,0 +1,55 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<IsPackable>false</IsPackable>
<RootNamespace>Yavsc.Tests</RootNamespace>
<UserSecretsId>78a4efec-68dc-4745-ba06-d8545ef9ee91</UserSecretsId>
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
<OutputType>exe</OutputType>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="coverlet.collector" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="Microsoft.NET.Test.Sdk" />
<PackageReference Include="IdentityModel.OidcClient" />
<PackageReference Include="Selenium.WebDriver" />
<PackageReference Include="Microsoft.AspNetCore.Hosting" />
<PackageReference Include="Microsoft.Extensions.Caching.Memory" />
<PackageReference Include="Microsoft.Extensions.Options" />
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" />
<PackageReference Include="xunit.v3" />
<PackageReference Include="xunit.v3.common" />
<PackageReference Include="xunit.v3.extensibility.core" />
<PackageReference Include="xunit.runner.visualstudio" />
</ItemGroup>
<ItemGroup>
<Content Include="appsettings.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup>
<Content Include="appsettings.*.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Yavsc.Org\Yavsc.Org.csproj" />
<ProjectReference Include="..\Yavsc.Abstract\Yavsc.Abstract.csproj" />
<ProjectReference Include="..\Yavsc.Server\Yavsc.Server.csproj" />
</ItemGroup>
<ItemGroup>
<Using Include="Xunit" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" />
</ItemGroup>
<Target Name="CopyStaticWebAssetsManifest" AfterTargets="Build">
<Copy SourceFiles="$(OutDir)Yavsc.Org.staticwebassets.endpoints.json" DestinationFiles="$(OutDir)$(MSBuildProjectName).staticwebassets.endpoints.json" SkipUnchangedFiles="true" Condition="Exists('$(OutDir)Yavsc.Org.staticwebassets.endpoints.json')" />
</Target>
</Project>

View file

@ -0,0 +1,67 @@
{
"Site": {
"Authority": "https://mercure.pschneider.fr",
"Title": "Yavsc dev",
"Slogan": "Yavsc : WIP.",
"Banner": "/images/yavsc.png",
"HomeViewName": "Home",
"FavIcon": "/favicon.ico",
"Icon": "/images/yavsc.png",
"GitRepository": "testingrepo",
"Owner": {
"Name": "Site Owner Name",
"EMail": "your@email",
"PostalAddress": {
"Street1": "Your Address",
"Street2": "your street",
"PostalCode": "543 21~3",
"City": "",
"State": "",
"Province": null
}
},
"Admin": {
"Name": "Administrator name",
"EMail": "daAdmin@e.mail"
}
},
"Smtp": {
"Server": "localhost",
"Port": 465
},
"Logging": {
"IncludeScopes": {},
"LogLevel": {
"Default": "Debug",
"System": "Warning",
"Microsoft": "Warning"
}
},
"ConnectionStrings": {
"YavscConnection": "InMemory"
},
"DataProtection": {
"Keys": {
"Dir": "DataProtection-Keys"
},
"RSAParamFile": "RSA-Params.json",
"ExpiresInHours": 168
},
"ApiKey": "lame-key",
"Testing": {
"ConnectionStrings": {
"Default": "lame-default-connection-string",
"DatabaseCtor": "lame-database-ctor-connection-string"
},
"YavscWebPath": "../../src/Yavsc",
"ValidCreds": {
"UserName": "lame-user",
"Password": "lame-password"
},
"InvalidCreds": {
"UserName": "fakeuser",
"Password": "f/\\kePassw0rd"
}
}
}

View file

@ -0,0 +1,5 @@
{
"diagnosticMessages": false,
"methodDisplay": "classAndMethod",
"parallelizeTestCollections": true
}