dealspace/lib/watermark_test.go

214 lines
5.7 KiB
Go

package lib
import (
"archive/zip"
"bytes"
"image"
"image/color"
"image/png"
"testing"
)
// TestWatermarkPDF tests PDF watermarking with a minimal PDF.
func TestWatermarkPDF(t *testing.T) {
// Minimal valid PDF (empty page)
minimalPDF := []byte(`%PDF-1.4
1 0 obj
<< /Type /Catalog /Pages 2 0 R >>
endobj
2 0 obj
<< /Type /Pages /Kids [3 0 R] /Count 1 >>
endobj
3 0 obj
<< /Type /Page /Parent 2 0 R /MediaBox [0 0 612 792] >>
endobj
xref
0 4
0000000000 65535 f
0000000009 00000 n
0000000058 00000 n
0000000115 00000 n
trailer
<< /Size 4 /Root 1 0 R >>
startxref
192
%%EOF`)
label := "CONFIDENTIAL - Test User - 2026-02-28 - TestProject"
out, err := WatermarkPDF(minimalPDF, label)
if err != nil {
t.Fatalf("WatermarkPDF failed: %v", err)
}
// Basic checks
if len(out) == 0 {
t.Fatal("WatermarkPDF returned empty output")
}
// Watermarked PDF should be larger than original
if len(out) <= len(minimalPDF) {
t.Errorf("Watermarked PDF (%d bytes) should be larger than original (%d bytes)", len(out), len(minimalPDF))
}
// Should still start with PDF header
if !bytes.HasPrefix(out, []byte("%PDF")) {
t.Error("Output doesn't look like a PDF (missing %PDF header)")
}
}
// TestWatermarkImagePNG tests image watermarking with PNG.
func TestWatermarkImagePNG(t *testing.T) {
// Create a minimal test PNG (100x100 red square)
img := image.NewRGBA(image.Rect(0, 0, 100, 100))
red := color.RGBA{255, 0, 0, 255}
for y := 0; y < 100; y++ {
for x := 0; x < 100; x++ {
img.Set(x, y, red)
}
}
var buf bytes.Buffer
if err := png.Encode(&buf, img); err != nil {
t.Fatalf("Failed to create test PNG: %v", err)
}
originalPNG := buf.Bytes()
label := "CONFIDENTIAL - Test User - 2026-02-28"
out, err := WatermarkImage(originalPNG, "image/png", label)
if err != nil {
t.Fatalf("WatermarkImage failed: %v", err)
}
// Basic checks
if len(out) == 0 {
t.Fatal("WatermarkImage returned empty output")
}
// Verify it's still a valid PNG
_, err = png.Decode(bytes.NewReader(out))
if err != nil {
t.Errorf("Output is not a valid PNG: %v", err)
}
}
// TestWatermarkDOCX tests DOCX watermarking with a minimal document.
func TestWatermarkDOCX(t *testing.T) {
// Create a minimal DOCX (which is a ZIP file with specific structure)
minimalDOCX := createMinimalDOCX()
label := "CONFIDENTIAL - Test User - 2026-02-28 - TestProject"
out, err := WatermarkDOCX(minimalDOCX, label)
if err != nil {
t.Fatalf("WatermarkDOCX failed: %v", err)
}
// Basic checks
if len(out) == 0 {
t.Fatal("WatermarkDOCX returned empty output")
}
// Watermarked DOCX should be larger (we added header)
if len(out) <= len(minimalDOCX) {
t.Errorf("Watermarked DOCX (%d bytes) should be larger than original (%d bytes)", len(out), len(minimalDOCX))
}
// Should still be a valid ZIP
if !bytes.HasPrefix(out, []byte("PK")) {
t.Error("Output doesn't look like a DOCX/ZIP (missing PK header)")
}
}
// TestWatermarkDispatch tests the main dispatch function.
func TestWatermarkDispatch(t *testing.T) {
tests := []struct {
name string
mimeType string
data []byte
}{
{"unknown type passthrough", "application/octet-stream", []byte("hello world")},
{"text passthrough", "text/plain", []byte("hello world")},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
out, err := Watermark(tt.data, tt.mimeType, "test label")
if err != nil {
t.Errorf("Watermark failed: %v", err)
}
// Unknown types should pass through unchanged
if !bytes.Equal(out, tt.data) {
t.Error("Unknown MIME type should pass through unchanged")
}
})
}
}
// TestWatermarkEmptyInput tests error handling for empty inputs.
func TestWatermarkEmptyInput(t *testing.T) {
_, err := WatermarkPDF(nil, "test")
if err == nil {
t.Error("WatermarkPDF should fail on nil input")
}
_, err = WatermarkImage(nil, "image/png", "test")
if err == nil {
t.Error("WatermarkImage should fail on nil input")
}
_, err = WatermarkDOCX(nil, "test")
if err == nil {
t.Error("WatermarkDOCX should fail on nil input")
}
}
// createMinimalDOCX creates a minimal valid DOCX for testing.
func createMinimalDOCX() []byte {
var buf bytes.Buffer
w := zip.NewWriter(&buf)
// [Content_Types].xml
contentTypes := `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">
<Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/>
<Default Extension="xml" ContentType="application/xml"/>
<Override PartName="/word/document.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml"/>
</Types>`
f, _ := w.Create("[Content_Types].xml")
f.Write([]byte(contentTypes))
// _rels/.rels
rels := `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
<Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="word/document.xml"/>
</Relationships>`
f, _ = w.Create("_rels/.rels")
f.Write([]byte(rels))
// word/document.xml
doc := `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
<w:body>
<w:p>
<w:r>
<w:t>Test Document</w:t>
</w:r>
</w:p>
</w:body>
</w:document>`
f, _ = w.Create("word/document.xml")
f.Write([]byte(doc))
// word/_rels/document.xml.rels
docRels := `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
</Relationships>`
f, _ = w.Create("word/_rels/document.xml.rels")
f.Write([]byte(docRels))
w.Close()
return buf.Bytes()
}