3 Commits
Author SHA1 Message Date
RichardandClaude Opus 5 53231d62bc Testen laeuft jetzt ueber das Deploymentcenter; Befunde in den Bugtracker
CI / Build & Tests (Linux) (push) Canceled after 0s
CI / Plattformneutralität & Hygiene (push) Canceled after 0s
Die Testanleitung im PolyTrader-Repo ist nur noch ein Zeiger auf die
massgebliche Fassung in Deploymentcenter/docs/TESTUMGEBUNG.md, plus dem, was
PolyTrader-eigen ist: Display :99, Datenbank polytrader_test, Dienstkonto,
Publish-Verzeichnis, /tmp-Praefix. Die Anleitung galt immer projektuebergreifend
und lag nur hier, weil sie beim ersten Linux-Lauf von PolyTrader entstand. Eine
Tabelle loest die alten Abschnittsnummern auf, damit vorhandene Verweise lesbar
bleiben.

Befundbericht: Nachtrag 6 haelt die Gegenprobe am Bild vom 28.08.2026 fest -
den ersten Lauf nach dem neuen Verfahren. Linux-Build 0 Fehler / 15 Warnungen
wie unter Windows, 484 Tests gruen, --smoke-ui unter xvfb-run -n 99 durchgaengig
gruen. B-6, B-7, B-11, B-13, B-14 und die B-12-Selbstheilung sind damit auf der
Zielplattform bestaetigt; B-9 hat sich als kein Fehler entpuppt, und die
Spaltenumstellung hatte einen eigenen Fehler erzeugt.

Damit ist dieser Bericht abgeschlossen. Was offen bleibt, steht ab jetzt im
Bugtracker (Items #50-#52 und #56 fuer polytrader, #53-#55 und #57 fuer
deploymentcenter) statt in Befundberichten - dort geht es nicht mehr verloren,
und Befunde an fremden Projekten landen dort, wo jemand sie beheben kann.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-28 21:06:34 +02:00
RichardandClaude Opus 5 57cfca5fe9 Testlauf, dem man glauben kann: Zeitzonen-Tests und Schluss mit Nebenlaeufigkeit
Zwei Tests zur Zeitzonen-Umstellung aus dem letzten Commit: der Zeitstempel
eines Logeintrags traegt den Versatz der ANZEIGE-Zone (nicht den des Systems),
und die abgelegte JSONL-Zeile traegt ihn mit. Sie liegen bewusst in
AppTimeZoneTests - dort steht der uebrige Zeitzonen-Zustand, und innerhalb einer
Klasse laufen Tests nacheinander.

Das genuegte aber nicht. Von sechs Laeufen fiel einer um und liess sich danach
nicht wieder einfangen. Die Ursache ist kein einzelner Test, sondern die Bauart:
Mehrere Bausteine halten prozessweiten Zustand, den Tests bewusst umstellen -
AppTimeZone, SecretProtection, die Diagnosezaehler von AccountIdSetJson. xUnit
fuehrt Testklassen standardmaessig nebenlaeufig aus, also sieht eine Klasse
mitten im eigenen Lauf, was eine andere gerade umgestellt hat.

Neu ist das nicht, aber die Zeitzonen-Umstellung hat es verschaerft: Seit die
Logzeitstempel der Anzeige-Zeitzone folgen, haengt jedes LogMessageEventArgs an
AppTimeZone - waehrend AppTimeZoneTests dieselbe Einstellung nach Auckland und
zurueck dreht.

Deshalb laufen die Testklassen jetzt nacheinander, und die Zaehlerpruefungen
arbeiten mit der Differenz statt mit absoluten Werten. Kosten: 4-5 s statt 3 s
Laufzeit. Ein Testlauf, dem man glauben kann, ist das allemal wert - danach
sechs Laeufe in Folge gruen.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-28 21:06:18 +02:00
RichardandClaude Opus 5 f4e017fe58 Spalten wieder vollstaendig; die Kalender-Vermutung war falsch
Zwei Nacharbeiten aus der Sichtpruefung auf der Linux-VM vom 28.08.2026.

Master-Trader-Grid: Die Breitenumstellung von gestern hat mehr kaputtgemacht
als geheilt. Von zwoelf Spalten blieben sieben brauchbar - hinter "Winrate %"
stand eine Spalte mit abgeschnittenen Werten ohne Ueberschrift, dahinter vier
leere Streifen. PnL, Copy-PnL, Profit-Faktor, Ø PnL/Trade und Copy-Trades waren
weder lesbar noch als Spalte erkennbar. Das ist schlimmer als der urspruengliche
Befund: dort fehlten Ueberschriften, hier die Werte.

Ursache war die Sternbreite auf "Name". Zwoelf Spalten passen neben dem 460 px
breiten Detailbereich nicht ins Fenster; statt waagerecht zu schieben, verteilt
das DataGrid den Mangel und zerdrueckt die Auto-Spalten am Ende. Jetzt steht
auch "Name" auf Auto: jede Spalte behaelt ihre Breite, das Grid bekommt einen
waagerechten Rollbalken. Im Bild gegengeprueft.

Kalendersymbol (Befund B-9): zurueckgenommen. Die gestrige Aenderung war eine
Vermutung, und sie war falsch. Die vermeintlich sinnlose Zahl im Datumsfeld ist
das heutige Datum - am 26.08. eine "26", am 28.08. eine "28". Avalonia zeichnet
dort die heutige Tageszahl in ein Kalenderblatt, unter Linux fehlt der
Blatt-Glyph. Zwei Gegenproben liessen das Bild unveraendert: die Formatvorlage
per :not(#PART_Button) vom Knopf ferngehalten, und das Farbschema gewechselt.
Eine Aenderung ohne Wirkung und ohne Begruendung gehoert nicht in den Code; an
ihrer Stelle steht ein Kommentar, damit der Naechste nicht dieselbe Spur
verfolgt. Rein kosmetisch, Bugtracker-Item #50.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-28 21:05:49 +02:00
8 changed files with 285 additions and 408 deletions
+8 -3
View File
@@ -92,8 +92,13 @@ Nebenbefund: **sämtliche Dezimal-Einstellungen waren in der Oberfläche nicht m
also die komplette Risikokonfiguration. Einzelheiten und Begründungen im Nachtrag 5 von also die komplette Risikokonfiguration. Einzelheiten und Begründungen im Nachtrag 5 von
[BEFUND-Linux-Betrieb-2026-08-26.md](./pruefplaene/BEFUND-Linux-Betrieb-2026-08-26.md). [BEFUND-Linux-Betrieb-2026-08-26.md](./pruefplaene/BEFUND-Linux-Betrieb-2026-08-26.md).
⬜ **Offener Rest:** Gegenprobe am Bild auf der VM (Spaltenbreiten mit echten Daten, **Stand 28.08.2026:** Die Gegenprobe am Bild ist gelaufen (Nachtrag 6). Alle Behebungen sind auf
Kalendersymbol B-9, Splitter im Copytrading-Detailbereich). Verfahren und Testdaten stehen bereit. der Zielplattform bestätigt; B-9 hat sich als **kein Fehler** entpuppt (Avalonia zeichnet die
heutige Tageszahl — am 26.08. eine „26", am 28.08. eine „28"), und die Spaltenumstellung hatte
einen eigenen Fehler erzeugt, der ebenfalls behoben ist.
⬜ **Offener Rest:** Splitterpositionen und Leerzustände im Gebrauch beurteilen. Alles Weitere
steht ab jetzt im **Bugtracker** (Items #50–#57), nicht mehr in Befundberichten.
**Blockiert nichts** — aber es ist die letzte offene Zusage der UI-Portierung. **Blockiert nichts** — aber es ist die letzte offene Zusage der UI-Portierung.
Regeln für Nacharbeiten: [LEITFADEN-Avalonia-Portierung.md](./LEITFADEN-Avalonia-Portierung.md). Regeln für Nacharbeiten: [LEITFADEN-Avalonia-Portierung.md](./LEITFADEN-Avalonia-Portierung.md).
@@ -431,7 +436,7 @@ Prüfplan: [pruefplaene/PREDICTALYTICS-PRUEFPLAN-Master-Auswahl.md](./pruefplaen
| | Punkt | Details | | | Punkt | Details |
|---|---|---| |---|---|---|
| **T1** | ⬜ 15 Build-Warnungen | Alle im Avalonia-Projekt: 13 × `CS8618` (Felder in Fenster-Konstruktoren), 1 × `CS8848` (Vorrang bei `switch`), 1 × `CS8602` (möglicher Nullverweis, `PdfExporter.cs:40`). Die letzten beiden sind einen Blick wert — dahinter kann ein echter Fehler stecken. Erst wenn sie weg sind, ist `-warnaserror` in der CI sinnvoll | | **T1** | ⬜ 15 Build-Warnungen | Alle im Avalonia-Projekt: 13 × `CS8618` (Felder in Fenster-Konstruktoren), 1 × `CS8848` (Vorrang bei `switch`), 1 × `CS8602` (möglicher Nullverweis, `PdfExporter.cs:40`). Die letzten beiden sind einen Blick wert — dahinter kann ein echter Fehler stecken. Erst wenn sie weg sind, ist `-warnaserror` in der CI sinnvoll |
| **T2** | ⬜ TerminalLogger | Stempelt mit `DateTime.Now` statt der konfigurierten `AppTimeZone`. Auf einem UTC-Linuxserver passen die Logdatei-Grenzen nicht zur angezeigten Uhrzeit. Zusammen mit der Umstellung auf `Microsoft.Extensions.Logging` erledigen — **bei A4a nachweisbar** | | **T2** | ⬜ TerminalLogger | **Zeitzonen-Teil am 27.08.2026 erledigt** (vorgezogen, weil A4a den Fehler nachgewiesen hat): Zeitstempel sind jetzt `DateTimeOffset` in der Anzeige-Zeitzone, Logdatei-Grenzen und Oberfläche passen zusammen, und die `.log`-Dateien heißen wie die `.jsonl` (`yyyy-MM-dd`). **Offen bleibt** die eigentliche Umstellung auf `Microsoft.Extensions.Logging` |
| **T3** | ⬜ God-Methoden | `PollLiveAccountsAsync`, `ProcessAccountOrderAsync` splitten; duplizierte Closed-Trade-Erzeugung zentralisieren | | **T3** | ⬜ God-Methoden | `PollLiveAccountsAsync`, `ProcessAccountOrderAsync` splitten; duplizierte Closed-Trade-Erzeugung zentralisieren |
| **T4** | ⬜ CopyTrading-Follow-ups | TradeId-Autoincrement, Dedup, Performance | | **T4** | ⬜ CopyTrading-Follow-ups | TradeId-Autoincrement, Dedup, Performance |
| **T5** | ⬜ Barlow-Schriften | Im UI-Redesign vorgesehen, nie eingebettet | | **T5** | ⬜ Barlow-Schriften | Im UI-Redesign vorgesehen, nie eingebettet |
+83 -374
View File
@@ -1,389 +1,98 @@
# Linux-Testumgebung (gemeinsam genutzte VM) # Linux-Testumgebung — PolyTrader-Teil
**Stand: 26.08.2026** · Eingerichtet beim ersten Linux-Lauf von PolyTrader (Roadmap A4a). **Stand: 28.08.2026**
> **Diese Anleitung gilt projektübergreifend.** Sie richtet sich an alle Agenten und Entwickler, > ## ⚠️ Die Anleitung ist umgezogen
> die auf der VM testen — nicht nur an PolyTrader. Die maßgebliche Fassung liegt auf der Maschine >
> selbst unter **`~/TESTUMGEBUNG.md`**; dieses Dokument im PolyTrader-Repo ist die versionierte > Die vollständige, gepflegte Fassung liegt jetzt im **Deploymentcenter-Repo**:
> Kopie. Wer etwas an der Umgebung ändert, zieht **beide** nach. >
> **`J:\Softwareprojekte\Deploymentcenter\docs\TESTUMGEBUNG.md`**
>
> Diese Datei hier enthält nur noch, was **PolyTrader-eigen** ist.
**Warum der Umzug.** Die Anleitung galt von Anfang an projektübergreifend — sie regelt
Datenbanknamen, X-Displays und Portvergabe für *alle* Projekte auf der geteilten VM. Sie lag
trotzdem im PolyTrader-Repo, weil sie beim ersten Linux-Lauf von PolyTrader entstanden ist.
Das war die falsche Heimat, und es fiel auf, als die Testumgebung an die vorhandene Infrastruktur
angeschlossen wurde: Lizenzschlüssel, Release-Ablage und Bugtracker gehören dem Deploymentcenter,
und die Beschreibung, wie ein Agent sie benutzt, gehört dorthin, wo sie mit ihnen gemeinsam
gepflegt wird. Ein Agent in Predictalytics hätte die Regeln für sein X-Display sonst in einem
fremden Handelsprojekt suchen müssen.
--- ---
## 1. Was diese Maschine ist ## Was neu ist
Eine Ubuntu-Desktop-VM zum **Testen** — nicht für Produktivbetrieb, nicht für echte Zugangsdaten, Auf der VM muss nichts mehr von Hand besorgt werden, was das Deploymentcenter schon hat:
nicht für Geld bewegende Läufe.
| | | ```bash
curl -fsSL https://dc.mhdf.de/testkit/dc-testkit.sh -o ~/bin/dc-testkit && chmod +x ~/bin/dc-testkit
dc-testkit doctor # Werkzeuge, Netz, Token, Projekt
dc-testkit license # befristeten DEV-Lizenzschlüssel selbst holen
dc-testkit run # ausliefern, prüfen, aktualisieren — meldet, was scheitert
dc-testkit report --type bug --title "…" --desc "…"
```
Voraussetzung ist ein **Werkbank-Token** für PolyTrader (Besitzerart `dev_agent`, Rechte
`bugtracker:manage` und `license:dev`). Das legt ein Mensch einmalig im Deploymentcenter-WebUI an;
siehe `TESTUMGEBUNG.md` §4.2.
**Kein Kundenschlüssel mehr auf der Test-VM.** Ein DEV-Schlüssel läuft nach 30 Tagen ab, ist in
der Aktivierungsliste als Testhardware erkennbar und verbraucht keine Aktivierungsplätze einer
echten Lizenz.
---
## PolyTrader-eigene Festlegungen
Diese Werte stehen auch in der Belegungsliste der Hauptanleitung (§7); hier stehen sie, damit man
sie beim Arbeiten am Projekt nicht dort suchen muss.
| Was | Wert |
|---|---| |---|---|
| System | Ubuntu 26.04 LTS, Kernel 7.0, x86_64 | | X-Display | **`:99`** — fest für PolyTrader |
| Ausstattung | 31 GB RAM, 5 CPUs, ~33 GB freier Plattenplatz | | Datenbank | `polytrader_test`, Benutzer `polytrader_test`@`localhost` |
| Erreichbar | `192.168.40.48` über einen WireGuard-Tunnel | | Passwortdatei | `~/.polytrader_test_dbpw` (Rechte 600) |
| Benutzer | `richard` | | systemd-Unit | `polytrader.service`, Konto `polytrader`, Verzeichnis `/opt/polytrader` |
| Besonderheit | **Desktop-Umgebung vorhanden** — grafische Anwendungen sind testbar, nicht nur Dienste | | Publish-Verzeichnis | `~/polytrader-test` |
| Dateien in `/tmp` | immer mit Präfix `polytrader_` |
| Fenstername für `xdotool` | `^PolyTrader$` |
**Warum eine Desktop-VM?** Weil sich damit beides prüfen lässt: der kopflose Serverbetrieb ⚠️ **`xvfb-run -n 99`, niemals `-a`.** Das `-a` sucht sich selbst ein freies Display und beginnt
(systemd) *und* die grafische Oberfläche auf der Zielplattform. dabei bei `:99` — also ausgerechnet auf PolyTraders Nummer.
⚠️ **Keine Produktivdatenbank verbinden.** Der `MarketSyncService` synchronisiert selbständig
Marktdaten, sobald er läuft — auch dann, wenn niemand ihn darum gebeten hat.
--- ---
## 2. Zugang einrichten ## Wo die alten Abschnitte jetzt stehen
Der Zugang läuft über **SSH-Schlüssel**. Passwort-Anmeldung wird nicht verwendet — Agenten geben Ältere Dokumente in diesem Repo verweisen auf Abschnittsnummern dieser Datei. Die Nummerierung hat
grundsätzlich keine Passwörter ein. sich durch den neuen Testkit-Abschnitt verschoben:
### 2.1 Schlüssel erzeugen (auf dem eigenen Rechner) | Alt (hier) | Neu (`Deploymentcenter/docs/TESTUMGEBUNG.md`) |
```bash
ssh-keygen -t ed25519 -f ~/.ssh/testvm_<projekt> -N "" -C "<projekt>@testvm"
```
Ein **eigener Schlüssel je Projekt** — dann lässt sich einzelnen Projekten der Zugang entziehen,
ohne alle anderen auszusperren.
### 2.2 Öffentlichen Schlüssel hinterlegen
Der Inhalt von `~/.ssh/testvm_<projekt>.pub` wird auf der VM in `~/.ssh/authorized_keys`
eingetragen. **Das muss ein Mensch tun** (einmalig, mit Passwort). Direkt auf der VM:
```bash
mkdir -p ~/.ssh && chmod 700 ~/.ssh && echo '<INHALT DER .pub-DATEI>' >> ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys
```
### 2.3 SSH-Konfiguration (auf dem eigenen Rechner)
```
Host testvm
HostName 192.168.40.48
User richard
IdentityFile ~/.ssh/testvm_<projekt>
IdentitiesOnly yes
ServerAliveInterval 30
ServerAliveCountMax 4
```
⚠️ **Kein `ControlMaster`/`ControlPath` eintragen, wenn der Client unter Windows läuft** —
Windows-OpenSSH unterstützt Verbindungs-Multiplexing nicht und scheitert mit
„Failed to connect to new control master". Stattdessen mehrere Prüfschritte pro Aufruf bündeln.
Test: `ssh testvm "hostname"`
---
## 3. Was installiert ist
| Werkzeug | Version | Anmerkung |
|---|---|---|
| .NET SDK | 10.0.111 (Runtime 10.0.11) | SDK, nicht nur Runtime — auf der VM kann gebaut werden |
| MariaDB | 11.8.6 | Dienst `mariadb`, lauscht auf `localhost:3306` |
| Git | 2.53.0 | |
| Xvfb | vorhanden | Virtueller Framebuffer für GUI-Tests ohne Bildschirm (siehe 4.7) |
| xdotool | vorhanden | Klicks und Tastatureingaben skripten |
| ImageMagick | vorhanden | `import` für Bildschirmaufnahmen, `convert` zum Zuschneiden |
| JetBrains Rider | vorhanden | |
| Docker | **nicht** installiert | |
| Node.js | **nicht** installiert | |
**Nicht vorhandene Werkzeuge dürfen installiert werden** — aber bitte in der Belegungsliste
(Abschnitt 6) vermerken, damit andere wissen, was sich geändert hat.
---
## 4. Konventionen für mehrere Projekte
Die Maschine wird von mehreren Projekten geteilt. Diese Regeln verhindern, dass sich Tests
gegenseitig zerstören:
### 4.1 Datenbanken — je Projekt eine eigene
**Niemals** eine fremde Datenbank mitbenutzen und **niemals** eine Produktivdatenbank verbinden.
```bash
# Passwort erzeugen und ablegen (nur für den Besitzer lesbar)
PW=$(openssl rand -base64 18 | tr -d "/+=" | head -c 20)
echo "$PW" > ~/.<projekt>_test_dbpw && chmod 600 ~/.<projekt>_test_dbpw
sudo mariadb -e "CREATE DATABASE IF NOT EXISTS <projekt>_test CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER IF NOT EXISTS '<projekt>_test'@'localhost' IDENTIFIED BY '$PW';
GRANT ALL PRIVILEGES ON <projekt>_test.* TO '<projekt>_test'@'localhost';
FLUSH PRIVILEGES;"
```
| Was | Schema | Beispiel |
|---|---|---|
| Datenbank | `<projekt>_test` | `polytrader_test` |
| Benutzer | `<projekt>_test`@`localhost` | `polytrader_test` |
| Passwortdatei | `~/.<projekt>_test_dbpw` (Rechte 600) | |
Die Rechte enden **an der eigenen Datenbank** — `GRANT ALL ON <projekt>_test.*`, nie global.
### 4.2 Verzeichnisse
| Zweck | Ort |
|---|---| |---|---|
| Quellcode | `~/RiderProjects/<projekt>` | | §1 Was diese Maschine ist | §1 — unverändert |
| Publish/Testlauf im Benutzerkontext | `~/<projekt>-test` (voller Projektname, keine Kürzel) | | §2 Zugang einrichten | §2 — unverändert |
| Installation als Dienst | `/opt/<projekt>` | | §3 Was installiert ist | §3 — unverändert |
| — | **§4 Das Testkit** (neu) |
### 4.3 systemd-Dienste | §4.1 Datenbanken | §5.1 |
| §4.2 Verzeichnisse | §5.2 |
Unit-Name = Projektname: `<projekt>.service`. Eigenes Dienstkonto je Projekt: | §4.3 systemd-Dienste | §5.3 |
| §4.4 Ports | §5.4 |
```bash | §4.5 X-Displays | §5.5 |
sudo useradd --system --home-dir /opt/<projekt> --shell /usr/sbin/nologin <projekt> | §4.6 Erhöhte Rechte | §5.6 |
``` | §4.7 GUI ohne Bildschirm | §5.7 |
| **§4.8 Oberflächen selbst begutachten** | **§5.8** |
Nach dem Test **abschalten** (Abschnitt 7) — sonst laufen irgendwann fünf Dienste, die niemand | §5 Was hier nicht passieren darf | §6 |
mehr zuordnen kann, und der nächste Test misst gegen fremde Last. | §6 Belegung | §7 |
| **§7 Aufräumen** | **§8** |
### 4.4 Ports | §8 Bekannte Einschränkungen | §9 |
Vor dem Belegen prüfen: `ss -tlnp`. Belegte Ports stehen in Abschnitt 6. Wer einen Port dauerhaft Betroffen sind [BEFUND-Linux-Betrieb-2026-08-26.md](./pruefplaene/BEFUND-Linux-Betrieb-2026-08-26.md)
belegt, trägt ihn dort ein. (§4.8 und §7) und [ROADMAP.md](./ROADMAP.md). Die Verweise wurden bewusst **nicht** umgeschrieben:
Ein Befundbericht ist eine Momentaufnahme und soll lesen wie am Tag seiner Erstellung. Diese
### 4.5 X-Displays — feste Nummer je Projekt Tabelle löst sie auf.
Damit **mehrere Agenten gleichzeitig** Oberflächen testen können, bekommt jedes Projekt ein
eigenes virtuelles Display. Zwei Prozesse auf demselben Display sehen die Fenster des jeweils
anderen — Klicks landen im falschen Programm, Aufnahmen zeigen fremde Anwendungen.
| Display | Projekt |
|---|---|
| `:99` | **PolyTrader** |
| `:98` | **Predictalytics** |
| `:97` | **ClawdDotNet** |
| `:96` | **IBKRTrader** |
| `:95` und **absteigend** | künftige Projekte (nächstes freies von oben nach unten) |
Wer ein neues Projekt aufnimmt, trägt seine Nummer in die Belegungsliste (Abschnitt 6) ein.
**Belegte Displays prüfen:**
```bash
ls /tmp/.X11-unix/ # X0 = der echte Desktop, X99 = PolyTrader usw.
pgrep -a Xvfb # zeigt, welcher Xvfb auf welchem Display läuft
```
**Verwaiste Sockets** — beide Befehle gemeinsam lesen: Steht in `/tmp/.X11-unix/` ein `X98`, zu
dem `pgrep` keinen Prozess zeigt, ist das ein Überbleibsel eines abgestürzten oder hart beendeten
Xvfb. Die Nummer wirkt dann belegt, obwohl sie frei ist. Aufräumen:
```bash
sudo rm -f /tmp/.X11-unix/X98
```
*(Genau so entstanden: ein `xvfb-run -a`, das sich :98 nahm, weil :99 gerade lief — siehe die
Warnung in 4.7.)*
⚠️ **Drei Regeln, damit der Parallelbetrieb wirklich trägt:**
1. **`DISPLAY` in jedem Skript setzen** (`export DISPLAY=:98`). Fehlt die Variable, landet man auf
`:0` — dem echten Desktop — und stört den Menschen, der dort gerade arbeitet.
2. **Nie `pkill Xvfb` ohne Displaynummer.** Das erschlägt die Sitzungen aller anderen Projekte.
Immer `pkill -f "Xvfb :98"`. Dasselbe gilt für die eigene Anwendung: `pkill -f` mit einem zu
allgemeinen Muster trifft fremde Prozesse — besser die gemerkte PID beenden.
3. **Dateien in `/tmp` mit Projektpräfix.** `/tmp/v_1.png` überschreibt sich gegenseitig; nimm
`/tmp/<projekt>_v_1.png`. Gleiches gilt für Logdateien und Skripte.
### 4.6 Erhöhte Rechte
`sudo` verlangt normalerweise ein Passwort. Für Testläufe kann es befristet freigeschaltet werden
(**ein Mensch, einmalig**):
```bash
echo "richard ALL=(ALL) NOPASSWD:ALL" | sudo tee /etc/sudoers.d/99-testing && sudo chmod 440 /etc/sudoers.d/99-testing
```
**Nach Abschluss wieder entfernen:**
```bash
sudo rm /etc/sudoers.d/99-testing
```
Das hebt den Schutz für das Konto vollständig auf — vertretbar auf einer Test-VM hinter VPN, aber
nicht dauerhaft und niemals auf einer Produktivmaschine.
### 4.7 GUI-Anwendungen ohne Bildschirm testen
Braucht eine Anwendung X11, scheitert sie über SSH mit `XOpenDisplay failed`. Für einen einzelnen
Durchlauf genügt:
```bash
xvfb-run -n <deine-nummer> ./MeineAnwendung --irgendein-schalter
```
⚠️ **Nicht `xvfb-run -a` verwenden.** Das `-a` sucht sich selbst ein freies Display und **beginnt
dabei bei :99** — also ausgerechnet auf PolyTraders Nummer. Läuft dort gerade nichts, belegt es
diese Nummer; startet PolyTrader kurz darauf, kollidieren beide. Mit `-n 98` (bzw. der eigenen
Nummer aus 4.5) bleibt die Zuordnung eindeutig.
### 4.8 Oberflächen selbst begutachten — ohne Bildschirm, ohne fremde Hilfe
**Ein Agent kann die Oberfläche eigenständig prüfen.** Kein Mensch muss am Bildschirm sitzen und
Bildschirmfotos schicken. Das Verfahren hat sich am 26.08.2026 bewährt und **sieben Befunde
zutage gefördert**, darunter einen, der sämtliche Tabellen der Anwendung unsichtbar machte.
Drei Werkzeuge, alle installiert: **Xvfb** (virtuelles Display), **xdotool** (Klicken und Tippen),
**ImageMagick** (`import` für Aufnahmen, `convert` zum Zuschneiden).
Anders als `xvfb-run` wird das Display hier **dauerhaft** gestartet, damit sich zwischen den
Aufnahmen navigieren lässt:
```bash
#!/bin/bash
# Kopf anpassen - das ist alles, was sich je Projekt unterscheidet:
PROJEKT="polytrader"
DISP=":99" # eigene Nummer aus Abschnitt 4.5!
FENSTER="^PolyTrader$"
cd ~/$PROJEKT-test
export DISPLAY=$DISP
pkill -f "Xvfb $DISP" 2>/dev/null; sleep 1
Xvfb $DISP -screen 0 1600x1000x24 >/dev/null 2>&1 &
sleep 3
nohup ./MeineAnwendung > /tmp/${PROJEKT}_gui.log 2>&1 &
APP=$!
sleep 25 # Startzeit großzügig bemessen
# Fenster-ID ermitteln (bestaetigt, dass die Oberflaeche ueberhaupt erscheint)
xdotool search --name "$FENSTER" | head -1
# navigieren und aufnehmen - Dateinamen mit Projektpraefix, sonst
# ueberschreiben sich parallel laufende Projekte gegenseitig
shoot() { sleep 5; import -window root /tmp/${PROJEKT}_$1.png; echo " $1"; }
shoot 1_start
xdotool mousemove 127 205 click 1; shoot 2_zweite_ansicht
xdotool mousemove 127 237 click 1; shoot 3_dritte_ansicht
kill $APP 2>/dev/null; sleep 2; pkill -f "Xvfb $DISP" 2>/dev/null
```
Danach die Bilder abholen und ansehen:
```bash
scp testvm:'/tmp/polytrader_*.png' .
```
**Was sich dabei bewährt hat:**
- **Großzügige Wartezeiten.** 25 Sekunden nach dem Start, 5 Sekunden nach jedem Klick. Wird zu
früh aufgenommen, entsteht ein halb gezeichnetes Fenster, und man sucht Fehler, die keine sind.
- **Klickpunkte aus dem ersten Bild ablesen.** Erst eine Aufnahme machen, dort die Koordinaten der
Menüpunkte bestimmen, dann das Skript damit bestücken.
- **Details vergrößern statt raten.** Bei einem Verdacht den Ausschnitt herausschneiden:
```bash
convert v_1_start.png -crop 560x130+265+540 +repage -resize 200% /tmp/detail.png
```
- **Kontraste nachmessen statt schätzen.** Ob Text lesbar ist, lässt sich ausrechnen — Pixelfarben
auslesen und das WCAG-Verhältnis bestimmen (4,5:1 für normalen Text). So wird aus „wirkt blass"
ein belegter Befund.
- **Nur eine Instanz des eigenen Projekts gleichzeitig.** Läuft der systemd-Dienst noch, schreiben
zwei Prozesse in dieselbe Datenbank und es entstehen Fehler, die nichts mit der Oberfläche zu tun
haben. Vorher `sudo systemctl stop <projekt>`. (Ein **anderes** Projekt auf seinem eigenen
Display stört dagegen nicht — dafür ist die Trennung aus 4.5 da.)
- **Auf Zeichen achten, die falsch aussehen.** Fehlt eine Icon-Schriftart, ersetzt Fontconfig sie
stillschweigend durch etwas anderes — ein Kalendersymbol wird dann zu einer sinnlosen Zahl. Das
fällt nur im Bild auf, nie im Log.
Für einen Test **mit echtem Bildschirm** kann trotzdem jemand am Desktop der VM sitzen (oder RDP
nutzen, Port 3389 ist offen) — nötig ist es für die Sichtprüfung nicht.
---
## 5. Was hier nicht passieren darf
1. **Keine Produktivdatenbank verbinden.** Hintergrunddienste schreiben oft ungefragt — bei
PolyTrader etwa synchronisiert der `MarketSyncService` selbständig Marktdaten.
2. **Keine echten Handels-/Zugangsdaten hinterlegen.** Keine privaten Schlüssel, keine
Live-API-Zugänge, keine Lizenzschlüssel für Produktivsysteme.
3. **Keine fremden Projektverzeichnisse oder Datenbanken anfassen.**
4. **Nichts Dauerhaftes hinterlassen**, was nicht in Abschnitt 6 steht.
---
## 6. Belegung (bei Änderungen fortschreiben)
**Datenbanken**
| Datenbank | Benutzer | Projekt | seit |
|---|---|---|---|
| `polytrader_test` | `polytrader_test`@localhost | PolyTrader | 26.08.2026 |
**Dienste**
| Unit | Konto | Verzeichnis | Projekt | seit |
|---|---|---|---|---|
| `polytrader.service` | `polytrader` | `/opt/polytrader` | PolyTrader | 26.08.2026 |
**Verzeichnisse**
| Projekt | Publish-Verzeichnis | Dienst |
|---|---|---|
| PolyTrader | `~/polytrader-test` | `/opt/polytrader` |
**X-Displays** (Vergabe siehe 4.5)
| Display | Projekt | Stand |
|---|---|---|
| `:0` | echter Desktop — **nie belegen** | |
| `:99` | PolyTrader | 26.08.2026 |
| `:98` | Predictalytics | reserviert |
| `:97` | ClawdDotNet | reserviert |
| `:96` | IBKRTrader | reserviert |
| `:95` abwärts | frei für künftige Projekte | |
**Ports** (Stand 26.08.2026)
| Port | Belegt durch |
|---|---|
| 22 | SSH |
| 3306 | MariaDB (nur localhost) |
| 3389 | RDP |
| 5000 | vorhandener Dienst (nur IPv6-localhost) |
| 631 | CUPS |
**Nachträglich installiert:** MariaDB 11.8.6, Xvfb, xdotool, ImageMagick, x11-utils
(alle am 26.08.2026 für PolyTrader).
---
## 7. Aufräumen nach dem Test
```bash
# Virtuelles Display beenden (falls noch offen) - NUR das eigene!
pkill -f "Xvfb :<deine-nummer>"
rm -f /tmp/<projekt>_*.png /tmp/<projekt>_gui.log
# Dienst abschalten und entfernen
sudo systemctl disable --now <projekt>
sudo rm -f /etc/systemd/system/<projekt>.service /etc/logrotate.d/<projekt>
sudo systemctl daemon-reload
# Dateien
sudo rm -rf /opt/<projekt> ~/<projekt>-test
sudo userdel <projekt>
# Datenbank (nur wenn nicht mehr gebraucht)
sudo mariadb -e "DROP DATABASE <projekt>_test; DROP USER '<projekt>_test'@'localhost';"
rm -f ~/.<projekt>_test_dbpw
# sudo-Freigabe zurücknehmen
sudo rm -f /etc/sudoers.d/99-testing
```
Danach Abschnitt 6 nachziehen.
---
## 8. Bekannte Einschränkungen
- **Claude Code lässt sich auf dieser VM nicht betreiben.** Es blockiert die Maschine bis zum
Neustart; mehrere Reparaturversuche waren erfolglos, es wird auf ein Update gewartet. Agenten
arbeiten deshalb **über SSH von außen**. (Speichermangel ist **nicht** die Ursache — die
Maschine hat 31 GB, davon 25 GB frei.)
- **Verbindungs-Multiplexing funktioniert nicht mit Windows-OpenSSH** (siehe 2.3).
- **Die Systemzeitzone ist Testgegenstand.** Für realistische Servertests wird sie zeitweise auf
UTC gestellt (`sudo timedatectl set-timezone UTC`). Wer sie ändert, stellt sie danach zurück —
Standard hier ist `Europe/Berlin`.
@@ -1,4 +1,4 @@
# Befundbericht: Erster Linux-Lauf # Befundbericht: Erster Linux-Lauf
**Datum: 26.08.2026** · Prüfplan: [PRUEFPLAN-Linux-Betrieb.md](./PRUEFPLAN-Linux-Betrieb.md) · **Datum: 26.08.2026** · Prüfplan: [PRUEFPLAN-Linux-Betrieb.md](./PRUEFPLAN-Linux-Betrieb.md) ·
Roadmap: [A4a](../ROADMAP.md) Roadmap: [A4a](../ROADMAP.md)
@@ -559,7 +559,7 @@ sinnvoll festlegen lassen. Das Verfahren ist reproduzierbar:
# Nachtrag 5: Behebung der offenen Befunde (27.08.2026) # Nachtrag 5: Behebung der offenen Befunde (27.08.2026)
Die Nacharbeit ist unter Windows entstanden und dort gebaut und geprüft Die Nacharbeit ist unter Windows entstanden und dort gebaut und geprüft
(`dotnet build -c Release`: 0 Fehler / 15 Warnungen wie zuvor; **484 Tests grün**, 8 davon neu; (`dotnet build -c Release`: 0 Fehler / 15 Warnungen wie zuvor; **486 Tests grün**, 10 davon neu;
`--smoke-ui` durchgängig `[OK]`). Die **Gegenprobe am Bild steht aus** — sie gehört auf die `--smoke-ui` durchgängig `[OK]`). Die **Gegenprobe am Bild steht aus** — sie gehört auf die
Linux-VM, nach dem Verfahren aus [TESTUMGEBUNG-Linux-VM.md](../TESTUMGEBUNG-Linux-VM.md) §4.8 Linux-VM, nach dem Verfahren aus [TESTUMGEBUNG-Linux-VM.md](../TESTUMGEBUNG-Linux-VM.md) §4.8
mit `testdaten-ui.sql`. mit `testdaten-ui.sql`.
@@ -577,6 +577,7 @@ mit `testdaten-ui.sql`.
| **B-13** · Name unsichtbar | Im Master-Trader-Grid steht **Name** jetzt direkt hinter `#` mit `MinWidth="180"`; die Wallet-Adresse folgt gekürzt (`0x9e2b…3b95`, neuer `ShortAddressConverter`), die vollständige Adresse im Tooltip | | **B-13** · Name unsichtbar | Im Master-Trader-Grid steht **Name** jetzt direkt hinter `#` mit `MinWidth="180"`; die Wallet-Adresse folgt gekürzt (`0x9e2b…3b95`, neuer `ShortAddressConverter`), die vollständige Adresse im Tooltip |
| **B-14** · mehrzeilige Texte | Neuer Feldtyp `MultilineTextSettingsField`; „Beschreibung" und „Begründung" sind am Modell mit `[DataType(DataType.MultilineText)]` ausgezeichnet und erscheinen als sechszeiliges Feld mit Umbruch | | **B-14** · mehrzeilige Texte | Neuer Feldtyp `MultilineTextSettingsField`; „Beschreibung" und „Begründung" sind am Modell mit `[DataType(DataType.MultilineText)]` ausgezeichnet und erscheinen als sechszeiliges Feld mit Umbruch |
| **B-15** · ein Datensatz legt das Modul lahm | Die Wertkonvertierung läuft über `AccountIdSetJson`: JSON-Array wie bisher, eine reine Zahlenliste (`"1,2"`) wird als Altformat noch verstanden, alles andere ergibt eine **leere** Zuordnung. Der Trader wird also geladen und kopiert auf kein Konto — die sichere Richtung. Die Hydration meldet Anzahl und letzten Rohwert als Warnung | | **B-15** · ein Datensatz legt das Modul lahm | Die Wertkonvertierung läuft über `AccountIdSetJson`: JSON-Array wie bisher, eine reine Zahlenliste (`"1,2"`) wird als Altformat noch verstanden, alles andere ergibt eine **leere** Zuordnung. Der Trader wird also geladen und kopiert auf kein Konto — die sichere Richtung. Die Hydration meldet Anzahl und letzten Rohwert als Warnung |
| **§8** · Logs stempeln UTC, Oberfläche zeigt Ortszeit | **Auf Richards Entscheidung aus T2 vorgezogen.** `LogMessageEventArgs.Timestamp` ist jetzt ein `DateTimeOffset` in der Anzeige-Zeitzone (neu: `AppTimeZone.Now` / `.Today`) statt `DateTime.Now` der Systemuhr. Damit stimmen Zeitstempel, `zzz`-Offset in der JSONL und die Tagesgrenze der Logdateien mit der Oberfläche überein. Mitgezogen: der Log Viewer rechnet nicht mehr doppelt um, Startseite und Viewer wählen die Tagesdatei nach der Anzeige-Zone, und der Supervisor-Tagesbericht läuft zur genannten Stunde in dieser Zone statt in der des Systems. Der Rest von **T2** (Umstellung auf `Microsoft.Extensions.Logging`) bleibt offen |
| **B-12** · Lizenzschlüssel im Klartext | **Ursache gefunden:** `LicenseCli` läuft vor dem Host-Aufbau und rief damit `SecretProtection.Protect` ohne geladenen Master-Key auf — der reicht den Klartext unverändert durch. Die CLI lädt den Master-Key jetzt selbst. Zusätzlich zieht der Start einen noch im Klartext liegenden Schlüssel einmalig verschlüsselt nach (selbstheilende Migration wie bei den DB-Secrets) | | **B-12** · Lizenzschlüssel im Klartext | **Ursache gefunden:** `LicenseCli` läuft vor dem Host-Aufbau und rief damit `SecretProtection.Protect` ohne geladenen Master-Key auf — der reicht den Klartext unverändert durch. Die CLI lädt den Master-Key jetzt selbst. Zusätzlich zieht der Start einen noch im Klartext liegenden Schlüssel einmalig verschlüsselt nach (selbstheilende Migration wie bei den DB-Secrets) |
## Nebenbefund aus der Nacharbeit — **alle Dezimal-Einstellungen waren nicht änderbar** ## Nebenbefund aus der Nacharbeit — **alle Dezimal-Einstellungen waren nicht änderbar**
@@ -632,7 +633,106 @@ unverändert für alle Schaltflächen. **Bewusst so eng geschnitten:** Trifft di
| Befund | Grund | | Befund | Grund |
|---|---| |---|---|
| **B-3** · `--smoke-ui` verlangt eine Datenbank | Eigener Schnitt: Persistenz überspringen oder gegen EF-InMemory bauen. Betrifft fünf DbContexts und hängt daran, ob die CI diesen Modus nutzen soll — siehe A2 | | **B-3** · `--smoke-ui` verlangt eine Datenbank | **Entscheidung Richard, 27.08.2026: vorerst nichts tun.** A2 (CI-Runner) ist pausiert, es gibt keinen Runner — die Prüfung läuft ohnehin von Hand, und auf der Test-VM sind Datenbank und `xvfb` vorhanden. Wieder aufzugreifen, wenn A2 weitergeht |
| **B-4** · `--smoke-ui` braucht einen X-Server | Zwei Wege (`xvfb-run` im Build-Image vs. `Avalonia.Headless` im Code). Gehört zu derselben Entscheidung wie B-3 | | **B-4** · `--smoke-ui` braucht einen X-Server | Dieselbe Entscheidung. Für den Handlauf auf der VM genügt ein vorgeschaltetes Xvfb — aber **`xvfb-run -n 99`, nicht `-a`**: `-a` sucht sich selbst ein Display und fängt bei `:99` an, also auf PolyTraders fester Nummer (siehe [TESTUMGEBUNG-Linux-VM.md](../TESTUMGEBUNG-Linux-VM.md), Displaynummern je Projekt) |
| **§8** · Logs stempeln UTC, Oberfläche zeigt Ortszeit | Gehört zu **T2** (Umstellung auf `Microsoft.Extensions.Logging`). Ein sauberer Fix braucht `DateTimeOffset` statt `DateTime` in `LogMessageEventArgs` — das berührt jede Logzeile der Anwendung und ist kein Nebenbei-Schnitt |
| Leerzustände melden nichts | Betrifft rund vierzehn Grids und ist reine Zutat, kein Fehler. Sinnvoll zusammen mit der nächsten Sichtprüfung zu entscheiden | | Leerzustände melden nichts | Betrifft rund vierzehn Grids und ist reine Zutat, kein Fehler. Sinnvoll zusammen mit der nächsten Sichtprüfung zu entscheiden |
---
# Nachtrag 6: Gegenprobe am Bild (28.08.2026)
Erster Lauf nach dem neuen Verfahren aus
[`Deploymentcenter/docs/TESTUMGEBUNG.md`](../TESTUMGEBUNG-Linux-VM.md) — Sichtprüfung auf
Display `:99`, mit den Testdaten aus `testdaten-ui.sql`. **Ab hier landen Befunde im Bugtracker
statt in diesem Bericht**; die Nummern in Klammern sind Item-IDs.
**Linux-Build:** 0 Fehler, 15 Warnungen — Zahl für Zahl wie unter Windows. **484 Tests grün.**
`--smoke-ui` unter `xvfb-run -n 99` durchgängig `[OK]`, einschließlich der neuen Prüfung auf
bearbeitbare Einstellungsfelder.
## Im Bild bestätigt
| Befund | Beleg |
|---|---|
| **B-6** Kontrast | Die Zahlen in den KPI-Kacheln stehen im Dunkelmodus jetzt in der aufgehellten Bedeutungsfarbe — CopyTrading grün, ResolutionFarming und Gesamt rot. Der Themenwechsel-Fehler ist weg |
| **B-7** doppelter Modulname | Kachel trägt „CopyTrading" nur noch als Überschrift |
| **B-11** Überschriften | Im ResolutionFarming-Grid stehen „Fee (bps)", „Netto-Edge %", „Outcome" und „Score" vollständig; im Dashboard „Wallet (USDC)", „3T Winrate %", „Overall P/L". Vorher waren dieselben Köpfe abgeschnitten |
| **B-13** Name unsichtbar | Alle fünf Anzeigenamen lesbar (*WeatherWhale*, *PolitiSniper*, *SportsEdge Capital*, *MacroTrend Research*, *CryptoDegen9000*), Wallet gekürzt als `0x9e2b…3b95` |
| **B-14** einzeilige Felder | „Beschreibung" bricht jetzt über drei Zeilen um |
| **B-12** Klartextschlüssel | Beim ersten Start auf der VM im Log: „🔐 Lizenzschlüssel lag im Klartext vor und wurde verschlüsselt nachgezogen." Die selbstheilende Migration wirkt auf der Zielplattform |
| Account-Übersicht | „Demo Testkonto stillgelegt (Demo)" und „CopyTrading, ResolutionFarming" stehen vollständig — beides war zuvor abgeschnitten |
## B-9 · Kalendersymbol — **kein Fehler von PolyTrader** (Item #50)
**Die Zahl ist das heutige Datum.** Am 26.08. stand dort eine „26", am 28.08. eine „28". Avalonia
zeichnet im Knopf des `CalendarDatePicker` die heutige Tageszahl in ein Kalenderblatt — es gibt
dafür eigens die Themenressource `CalendarDatePickerCurrentDayFontSize`. Unter Linux fehlt der
Blatt-Glyph; übrig bleiben die Zahl und ein Punkt darüber.
Zwei Gegenproben, beide ohne Wirkung auf das Bild:
1. Die Formatvorlage der Anwendung per `Button:not(#PART_Button)` vom Knopf ferngehalten.
2. Farbschema auf hell umgeschaltet.
Die am 27.08. auf Verdacht eingebaute Änderung an `App.axaml` ist deshalb **zurückgenommen** —
sie war wirkungslos, und eine Änderung ohne Wirkung und ohne Begründung gehört nicht in den Code.
An ihrer Stelle steht ein Kommentar, damit der Nächste nicht dieselbe Spur verfolgt.
> Zu lernen daraus: Vier Testläufe haben dieselbe Stelle als Artefakt gedeutet. Aufgefallen ist
> es erst, weil an **zwei verschiedenen Tagen** hingesehen wurde. Ein Bildschirmfoto ist eine
> Momentaufnahme — was sich mit dem Datum ändert, verrät sich nur im Vergleich.
## Neuer Fehler durch die eigene Behebung (Item #56) — **behoben**
Die Spaltenbreiten-Umstellung vom 27.08. hat im Master-Trader-Grid etwas kaputtgemacht, das
vorher funktionierte: Von zwölf Spalten blieben sieben brauchbar. Nach „Winrate %" stand eine
Spalte mit abgeschnittenen Werten **ohne Überschrift**, dahinter vier leere Streifen von wenigen
Pixeln — PnL, Copy-PnL, Profit-Faktor, Ø PnL/Trade und Copy-Trades waren weder lesbar noch als
Spalte erkennbar.
**Das war schlimmer als B-11 selbst.** Dort fehlten Überschriften, hier fehlten die Werte.
Ursache: `Name` stand auf `Width="*"` mit `MinWidth`. Zwölf Spalten passen neben dem 460 px
breiten Detailbereich nicht ins Fenster; statt waagerecht zu schieben, verteilt das DataGrid den
Mangel — die Sternspalte behält ihre Mindestbreite, die `Auto`-Spalten am Ende werden zerdrückt.
Behoben durch `Width="Auto"` auch für `Name`. Ohne Sternspalte behält jede Spalte ihre natürliche
Breite, und das Grid lässt sich waagerecht schieben. Im Bild gegengeprüft: „PnL (7T)" zeigt
vollständige Werte, der Rollbalken ist da.
**Regel für alle Grids:** Eine Sternspalte nur dort, wo tatsächlich Platz übrig bleibt. Wo mehr
Spalten als Platz da sind, ist `Auto` plus waagerechtes Schieben das ehrlichere Verhalten — es
versteckt nichts.
## Nebenbei: ein wackliger Testlauf abgestellt
Von sechs Testlaeufen fiel einer um und liess sich danach nicht wieder einfangen. Ursache ist
kein einzelner Test, sondern die Bauart: Mehrere Bausteine halten **prozessweiten** Zustand, den
Tests bewusst umstellen — `AppTimeZone`, `SecretProtection`, die Diagnosezähler von
`AccountIdSetJson`. xUnit führt Testklassen standardmäßig **nebenläufig** aus, also sieht eine
Klasse mitten im eigenen Lauf, was eine andere gerade umgestellt hat.
Neu ist das nicht, aber die Zeitzonen-Umstellung aus §8 hat es verschärft: Seit die Logzeitstempel
der Anzeige-Zeitzone folgen, hängt **jedes** `LogMessageEventArgs` an `AppTimeZone` — während
`AppTimeZoneTests` dieselbe Einstellung nach Auckland und zurück dreht.
Abgestellt durch `[assembly: CollectionBehavior(DisableTestParallelization = true)]`
(`tests/PolyTrader.Tests/AssemblyInfo.cs`); die Zählerprüfungen arbeiten zusätzlich mit der
Differenz statt mit absoluten Werten. Kosten: 3 s → 4–5 s Laufzeit. Danach sechs Läufe in Folge
grün.
## Was jetzt im Bugtracker steht
| Item | Projekt | Inhalt |
|---|---|---|
| **#50** | polytrader | B-9 Kalendersymbol — Diagnose oben, rein kosmetisch |
| **#51** | polytrader | Leere Tabellen sagen nicht, warum sie leer sind |
| **#52** | polytrader | `--smoke-ui` setzt Datenbank und X-Server voraus (B-3/B-4) |
| **#56** | polytrader | Master-Trader-Grid: zerdrückte Spalten — behoben, s.o. |
| **#53** | deploymentcenter | `dc-testkit.sh` hat CRLF und ist auf Linux nicht ausführbar |
| **#54** | deploymentcenter | `dc-testkit doctor` meldet „Bereit." trotz zu altem Server und falschem Token |
| **#55** | deploymentcenter | Fremdmeldung: Laufzeit-Token akzeptiert, kein `triage_external`, kein `reported_from` |
| **#57** | deploymentcenter | `TESTUMGEBUNG.md`: drei Ungenauigkeiten aus dem ersten Durchlauf |
**Damit ist dieser Bericht abgeschlossen.** Was noch offen ist, steht im Bugtracker.
+8 -13
View File
@@ -181,8 +181,16 @@
einer 1px-Linie. Fluent bringt gerundete, gefüllte Flächen mit – deshalb hier einer 1px-Linie. Fluent bringt gerundete, gefüllte Flächen mit – deshalb hier
flächendeckend zurückgesetzt. --> flächendeckend zurückgesetzt. -->
<!-- Zum Kalenderknopf des CalendarDatePicker (Befund B-9 des Linux-Sichttests, wo statt
eines Kalendersymbols eine "26" stand): Das ist NICHT unsere Formatvorlage. Am
28.08.2026 auf der Linux-VM nachgemessen - die Zahl ist das heutige Datum (am 26.08.
eine "26", am 28.08. eine "28"), und sie bleibt unveraendert, wenn man diese
Vorlage per ":not(#PART_Button)" vom Knopf fernhaelt oder das Farbschema wechselt.
Avalonia zeichnet dort die heutige Tageszahl in ein Kalenderblatt; unter Linux fehlt
der Blatt-Glyph, uebrig bleibt ein Punkt. Rein kosmetisch, siehe Bugtracker-Item #50. -->
<Style Selector="Button"> <Style Selector="Button">
<Setter Property="CornerRadius" Value="0" /> <Setter Property="CornerRadius" Value="0" />
<Setter Property="FontFamily" Value="{DynamicResource AppFontHeading}" />
<Setter Property="FontSize" Value="13" /> <Setter Property="FontSize" Value="13" />
<Setter Property="Padding" Value="10,5" /> <Setter Property="Padding" Value="10,5" />
<Setter Property="Background" Value="Transparent" /> <Setter Property="Background" Value="Transparent" />
@@ -190,19 +198,6 @@
<Setter Property="BorderThickness" Value="1" /> <Setter Property="BorderThickness" Value="1" />
</Style> </Style>
<!-- Die Schriftart bewusst OHNE die Schaltflaechen, die zur Vorlage eines anderen
Steuerelements gehoeren (#PART_Button - z.B. der Kalenderknopf des
CalendarDatePicker). In Avalonia stechen die Styles der Anwendung die Setter eines
ControlTheme aus; unsere Schriftliste landete damit auch auf dem Kalenderknopf und
verdraengte die Symbolschrift, aus der sein Zeichen stammt. Unter Windows faellt das
nicht auf, unter Linux setzt Fontconfig stillschweigend irgendeine andere Schrift
ein, in der derselbe Zeichencode etwas voellig anderes bedeutet - im Test stand dort
statt eines Kalenders eine sinnlose "26" (Befund B-9 des Linux-Sichttests).
ACHTUNG: nur am Bild zu pruefen, nie im Log - Gegenprobe auf der Linux-VM noetig. -->
<Style Selector="Button:not(#PART_Button)">
<Setter Property="FontFamily" Value="{DynamicResource AppFontHeading}" />
</Style>
<!-- Fluent malt Füllung und Rahmen im Vorlagenteil und überschreibt sie je Zustand. <!-- Fluent malt Füllung und Rahmen im Vorlagenteil und überschreibt sie je Zustand.
Deshalb müssen Schwebe- und Druckzustand dort gesetzt werden, sonst blitzt beim Deshalb müssen Schwebe- und Druckzustand dort gesetzt werden, sonst blitzt beim
Überfahren wieder das Fluent-Grau durch. --> Überfahren wieder das Fluent-Grau durch. -->
@@ -30,13 +30,22 @@
<Grid ColumnDefinitions="*,4,460"> <Grid ColumnDefinitions="*,4,460">
<DataGrid Name="gridTraders" Grid.Column="0" x:DataType="ct:TrackedTrader"> <DataGrid Name="gridTraders" Grid.Column="0" x:DataType="ct:TrackedTrader">
<DataGrid.Columns> <DataGrid.Columns>
<!-- Der Name steht vorn und bekommt den freien Platz: er ist die <!-- Der Name steht vorn: er ist die wichtigste Information der
wichtigste Information der Zeile. Die Wallet-Adresse folgt Zeile. Die Wallet-Adresse folgt gekuerzt (vollstaendig im
gekuerzt (vollstaendig im Tooltip) - in voller Laenge drueckte Tooltip) - in voller Laenge drueckte sie zusammen mit den
sie zusammen mit den festen Breiten der uebrigen Spalten den festen Breiten der uebrigen Spalten den Namen auf wenige
Namen auf wenige Pixel zusammen (Befund B-13). --> Pixel zusammen (Befund B-13).
KEINE Sternbreite in diesem Grid: zwoelf Spalten passen neben
dem 460 px breiten Detailbereich nicht ins Fenster. Eine
Sternspalte nimmt sich dann den Platz, den die festen
brauchen - beim Sichttest am 28.08.2026 blieben die letzten
fuenf Spalten (PnL, Copy-PnL, Profit-Faktor, Ø PnL/Trade,
Copy-Trades) als leere Streifen ohne Beschriftung uebrig.
Mit lauter Auto-Breiten behaelt jede Spalte ihre Breite und
das Grid laesst sich stattdessen waagerecht schieben. -->
<DataGridTextColumn Header="#" Width="Auto" Binding="{Binding Id}" /> <DataGridTextColumn Header="#" Width="Auto" Binding="{Binding Id}" />
<DataGridTextColumn Header="Name" Width="*" MinWidth="180" Binding="{Binding DisplayName}" /> <DataGridTextColumn Header="Name" Width="Auto" MinWidth="180" Binding="{Binding DisplayName}" />
<DataGridTemplateColumn Header="Wallet" Width="Auto"> <DataGridTemplateColumn Header="Wallet" Width="Auto">
<DataGridTemplateColumn.CellTemplate> <DataGridTemplateColumn.CellTemplate>
<DataTemplate x:DataType="ct:TrackedTrader"> <DataTemplate x:DataType="ct:TrackedTrader">
+39 -1
View File
@@ -1,4 +1,4 @@
using System; using System;
using PolyTraderSharp.Services; using PolyTraderSharp.Services;
using Xunit; using Xunit;
@@ -66,5 +66,43 @@ namespace PolyTrader.Tests
var local = new DateTime(2026, 1, 15, 12, 0, 0, DateTimeKind.Local); var local = new DateTime(2026, 1, 15, 12, 0, 0, DateTimeKind.Local);
Assert.Equal(12, AppTimeZone.ToDisplay(local).Hour); Assert.Equal(12, AppTimeZone.ToDisplay(local).Hour);
} }
// ── §8 des Linux-Befundberichts: Logs stempelten UTC, die Oberfläche zeigte Ortszeit ──
[Fact]
public void Now_carries_the_offset_of_the_display_zone()
{
AppTimeZone.Configure("Pacific/Auckland");
try
{
var now = AppTimeZone.Now;
var expected = AppTimeZone.Current.GetUtcOffset(DateTimeOffset.UtcNow);
// Der Versatz gehört zur ANZEIGE-Zone. Ein umgerechneter DateTime trüge ihn nicht
// mit sich – beim Formatieren mit "zzz" käme dann der Versatz des Systems heraus.
Assert.Equal(expected, now.Offset);
Assert.Equal(DateTime.UtcNow.Add(expected).Date, AppTimeZone.Today);
}
finally { AppTimeZone.Configure(null); }
}
[Fact]
public void Log_entries_are_stamped_in_the_display_zone()
{
AppTimeZone.Configure("Europe/Berlin");
try
{
var e = new LogMessageEventArgs("egal", LogLevel.Info);
Assert.Equal(AppTimeZone.Current.GetUtcOffset(e.Timestamp.UtcDateTime), e.Timestamp.Offset);
// Und die abgelegte JSONL-Zeile trägt denselben Versatz mit.
var parsed = DateTimeOffset.Parse(
LogJson.ParseLine(LogJson.Format(e))!.Time,
System.Globalization.CultureInfo.InvariantCulture);
Assert.Equal(e.Timestamp.Offset, parsed.Offset);
Assert.Equal(e.Timestamp.ToUnixTimeSeconds(), parsed.ToUnixTimeSeconds());
}
finally { AppTimeZone.Configure(null); }
}
} }
} }
+17
View File
@@ -0,0 +1,17 @@
using Xunit;
// Testklassen laufen NACHEINANDER, nicht parallel.
//
// Warum: Mehrere Bausteine der Anwendung halten prozessweiten Zustand, und die Tests stellen ihn
// bewusst um — AppTimeZone (Anzeige-Zeitzone), SecretProtection (Master-Key) und die
// Diagnosezähler von AccountIdSetJson. xUnit führt Testklassen standardmäßig nebenläufig aus;
// dann sieht eine Klasse die Umstellung einer anderen mitten im eigenen Lauf.
//
// Das ist nicht theoretisch: Seit die Logzeitstempel der Anzeige-Zeitzone folgen (§8 des
// Linux-Befundberichts vom 26.08.2026), hängt jedes LogMessageEventArgs an AppTimeZone — während
// AppTimeZoneTests dieselbe Einstellung nach Auckland und zurück dreht. Am 28.08.2026 fiel dabei
// ein Lauf von sechs um, ohne sich danach wieder einfangen zu lassen.
//
// Der Preis ist gering: Der gesamte Satz läuft in rund drei Sekunden. Ein Testlauf, dem man
// glauben kann, ist das allemal wert.
[assembly: CollectionBehavior(DisableTestParallelization = true)]
+10 -6
View File
@@ -57,36 +57,40 @@ namespace PolyTrader.Tests
Assert.Empty(AccountIdSetJson.Deserialize("[]")); Assert.Empty(AccountIdSetJson.Deserialize("[]"));
} }
// Der Zaehler ist prozessweit. Deshalb wird die DIFFERENZ geprueft und nicht der
// absolute Wert - so bleibt der Test auch dann richtig, wenn vorher schon jemand
// einen unlesbaren Wert durchgereicht hat.
[Fact] [Fact]
public void Plain_number_list_is_still_understood() public void Plain_number_list_is_still_understood()
{ {
AccountIdSetJson.ResetDiagnostics(); int before = AccountIdSetJson.MalformedCount;
Assert.Equal(new System.Collections.Generic.HashSet<int> { 1, 2 }, Assert.Equal(new System.Collections.Generic.HashSet<int> { 1, 2 },
AccountIdSetJson.Deserialize("1,2")); AccountIdSetJson.Deserialize("1,2"));
Assert.Equal(0, AccountIdSetJson.MalformedCount); Assert.Equal(before, AccountIdSetJson.MalformedCount);
} }
[Fact] [Fact]
public void Unreadable_value_yields_an_empty_set_and_is_counted() public void Unreadable_value_yields_an_empty_set_and_is_counted()
{ {
AccountIdSetJson.ResetDiagnostics(); int before = AccountIdSetJson.MalformedCount;
// Der Fall aus dem Test: kaputter Wert statt JSON-Array. Frueher warf die // Der Fall aus dem Test: kaputter Wert statt JSON-Array. Frueher warf die
// EF-Wertkonvertierung hier und es wurde KEIN einziger Trader geladen. // EF-Wertkonvertierung hier und es wurde KEIN einziger Trader geladen.
Assert.Empty(AccountIdSetJson.Deserialize("{kaputt")); Assert.Empty(AccountIdSetJson.Deserialize("{kaputt"));
Assert.Equal(1, AccountIdSetJson.MalformedCount); Assert.Equal(before + 1, AccountIdSetJson.MalformedCount);
Assert.Equal("{kaputt", AccountIdSetJson.LastMalformedValue); Assert.Equal("{kaputt", AccountIdSetJson.LastMalformedValue);
} }
[Fact] [Fact]
public void Text_that_is_not_a_number_list_is_not_silently_accepted() public void Text_that_is_not_a_number_list_is_not_silently_accepted()
{ {
AccountIdSetJson.ResetDiagnostics(); int before = AccountIdSetJson.MalformedCount;
Assert.Empty(AccountIdSetJson.Deserialize("1,zwei")); Assert.Empty(AccountIdSetJson.Deserialize("1,zwei"));
Assert.Equal(1, AccountIdSetJson.MalformedCount); Assert.Equal(before + 1, AccountIdSetJson.MalformedCount);
} }
} }
} }