Merge pull request 'configure a test scope' (#2) from fix/tests into main
Reviewed-on: #2
This commit is contained in:
commit
143149a01e
18 changed files with 189 additions and 237 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
|
@ -45,3 +45,5 @@ yavsc-pre
|
||||||
*.env
|
*.env
|
||||||
generated/
|
generated/
|
||||||
*.lscache
|
*.lscache
|
||||||
|
*.Development.json
|
||||||
|
*.log
|
||||||
|
|
|
||||||
|
|
@ -65,8 +65,9 @@ internal class Program
|
||||||
new() { ValidateAudience = false, RoleClaimType = Constants.RoleClaimType };
|
new() { ValidateAudience = false, RoleClaimType = Constants.RoleClaimType };
|
||||||
options.MapInboundClaims = true;
|
options.MapInboundClaims = true;
|
||||||
});
|
});
|
||||||
|
|
||||||
services.AddDbContext<ApplicationDbContext>(options =>
|
services.AddDbContext<ApplicationDbContext>(options =>
|
||||||
|
|
||||||
options.UseNpgsql(builder.Configuration.GetConnectionString("DefaultConnection")));
|
options.UseNpgsql(builder.Configuration.GetConnectionString("DefaultConnection")));
|
||||||
|
|
||||||
services.AddTransient<ITrueEmailSender, MailSender>()
|
services.AddTransient<ITrueEmailSender, MailSender>()
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,9 @@ namespace Yavsc.Models.Billing
|
||||||
{
|
{
|
||||||
public static class BillingCodes
|
public static class BillingCodes
|
||||||
{
|
{
|
||||||
public const string Rdv = "Rdv";
|
public const string Rdv = nameof(Rdv);
|
||||||
public const string MBrush = "MBrush";
|
public const string MBrush = nameof(MBrush);
|
||||||
|
|
||||||
public const string Brush = "Brush";
|
public const string Brush = nameof(Brush);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -70,7 +70,7 @@ internal class Program
|
||||||
new() { ValidateAudience = false, RoleClaimType = Constants.RoleClaimType };
|
new() { ValidateAudience = false, RoleClaimType = Constants.RoleClaimType };
|
||||||
options.MapInboundClaims = true;
|
options.MapInboundClaims = true;
|
||||||
});
|
});
|
||||||
|
|
||||||
services.AddDbContext<ApplicationDbContext>(options =>
|
services.AddDbContext<ApplicationDbContext>(options =>
|
||||||
options.UseNpgsql(builder.Configuration.GetConnectionString(Constants.YavscConnectionStringName)));
|
options.UseNpgsql(builder.Configuration.GetConnectionString(Constants.YavscConnectionStringName)));
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -85,18 +85,9 @@ public static class HostingExtensions
|
||||||
options.ResourcesPath = "Resources";
|
options.ResourcesPath = "Resources";
|
||||||
}).AddDataAnnotationsLocalization();
|
}).AddDataAnnotationsLocalization();
|
||||||
|
|
||||||
bool useTestEmailSender = builder.Configuration.GetValue<bool>("UseTestEmailSender", false);
|
|
||||||
|
|
||||||
if (useTestEmailSender)
|
|
||||||
{
|
|
||||||
services.AddTransient<ITrueEmailSender, TestMailSender>()
|
|
||||||
.AddTransient<Microsoft.AspNetCore.Identity.UI.Services.IEmailSender, TestMailSender>();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
services.AddTransient<ITrueEmailSender, MailSender>()
|
services.AddTransient<ITrueEmailSender, MailSender>()
|
||||||
.AddTransient<Microsoft.AspNetCore.Identity.UI.Services.IEmailSender, MailSender>();
|
.AddTransient<Microsoft.AspNetCore.Identity.UI.Services.IEmailSender, MailSender>();
|
||||||
}
|
|
||||||
|
|
||||||
services.AddTransient<IYavscMessageSender, YavscMessageSender>()
|
services.AddTransient<IYavscMessageSender, YavscMessageSender>()
|
||||||
.AddTransient<IBillingService, BillingService>()
|
.AddTransient<IBillingService, BillingService>()
|
||||||
|
|
@ -147,21 +138,12 @@ public static class HostingExtensions
|
||||||
public static IdentityBuilder AddIdentityDBAndStores(this WebApplicationBuilder builder)
|
public static IdentityBuilder AddIdentityDBAndStores(this WebApplicationBuilder builder)
|
||||||
{
|
{
|
||||||
IServiceCollection services = builder.Services;
|
IServiceCollection services = builder.Services;
|
||||||
bool useInMemory = builder.Configuration.GetValue<bool>("UseInMemoryDatabase", false);
|
|
||||||
|
services.AddDbContext<ApplicationDbContext>(options =>
|
||||||
if (useInMemory)
|
|
||||||
{
|
{
|
||||||
services.AddDbContext<ApplicationDbContext>(options =>
|
options.UseNpgsql(builder.Configuration.GetConnectionString(Constants.YavscConnectionStringName),
|
||||||
options.UseInMemoryDatabase("YavscInMemory"));
|
options => options.MigrationsAssembly(typeof(Program).Assembly));
|
||||||
}
|
});
|
||||||
else
|
|
||||||
{
|
|
||||||
services.AddDbContext<ApplicationDbContext>(options =>
|
|
||||||
{
|
|
||||||
options.UseNpgsql(builder.Configuration.GetConnectionString(Constants.YavscConnectionStringName),
|
|
||||||
options => options.MigrationsAssembly(typeof(Program).Assembly));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
var identityBuilder = services.AddIdentity<ApplicationUser, IdentityRole>(
|
var identityBuilder = services.AddIdentity<ApplicationUser, IdentityRole>(
|
||||||
options =>
|
options =>
|
||||||
|
|
@ -280,8 +262,8 @@ public static class HostingExtensions
|
||||||
});
|
});
|
||||||
var migrationsAssembly = typeof(Program).GetTypeInfo().Assembly.GetName().Name;
|
var migrationsAssembly = typeof(Program).GetTypeInfo().Assembly.GetName().Name;
|
||||||
var connectionString = builder.Configuration.GetConnectionString(Constants.YavscConnectionStringName);
|
var connectionString = builder.Configuration.GetConnectionString(Constants.YavscConnectionStringName);
|
||||||
bool useInMemory = builder.Configuration.GetValue<bool>("UseInMemoryDatabase", false);
|
|
||||||
string inMemoryDatabaseName = "YavscInMemory";
|
string sqliteConnectionString = $"Data Source={Path.Combine(Path.GetTempPath(), "yavsc_test.db")}";
|
||||||
|
|
||||||
var identityServerBuilder = builder.Services.AddIdentityServer(options =>
|
var identityServerBuilder = builder.Services.AddIdentityServer(options =>
|
||||||
{
|
{
|
||||||
|
|
@ -301,40 +283,15 @@ public static class HostingExtensions
|
||||||
.AddResourceStore<ResourceStore>()
|
.AddResourceStore<ResourceStore>()
|
||||||
.AddConfigurationStore(options =>
|
.AddConfigurationStore(options =>
|
||||||
{
|
{
|
||||||
if (useInMemory)
|
options.ConfigureDbContext = b => b.UseNpgsql(connectionString,
|
||||||
{
|
sql => sql.MigrationsAssembly(migrationsAssembly))
|
||||||
options.ConfigureDbContext = b => b.UseInMemoryDatabase(inMemoryDatabaseName);
|
.UseSeeding(EnsureDefaultApplicationScopes());
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
options.ConfigureDbContext = b => b.UseNpgsql(connectionString,
|
|
||||||
sql => sql.MigrationsAssembly(migrationsAssembly))
|
|
||||||
.UseSeeding((context, _) =>
|
|
||||||
{
|
|
||||||
foreach (String scope in new string[] { "blog", "admin", "contract", "com"})
|
|
||||||
{
|
|
||||||
var testBlog = context.Set<ApiScope>().FirstOrDefault(b => b.Name == scope);
|
|
||||||
if (testBlog == null)
|
|
||||||
{
|
|
||||||
context.Set<ApiScope>().Add(new ApiScope { Name = scope });
|
|
||||||
context.SaveChanges();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
.AddOperationalStore(options =>
|
.AddOperationalStore(options =>
|
||||||
{
|
{
|
||||||
if (useInMemory)
|
options.ConfigureDbContext = b => b.UseNpgsql(connectionString,
|
||||||
{
|
sql => sql.MigrationsAssembly(migrationsAssembly));
|
||||||
options.ConfigureDbContext = b => b.UseInMemoryDatabase(inMemoryDatabaseName);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
options.ConfigureDbContext = b => b.UseNpgsql(connectionString,
|
|
||||||
sql => sql.MigrationsAssembly(migrationsAssembly));
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (builder.Environment.IsDevelopment())
|
if (builder.Environment.IsDevelopment())
|
||||||
|
|
@ -344,6 +301,23 @@ public static class HostingExtensions
|
||||||
return identityServerBuilder;
|
return identityServerBuilder;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static Action<DbContext, bool> EnsureDefaultApplicationScopes()
|
||||||
|
{
|
||||||
|
return (context, _) =>
|
||||||
|
{
|
||||||
|
foreach (String scope in new string[] { "blog", "admin", "contract", "com" })
|
||||||
|
{
|
||||||
|
var existentScope = context.Set<ApiScope>().FirstOrDefault(b => b.Name == scope);
|
||||||
|
if (existentScope == null)
|
||||||
|
{
|
||||||
|
context.Set<ApiScope>().Add(new ApiScope { Name = scope });
|
||||||
|
context.SaveChanges();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
private static void ConfigureRequestLocalization(IServiceCollection services)
|
private static void ConfigureRequestLocalization(IServiceCollection services)
|
||||||
{
|
{
|
||||||
services.Configure<RequestLocalizationOptions>(options =>
|
services.Configure<RequestLocalizationOptions>(options =>
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,6 @@
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" />
|
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" />
|
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" />
|
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" />
|
||||||
<PackageReference Include="Google.Apis.Compute.v1" />
|
<PackageReference Include="Google.Apis.Compute.v1" />
|
||||||
|
|
|
||||||
|
|
@ -46,26 +46,15 @@ namespace Yavsc.Helpers
|
||||||
lock (_billingLock)
|
lock (_billingLock)
|
||||||
{
|
{
|
||||||
string typeName = typeof(T).Name;
|
string typeName = typeof(T).Name;
|
||||||
|
if (BillingService.GlobalBillingMap.ContainsKey(typeName))
|
||||||
// Only add if not already present (idempotent operation)
|
|
||||||
if (!BillingService.Billing.ContainsKey(code))
|
|
||||||
{
|
|
||||||
BillingService.Billing.Add(code, getter);
|
|
||||||
}
|
|
||||||
else if (!BillingService.GlobalBillingMap.ContainsKey(typeName) ||
|
|
||||||
BillingService.GlobalBillingMap[typeName] != code)
|
|
||||||
{
|
|
||||||
throw new InvalidOperationException($"Billing setup: code '{code}' already registered");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!BillingService.GlobalBillingMap.ContainsKey(typeName))
|
|
||||||
{
|
|
||||||
BillingService.GlobalBillingMap.Add(typeName, code);
|
|
||||||
}
|
|
||||||
else if (BillingService.GlobalBillingMap[typeName] != code)
|
|
||||||
{
|
{
|
||||||
throw new InvalidOperationException($"Billing setup: type '{typeName}' already registered with different code");
|
throw new InvalidOperationException($"Billing setup: type '{typeName}' already registered with different code");
|
||||||
}
|
}
|
||||||
|
if (BillingService.Billing.ContainsKey(code))
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException($"Billing setup: code '{code}' already registered with different type");
|
||||||
|
}
|
||||||
|
BillingService.Billing.Add(code, getter);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -42,18 +42,30 @@ namespace Yavsc.Models
|
||||||
}
|
}
|
||||||
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options)
|
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options)
|
||||||
{
|
{
|
||||||
|
if (Database.IsRelational())
|
||||||
|
{
|
||||||
|
Database.SetCommandTimeout(180);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnModelCreating(ModelBuilder builder)
|
protected override void OnModelCreating(ModelBuilder builder)
|
||||||
{
|
{
|
||||||
base.OnModelCreating(builder);
|
base.OnModelCreating(builder);
|
||||||
|
if (Database.IsNpgsql())
|
||||||
|
{
|
||||||
|
NOW_SQL="LOCALTIMESTAMP";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
NOW_SQL="CURRENT_TIMESTAMP";
|
||||||
|
}
|
||||||
builder.UseIdentityByDefaultColumns();
|
builder.UseIdentityByDefaultColumns();
|
||||||
|
|
||||||
// Customize the ASP.NET Identity model and override the defaults if needed.
|
// Customize the ASP.NET Identity model and override the defaults if needed.
|
||||||
// For example, you can rename the ASP.NET Identity table names and more.
|
// For example, you can rename the ASP.NET Identity table names and more.
|
||||||
// Add your customizations after calling base.OnModelCreating(builder);
|
// Add your customizations after calling base.OnModelCreating(builder);
|
||||||
builder.Entity<Contact>().HasKey(x => new { x.OwnerId, x.UserId });
|
builder.Entity<Contact>().HasKey(x => new { x.OwnerId, x.UserId });
|
||||||
builder.Entity<DeviceDeclaration>().Property(x => x.DeclarationDate).HasDefaultValueSql("LOCALTIMESTAMP");
|
builder.Entity<DeviceDeclaration>().Property(x => x.DeclarationDate).HasDefaultValueSql(NOW_SQL);
|
||||||
builder.Entity<BlogTag>().HasKey(x => new { x.PostId, x.TagId });
|
builder.Entity<BlogTag>().HasKey(x => new { x.PostId, x.TagId });
|
||||||
|
|
||||||
builder.Entity<ApplicationUser>().Property(u => u.FullName).IsRequired(false);
|
builder.Entity<ApplicationUser>().Property(u => u.FullName).IsRequired(false);
|
||||||
|
|
@ -336,7 +348,6 @@ namespace Yavsc.Models
|
||||||
public DbSet<PersistedGrant> PersistedGrants { get; set; }
|
public DbSet<PersistedGrant> PersistedGrants { get; set; }
|
||||||
public DbSet<DeviceFlowCodes> DeviceFlowCodes { get; set; }
|
public DbSet<DeviceFlowCodes> DeviceFlowCodes { get; set; }
|
||||||
|
|
||||||
public DbSet<YavscApiScope> YavscApiScopes { get; set; }
|
public string NOW_SQL { get; private set; }
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -47,9 +47,9 @@ namespace Yavsc.Services
|
||||||
/// <returns>a MessageWithPayloadResponse,
|
/// <returns>a MessageWithPayloadResponse,
|
||||||
/// <c>bool somethingsent = (response.failure == 0 && response.success > 0)</c>
|
/// <c>bool somethingsent = (response.failure == 0 && response.success > 0)</c>
|
||||||
/// </returns>
|
/// </returns>
|
||||||
public async Task SendEmailAsync(string email, string subject, string htmlMessage)
|
public Task SendEmailAsync(string email, string subject, string htmlMessage)
|
||||||
{
|
{
|
||||||
await SendEmailAsync("", email, subject, htmlMessage);
|
return SendEmailAsync("", email, subject, htmlMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<string> SendEmailAsync(string name, string email, string subject, string htmlMessage)
|
public async Task<string> SendEmailAsync(string name, string email, string subject, string htmlMessage)
|
||||||
|
|
@ -71,8 +71,9 @@ namespace Yavsc.Services
|
||||||
);
|
);
|
||||||
using (SmtpClient sc = new())
|
using (SmtpClient sc = new())
|
||||||
{
|
{
|
||||||
|
sc.Timeout = 30000;
|
||||||
sc.Connect(
|
sc.Connect(
|
||||||
smtpSettings.Server,
|
smtpSettings.Host,
|
||||||
smtpSettings.Port,
|
smtpSettings.Port,
|
||||||
SecureSocketOptions.Auto
|
SecureSocketOptions.Auto
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -1,30 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Microsoft.AspNetCore.Identity.UI.Services;
|
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
using Yavsc.Interface;
|
|
||||||
|
|
||||||
namespace Yavsc.Services
|
|
||||||
{
|
|
||||||
public class TestMailSender : ITrueEmailSender, IEmailSender
|
|
||||||
{
|
|
||||||
private readonly ILogger<TestMailSender> logger;
|
|
||||||
|
|
||||||
public TestMailSender(ILoggerFactory loggerFactory)
|
|
||||||
{
|
|
||||||
logger = loggerFactory.CreateLogger<TestMailSender>();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task SendEmailAsync(string email, string subject, string htmlMessage)
|
|
||||||
{
|
|
||||||
logger.LogInformation("[TestMailSender] SendEmailAsync to {Email} subject={Subject}", email, subject);
|
|
||||||
return Task.CompletedTask;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task<string> SendEmailAsync(string name, string email, string subject, string htmlMessage)
|
|
||||||
{
|
|
||||||
logger.LogInformation("[TestMailSender] SendEmailAsync to {Email} subject={Subject} name={Name}", email, subject, name);
|
|
||||||
return Task.FromResult($"test-message-{Guid.NewGuid()}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -2,17 +2,13 @@ namespace Yavsc.Settings
|
||||||
{
|
{
|
||||||
public class SmtpSettings
|
public class SmtpSettings
|
||||||
{
|
{
|
||||||
public string Server { get; set; }
|
|
||||||
public string Host
|
public string Host
|
||||||
{
|
{
|
||||||
get => Server;
|
get ;
|
||||||
set => Server = value;
|
set ;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int Port { get; set; }
|
public int Port { get; set; }
|
||||||
|
|
||||||
public string SenderName { get; set; }
|
|
||||||
public string SenderEmail { get; set; }
|
|
||||||
public string UserName { get; set; }
|
public string UserName { get; set; }
|
||||||
public string Password { get; set; }
|
public string Password { get; set; }
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -53,8 +53,6 @@ namespace yavscTests
|
||||||
public void HaveConfigurationRoot()
|
public void HaveConfigurationRoot()
|
||||||
{
|
{
|
||||||
var builder = new ConfigurationBuilder();
|
var builder = new ConfigurationBuilder();
|
||||||
builder.AddJsonFile( "appsettings.json", false);
|
|
||||||
builder.AddJsonFile( "appsettings.Development.json", true);
|
|
||||||
configurationRoot = builder.Build();
|
configurationRoot = builder.Build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,7 @@ namespace yavscTests
|
||||||
var firstRegistrar = new Func<ApplicationDbContext, long, IDecidableQuery>((db, id) =>
|
var firstRegistrar = new Func<ApplicationDbContext, long, IDecidableQuery>((db, id) =>
|
||||||
db.HairCutQueries.Include(q => q.Prestation).Include(q => q.Regularisation).Single(q => q.Id == id));
|
db.HairCutQueries.Include(q => q.Prestation).Include(q => q.Regularisation).Single(q => q.Id == id));
|
||||||
|
|
||||||
const string testCode = "TestBrush";
|
const string testCode = "Brush";
|
||||||
|
|
||||||
Assert.Throws<InvalidOperationException>(() =>
|
Assert.Throws<InvalidOperationException>(() =>
|
||||||
WorkflowHelpers.RegisterBilling<HairCutQuery>(testCode, firstRegistrar));
|
WorkflowHelpers.RegisterBilling<HairCutQuery>(testCode, firstRegistrar));
|
||||||
|
|
|
||||||
|
|
@ -25,20 +25,18 @@ namespace yavscTests
|
||||||
[Fact]
|
[Fact]
|
||||||
public void SendEMailSynchrone()
|
public void SendEMailSynchrone()
|
||||||
{
|
{
|
||||||
AssertAsync.CompletesIn(2, () =>
|
|
||||||
{
|
|
||||||
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();
|
|
||||||
|
|
||||||
});
|
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();
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,37 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace yavscTests {
|
|
||||||
|
|
||||||
public static class AssertAsync {
|
|
||||||
/// <summary>
|
|
||||||
/// Completes In
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="timeoutFromSecond"></param>
|
|
||||||
/// <param name="action"></param>
|
|
||||||
public static void CompletesIn(int timeoutFromSecond, Action action)
|
|
||||||
{
|
|
||||||
var task = Task.Run(action);
|
|
||||||
var completedInTime = Task.WaitAll(new[] { task }, TimeSpan.FromSeconds(timeoutFromSecond));
|
|
||||||
|
|
||||||
if (task.Exception != null)
|
|
||||||
{
|
|
||||||
if (task.Exception.InnerExceptions.Count == 1)
|
|
||||||
{
|
|
||||||
throw task.Exception.InnerExceptions[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
throw task.Exception;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!completedInTime)
|
|
||||||
{
|
|
||||||
throw new TimeoutException($"Task did not complete in {timeoutFromSecond} seconds.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
|
||||||
using IdentityServer8.EntityFramework.Entities;
|
using IdentityServer8.EntityFramework.Entities;
|
||||||
using IdentityServer8.Models;
|
using IdentityServer8.Models;
|
||||||
using Microsoft.AspNetCore.Builder;
|
using Microsoft.AspNetCore.Builder;
|
||||||
|
|
@ -5,7 +6,7 @@ using Microsoft.AspNetCore.Hosting;
|
||||||
using Microsoft.AspNetCore.Hosting.Server;
|
using Microsoft.AspNetCore.Hosting.Server;
|
||||||
using Microsoft.AspNetCore.Hosting.Server.Features;
|
using Microsoft.AspNetCore.Hosting.Server.Features;
|
||||||
using Microsoft.AspNetCore.Identity;
|
using Microsoft.AspNetCore.Identity;
|
||||||
using Microsoft.AspNetCore.Server.Kestrel.Core;
|
using Microsoft.Data.SqlClient;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.Extensions.Configuration;
|
using Microsoft.Extensions.Configuration;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
|
@ -136,20 +137,16 @@ namespace isnd.tests
|
||||||
|
|
||||||
var builder = WebApplication.CreateBuilder();
|
var builder = WebApplication.CreateBuilder();
|
||||||
builder.Environment.EnvironmentName = "Development";
|
builder.Environment.EnvironmentName = "Development";
|
||||||
|
// Set ContentRoot to the Yavsc.Org project directory so WebRootPath resolves correctly
|
||||||
|
var testAssemblyLocation = AppDomain.CurrentDomain.BaseDirectory;
|
||||||
|
var yavscOrgPath = Path.GetFullPath(Path.Combine(testAssemblyLocation, "../../src/Yavsc.Org"));
|
||||||
|
builder.Environment.ContentRootPath = yavscOrgPath;
|
||||||
|
|
||||||
ConfigureLogger();
|
ConfigureLogger();
|
||||||
builder.Configuration
|
var config = builder.Configuration
|
||||||
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: false)
|
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: false)
|
||||||
.AddJsonFile($"appsettings.{builder.Environment.EnvironmentName}.json", optional: true, reloadOnChange: false)
|
.AddJsonFile($"appsettings.{builder.Environment.EnvironmentName}.json", optional: false, reloadOnChange: false)
|
||||||
.AddEnvironmentVariables()
|
.AddEnvironmentVariables().Build();
|
||||||
.AddInMemoryCollection(new Dictionary<string, string?>
|
|
||||||
{
|
|
||||||
["UseInMemoryDatabase"] = "true",
|
|
||||||
["UseTestEmailSender"] = "true",
|
|
||||||
["Smtp:Host"] = "localhost",
|
|
||||||
["Smtp:Port"] = "25",
|
|
||||||
["Smtp:SenderName"] = "Yavsc Test",
|
|
||||||
["Smtp:SenderEmail"] = "test@example.com"
|
|
||||||
});
|
|
||||||
|
|
||||||
// Configure Kestrel for HTTPS with self-signed certificate on a dynamic port
|
// Configure Kestrel for HTTPS with self-signed certificate on a dynamic port
|
||||||
builder.WebHost.ConfigureKestrel(options =>
|
builder.WebHost.ConfigureKestrel(options =>
|
||||||
|
|
@ -165,6 +162,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;
|
||||||
|
String cxStr = config.GetConnectionString(Constants.YavscConnectionStringName) ?? throw new InvalidOperationException("DefaultConnection string is not configured.");
|
||||||
|
|
||||||
using (var migrationScope = _app.Services.CreateScope())
|
using (var migrationScope = _app.Services.CreateScope())
|
||||||
{
|
{
|
||||||
|
|
@ -172,7 +170,7 @@ namespace isnd.tests
|
||||||
db.Database.EnsureDeleted();
|
db.Database.EnsureDeleted();
|
||||||
db.Database.EnsureCreated();
|
db.Database.EnsureCreated();
|
||||||
TestingUserName = "Tester";
|
TestingUserName = "Tester";
|
||||||
TestingUserPassword = "tesT456+*";
|
TestingUserPassword = "Test123!";
|
||||||
TestClientId = "testClientId";
|
TestClientId = "testClientId";
|
||||||
TestingUserEmail = "test@no-reply.com";
|
TestingUserEmail = "test@no-reply.com";
|
||||||
TestingUser = null;
|
TestingUser = null;
|
||||||
|
|
@ -181,6 +179,60 @@ namespace isnd.tests
|
||||||
AddAuthorizedClient(migrationScope, TestClientId, TestClientSecret);
|
AddAuthorizedClient(migrationScope, TestClientId, TestClientSecret);
|
||||||
TestingUser = await db.Users.FirstOrDefaultAsync(u => u.UserName == TestingUserName);
|
TestingUser = await db.Users.FirstOrDefaultAsync(u => u.UserName == TestingUserName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Seed IdentityServer ConfigurationDbContext with API resources and scopes
|
||||||
|
using (var configScope = _app.Services.CreateScope())
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var configDbContext = configScope.ServiceProvider.GetService<IdentityServer8.EntityFramework.DbContexts.ConfigurationDbContext>();
|
||||||
|
if (configDbContext != null)
|
||||||
|
{
|
||||||
|
configDbContext.Database.EnsureCreated();
|
||||||
|
|
||||||
|
// Add test API scope if it doesn't exist
|
||||||
|
var testScope = configDbContext.ApiScopes.FirstOrDefault(s => s.Name == "test");
|
||||||
|
if (testScope == null)
|
||||||
|
{
|
||||||
|
configDbContext.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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
configDbContext.ApiResources.Add(apiResource);
|
||||||
|
configDbContext.SaveChanges();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_sharedLogger?.LogWarning($"Failed to seed ConfigurationDbContext: {ex.Message}");
|
||||||
|
// Don't fail the fixture if seeding fails
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
await _app!.ConfigurePipeline();
|
await _app!.ConfigurePipeline();
|
||||||
_app.UseSession();
|
_app.UseSession();
|
||||||
await _app.StartAsync();
|
await _app.StartAsync();
|
||||||
|
|
@ -212,66 +264,59 @@ namespace isnd.tests
|
||||||
|
|
||||||
private void AddAuthorizedClient(IServiceScope scope, string testClientId, string testClientSecret)
|
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.");
|
||||||
|
|
||||||
var db = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>();
|
|
||||||
Client testingClient = new Client
|
Client testingClient = new Client
|
||||||
{
|
{
|
||||||
ClientId = testClientId,
|
ClientId = testClientId,
|
||||||
AccessTokenLifetime = 3600000,
|
AccessTokenLifetime = 3600000,
|
||||||
AccessTokenType = 1,
|
AccessTokenType = 1,
|
||||||
BackChannelLogoutUri = SiteSettings!.Audience,
|
|
||||||
ClientName = "Testing client",
|
ClientName = "Testing client",
|
||||||
Enabled = true
|
Enabled = true,
|
||||||
|
RequireClientSecret = true
|
||||||
};
|
};
|
||||||
db.Clients.Add(testingClient);
|
configDb.Set<Client>().Add(testingClient);
|
||||||
db.SaveChanges();
|
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
|
ClientSecret secret = new ClientSecret
|
||||||
{
|
{
|
||||||
Value = testClientSecret.Sha256(),
|
Value = testClientSecret.Sha256(),
|
||||||
|
Type = IdentityServer8.IdentityServerConstants.SecretTypes.SharedSecret,
|
||||||
ClientId = testingClient.Id
|
ClientId = testingClient.Id
|
||||||
};
|
};
|
||||||
db.ClientSecrets.Add(secret);
|
configDb.Set<ClientSecret>().Add(secret);
|
||||||
|
|
||||||
var testOrigin = new ClientCorsOrigin
|
configDb.Set<ClientGrantType>().Add(new ClientGrantType
|
||||||
{
|
|
||||||
ClientId = testingClient.Id,
|
|
||||||
Origin = SiteSettings!.Audience
|
|
||||||
|
|
||||||
};
|
|
||||||
db.ClientCorsOrigins.Add(testOrigin);
|
|
||||||
db.ClientGrantTypes.Add(new ClientGrantType
|
|
||||||
{
|
{
|
||||||
ClientId = testingClient.Id,
|
ClientId = testingClient.Id,
|
||||||
GrantType = "client_credentials"
|
GrantType = "client_credentials"
|
||||||
});
|
});
|
||||||
db.ClientGrantTypes.Add(new ClientGrantType
|
configDb.Set<ClientGrantType>().Add(new ClientGrantType
|
||||||
{
|
{
|
||||||
ClientId = testingClient.Id,
|
ClientId = testingClient.Id,
|
||||||
GrantType = "password"
|
GrantType = "password"
|
||||||
});
|
});
|
||||||
db.ClientGrantTypes.Add(new ClientGrantType
|
configDb.Set<ClientScope>().Add(new ClientScope
|
||||||
{
|
|
||||||
ClientId = testingClient.Id,
|
|
||||||
GrantType = "code"
|
|
||||||
});
|
|
||||||
db.ClientScopes.Add(new ClientScope
|
|
||||||
{
|
{
|
||||||
ClientId = testingClient.Id,
|
ClientId = testingClient.Id,
|
||||||
Scope = "test"
|
Scope = "test"
|
||||||
});
|
});
|
||||||
db.ApiScopes.Add(new IdentityServer8.EntityFramework.Entities.ApiScope
|
|
||||||
{
|
|
||||||
Name = "test",
|
|
||||||
Enabled = true
|
|
||||||
});
|
|
||||||
db.ClientRedirectUris.Add(new ClientRedirectUri
|
|
||||||
{
|
|
||||||
ClientId = testingClient.Id,
|
|
||||||
RedirectUri = SiteSettings!.Audience
|
|
||||||
|
|
||||||
});
|
configDb.SaveChanges();
|
||||||
|
|
||||||
db.SaveChanges();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void EnsureUser(string testingUserName, string password, string email, IServiceScope scope)
|
public void EnsureUser(string testingUserName, string password, string email, IServiceScope scope)
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
{
|
{
|
||||||
"Site": {
|
"Site": {
|
||||||
"Authority": "https://localhost",
|
"Audience": "https://localhost",
|
||||||
|
"Authority": "https://mercure.pschneider.fr",
|
||||||
"Title": "Yavsc dev",
|
"Title": "Yavsc dev",
|
||||||
"Slogan": "Yavsc : WIP.",
|
"Slogan": "Yavsc : WIP.",
|
||||||
"Banner": "/images/yavsc.png",
|
"Banner": "/images/yavsc.png",
|
||||||
|
|
@ -26,14 +27,11 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Smtp": {
|
"Smtp": {
|
||||||
"Host": "localhost",
|
"Server": "localhost",
|
||||||
"Port": 25,
|
"Port": 465
|
||||||
"EnableSSL": false
|
|
||||||
},
|
},
|
||||||
"Logging": {
|
"Logging": {
|
||||||
"IncludeScopes": {
|
"IncludeScopes": {},
|
||||||
|
|
||||||
},
|
|
||||||
"LogLevel": {
|
"LogLevel": {
|
||||||
"Default": "Debug",
|
"Default": "Debug",
|
||||||
"System": "Warning",
|
"System": "Warning",
|
||||||
|
|
@ -41,8 +39,8 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"ConnectionStrings": {
|
"ConnectionStrings": {
|
||||||
"YavscConnection": "Server=lame-NpgsqlHostName;Port=5432;Database=lame-DataBase;Username=lame-Username;Password=lame-dbPassword;"
|
"YavscConnection": "Server=localhost;Port=5432;Database=testingYavsc;Username=lame-Username;Password=lame-dbPassword;"
|
||||||
},
|
},
|
||||||
"DataProtection": {
|
"DataProtection": {
|
||||||
"Keys": {
|
"Keys": {
|
||||||
"Dir": "DataProtection-Keys"
|
"Dir": "DataProtection-Keys"
|
||||||
|
|
@ -56,14 +54,14 @@
|
||||||
"Default": "lame-default-connection-string",
|
"Default": "lame-default-connection-string",
|
||||||
"DatabaseCtor": "lame-database-ctor-connection-string"
|
"DatabaseCtor": "lame-database-ctor-connection-string"
|
||||||
},
|
},
|
||||||
"YavscWebPath": "../../src/Yavsc",
|
"YavscWebPath": "../../src/Yavsc",
|
||||||
"ValidCreds": {
|
"ValidCreds": {
|
||||||
"UserName": "lame-user",
|
"UserName": "lame-user",
|
||||||
"Password": "lame-password"
|
"Password": "lame-password"
|
||||||
},
|
},
|
||||||
"InvalidCreds": {
|
"InvalidCreds": {
|
||||||
"UserName": "fakeuser",
|
"UserName": "fakeuser",
|
||||||
"Password": "f/\\kePassw0rd"
|
"Password": "f/\\kePassw0rd"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,10 +1,11 @@
|
||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net10.0</TargetFramework>
|
<TargetFramework>net10.0</TargetFramework>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<IsPackable>false</IsPackable>
|
<IsPackable>false</IsPackable>
|
||||||
<RootNamespace>Yavsc.Tests</RootNamespace>
|
<RootNamespace>Yavsc.Tests</RootNamespace>
|
||||||
|
<UserSecretsId>78a4efec-68dc-4745-ba06-d8545ef9ee91</UserSecretsId>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="coverlet.collector" />
|
<PackageReference Include="coverlet.collector" />
|
||||||
|
|
@ -29,6 +30,11 @@
|
||||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
</Content>
|
</Content>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Content Include="appsettings.*.json">
|
||||||
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
|
</Content>
|
||||||
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\..\src\Yavsc.Org\Yavsc.Org.csproj" />
|
<ProjectReference Include="..\..\src\Yavsc.Org\Yavsc.Org.csproj" />
|
||||||
<ProjectReference Include="..\..\src\Yavsc.Abstract\Yavsc.Abstract.csproj" />
|
<ProjectReference Include="..\..\src\Yavsc.Abstract\Yavsc.Abstract.csproj" />
|
||||||
|
|
@ -37,4 +43,5 @@
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Using Include="Xunit" />
|
<Using Include="Xunit" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue