package signature_test import ( "testing" "time" "sneak.berlin/go/pixa/internal/imagerequest" "sneak.berlin/go/pixa/internal/signature" ) // goldenExpiresUnix is the fixed expiration timestamp used by all golden // vectors: 2024-01-01T00:00:00Z. const goldenExpiresUnix int64 = 1704067200 // goldenSigningKey is the fixed signing key used by all golden vectors. const goldenSigningKey = "golden-test-key" type goldenVector struct { name string req imagerequest.Request // wantSignature is the exact base64url (RFC 4648 URL-safe, // padded) HMAC-SHA256 signature for the request expiring at // goldenExpiresUnix. wantSignature string } // goldenVectors returns the known-answer vectors. The expected values // were computed once and are hardcoded here. func goldenVectors() []goldenVector { return []goldenVector{ { name: "resized without query", req: imagerequest.Request{ Source: imagerequest.Source{ Host: testHost, Path: testPath, Query: "", }, Transform: imagerequest.Transform{ Size: imagerequest.Size{Width: 800, Height: 600}, Format: testFormatWebP, Quality: 85, FitMode: testFitCover, }, }, // Signed data: // "cdn.example.com:/photos/cat.jpg::800:600:webp:1704067200:85:cover" wantSignature: "kdqeGoW2SX7qnaYtoB970wEnLydn0UnIgQYQLfAnjXQ=", }, { name: "resized with query string", req: imagerequest.Request{ Source: imagerequest.Source{ Host: testHost, Path: testPath, Query: "token=abc&v=2", }, Transform: imagerequest.Transform{ Size: imagerequest.Size{Width: 800, Height: 600}, Format: testFormatWebP, Quality: 85, FitMode: testFitCover, }, }, // Signed data: // "cdn.example.com:/photos/cat.jpg:token=abc&v=2:800:600:webp:1704067200:85:cover" wantSignature: "pKgVBOTd_Q_EikI7MNQLC9Q8Hurdxzyv3EIYvVhqc2I=", }, { name: "original size without query", req: imagerequest.Request{ Source: imagerequest.Source{ Host: testHost, Path: testPath, Query: "", }, Transform: imagerequest.Transform{ Size: imagerequest.Size{Width: 0, Height: 0}, Format: testFormatPNG, Quality: 85, FitMode: testFitCover, }, }, // Signed data: // "cdn.example.com:/photos/cat.jpg::0:0:png:1704067200:85:cover" wantSignature: "6_rZ0yyVbGZRs8kG7n7HLgLi5Jt8vjiWQljIEL1jbIs=", }, { name: "non-default quality and fit", req: imagerequest.Request{ Source: imagerequest.Source{ Host: testHost, Path: testPath, Query: "", }, Transform: imagerequest.Transform{ Size: imagerequest.Size{Width: 800, Height: 600}, Format: testFormatWebP, Quality: 40, FitMode: testFitContain, }, }, // Signed data: // "cdn.example.com:/photos/cat.jpg::800:600:webp:1704067200:40:contain" wantSignature: "pGaXpPUbI3A7nMx-4T9bfq9bYWBNL0kY4bxlcv3g1F8=", }, } } // TestSigner_GoldenVectors pins the exact HMAC-SHA256 signature output for // fully-specified requests with a hardcoded signing key. // // If any of these assertions fail, the signed byte format // ("host:path:query:width:height:format:expiration:quality:fit") or the // base64url encoding has changed. Update these constants only when that // change is intended. func TestSigner_GoldenVectors(t *testing.T) { t.Parallel() signer := signature.New(goldenSigningKey) for _, tt := range goldenVectors() { t.Run(tt.name, func(t *testing.T) { t.Parallel() signReq := tt.req gotSignature := signer.Sign(&signReq, time.Unix(goldenExpiresUnix, 0)) if gotSignature != tt.wantSignature { t.Errorf("Sign() = %q, want %q (signed byte format changed?)", gotSignature, tt.wantSignature) } }) } }