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>
59 lines
1.6 KiB
Go
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)
|
|
}
|
|
}
|