From 3a2b93b337406b2fed573a1ef5de436e01fc1f2f Mon Sep 17 00:00:00 2001 From: Januth Nimnal Date: Sun, 27 Sep 2026 07:25:48 +0530 Subject: [PATCH 1/2] fix: wrap Parse error in UUID.Scan with %w --- sql.go | 2 +- sql_test.go | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+), 1 deletion(-) diff --git a/sql.go b/sql.go index 2e02ec0..edd7846 100644 --- a/sql.go +++ b/sql.go @@ -26,7 +26,7 @@ func (uuid *UUID) Scan(src interface{}) error { // see Parse for required string format u, err := Parse(src) if err != nil { - return fmt.Errorf("Scan: %v", err) + return fmt.Errorf("Scan: %w", err) } *uuid = u diff --git a/sql_test.go b/sql_test.go index 929b10f..cdc8488 100644 --- a/sql_test.go +++ b/sql_test.go @@ -5,6 +5,7 @@ package uuid import ( + "errors" "strings" "testing" ) @@ -111,3 +112,50 @@ func TestValue(t *testing.T) { t.Error("Value() did not return expected string") } } + +// TestScanErrorWrapping checks that Scan reports the underlying parse failure +// rather than flattening it into an opaque string. The package exports +// ErrInvalidLength, ErrInvalidUUIDFormat and ErrInvalidURNPrefix together with +// the IsInvalidLengthError helper precisely so that callers can classify parse +// errors with errors.Is/errors.As; Scan must not defeat that. +func TestScanErrorWrapping(t *testing.T) { + testCases := []struct { + name string + text string + src interface{} + match func(error) bool + }{ + {"string/invalid-length", "12345", "12345", IsInvalidLengthError}, + {"bytes/invalid-length", "12345", []byte("12345"), IsInvalidLengthError}, + {"string/invalid-format", "12345678gabc1234abcd1234abcd1234", "12345678gabc1234abcd1234abcd1234", func(err error) bool { + return errors.Is(err, ErrInvalidUUIDFormat) + }}, + {"bytes/invalid-format", "12345678gabc1234abcd1234abcd1234", []byte("12345678gabc1234abcd1234abcd1234"), func(err error) bool { + return errors.Is(err, ErrInvalidUUIDFormat) + }}, + {"string/invalid-urn-prefix", "urn:test:123e4567-e89b-12d3-a456-426655440000", "urn:test:123e4567-e89b-12d3-a456-426655440000", func(err error) bool { + return errors.Is(err, ErrInvalidURNPrefix) + }}, + {"bytes/invalid-urn-prefix", "urn:test:123e4567-e89b-12d3-a456-426655440000", []byte("urn:test:123e4567-e89b-12d3-a456-426655440000"), func(err error) bool { + return errors.Is(err, ErrInvalidURNPrefix) + }}, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + var uuid UUID + err := uuid.Scan(tc.src) + if err == nil { + t.Fatalf("Scan(%v) succeeded, want error", tc.src) + } + if !tc.match(err) { + t.Errorf("Scan(%v) = %v: underlying parse error type was lost", tc.src, err) + } + // The error message itself must be unchanged. + _, parseErr := Parse(tc.text) + if want := "Scan: " + parseErr.Error(); err.Error() != want { + t.Errorf("Scan(%v) message = %q, want %q", tc.src, err.Error(), want) + } + }) + } +} From cbc405103de25fd7586655723b10f695839363ed Mon Sep 17 00:00:00 2001 From: Januth Nimnal Date: Tue, 29 Sep 2026 00:18:01 +0530 Subject: [PATCH 2/2] Re-run CI checks