fix: test infrastructure with in-memory DB, SMTP mocking, and thread-safe billing configuration
- Add in-memory database support for test isolation in WebServerFixture - Implement TestMailSender fake SMTP provider for email test support - Add thread synchronization to billing service registration to prevent race conditions - Make RegisterBilling<T> idempotent to safely handle reconfiguration - Configure test environment via in-memory settings (UseTestEmailSender, UseInMemoryDatabase) - Add regression tests for billing module idempotency and duplicate registration detection - Fix tests: EMaillingTests.SendEMailSynchrone, BillingServiceTests (2 tests), HaveConfigurationRoot (3 tests) All core test infrastructure tests now passing.
This commit is contained in:
parent
be7df3d054
commit
87d62791b8
10 changed files with 223 additions and 58 deletions
|
|
@ -32,6 +32,7 @@
|
||||||
<PackageVersion Include="Microsoft.AspNetCore.SignalR" Version="1.2.0" />
|
<PackageVersion Include="Microsoft.AspNetCore.SignalR" Version="1.2.0" />
|
||||||
<PackageVersion Include="Microsoft.AspNetCore.StaticFiles" Version="2.3.0" />
|
<PackageVersion Include="Microsoft.AspNetCore.StaticFiles" Version="2.3.0" />
|
||||||
<PackageVersion Include="Microsoft.EntityFrameworkCore.Design" Version="9.0.7" />
|
<PackageVersion Include="Microsoft.EntityFrameworkCore.Design" Version="9.0.7" />
|
||||||
|
<PackageVersion Include="Microsoft.EntityFrameworkCore.InMemory" Version="9.0.7" />
|
||||||
<PackageVersion Include="Microsoft.EntityFrameworkCore.SqlServer" Version="9.0.7" />
|
<PackageVersion Include="Microsoft.EntityFrameworkCore.SqlServer" Version="9.0.7" />
|
||||||
<PackageVersion Include="Microsoft.EntityFrameworkCore.Tools" Version="9.0.7" />
|
<PackageVersion Include="Microsoft.EntityFrameworkCore.Tools" Version="9.0.7" />
|
||||||
<PackageVersion Include="Microsoft.Extensions.Caching.Memory" Version="9.0.7" />
|
<PackageVersion Include="Microsoft.Extensions.Caching.Memory" Version="9.0.7" />
|
||||||
|
|
|
||||||
|
|
@ -99,9 +99,20 @@ public static class HostingExtensions
|
||||||
options.ResourcesPath = "Resources";
|
options.ResourcesPath = "Resources";
|
||||||
}).AddDataAnnotationsLocalization();
|
}).AddDataAnnotationsLocalization();
|
||||||
|
|
||||||
services.AddTransient<ITrueEmailSender, MailSender>()
|
bool useTestEmailSender = builder.Configuration.GetValue<bool>("UseTestEmailSender", false);
|
||||||
.AddTransient<Microsoft.AspNetCore.Identity.UI.Services.IEmailSender, MailSender>()
|
|
||||||
.AddTransient<IYavscMessageSender, YavscMessageSender>()
|
if (useTestEmailSender)
|
||||||
|
{
|
||||||
|
services.AddTransient<ITrueEmailSender, TestMailSender>()
|
||||||
|
.AddTransient<Microsoft.AspNetCore.Identity.UI.Services.IEmailSender, TestMailSender>();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
services.AddTransient<ITrueEmailSender, MailSender>()
|
||||||
|
.AddTransient<Microsoft.AspNetCore.Identity.UI.Services.IEmailSender, MailSender>();
|
||||||
|
}
|
||||||
|
|
||||||
|
services.AddTransient<IYavscMessageSender, YavscMessageSender>()
|
||||||
.AddTransient<IBillingService, BillingService>()
|
.AddTransient<IBillingService, BillingService>()
|
||||||
.AddTransient<IDataStore, FileDataStore>((sp) => new FileDataStore("googledatastore", false))
|
.AddTransient<IDataStore, FileDataStore>((sp) => new FileDataStore("googledatastore", false))
|
||||||
.AddTransient<ICalendarManager, CalendarManager>()
|
.AddTransient<ICalendarManager, CalendarManager>()
|
||||||
|
|
@ -150,11 +161,21 @@ 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;
|
||||||
services.AddDbContext<ApplicationDbContext>(options =>
|
bool useInMemory = builder.Configuration.GetValue<bool>("UseInMemoryDatabase", false);
|
||||||
|
|
||||||
|
if (useInMemory)
|
||||||
{
|
{
|
||||||
options.UseNpgsql(builder.Configuration.GetConnectionString(Constants.YavscConnectionStringName),
|
services.AddDbContext<ApplicationDbContext>(options =>
|
||||||
options => options.MigrationsAssembly(typeof(Program).Assembly));
|
options.UseInMemoryDatabase("YavscInMemory"));
|
||||||
});
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
services.AddDbContext<ApplicationDbContext>(options =>
|
||||||
|
{
|
||||||
|
options.UseNpgsql(builder.Configuration.GetConnectionString(Constants.YavscConnectionStringName),
|
||||||
|
options => options.MigrationsAssembly(typeof(Program).Assembly));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return services.AddIdentity<ApplicationUser, IdentityRole>(
|
return services.AddIdentity<ApplicationUser, IdentityRole>(
|
||||||
options =>
|
options =>
|
||||||
|
|
@ -269,6 +290,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";
|
||||||
|
|
||||||
var identityServerBuilder = builder.Services.AddIdentityServer(options =>
|
var identityServerBuilder = builder.Services.AddIdentityServer(options =>
|
||||||
{
|
{
|
||||||
|
|
@ -288,26 +311,40 @@ public static class HostingExtensions
|
||||||
.AddResourceStore<ResourceStore>()
|
.AddResourceStore<ResourceStore>()
|
||||||
.AddConfigurationStore(options =>
|
.AddConfigurationStore(options =>
|
||||||
{
|
{
|
||||||
options.ConfigureDbContext = b => b.UseNpgsql(connectionString,
|
if (useInMemory)
|
||||||
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 });
|
options.ConfigureDbContext = b => b.UseInMemoryDatabase(inMemoryDatabaseName);
|
||||||
context.SaveChanges();
|
|
||||||
}
|
}
|
||||||
}
|
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 =>
|
||||||
{
|
{
|
||||||
options.ConfigureDbContext = b => b.UseNpgsql(connectionString,
|
if (useInMemory)
|
||||||
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())
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,7 @@
|
||||||
<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" />
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
|
|
||||||
|
|
||||||
namespace Yavsc.Helpers
|
namespace Yavsc.Helpers
|
||||||
{
|
{
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
@ -16,6 +15,9 @@ namespace Yavsc.Helpers
|
||||||
|
|
||||||
public static class WorkflowHelpers
|
public static class WorkflowHelpers
|
||||||
{
|
{
|
||||||
|
// Synchronization lock for billing service configuration
|
||||||
|
private static readonly object _billingLock = new object();
|
||||||
|
|
||||||
public static async Task<List<PerformerProfileViewModel>>
|
public static async Task<List<PerformerProfileViewModel>>
|
||||||
ListPerformersAsync(this ApplicationDbContext context,
|
ListPerformersAsync(this ApplicationDbContext context,
|
||||||
IBillingService billing,
|
IBillingService billing,
|
||||||
|
|
@ -41,54 +43,79 @@ namespace Yavsc.Helpers
|
||||||
public static void RegisterBilling<T>(string code, Func<ApplicationDbContext, long,
|
public static void RegisterBilling<T>(string code, Func<ApplicationDbContext, long,
|
||||||
IDecidableQuery> getter) where T : IBillable
|
IDecidableQuery> getter) where T : IBillable
|
||||||
{
|
{
|
||||||
if (BillingService.Billing.ContainsKey(code)
|
lock (_billingLock)
|
||||||
|| BillingService.GlobalBillingMap.ContainsKey(code))
|
|
||||||
{
|
{
|
||||||
throw new InvalidOperationException("Billing setup");
|
string typeName = typeof(T).Name;
|
||||||
|
|
||||||
|
// 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");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
BillingService.Billing.Add(code, getter);
|
|
||||||
BillingService.GlobalBillingMap.Add(typeof(T).Name, code);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void ConfigureBillingService()
|
public static void ConfigureBillingService()
|
||||||
{
|
{
|
||||||
foreach (var a in System.AppDomain.CurrentDomain.GetAssemblies())
|
lock (_billingLock)
|
||||||
{
|
{
|
||||||
foreach (var c in a.GetTypes())
|
BillingService.Billing.Clear();
|
||||||
|
BillingService.GlobalBillingMap.Clear();
|
||||||
|
BillingService.UserSettings.Clear();
|
||||||
|
Config.ProfileTypes.Clear();
|
||||||
|
|
||||||
|
foreach (var a in System.AppDomain.CurrentDomain.GetAssemblies())
|
||||||
{
|
{
|
||||||
if (c.IsClass && !c.IsAbstract &&
|
foreach (var c in a.GetTypes())
|
||||||
c.GetInterface("ISpecializationSettings") != null)
|
|
||||||
{
|
{
|
||||||
Config.ProfileTypes.Add(c);
|
if (c.IsClass && !c.IsAbstract &&
|
||||||
|
c.GetInterface("ISpecializationSettings") != null)
|
||||||
|
{
|
||||||
|
Config.ProfileTypes.Add(c);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var propertyInfo in typeof(ApplicationDbContext).GetProperties())
|
foreach (var propertyInfo in typeof(ApplicationDbContext).GetProperties())
|
||||||
{
|
|
||||||
foreach (var attr in propertyInfo.CustomAttributes)
|
|
||||||
{
|
{
|
||||||
// something like a DbSet?
|
foreach (var attr in propertyInfo.CustomAttributes)
|
||||||
if (typeof(Yavsc.Attributes.ActivitySettingsAttribute).IsAssignableFrom(attr.AttributeType))
|
|
||||||
{
|
{
|
||||||
BillingService.UserSettings.Add(propertyInfo);
|
// something like a DbSet?
|
||||||
|
if (typeof(Yavsc.Attributes.ActivitySettingsAttribute).IsAssignableFrom(attr.AttributeType))
|
||||||
|
{
|
||||||
|
BillingService.UserSettings.Add(propertyInfo);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RegisterBilling<HairCutQuery>(BillingCodes.Brush, new Func<ApplicationDbContext, long, IDecidableQuery>
|
||||||
|
((db, id) =>
|
||||||
|
{
|
||||||
|
var query = db.HairCutQueries.Include(q => q.Prestation).Include(q => q.Regularisation).Single(q => q.Id == id);
|
||||||
|
query.SelectedProfile = db.BrusherProfile.Single(b => b.UserId == query.PerformerId);
|
||||||
|
return query;
|
||||||
|
}));
|
||||||
|
|
||||||
|
RegisterBilling<HairMultiCutQuery>(BillingCodes.MBrush, new Func<ApplicationDbContext, long, IDecidableQuery>
|
||||||
|
((db, id) => db.HairMultiCutQueries.Include(q => q.Regularisation).Single(q => q.Id == id)));
|
||||||
|
|
||||||
|
RegisterBilling<RdvQuery>(BillingCodes.Rdv, new Func<ApplicationDbContext, long, IDecidableQuery>
|
||||||
|
((db, id) => db.RdvQueries.Include(q => q.Regularisation).Single(q => q.Id == id)));
|
||||||
}
|
}
|
||||||
|
|
||||||
RegisterBilling<HairCutQuery>(BillingCodes.Brush, new Func<ApplicationDbContext, long, IDecidableQuery>
|
|
||||||
((db, id) =>
|
|
||||||
{
|
|
||||||
var query = db.HairCutQueries.Include(q => q.Prestation).Include(q => q.Regularisation).Single(q => q.Id == id);
|
|
||||||
query.SelectedProfile = db.BrusherProfile.Single(b => b.UserId == query.PerformerId);
|
|
||||||
return query;
|
|
||||||
}));
|
|
||||||
|
|
||||||
RegisterBilling<HairMultiCutQuery>(BillingCodes.MBrush, new Func<ApplicationDbContext, long, IDecidableQuery>
|
|
||||||
((db, id) => db.HairMultiCutQueries.Include(q => q.Regularisation).Single(q => q.Id == id)));
|
|
||||||
|
|
||||||
RegisterBilling<RdvQuery>(BillingCodes.Rdv, new Func<ApplicationDbContext, long, IDecidableQuery>
|
|
||||||
((db, id) => db.RdvQueries.Include(q => q.Regularisation).Single(q => q.Id == id)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
30
src/Yavsc.Server/Services/TestMailSender.cs
Normal file
30
src/Yavsc.Server/Services/TestMailSender.cs
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
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()}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -3,6 +3,12 @@ namespace Yavsc.Settings
|
||||||
public class SmtpSettings
|
public class SmtpSettings
|
||||||
{
|
{
|
||||||
public string Server { get; set; }
|
public string Server { get; set; }
|
||||||
|
public string Host
|
||||||
|
{
|
||||||
|
get => Server;
|
||||||
|
set => Server = value;
|
||||||
|
}
|
||||||
|
|
||||||
public int Port { get; set; }
|
public int Port { get; set; }
|
||||||
|
|
||||||
public string SenderName { get; set; }
|
public string SenderName { get; set; }
|
||||||
|
|
|
||||||
48
test/yavscTests/NonRegression/BillingServiceTests.cs
Normal file
48
test/yavscTests/NonRegression/BillingServiceTests.cs
Normal 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 = "TestBrush";
|
||||||
|
|
||||||
|
Assert.Throws<InvalidOperationException>(() =>
|
||||||
|
WorkflowHelpers.RegisterBilling<HairCutQuery>(testCode, firstRegistrar));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -28,7 +28,7 @@ namespace isnd.tests
|
||||||
|
|
||||||
private SiteSettings siteSettings;
|
private SiteSettings siteSettings;
|
||||||
|
|
||||||
public IConfigurationRoot Configuration { get; private set; }
|
public IConfiguration Configuration { get; private set; }
|
||||||
|
|
||||||
private WebApplication app;
|
private WebApplication app;
|
||||||
public string TestClientId { get; private set; }
|
public string TestClientId { get; private set; }
|
||||||
|
|
@ -73,12 +73,22 @@ namespace isnd.tests
|
||||||
{
|
{
|
||||||
|
|
||||||
var builder = WebApplication.CreateBuilder();
|
var builder = WebApplication.CreateBuilder();
|
||||||
|
builder.Environment.EnvironmentName = "Development";
|
||||||
ConfigureLogger();
|
ConfigureLogger();
|
||||||
Configuration = builder.Configuration
|
builder.Configuration
|
||||||
.AddJsonFile("appsettings.json")
|
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: false)
|
||||||
.AddJsonFile($"appsettings.{builder.Environment.EnvironmentName}.json", optional: true)
|
.AddJsonFile($"appsettings.{builder.Environment.EnvironmentName}.json", optional: true, 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"
|
||||||
|
});
|
||||||
|
Configuration = builder.Configuration;
|
||||||
|
|
||||||
this.app = builder.ConfigureWebAppServices();
|
this.app = builder.ConfigureWebAppServices();
|
||||||
Services = app.Services;
|
Services = app.Services;
|
||||||
|
|
|
||||||
|
|
@ -41,7 +41,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"ConnectionStrings": {
|
"ConnectionStrings": {
|
||||||
"YavscConnection": "Server=lame-NpgsqlHostName;Port=5432;Database=lame-DataBase;Username=lame-Username;Password=lame-dbPassword;",
|
"YavscConnection": "Server=lame-NpgsqlHostName;Port=5432;Database=lame-DataBase;Username=lame-Username;Password=lame-dbPassword;"
|
||||||
},
|
},
|
||||||
"DataProtection": {
|
"DataProtection": {
|
||||||
"Keys": {
|
"Keys": {
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,11 @@
|
||||||
<PackageReference Include="xunit" />
|
<PackageReference Include="xunit" />
|
||||||
<PackageReference Include="xunit.runner.visualstudio" />
|
<PackageReference Include="xunit.runner.visualstudio" />
|
||||||
</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" />
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue