npm run lint rief next lint auf, das es in Next.js 16 nicht mehr gibt — seit dem Next-16-Upgrade lief effektiv gar kein Lint mehr. Umgestellt auf eslint . mit Flat-Config (eslint.config.mjs statt .eslintrc.json). Der dadurch wieder sichtbare Lint-Lauf zeigte 30 Findings (25 Fehler, 5 Warnungen), alle gefixt: - 19x react-hooks/set-state-in-effect: Loading-States wo möglich als echte Ableitung statt eigenem Effect-State (use-mobile.tsx komplett auf useSyncExternalStore umgebaut), sonst async-Wrapper mit Cancel-Guard um bestehende Loader — Timing/Ladeanzeige unverändert. - react-hooks/refs (useSearch.ts): Ref-Schreibzugriff aus dem Render in einen Effect verschoben. - 4x no-html-link-for-pages: <a href> durch next/link ersetzt in admin/login, forgot-password, signup. - Rest (exhaustive-deps, no-img-element, unused disable) einzeln gefixt. - 4 bewusst belassene disable-Kommentare mit Begründung (shadcn/ui-Datei, QR-Code-data-URL, Full-Reload nach Auth laut Projektregel). eslint-Major-Upgrade auf 10 selbst bleibt blockiert: eslint-plugin-react/ jsx-a11y/import unterstützen ESLint 10 in ihrer aktuellen Latest-Version noch nicht (Crash beim Laden), siehe Feature-Spec PROJ-79. Verifiziert auf 132 (Build-Sandbox, kein Live-Deploy): npm ci/tsc/lint/ build grün, 8 Kern-Routen per Standalone-Server auf HTTP 200 geprüft. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019j28kGcaJAhBnrYX34hGdt
159 lines
7.5 KiB
Markdown
159 lines
7.5 KiB
Markdown
---
|
|
id: PROJ-74
|
|
title: Vorbestehende Test-/Vet-Signatur-Drift beheben (go vet/test wieder komplett grün)
|
|
status: Deployed
|
|
created: 2026-08-05
|
|
---
|
|
|
|
## Problem
|
|
|
|
Bei der Verifikation von PROJ-73-Vorarbeiten auf 192.168.1.132
|
|
(`go build ./... && go vet ./... && go test ./...`) traten mehrere
|
|
vorbestehende, vom aktuellen Crash-Härtung-Fix unabhängige Fehler auf:
|
|
|
|
- `cmd/archivmail-export/main.go:39` — `storage.New` wird mit einem
|
|
`string` statt `storage.Config` aufgerufen. `storage.New`-Signatur wurde
|
|
in einem früheren Commit geändert, dieses CLI-Tool nicht nachgezogen.
|
|
Build schlägt komplett fehl.
|
|
- `internal/storage/storage.go:508` — Self-Assignment, von `go vet`
|
|
gemeldet.
|
|
- `internal/storage/storage_test.go`, `internal/audit/audit_test.go`,
|
|
`internal/api/api_test.go`, `internal/userstore/userstore_test.go` —
|
|
Testdateien passen nicht mehr zu aktuellen Signaturen (`storage.New`,
|
|
`os.Discard`, `UpsertLDAPUser`). Build der Testpakete schlägt fehl.
|
|
- `pkg/mailparser/parser_test.go:98` (`TestParseMissingDate`) — Testfall
|
|
erwartet Fallback auf `time.Now()` bei fehlendem Date-Header, aktuelles
|
|
Verhalten weicht ab. Nicht Teil der Multipart-Depth/strings.Builder-
|
|
Änderungen aus PROJ-73-Vorarbeiten.
|
|
- `internal/auth`-Tests — `userstore.New: cannot parse .../users.db`,
|
|
vermutlich Umgebungs-/Test-DSN-Problem statt Code-Bug.
|
|
|
|
Solange diese Drift besteht, ist `go vet ./...` / `go test ./...` insgesamt
|
|
rot und liefert kein verlässliches Gesamtbild mehr — jede künftige
|
|
Verifikation muss den Diff manuell gegen die bekannten Altlasten abgleichen.
|
|
|
|
## Lösung (Vorschlag)
|
|
|
|
1. `archivmail-export`: `storage.New`-Aufruf auf aktuelle `storage.Config`-
|
|
Signatur anpassen (Config-Feld statt reinem String-Pfad).
|
|
2. `storage.go:508` Self-Assignment beheben.
|
|
3. Testdateien (`storage_test.go`, `audit_test.go`, `api_test.go`,
|
|
`userstore_test.go`) auf aktuelle Signaturen (`storage.New`,
|
|
`os.Discard`, `UpsertLDAPUser`) nachziehen.
|
|
4. `TestParseMissingDate` klären: ist der Test oder das aktuelle
|
|
Date-Fallback-Verhalten falsch? Danach eins von beiden anpassen.
|
|
5. `internal/auth`-Test-DSN-Problem auf 132 diagnostizieren (evtl. reines
|
|
Testumgebungs-Setup-Problem, kein Code-Bug).
|
|
|
|
## Implementation Notes
|
|
|
|
Umgesetzt (lokal, ohne Build/Test — kein Go-Toolchain auf der Workstation;
|
|
Verifikation erfolgt separat auf 192.168.1.132).
|
|
|
|
### 1. `cmd/archivmail-export/main.go:39`
|
|
|
|
`storage.New(cfg.Storage.StorePath)` → `storage.New(storage.Config{Dir, Keyfile,
|
|
DSN, CompressEnabled})`, analog zu `cmd/archivmail/cmd_export.go:88-93`.
|
|
Zusätzlich `defer mailStore.Close()` ergänzt (der Store hält jetzt einen
|
|
pgxpool, der bisherige Aufruf kannte noch keine DB-Verbindung).
|
|
Ohne `Keyfile`/`Compress` hätte das Tool verschlüsselte bzw. gzip-komprimierte
|
|
Mails als Rohbytes exportiert — der Fix stellt das alte Verhalten
|
|
("exportiere Klartext-EML") wieder her.
|
|
|
|
### 2. `internal/storage/storage.go:~506` Self-Assignment
|
|
|
|
`pm.MessageID = pm.MessageID` war echter toter Code (Kommentar im Original:
|
|
"no-op; thread resolved inside insertMeta"). Der komplette `if pm.InReplyTo != ""
|
|
|| len(pm.References) > 0`-Block hatte keinerlei Effekt und wurde entfernt; der
|
|
erklärende Kommentar bleibt als Doku über `resolveThreadID` stehen. Kein
|
|
Verhaltensunterschied.
|
|
|
|
### 3. Testdateien an aktuelle Signaturen angeglichen
|
|
|
|
- `internal/storage/storage_test.go`: `storage.New(dir)` →
|
|
`storage.New(storage.Config{Dir: dir})` (5x); `store.Save(raw, time.Now())` →
|
|
`store.Save(context.Background(), raw, time.Now(), nil)` (Signatur ist jetzt
|
|
`Save(ctx, raw, _ time.Time, tenantID *int64)`); Import `context` ergänzt.
|
|
- `internal/audit/audit_test.go`: `os.Discard` → `io.Discard` (4x), Import `io`
|
|
ergänzt (`os` bleibt für `os.Getenv`/`os.Open` nötig). `audit.New`,
|
|
`Log`, `Query` waren unverändert.
|
|
- `internal/api/api_test.go`: `os.Discard` → `io.Discard` + Import `io`;
|
|
`storage.New(filepath.Join(dir,"store"))` →
|
|
`storage.New(storage.Config{Dir: ...})`. `api.New`, `auth.New`,
|
|
`userstore.Create` gegen den Produktionscode geprüft — unverändert.
|
|
- `internal/userstore/userstore_test.go`: `UpsertLDAPUser(username, email, role)`
|
|
→ `UpsertLDAPUser(username, email, role, nil)` (4. Parameter `tenantID *int64`,
|
|
`nil` = tenant-loser LDAP-User, entspricht dem alten Verhalten).
|
|
|
|
### 4. `TestParseMissingDate` — Entscheidung: Test angepasst, Code bleibt
|
|
|
|
Aktuelles Verhalten in `pkg/mailparser/parser.go:188-191`: Wenn der Date-Header
|
|
fehlt oder durch keinen der Fallback-Layouts parsbar ist, bleibt `pm.Date` der
|
|
Zero-Value (expliziter Kommentar: "Leave pm.Date as zero — storage will use DB
|
|
DEFAULT NOW()"). Der `time.Now()`-Fallback ist bewusst in die Storage-Schicht
|
|
gewandert: `internal/storage/storage.go:998-1000` (`insertMeta`) und
|
|
`:1034-1036` (`SaveMeta`) setzen `received_at = time.Now()`, wenn `pm.Date`
|
|
zero ist.
|
|
|
|
Das ist die sinnvollere Aufteilung und wird deshalb **nicht** zurückgedreht:
|
|
Der Parser bildet die Mail ab, wie sie ist, und erfindet kein Datum — dadurch
|
|
bleibt für jeden Konsumenten unterscheidbar, ob ein Datum aus der Mail stammt
|
|
oder vom Archiv gesetzt wurde (GoBD-Nachvollziehbarkeit). Ein `time.Now()`
|
|
direkt im Parser würde diese Information unwiederbringlich verschlucken.
|
|
Der Test prüft nun `p.Date.IsZero() == true` und dokumentiert die Aufteilung
|
|
inkl. Verweis auf die Storage-Fallbacks.
|
|
|
|
### 5. `internal/auth`-Tests — Ursache: veralteter SQLite-DSN
|
|
|
|
Kein Produktionscode-Bug. `internal/auth/auth_test.go:13` rief
|
|
`userstore.New(filepath.Join(t.TempDir(), "users.db"))` auf — ein Relikt aus der
|
|
SQLite-Zeit. `userstore.New` erwartet seit der PostgreSQL-Migration einen
|
|
pgx-DSN (`userstore.go:71`, `pgxpool.New`), weshalb pgx den Dateipfad mit
|
|
"cannot parse .../users.db" ablehnte.
|
|
|
|
Fix: `newTestAuth` nutzt jetzt dasselbe Muster wie `userstore_test.go` /
|
|
`api_test.go` — `TEST_DATABASE_URL` + pro Test isoliertes Schema
|
|
(`authtest_<testname>`, `search_path=`-Suffix am DSN), `t.Skip` wenn
|
|
`TEST_DATABASE_URL` nicht gesetzt ist, Cleanup per `DROP SCHEMA ... CASCADE`.
|
|
`TestHasRole` ist reine Logik und läuft weiterhin ohne PostgreSQL.
|
|
|
|
### Geänderte Dateien
|
|
|
|
- `cmd/archivmail-export/main.go`
|
|
- `internal/storage/storage.go`
|
|
- `internal/storage/storage_test.go`
|
|
- `internal/audit/audit_test.go`
|
|
- `internal/api/api_test.go`
|
|
- `internal/userstore/userstore_test.go`
|
|
- `internal/auth/auth_test.go`
|
|
- `pkg/mailparser/parser_test.go`
|
|
|
|
### Verifikation auf 132 (2026-08-05)
|
|
|
|
Mit `TEST_DATABASE_URL` gegen die lokale Postgres-Instanz auf 132 (eigenes
|
|
Schema pro Testlauf, per `DROP SCHEMA CASCADE` aufgeräumt) und erreichbarem
|
|
Manticore: `go build ./...`, `go vet ./...` und `go test ./...` liefen
|
|
komplett grün, alle Pakete PASS, keine Skips.
|
|
|
|
Zwei zusätzliche Fixes waren nötig, über die ursprüngliche Analyse hinaus:
|
|
- `internal/api/api_test.go` — ungenutzter Import `"net/http"` entfernt
|
|
(von `go vet` gemeldet).
|
|
- `internal/api/api_test.go` Helper `login()` las den JWT bisher aus dem
|
|
JSON-Response-Body (`resp["token"]`), was zu `nil`-Panic führte. Die
|
|
echte API liefert den Token nur noch als httpOnly-Cookie
|
|
(`archivmail_session`, siehe `internal/api/auth_handlers.go:81-99`) —
|
|
Auth-Contract-Drift, unabhängig von PROJ-73/74. Test liest den Token
|
|
jetzt aus `w.Result().Cookies()`.
|
|
|
|
Deployed auf 132 am 2026-08-05.
|
|
|
|
Deployed auf 131 (Produktiv) am 2026-08-05.
|
|
|
|
## Acceptance Criteria
|
|
|
|
- [x] `CGO_ENABLED=0 go build ./...` auf 132 fehlerfrei (alle Pakete
|
|
inkl. `archivmail-export`).
|
|
- [x] `go vet ./...` ohne Meldungen.
|
|
- [x] `go test ./...` komplett grün (oder verbleibende Fails explizit als
|
|
bekannt/erwartet dokumentiert, nicht stillschweigend).
|