feat: Web-UI als zweiter Ausgabeweg neben Avalonia-Shell (#490)
- Einheitlicher WebserverWorker in IBKRTrader.Hosting auf Kestrel-Basis - Token-Authentifizierung (AuthService) mit zeitkonstantem Vergleich und HMAC-Session-Tokens - OrderManager im Core fuer Bestands- und Reservierungsabgleich offener Broker-Orders - SettingsSchema fuer reflexionsbasiertes Auslesen und sichere Aktualisierung ohne Klartext-Passwoerter - SSE-Streaming (LogStreamService) fuer Echtzeit-Logs ueber isolierte Channels - Statische Single-Page-Application in wwwroot (Avalonia Fluent / Polytrader Theme) fuer alle 9 Ansichten - Unit-Tests fuer OrderManager, AuthService, SettingsSchema, LogStreamService und WebserverWorker (498 Tests gruen)
This commit is contained in:
@@ -0,0 +1,99 @@
|
||||
using FluentAssertions;
|
||||
using IBKRTrader.Core.Settings;
|
||||
|
||||
namespace IBKRTrader.Tests.Settings;
|
||||
|
||||
[Trait("cat", "unit")]
|
||||
public class SettingsSchemaTests
|
||||
{
|
||||
[Fact]
|
||||
public void Build_ExtractsSectionsAndFieldsFromAppSettings()
|
||||
{
|
||||
var settings = new AppSettings();
|
||||
var sections = SettingsSchema.Build(settings);
|
||||
|
||||
sections.Should().NotBeEmpty();
|
||||
sections.SelectMany(s => s.Fields).Should().NotBeEmpty();
|
||||
|
||||
// Webserver-Abschnitt prüfen
|
||||
var ws = sections.FirstOrDefault(s => s.Fields.Any(f => f.Path == "Webserver.Port"));
|
||||
ws.Should().NotBeNull();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ToDto_MasksPasswordsAndTokens()
|
||||
{
|
||||
var settings = new AppSettings();
|
||||
settings.Webserver.AuthToken = "super-secret-1234";
|
||||
settings.Database.Password = "db-pass-secret";
|
||||
|
||||
var dtos = SettingsSchema.ToDto(settings);
|
||||
var allFields = dtos.SelectMany(s => s.Fields).ToList();
|
||||
|
||||
// AuthToken prüfen
|
||||
var tokenField = allFields.FirstOrDefault(f => f.Path == "Webserver.AuthToken");
|
||||
tokenField.Should().NotBeNull();
|
||||
tokenField!.IsPassword.Should().BeTrue();
|
||||
tokenField.HasValue.Should().BeTrue();
|
||||
tokenField.Value.Should().BeNull("Passwörter und Tokens dürfen niemals im Klartext im DTO stehen!");
|
||||
|
||||
// DB Password prüfen
|
||||
var passField = allFields.FirstOrDefault(f => f.Path == "Database.Password");
|
||||
passField.Should().NotBeNull();
|
||||
passField!.IsPassword.Should().BeTrue();
|
||||
passField.Value.Should().BeNull();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TrySetValue_UpdatesValueByPath()
|
||||
{
|
||||
var settings = new AppSettings();
|
||||
settings.Webserver.Port = 5001;
|
||||
|
||||
var success = SettingsSchema.TrySetValue(settings, "Webserver.Port", 6002);
|
||||
|
||||
success.Should().BeTrue();
|
||||
settings.Webserver.Port.Should().Be(6002);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TrySetValue_PreservesPasswordWhenEmptyPassed()
|
||||
{
|
||||
var settings = new AppSettings();
|
||||
settings.Webserver.AuthToken = "keep-this-token";
|
||||
|
||||
var success = SettingsSchema.TrySetValue(settings, "Webserver.AuthToken", "");
|
||||
|
||||
success.Should().BeTrue();
|
||||
settings.Webserver.AuthToken.Should().Be("keep-this-token");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TrySetValue_ReturnsFalseForUnknownPath()
|
||||
{
|
||||
var settings = new AppSettings();
|
||||
|
||||
var success = SettingsSchema.TrySetValue(settings, "NonExistent.Field", "val");
|
||||
|
||||
success.Should().BeFalse();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TrySetValue_ParsesStringRepresentationOfTypes()
|
||||
{
|
||||
var settings = new AppSettings();
|
||||
|
||||
// int als string
|
||||
SettingsSchema.TrySetValue(settings, "Webserver.Port", "7001").Should().BeTrue();
|
||||
settings.Webserver.Port.Should().Be(7001);
|
||||
|
||||
// bool als string
|
||||
SettingsSchema.TrySetValue(settings, "Webserver.Enabled", "true").Should().BeTrue();
|
||||
settings.Webserver.Enabled.Should().BeTrue();
|
||||
|
||||
// bool als false
|
||||
SettingsSchema.TrySetValue(settings, "Webserver.Enabled", "false").Should().BeTrue();
|
||||
settings.Webserver.Enabled.Should().BeFalse();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user