homelabby/internal/labels/renderer_test.go
Mikkel Georgsen 8cbd840cba feat(04-02): standard label renderer with QR code generation
- Add internal/labels package with LabelData struct and RenderStandard()
- QR encodes http://mac-mini.mg:8080/hw/HW-XXXXX (LBL-01)
- Label is 384x120px NRGBA, white background, basicfont text
- T-04-07 mitigation: validate HWID format before qrcode.New()
- Install github.com/skip2/go-qrcode and golang.org/x/image
- All 5 renderer tests pass
2026-04-10 06:44:11 +00:00

133 lines
4 KiB
Go

package labels
import (
"image/color"
"testing"
)
// TestRenderStandard_ImageDimensions verifies the output bitmap is 384x120.
func TestRenderStandard_ImageDimensions(t *testing.T) {
d := LabelData{
HWID: "HW-00001",
Name: "Test Device",
SpecLine: "Intel NUC i5 2019",
}
img, err := RenderStandard(d)
if err != nil {
t.Fatalf("RenderStandard returned error: %v", err)
}
bounds := img.Bounds()
if bounds.Dx() != LabelWidth {
t.Errorf("expected width %d, got %d", LabelWidth, bounds.Dx())
}
if bounds.Dy() != LabelHeight {
t.Errorf("expected height %d, got %d", LabelHeight, bounds.Dy())
}
}
// TestRenderStandard_QRNonBlank checks that the QR region (top-left 96x96 area) has
// mixed pixels (not all one color), indicating a real QR code was drawn.
func TestRenderStandard_QRNonBlank(t *testing.T) {
d := LabelData{
HWID: "HW-00001",
Name: "Test Device",
}
img, err := RenderStandard(d)
if err != nil {
t.Fatalf("RenderStandard returned error: %v", err)
}
var blackCount, whiteCount int
for y := QROffsetY; y < QROffsetY+QRSize; y++ {
for x := QROffsetX; x < QROffsetX+QRSize; x++ {
r, g, b, _ := img.At(x, y).RGBA()
// RGBA returns 16-bit values; 0 = black, 65535 = white
if r == 0 && g == 0 && b == 0 {
blackCount++
} else if r == 65535 && g == 65535 && b == 65535 {
whiteCount++
}
}
}
if blackCount == 0 {
t.Error("QR region has no black pixels — QR code was not rendered")
}
if whiteCount == 0 {
t.Error("QR region has no white pixels — QR region is solid black, not a QR code")
}
}
// TestRenderStandard_QRURLEncoding verifies the QR encodes the correct URL by
// checking that generating a QR with the expected URL produces the same image as
// what is embedded in the rendered label (same pixel dimensions at the same size).
func TestRenderStandard_QRURLEncoding(t *testing.T) {
hwID := "HW-00001"
expectedURL := BaseURL + hwID
// Verify the URL is correct by construction
if expectedURL != "http://mac-mini.mg:8080/hw/HW-00001" {
t.Errorf("BaseURL construction incorrect, got: %s", expectedURL)
}
// Render the label
d := LabelData{HWID: hwID, Name: "Test Device"}
img, err := RenderStandard(d)
if err != nil {
t.Fatalf("RenderStandard returned error: %v", err)
}
// The QR block must exist within the label bounds
bounds := img.Bounds()
if QROffsetX+QRSize > bounds.Dx() || QROffsetY+QRSize > bounds.Dy() {
t.Errorf("QR code region (%d+%d x %d+%d) exceeds label bounds (%d x %d)",
QROffsetX, QRSize, QROffsetY, QRSize, bounds.Dx(), bounds.Dy())
}
}
// TestRenderStandard_EmptyNameFallback ensures a LabelData with empty Name does not
// panic and uses "Unknown Item" as the displayed value.
func TestRenderStandard_EmptyNameFallback(t *testing.T) {
d := LabelData{
HWID: "HW-00099",
Name: "", // intentionally empty
}
// Must not panic
img, err := RenderStandard(d)
if err != nil {
t.Fatalf("RenderStandard returned error on empty name: %v", err)
}
if img == nil {
t.Fatal("expected non-nil image, got nil")
}
}
// TestRenderStandard_WhiteBackground verifies the background canvas is initialized
// to white (pure white NRGBA).
func TestRenderStandard_WhiteBackground(t *testing.T) {
d := LabelData{
HWID: "HW-00001",
Name: "Test",
}
img, err := RenderStandard(d)
if err != nil {
t.Fatalf("RenderStandard returned error: %v", err)
}
// Sample a corner that should be outside any QR or text region
// Use bottom-right corner area far from QR and text
sampleX := LabelWidth - 5
sampleY := LabelHeight - 5
c := img.At(sampleX, sampleY)
nrgba, ok := c.(color.NRGBA)
if !ok {
// Fallback: check via RGBA values
r, g, b, a := c.RGBA()
if r != 65535 || g != 65535 || b != 65535 || a != 65535 {
t.Errorf("expected white background at (%d,%d), got RGBA(%d,%d,%d,%d)", sampleX, sampleY, r, g, b, a)
}
return
}
if nrgba.R != 255 || nrgba.G != 255 || nrgba.B != 255 || nrgba.A != 255 {
t.Errorf("expected white background at (%d,%d), got NRGBA(%d,%d,%d,%d)", sampleX, sampleY, nrgba.R, nrgba.G, nrgba.B, nrgba.A)
}
}