Files
patrickandClaude Sonnet 5 2a084cbdc8 FDN-11: ressourcenlimits-f-r-asynchrone-verarbeitungsjobs
Basis: feature/fdn-09-verschluesselung-at-rest-schluesselverwaltung (enthaelt
FDN-04, wovon FDN-11 laut Ticket abhaengt; fdn-10 wurde NICHT als Basis
genommen, da FDN-04 kein Vorfahre von fdn-10 ist).

Ergaenzt harte, konfigurierbare Obergrenzen je Verarbeitungsjob (Zeit,
Speicher, Eingabegroesse/Seitenzahl) um internal/jobqueue: Ueberschreitung
fuehrt zum kontrollierten Abbruch in die Dead-Letter-Queue statt zur
Blockade des Workers, inkl. Nutzerbenachrichtigung ueber ein neues
Notifier-Interface.

- limits.go: Limits-Struct, DefaultLimits, Vorab-Pruefung von
  size_bytes/pages aus den Job-Metadaten vor Handler-Start
- notifier.go: Notifier-Interface + LogNotifier als Standard
- queue.go: Queue.FailPermanently fuer sofortigen DLQ-Transfer (kein
  Backoff/Retry bei Ressourcenlimit-Verstoss)
- worker.go: Worker.runWithLimits fuehrt Handler mit Zeit-Timeout und
  Speicher-Sampling (runtime.MemStats, prozessweite Annaeherung) aus und
  kehrt bei Ueberschreitung sofort zurueck statt zu blockieren

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QPsyieCYwvbUBsnN598SbP
2026-09-17 12:30:17 +02:00

55 lines
1.4 KiB
Go

package jobqueue
import (
"encoding/json"
"errors"
"testing"
)
// TestCheckInputLimits ist Akzeptanzkriterium 1 (Groesse/Seitenzahl
// konfigurierbar pruefbar) fuer die reine Prueffunktion, unabhaengig vom Worker.
func TestCheckInputLimits(t *testing.T) {
cases := []struct {
name string
payload string
limits Limits
wantExceed bool
}{
{
name: "innerhalb der obergrenzen",
payload: `{"size_bytes": 100, "pages": 5}`,
limits: Limits{MaxInputBytes: 1000, MaxPages: 10},
wantExceed: false,
},
{
name: "eingabegroesse ueberschritten",
payload: `{"size_bytes": 5000, "pages": 5}`,
limits: Limits{MaxInputBytes: 1000, MaxPages: 10},
wantExceed: true,
},
{
name: "seitenzahl ueberschritten",
payload: `{"size_bytes": 100, "pages": 999}`,
limits: Limits{MaxInputBytes: 1000, MaxPages: 10},
wantExceed: true,
},
{
name: "limit 0 bedeutet keine pruefung",
payload: `{"size_bytes": 999999, "pages": 999999}`,
limits: Limits{},
wantExceed: false,
},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
err := checkInputLimits(json.RawMessage(tc.payload), tc.limits)
var limitErr *LimitExceededError
gotExceed := errors.As(err, &limitErr)
if gotExceed != tc.wantExceed {
t.Fatalf("checkInputLimits(%s) exceed = %v, want %v (err=%v)", tc.payload, gotExceed, tc.wantExceed, err)
}
})
}
}