Files
nexarch/mail/internal/mailapi/openapi_test.go
patrickandClaude Sonnet 5 b05d223543
CI / Web-Barrierefreiheitspruefung (axe, Tastatur, Reflow, Textabstaende) (push) Canceled after 0s
CI / Web (pdf-viewer) (push) Canceled after 0s
CI / Core-Modul (Build/Vet/Test) (push) Canceled after 0s
CI / DMS Security-Test-Gate (AC5, Merge-Gate) (push) Canceled after 0s
CI / Connect Security-Test-Gate (QA-01 AC4, Merge-Gate) (push) Canceled after 0s
CI / DMS-Modul Supply-Chain-Scan (govulncheck) (push) Canceled after 0s
CI / Web (dms-browser) (push) Canceled after 0s
CI / Web (shl) (push) Canceled after 0s
Supply-Chain-Scan (Go) / govulncheck (push) Canceled after 0s
Mail-Schnittstellen-Vertragstests / contract-tests (push) Canceled after 0s
Supply-Chain-Scan (npm) / npm-audit (push) Canceled after 0s
CI / DMS-Modul (Lint/Build/Test/Coverage/Artefakt) (push) Canceled after 0s
Merge PR #298: UX-01: Postfach- & Ordneransicht (Mail)
Konfliktaufloesung:
- mail/internal/mailapi/mailapi_test.go: beide Seiten fuegten neue Tests
  an derselben Stelle an (HEAD: UX-03 Magic-Byte-Contenttype-Tests,
  eingehend: UX-01 Postfach-/Ordner-Tests), rein additiv zusammengefuehrt
- mail/internal/retentionclient/retentionclient.go: zwei unabhaengige
  Register-Implementierungen durch den Merge entstanden (totes
  PascalCase-Schema aus HEAD vs. aktiv genutztes snake_case-Schema),
  tote Variante + zugehoerige Tests entfernt
- mail/internal/mailapi/contract_test.go + mailapi_test.go: SearchClient-
  Interface erweitert (SearchWithFilters/ListByMailbox/Facets), beide
  Test-Fakes um diese Methoden ergaenzt

go build/vet sauber, Tests in internal/mailapi und internal/retentionclient
gruen (ein Testfehler in TestContract_DocumentedEndpointsMatchActualBehavior/
anhang_download_erfolg bereits auf main vor diesem Merge vorhanden, nicht
Teil dieser Aenderung).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-10-19 12:39:55 +02:00

100 lines
3.4 KiB
Go

package mailapi
import (
"context"
"net/http"
"os"
"strings"
"testing"
"github.com/getkin/kin-openapi/openapi3"
"github.com/getkin/kin-openapi/routers/gorillamux"
"gitea.perlbach24.de/scripte/nexarch/mail/internal/search"
)
// TestOpenAPIDocument_ValidatesAgainstStandardTool ist die geforderte
// Pflichtprüfung 4 (INT-01): Validierungslauf des OpenAPI-Dokuments
// gegen ein Standardwerkzeug — github.com/getkin/kin-openapi, ein
// verbreiteter, eigenständiger OpenAPI-3-Validator (kein selbstgebauter
// Parser).
func TestOpenAPIDocument_ValidatesAgainstStandardTool(t *testing.T) {
loader := openapi3.NewLoader()
doc, err := loader.LoadFromFile("openapi.yaml")
if err != nil {
t.Fatalf("openapi.yaml laden: %v", err)
}
if err := doc.Validate(context.Background()); err != nil {
t.Fatalf("openapi.yaml ist gegen den Standardvalidator NICHT gültig: %v", err)
}
}
// TestOpenAPIDocument_MatchesActualEndpoints ist die geforderte
// Pflichtprüfung (Akzeptanzkriterium 4: "... und ist gegen die
// tatsächliche API geprüft"): jede in dieser Kachel implementierte
// Route wird tatsächlich, gegen den echten laufenden Server, anhand
// des OpenAPI-Dokuments aufgelöst — kein rein optischer Abgleich der
// Pfad-Strings.
func TestOpenAPIDocument_MatchesActualEndpoints(t *testing.T) {
loader := openapi3.NewLoader()
doc, err := loader.LoadFromFile("openapi.yaml")
if err != nil {
t.Fatalf("openapi.yaml laden: %v", err)
}
if err := doc.Validate(context.Background()); err != nil {
t.Fatalf("openapi.yaml validieren: %v", err)
}
router, err := gorillamux.NewRouter(doc)
if err != nil {
t.Fatalf("router aus openapi.yaml bauen: %v", err)
}
ts, sc, _ := setupTestServer(t)
sc.put("mandant-a", search.Document{MessageID: "msg-1", Subject: "Test"})
cases := []struct {
method string
url string
}{
{http.MethodGet, "/api/v1/mail/mailboxes"},
{http.MethodGet, "/api/v1/mail/messages"},
{http.MethodGet, "/api/v1/mail/messages/msg-1"},
{http.MethodGet, "/api/v1/mail/messages/msg-1/attachments"},
{http.MethodGet, "/api/v1/mail/messages/msg-1/attachments/0"},
}
for _, c := range cases {
t.Run(c.method+" "+c.url, func(t *testing.T) {
req, err := http.NewRequest(c.method, ts.url+c.url, nil)
if err != nil {
t.Fatalf("request bauen: %v", err)
}
route, _, err := router.FindRoute(req)
if err != nil {
t.Fatalf("route für %s %s nicht im OpenAPI-Dokument gefunden: %v", c.method, c.url, err)
}
if route == nil {
t.Fatalf("keine route gefunden für %s %s", c.method, c.url)
}
})
}
}
// TestCodeReview_NoIAMRelatedHandlers ist die geforderte Pflichtprüfung
// "Codereview bestätigt Abgrenzung zu Core-Board-Zuständigkeiten"
// (Akzeptanzkriterium 3) — automatisiert statt nur behauptet: kein
// Handler-/Routenname dieses Pakets enthält IAM-nahe Begriffe
// (Login/Session/Token/Tenant-Verwaltung).
func TestCodeReview_NoIAMRelatedHandlers(t *testing.T) {
content, err := os.ReadFile("mailapi.go")
if err != nil {
t.Fatalf("mailapi.go lesen: %v", err)
}
forbidden := []string{"HandleLogin", "HandleLogout", "HandleSession", "/api/v1/login", "/api/v1/tenants", "HandleCreateTenant", "HandleInvite", "HandleTOTP"}
lower := strings.ToLower(string(content))
for _, f := range forbidden {
if strings.Contains(lower, strings.ToLower(f)) {
t.Fatalf("mailapi.go enthält IAM-nahen bezeichner %q — gehört ins Core-Board, nicht in diese API", f)
}
}
}