Fix: appsettings.Local.json im Host laden + headless UI-Smoke-Test
- KRITISCH: Host.CreateDefaultBuilder lädt appsettings.Local.json nicht (nur
appsettings.json + appsettings.{Environment}.json). Dadurch war
Database:MySqlConnectionString zur Laufzeit leer -> Pomelo fiel auf
localhost:3306 zurück ("Connect Timeout expired"), die GUI-App hätte am
Server nie mit MySQL verbunden. Fix: appsettings.Local.json explizit per
ConfigureAppConfiguration ergänzen (in Main und im Smoke-Test).
- Neuer CLI-Modus --smoke-ui: baut einen minimalen Host, hydriert den State
aus MySQL und konstruiert jede registrierte View + den Launcher (ohne
Message-Loop / Trading-Services). Deckte den obigen Bug auf.
- Verifiziert: 3 Accounts / 32 Trader hydriert; alle 3 Modul-Views + Launcher
konstruieren fehlerfrei mit echten Daten.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
05e0576965
commit
6f478e8764
+98
-1
@@ -40,6 +40,14 @@ internal static class Program
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Headless-Smoke-Test der UI: konstruiert jede View + den Launcher (ohne Message-Loop
|
||||||
|
// und ohne Trading-/WSS-Services). Fängt Laufzeit-Konstruktionsfehler ab. Kein Fenster.
|
||||||
|
if (args.Length > 0 && string.Equals(args[0], "--smoke-ui", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
Environment.ExitCode = RunSmokeUi();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
ApplicationConfiguration.Initialize();
|
ApplicationConfiguration.Initialize();
|
||||||
|
|
||||||
var modules = new System.Collections.Generic.List<IPolyTraderModule>
|
var modules = new System.Collections.Generic.List<IPolyTraderModule>
|
||||||
@@ -47,7 +55,13 @@ internal static class Program
|
|||||||
new CopyTradingModule()
|
new CopyTradingModule()
|
||||||
};
|
};
|
||||||
|
|
||||||
AppHost = Host.CreateDefaultBuilder().ConfigureServices(delegate(HostBuilderContext context, IServiceCollection services)
|
AppHost = Host.CreateDefaultBuilder()
|
||||||
|
// Host.CreateDefaultBuilder lädt appsettings.Local.json NICHT (nur appsettings.json
|
||||||
|
// + appsettings.{Environment}.json). Die gitignorierte Local-Datei hält aber die
|
||||||
|
// MySQL-Connection – daher hier explizit ergänzen, sonst bleibt sie leer.
|
||||||
|
.ConfigureAppConfiguration((context, config) =>
|
||||||
|
config.AddJsonFile("appsettings.Local.json", optional: true, reloadOnChange: false))
|
||||||
|
.ConfigureServices(delegate(HostBuilderContext context, IServiceCollection services)
|
||||||
{
|
{
|
||||||
var databaseOptions = new DatabaseOptions
|
var databaseOptions = new DatabaseOptions
|
||||||
{
|
{
|
||||||
@@ -237,4 +251,87 @@ internal static class Program
|
|||||||
|
|
||||||
Services.ConfigMigrator.VerifyMySql(mySql);
|
Services.ConfigMigrator.VerifyMySql(mySql);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Headless-Smoke-Test: baut einen minimalen Host (Persistenz + State + Modul-Registrierung
|
||||||
|
/// + Shell/Launcher), hydriert den State aus MySQL und konstruiert jede registrierte View
|
||||||
|
/// sowie den Launcher – ohne Message-Loop und ohne Trading-/WSS-Services zu starten.
|
||||||
|
/// Gibt 0 zurück, wenn alles fehlerfrei konstruiert, sonst die Fehleranzahl.
|
||||||
|
/// </summary>
|
||||||
|
private static int RunSmokeUi()
|
||||||
|
{
|
||||||
|
ApplicationConfiguration.Initialize();
|
||||||
|
|
||||||
|
var modules = new System.Collections.Generic.List<IPolyTraderModule> { new CopyTradingModule() };
|
||||||
|
|
||||||
|
using var host = Host.CreateDefaultBuilder()
|
||||||
|
.ConfigureAppConfiguration((context, config) =>
|
||||||
|
config.AddJsonFile("appsettings.Local.json", optional: true, reloadOnChange: false))
|
||||||
|
.ConfigureServices((context, services) =>
|
||||||
|
{
|
||||||
|
var databaseOptions = new DatabaseOptions
|
||||||
|
{
|
||||||
|
MySqlConnectionString = context.Configuration["Database:MySqlConnectionString"] ?? string.Empty
|
||||||
|
};
|
||||||
|
services.Configure<DatabaseOptions>(context.Configuration.GetSection(DatabaseOptions.SectionName));
|
||||||
|
services.AddCorePersistence(databaseOptions);
|
||||||
|
services.AddSingleton<TerminalLogger>();
|
||||||
|
services.AddSingleton<TradingState>();
|
||||||
|
services.AddSingleton<StartupHydrationService>();
|
||||||
|
|
||||||
|
foreach (var module in modules)
|
||||||
|
{
|
||||||
|
services.AddSingleton(module);
|
||||||
|
module.RegisterServices(services, context.Configuration);
|
||||||
|
}
|
||||||
|
|
||||||
|
services.AddSingleton<PolyTraderSharp.Ui.ShellUiHost>();
|
||||||
|
services.AddTransient<PolyTraderSharp.Ui.LauncherForm>();
|
||||||
|
}).Build();
|
||||||
|
|
||||||
|
// State aus MySQL laden (Accounts/Trader/Settings), ohne die BackgroundServices zu starten.
|
||||||
|
try
|
||||||
|
{
|
||||||
|
host.Services.GetRequiredService<StartupHydrationService>()
|
||||||
|
.StartAsync(default).GetAwaiter().GetResult();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"[WARN] Hydration übersprungen: {ex.Message}");
|
||||||
|
}
|
||||||
|
|
||||||
|
var uiHost = host.Services.GetRequiredService<PolyTraderSharp.Ui.ShellUiHost>();
|
||||||
|
foreach (var module in modules)
|
||||||
|
module.RegisterUi(uiHost, host.Services);
|
||||||
|
|
||||||
|
int failures = 0;
|
||||||
|
Console.WriteLine("=== Smoke-UI: View-Konstruktion ===");
|
||||||
|
foreach (var view in uiHost.Views)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using var form = view.CreateForm();
|
||||||
|
Console.WriteLine($"[OK] {view.Id} ({view.Title})");
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
failures++;
|
||||||
|
Console.WriteLine($"[FEHLER] {view.Id}: {ex.GetType().Name}: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using var launcher = host.Services.GetRequiredService<PolyTraderSharp.Ui.LauncherForm>();
|
||||||
|
Console.WriteLine("[OK] LauncherForm konstruiert");
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
failures++;
|
||||||
|
Console.WriteLine($"[FEHLER] LauncherForm: {ex.GetType().Name}: {ex.Message}");
|
||||||
|
}
|
||||||
|
|
||||||
|
Console.WriteLine(failures == 0 ? "=== Smoke-UI OK ===" : $"=== Smoke-UI: {failures} Fehler ===");
|
||||||
|
return failures;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user