diff --git a/auth_test.go b/auth_test.go index d7629e88..46064daa 100644 --- a/auth_test.go +++ b/auth_test.go @@ -1,8 +1,12 @@ package auth import ( + "bytes" "context" "encoding/json" + "image" + _ "image/jpeg" + _ "image/png" "io" "net" "net/http" @@ -333,7 +337,18 @@ func TestIntegrationAvatar(t *testing.T) { b, err := io.ReadAll(resp.Body) require.NoError(t, err) - assert.Equal(t, 569, len(b)) + + // what the endpoint promises is a decodable image within the resize limit, not a byte count. + // the encoders are the standard library's, so their output moves with the Go release: this + // assertion read 569 bytes until Go 1.27 changed the compressor and produced 507 for the same + // picture, failing a test that nothing had touched + img, format, err := image.Decode(bytes.NewReader(b)) + require.NoError(t, err, "the avatar endpoint has to serve a decodable image") + assert.Equal(t, "png", format) + bounds := img.Bounds() + assert.LessOrEqual(t, bounds.Dx(), 120, "wider than the resize limit") + assert.LessOrEqual(t, bounds.Dy(), 120, "taller than the resize limit") + assert.NotEmpty(t, b) } func TestIntegrationList(t *testing.T) { diff --git a/avatar/avatar_test.go b/avatar/avatar_test.go index ca2ef71f..abe3e998 100644 --- a/avatar/avatar_test.go +++ b/avatar/avatar_test.go @@ -188,7 +188,11 @@ func TestAvatar_PutIdenticon(t *testing.T) { assert.Equal(t, "http://localhost:8080/avatar/b3daa77b4c04a9551b8781d03191fe098f325e67.image", res) fi, err := os.Stat("/tmp/avatars.test/30/b3daa77b4c04a9551b8781d03191fe098f325e67.image") assert.NoError(t, err) - assert.Equal(t, int64(999), fi.Size()) + // the identicon is encoded by the standard library, so its byte count moves with the Go + // release: this read 999 until Go 1.27 changed the encoder. Assert it was written and is a + // decodable image instead + assert.Positive(t, fi.Size()) + assertDecodableImage(t, "/tmp/avatars.test/30/b3daa77b4c04a9551b8781d03191fe098f325e67.image") } @@ -210,9 +214,14 @@ func TestAvatar_PutFailed(t *testing.T) { res, err := p.Put(u, client) require.NoError(t, err) assert.Equal(t, "http://localhost:8080/avatar/a1881c06eec96db9901c7bbfe41c42a3f08e9cb4.image", res) - fi, err := os.Stat("/tmp/avatars.test/84/a1881c06eec96db9901c7bbfe41c42a3f08e9cb4.image") + avatarPath := "/tmp/avatars.test/84/a1881c06eec96db9901c7bbfe41c42a3f08e9cb4.image" + fi, err := os.Stat(avatarPath) require.NoError(t, err) - assert.Equal(t, int64(992), fi.Size()) + // the identicon is encoded by the standard library, so its byte count moves with the Go + // release: this read 992 until Go 1.27 changed the encoder. Assert it was written and is a + // decodable image instead + assert.Positive(t, fi.Size()) + assertDecodableImage(t, avatarPath) } func TestAvatar_PutCapsBodySize(t *testing.T) { @@ -602,3 +611,14 @@ func TestAvatar_Retry(t *testing.T) { assert.Error(t, err) assert.True(t, time.Since(st) >= time.Microsecond*5) } + +// assertDecodableImage checks the file holds an image the standard library can read back, which is +// what an avatar store owes its caller. Byte counts belong to whichever encoder the Go release ships. +func assertDecodableImage(t *testing.T, path string) { + t.Helper() + f, err := os.Open(path) //nolint:gosec // test-controlled path + require.NoError(t, err) + defer f.Close() //nolint:errcheck // read-only handle in a test + _, _, err = image.Decode(f) + require.NoError(t, err, "%s has to be a decodable image", path) +} diff --git a/v2/auth_test.go b/v2/auth_test.go index 326ac3ba..c649702d 100644 --- a/v2/auth_test.go +++ b/v2/auth_test.go @@ -1,8 +1,12 @@ package auth import ( + "bytes" "context" "encoding/json" + "image" + _ "image/jpeg" + _ "image/png" "io" "net" "net/http" @@ -332,7 +336,18 @@ func TestIntegrationAvatar(t *testing.T) { b, err := io.ReadAll(resp.Body) require.NoError(t, err) - assert.Equal(t, 569, len(b)) + + // what the endpoint promises is a decodable image within the resize limit, not a byte count. + // the encoders are the standard library's, so their output moves with the Go release: this + // assertion read 569 bytes until Go 1.27 changed the compressor and produced 507 for the same + // picture, failing a test that nothing had touched + img, format, err := image.Decode(bytes.NewReader(b)) + require.NoError(t, err, "the avatar endpoint has to serve a decodable image") + assert.Equal(t, "png", format) + bounds := img.Bounds() + assert.LessOrEqual(t, bounds.Dx(), 120, "wider than the resize limit") + assert.LessOrEqual(t, bounds.Dy(), 120, "taller than the resize limit") + assert.NotEmpty(t, b) } func TestIntegrationList(t *testing.T) { diff --git a/v2/avatar/avatar_test.go b/v2/avatar/avatar_test.go index 6acc0496..c89a4a28 100644 --- a/v2/avatar/avatar_test.go +++ b/v2/avatar/avatar_test.go @@ -188,7 +188,11 @@ func TestAvatar_PutIdenticon(t *testing.T) { assert.Equal(t, "http://localhost:8080/avatar/b3daa77b4c04a9551b8781d03191fe098f325e67.image", res) fi, err := os.Stat("/tmp/avatars.test/30/b3daa77b4c04a9551b8781d03191fe098f325e67.image") assert.NoError(t, err) - assert.Equal(t, int64(999), fi.Size()) + // the identicon is encoded by the standard library, so its byte count moves with the Go + // release: this read 999 until Go 1.27 changed the encoder. Assert it was written and is a + // decodable image instead + assert.Positive(t, fi.Size()) + assertDecodableImage(t, "/tmp/avatars.test/30/b3daa77b4c04a9551b8781d03191fe098f325e67.image") } @@ -210,9 +214,14 @@ func TestAvatar_PutFailed(t *testing.T) { res, err := p.Put(u, client) require.NoError(t, err) assert.Equal(t, "http://localhost:8080/avatar/a1881c06eec96db9901c7bbfe41c42a3f08e9cb4.image", res) - fi, err := os.Stat("/tmp/avatars.test/84/a1881c06eec96db9901c7bbfe41c42a3f08e9cb4.image") + avatarPath := "/tmp/avatars.test/84/a1881c06eec96db9901c7bbfe41c42a3f08e9cb4.image" + fi, err := os.Stat(avatarPath) require.NoError(t, err) - assert.Equal(t, int64(992), fi.Size()) + // the identicon is encoded by the standard library, so its byte count moves with the Go + // release: this read 992 until Go 1.27 changed the encoder. Assert it was written and is a + // decodable image instead + assert.Positive(t, fi.Size()) + assertDecodableImage(t, avatarPath) } func TestAvatar_PutCapsBodySize(t *testing.T) { @@ -602,3 +611,14 @@ func TestAvatar_Retry(t *testing.T) { assert.Error(t, err) assert.True(t, time.Since(st) >= time.Microsecond*5) } + +// assertDecodableImage checks the file holds an image the standard library can read back, which is +// what an avatar store owes its caller. Byte counts belong to whichever encoder the Go release ships. +func assertDecodableImage(t *testing.T, path string) { + t.Helper() + f, err := os.Open(path) //nolint:gosec // test-controlled path + require.NoError(t, err) + defer f.Close() //nolint:errcheck // read-only handle in a test + _, _, err = image.Decode(f) + require.NoError(t, err, "%s has to be a decodable image", path) +}