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>
390 lines
14 KiB
Markdown
390 lines
14 KiB
Markdown
# Linux-Testumgebung (gemeinsam genutzte VM)
|
|
|
|
**Stand: 26.08.2026** · Eingerichtet beim ersten Linux-Lauf von PolyTrader (Roadmap A4a).
|
|
|
|
> **Diese Anleitung gilt projektübergreifend.** Sie richtet sich an alle Agenten und Entwickler,
|
|
> 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
|
|
> Kopie. Wer etwas an der Umgebung ändert, zieht **beide** nach.
|
|
|
|
---
|
|
|
|
## 1. Was diese Maschine ist
|
|
|
|
Eine Ubuntu-Desktop-VM zum **Testen** — nicht für Produktivbetrieb, nicht für echte Zugangsdaten,
|
|
nicht für Geld bewegende Läufe.
|
|
|
|
| | |
|
|
|---|---|
|
|
| System | Ubuntu 26.04 LTS, Kernel 7.0, x86_64 |
|
|
| Ausstattung | 31 GB RAM, 5 CPUs, ~33 GB freier Plattenplatz |
|
|
| Erreichbar | `192.168.40.48` über einen WireGuard-Tunnel |
|
|
| Benutzer | `richard` |
|
|
| Besonderheit | **Desktop-Umgebung vorhanden** — grafische Anwendungen sind testbar, nicht nur Dienste |
|
|
|
|
**Warum eine Desktop-VM?** Weil sich damit beides prüfen lässt: der kopflose Serverbetrieb
|
|
(systemd) *und* die grafische Oberfläche auf der Zielplattform.
|
|
|
|
---
|
|
|
|
## 2. Zugang einrichten
|
|
|
|
Der Zugang läuft über **SSH-Schlüssel**. Passwort-Anmeldung wird nicht verwendet — Agenten geben
|
|
grundsätzlich keine Passwörter ein.
|
|
|
|
### 2.1 Schlüssel erzeugen (auf dem eigenen Rechner)
|
|
|
|
```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>` |
|
|
| Publish/Testlauf im Benutzerkontext | `~/<projekt>-test` (voller Projektname, keine Kürzel) |
|
|
| Installation als Dienst | `/opt/<projekt>` |
|
|
|
|
### 4.3 systemd-Dienste
|
|
|
|
Unit-Name = Projektname: `<projekt>.service`. Eigenes Dienstkonto je Projekt:
|
|
|
|
```bash
|
|
sudo useradd --system --home-dir /opt/<projekt> --shell /usr/sbin/nologin <projekt>
|
|
```
|
|
|
|
Nach dem Test **abschalten** (Abschnitt 7) — sonst laufen irgendwann fünf Dienste, die niemand
|
|
mehr zuordnen kann, und der nächste Test misst gegen fremde Last.
|
|
|
|
### 4.4 Ports
|
|
|
|
Vor dem Belegen prüfen: `ss -tlnp`. Belegte Ports stehen in Abschnitt 6. Wer einen Port dauerhaft
|
|
belegt, trägt ihn dort ein.
|
|
|
|
### 4.5 X-Displays — feste Nummer je Projekt
|
|
|
|
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`.
|