package attachments import ( "encoding/base64" "strings" "testing" ) // TestExtract_OversizedAttachmentIsCorrectlyLimited ist die geforderte // Pflichtprüfung 1: Nachricht mit überdimensioniertem Anhang wird // korrekt begrenzt. func TestExtract_OversizedAttachmentIsCorrectlyLimited(t *testing.T) { oversized := strings.Repeat("A", 200) raw := "From: a@example.com\r\n" + "To: b@example.com\r\n" + "Subject: Test\r\n" + "MIME-Version: 1.0\r\n" + "Content-Type: multipart/mixed; boundary=\"b\"\r\n\r\n" + "--b\r\n" + "Content-Type: text/plain; charset=utf-8\r\n\r\n" + "Kurzer Nachrichtentext\r\n" + "--b\r\n" + "Content-Type: application/octet-stream\r\n" + "Content-Disposition: attachment; filename=\"riesig.bin\"\r\n\r\n" + oversized + "\r\n" + "--b--\r\n" result, err := Extract(strings.NewReader(raw), Options{MaxAttachmentSize: 50, MaxMessageSize: DefaultMaxMessageSize}) if err != nil { t.Fatalf("extract: %v", err) } if len(result.Attachments) != 0 { t.Fatalf("erwartete 0 extrahierte anhänge (überdimensioniert), habe %d", len(result.Attachments)) } if len(result.Skipped) != 1 || result.Skipped[0].Filename != "riesig.bin" { t.Fatalf("erwartete genau 1 übersprungenen anhang 'riesig.bin', habe: %+v", result.Skipped) } if len(result.TextParts) != 1 || string(result.TextParts[0].Content) != "Kurzer Nachrichtentext" { t.Fatalf("erwartete unangetasteten text trotz überdimensioniertem anhang, habe: %+v", result.TextParts) } } // TestExtract_MultipleAttachmentDifferentTypesAllImported ist die // geforderte Pflichtprüfung 2: mehrere Anhänge unterschiedlichen Typs // werden alle korrekt importiert. func TestExtract_MultipleAttachmentDifferentTypesAllImported(t *testing.T) { pdfContent := base64.StdEncoding.EncodeToString([]byte("%PDF-1.4 fake pdf bytes")) pngContent := base64.StdEncoding.EncodeToString([]byte{0x89, 'P', 'N', 'G', 0x0D, 0x0A, 0x1A, 0x0A, 0, 0, 0}) raw := "From: a@example.com\r\n" + "To: b@example.com\r\n" + "Subject: Test\r\n" + "MIME-Version: 1.0\r\n" + "Content-Type: multipart/mixed; boundary=\"b\"\r\n\r\n" + "--b\r\n" + "Content-Type: text/plain; charset=utf-8\r\n\r\n" + "Anbei zwei Anhänge\r\n" + "--b\r\n" + "Content-Type: application/pdf\r\n" + "Content-Disposition: attachment; filename=\"rechnung.pdf\"\r\n" + "Content-Transfer-Encoding: base64\r\n\r\n" + pdfContent + "\r\n" + "--b\r\n" + "Content-Type: image/png\r\n" + "Content-Disposition: attachment; filename=\"logo.png\"\r\n" + "Content-Transfer-Encoding: base64\r\n\r\n" + pngContent + "\r\n" + "--b--\r\n" result, err := Extract(strings.NewReader(raw), Options{}) if err != nil { t.Fatalf("extract: %v", err) } if len(result.Attachments) != 2 { t.Fatalf("erwartete 2 extrahierte anhänge, habe %d: %+v", len(result.Attachments), result.Attachments) } byName := map[string]Attachment{} for _, a := range result.Attachments { byName[a.Filename] = a } pdf, ok := byName["rechnung.pdf"] if !ok || pdf.DeclaredContentType != "application/pdf" { t.Fatalf("pdf-anhang fehlt oder falscher deklarierter typ: %+v", byName) } if !strings.Contains(pdf.VerifiedContentType, "text/plain") && !strings.Contains(pdf.VerifiedContentType, "application/") { // http.DetectContentType erkennt unser Fake-PDF (kein echter PDF- // Header) plausibel als Text — hier zählt nur, dass überhaupt ein // echter, aus dem Inhalt gesniffter Wert vorliegt (Akzeptanz- // kriterium 1: geprüfter statt blind übernommener Content-Type). t.Fatalf("erwartete real gesniffeden content-type, habe: %q", pdf.VerifiedContentType) } png, ok := byName["logo.png"] if !ok || png.DeclaredContentType != "image/png" { t.Fatalf("png-anhang fehlt oder falscher deklarierter typ: %+v", byName) } if png.VerifiedContentType != "image/png" { t.Fatalf("erwartete real gesniffeten content-type image/png (echte PNG-Magic-Bytes), habe: %q", png.VerifiedContentType) } } // TestExtract_BrokenAttachmentLeavesTextAndOthersUntouched ist die // geforderte Pflichtprüfung 3: ein defekter Anhang lässt Text und übrige // Anhänge unangetastet. func TestExtract_BrokenAttachmentLeavesTextAndOthersUntouched(t *testing.T) { goodContent := base64.StdEncoding.EncodeToString([]byte("echter anhangsinhalt")) raw := "From: a@example.com\r\n" + "To: b@example.com\r\n" + "Subject: Test\r\n" + "MIME-Version: 1.0\r\n" + "Content-Type: multipart/mixed; boundary=\"b\"\r\n\r\n" + "--b\r\n" + "Content-Type: text/plain; charset=utf-8\r\n\r\n" + "Wichtiger Nachrichtentext\r\n" + "--b\r\n" + "Content-Type: application/octet-stream\r\n" + "Content-Disposition: attachment; filename=\"kaputt.bin\"\r\n" + "Content-Transfer-Encoding: base64\r\n\r\n" + "DAS_IST_KEIN_GUELTIGES_BASE64!!!\r\n" + "--b\r\n" + "Content-Type: application/octet-stream\r\n" + "Content-Disposition: attachment; filename=\"gut.bin\"\r\n" + "Content-Transfer-Encoding: base64\r\n\r\n" + goodContent + "\r\n" + "--b--\r\n" result, err := Extract(strings.NewReader(raw), Options{}) if err != nil { t.Fatalf("extract: %v", err) } if len(result.TextParts) != 1 || string(result.TextParts[0].Content) != "Wichtiger Nachrichtentext" { t.Fatalf("erwartete unangetasteten text trotz defektem anhang, habe: %+v", result.TextParts) } if len(result.Attachments) != 1 || result.Attachments[0].Filename != "gut.bin" { t.Fatalf("erwartete den guten anhang unangetastet, habe: %+v", result.Attachments) } if string(result.Attachments[0].Content) != "echter anhangsinhalt" { t.Fatalf("guter anhang hat unerwarteten inhalt: %q", result.Attachments[0].Content) } }