Testanleitung: eigenes X-Display je Projekt fuer Parallelbetrieb
CI / Build & Tests (Linux) (push) Canceled after 0s
CI / Plattformneutralität & Hygiene (push) Canceled after 0s

Damit mehrere Agenten gleichzeitig Oberflaechen testen koennen, bekommt jedes
Projekt eine feste Displaynummer (Vorgabe Richard):

  :99 PolyTrader, :98 Predictalytics, :97 ClawdDotNet, :96 IBKRTrader,
  :95 und absteigend fuer kuenftige Projekte.

Zwei Prozesse auf demselben Display sehen die Fenster des jeweils anderen -
Klicks landen im falschen Programm, Aufnahmen zeigen fremde Anwendungen.

Beim Durcharbeiten fielen zwei weitere Kollisionspunkte auf, die zum
Parallelbetrieb dazugehoeren und mit aufgenommen wurden:

- Screenshot- und Logdateien in /tmp brauchen ein Projektpraefix. /tmp/v_1.png
  ueberschreiben sich parallel laufende Projekte gegenseitig.
- "xvfb-run -a" darf NICHT mehr verwendet werden: Das -a sucht sich selbst ein
  freies Display und beginnt dabei bei :99 - ausgerechnet PolyTraders Nummer.
  Stattdessen "xvfb-run -n <nummer>".

Die Warnung ist nicht theoretisch: Auf der VM lag ein verwaister X98-Socket,
entstanden durch genau so einen "xvfb-run -a"-Aufruf beim smoke-ui-Test, als
:99 belegt war. Er blockierte Predictalytics' reservierte Nummer, ohne dass ein
Prozess lief. Entfernt, und die Anleitung erklaert jetzt, wie man verwaiste
Sockets erkennt (ls /tmp/.X11-unix/ gegen pgrep -a Xvfb lesen) und aufraeumt.

Weiter ergaenzt:
- pkill nie ohne Displaynummer - "pkill Xvfb" erschlaegt die Sitzungen aller
  anderen Projekte.
- DISPLAY in jedem Skript setzen, sonst landet man auf :0, dem echten Desktop.
- Die Skriptvorlage hat jetzt einen Kopf mit PROJEKT/DISP/FENSTER; das ist
  alles, was sich je Projekt unterscheidet. Praktisch gegengetestet.
- Aufraeumabschnitt und Belegungsliste um Displays und Verzeichnisse erweitert.

Nebenbei aufgefallen: Das PolyTrader-Publish lag unter ~/pt-test statt
~/polytrader-test und verletzte damit die eigene Verzeichniskonvention.
Umbenannt.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Richard
2026-08-27 20:49:28 +02:00
co-authored by Claude Opus 5
parent 62570cfdd8
commit 9b2294cb4c
33 changed files with 979 additions and 244 deletions
@@ -0,0 +1,92 @@
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Reflection;
using PolyTrader.Modules.CopyTrading.Persistence.Ef;
using PolyTraderSharp.Models;
using Xunit;
namespace PolyTrader.Tests
{
/// <summary>
/// Sichert die Befunde des Linux-Sichttests vom 26.08.2026 ab
/// (docs/pruefplaene/BEFUND-Linux-Betrieb-2026-08-26.md), damit sie nicht zurückkehren.
/// </summary>
public class LinuxFindingsTests
{
// ── B-5: Windows-Pfad als Standardwert unter Linux ──────────────────────────────
[Fact]
public void Mullvad_default_path_matches_the_platform()
{
string expected = System.OperatingSystem.IsWindows()
? @"C:\Program Files\Mullvad VPN\resources\mullvad.exe"
: "/usr/bin/mullvad";
Assert.Equal(expected, ServerSettings.DefaultMullvadCliPath);
Assert.Equal(expected, new ServerSettings().MullvadCliPath);
}
// ── B-14: mehrzeilige Texte in einzeiligen Feldern ──────────────────────────────
[Theory]
[InlineData(nameof(TrackedTrader.Description))]
[InlineData(nameof(TrackedTrader.Reasoning))]
public void Long_free_text_properties_are_marked_multiline(string propertyName)
{
var p = typeof(TrackedTrader).GetProperty(propertyName)!;
var attr = p.GetCustomAttribute<DataTypeAttribute>();
Assert.NotNull(attr);
Assert.Equal(DataType.MultilineText, attr!.DataType);
}
// ── B-15: ein fehlerhafter Datensatz legt die Modul-Hydration lahm ──────────────
[Fact]
public void Assigned_accounts_round_trip_through_json()
{
var set = new System.Collections.Generic.HashSet<int> { 1, 2 };
Assert.Equal(set, AccountIdSetJson.Deserialize(AccountIdSetJson.Serialize(set)));
}
[Fact]
public void Empty_column_becomes_an_empty_assignment()
{
Assert.Empty(AccountIdSetJson.Deserialize(null));
Assert.Empty(AccountIdSetJson.Deserialize(""));
Assert.Empty(AccountIdSetJson.Deserialize("[]"));
}
[Fact]
public void Plain_number_list_is_still_understood()
{
AccountIdSetJson.ResetDiagnostics();
Assert.Equal(new System.Collections.Generic.HashSet<int> { 1, 2 },
AccountIdSetJson.Deserialize("1,2"));
Assert.Equal(0, AccountIdSetJson.MalformedCount);
}
[Fact]
public void Unreadable_value_yields_an_empty_set_and_is_counted()
{
AccountIdSetJson.ResetDiagnostics();
// Der Fall aus dem Test: kaputter Wert statt JSON-Array. Frueher warf die
// EF-Wertkonvertierung hier und es wurde KEIN einziger Trader geladen.
Assert.Empty(AccountIdSetJson.Deserialize("{kaputt"));
Assert.Equal(1, AccountIdSetJson.MalformedCount);
Assert.Equal("{kaputt", AccountIdSetJson.LastMalformedValue);
}
[Fact]
public void Text_that_is_not_a_number_list_is_not_silently_accepted()
{
AccountIdSetJson.ResetDiagnostics();
Assert.Empty(AccountIdSetJson.Deserialize("1,zwei"));
Assert.Equal(1, AccountIdSetJson.MalformedCount);
}
}
}