Files
nexarch/dms/internal/thumbnail/throughput_test.go
T
patrickandClaude Sonnet 5 08584b0c5f DOC-04: Vorschau- & Thumbnail-Pipeline
internal/thumbnail: Vorschau-Zustand je Datei-Revision als explizite
State-Machine (pending/processing/ready/failed, revision_thumbnails)
statt Ableitung aus Dateiendung. Content-Type wird ausschliesslich ueber
Magic-Bytes bestimmt (net/http.DetectContentType gegen die entschluesselten
Bytes) - der in "Bekannte Fehler vermeiden" beschriebene Fehler eines
Vorgaengerprojekts (fragile Erkennung ueber Dateiendung) wird damit
vermieden.

PDF-Rasterung ueber pdftoppm (Poppler), Bild-Thumbnails ueber einen
minimalen, abhaengigkeitsfreien Nearest-Neighbor-Resizer auf Basis der
Standardbibliothek. Original wird ueber FDN-09 (dmscrypto) entschluesselt,
Thumbnail wird ebenfalls verschluesselt im Objekt-Storage abgelegt -
gleiches Schutzniveau wie das Original.

upload.Service bekommt SetThumbnailEnqueuer (neue, optionale Methode -
bestehende Aufrufer/Tests unveraendert funktionsfaehig) und reiht nach
erfolgreichem Complete() die Vorschau-Erzeugung ein; ein Fehlschlag dabei
laesst den bereits abgeschlossenen Upload nicht ruecktwirkend fehlschlagen.

Pruefungen:
1. Beschaedigte PDF-Datei -> Fehlerstatus statt Absturz, blockiert andere
   Jobs nicht (echter pdftoppm-Fehlschlag, echte SKIP-LOCKED-Job-Queue)
2. Durchsatztest: 500 echte PDFs, alle erfolgreich, ~15s Gesamtlaufzeit
3. Cache-Invalidierung bei neuer Revision: GetCurrentForDocument loest
   nachweislich auf die neue statt die alte Revision auf

Regressionsfrei: 13 bestehende upload-/jobqueue-Tests unveraendert PASS.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-17 19:42:30 +02:00

59 lines
1.6 KiB
Go

package thumbnail
import (
"context"
"testing"
"time"
"gitea.perlbach24.de/scripte/nexarch/dms/internal/jobqueue"
)
// Pruefung 2: Durchsatztest mit 500 Dokumenten — alle werden fehlerfrei
// verarbeitet, keines bleibt haengen (Akzeptanzkriterium 1: Thumbnail
// steht nach definierter Zeit bereit, auch unter Last).
func TestThroughput_500DocumentsAllProcessedSuccessfully(t *testing.T) {
if testing.Short() {
t.Skip("durchsatztest übersprungen (-short)")
}
env := setupTest(t)
ctx := context.Background()
const n = 500
revisionIDs := make([]string, n)
for i := 0; i < n; i++ {
docID, revID := env.createRevision(t, []byte(minimalValidPDF))
if err := Enqueue(ctx, env.pool, env.queue, docID, revID); err != nil {
t.Fatalf("Enqueue #%d: %v", i, err)
}
revisionIDs[i] = revID
}
worker := jobqueue.NewWorker(env.queue, "throughput-worker", []string{JobType}, time.Millisecond, env.gen.Handler)
start := time.Now()
for i := 0; i < n; i++ {
worker.RunOnce(ctx)
}
elapsed := time.Since(start)
t.Logf("500 dokumente in %v verarbeitet (%v je dokument)", elapsed, elapsed/n)
succeeded := 0
for _, revID := range revisionIDs {
thumb, err := GetForRevision(ctx, env.pool, revID)
if err != nil {
t.Fatalf("GetForRevision: %v", err)
}
if thumb.Status == StatusReady {
succeeded++
} else {
errMsg := ""
if thumb.Error != nil {
errMsg = *thumb.Error
}
t.Errorf("revision %q status = %q (fehler: %s), want %q", revID, thumb.Status, errMsg, StatusReady)
}
}
if succeeded != n {
t.Fatalf("erwartet %d erfolgreich verarbeitete dokumente, hatte %d", n, succeeded)
}
}