// Package contracttest implementiert Core QA-07: Vertragstests fuer die // Core-Schnittstellen, die von den Fachmodulen (DMS/Mail/Archive/Workflow/ // AI/Connect) konsumiert werden — REST-Fehlerschema (API-01), JWKS-/Rechte- // Cache-Kontrakt (API-05), Modul-Registry (API-02) und Webhook-Zustellung // (API-07). Jeder Test prueft die TATSAECHLICHE Antwort einer echten // Core-Komponente gegen ihr dokumentiertes Schema — eine entfernte oder // umbenannte Pflichteigenschaft laesst den jeweiligen Test fehlschlagen, // BEVOR sie ein konsumierendes Modul bricht (Akzeptanzkriterium 2). package contracttest import ( "encoding/json" "fmt" ) // RequireJSONFields dekodiert data als JSON-Objekt und prueft, dass ALLE // angegebenen Top-Level-Schluessel vorhanden sind. Liefert die fehlenden // Schluessel zurueck — leer bedeutet: Vertrag eingehalten. func RequireJSONFields(data []byte, required []string) (missing []string, err error) { var decoded map[string]any if err := json.Unmarshal(data, &decoded); err != nil { return nil, fmt.Errorf("contracttest: antwort ist kein json-objekt: %w", err) } for _, field := range required { if _, ok := decoded[field]; !ok { missing = append(missing, field) } } return missing, nil } // RequireJSONArrayItemFields prueft, dass data ein JSON-Objekt mit einem // Array-Feld arrayField ist, dessen ERSTES Element alle itemFields enthaelt // — Vertrag fuer Listen-Antworten wie JWKS ("keys": [{...}]). func RequireJSONArrayItemFields(data []byte, arrayField string, itemFields []string) (missing []string, err error) { var decoded map[string]json.RawMessage if err := json.Unmarshal(data, &decoded); err != nil { return nil, fmt.Errorf("contracttest: antwort ist kein json-objekt: %w", err) } raw, ok := decoded[arrayField] if !ok { return itemFields, nil // das array-feld selbst fehlt bereits -> alles "fehlend" } var items []map[string]any if err := json.Unmarshal(raw, &items); err != nil { return nil, fmt.Errorf("contracttest: feld %q ist kein array: %w", arrayField, err) } if len(items) == 0 { return nil, fmt.Errorf("contracttest: feld %q ist leer, kann nicht gegen kontrakt geprueft werden", arrayField) } for _, field := range itemFields { if _, ok := items[0][field]; !ok { missing = append(missing, field) } } return missing, nil }