loading up the forgejo repo on tangled to test page performance

fix: package_blob.has_blake2b may be null (#7520)

- When looking for an existing blob, has_blake2b will be null when it was created prior to v26 migration in v11, when the field was introduced.
- Add unit test and minimal refactoring to load fixtures. The AddFixture function should not be where it currently is because it cannot be used by some packages (circular import). But that's a refactor that needs to be elsewhere for backporting purposes.

Fixes https://codeberg.org/forgejo/forgejo/issues/7519
Co-authored-by: Earl Warren <contact@earl-warren.org>
Co-committed-by: Earl Warren <contact@earl-warren.org>

authored by Earl Warren Earl Warren and committed by Earl Warren eb85681b eb3feaad

Changed files
+126 -17
models
fixtures
TestPackagesGetOrInsertBlob
packages
+17
models/fixtures/TestPackagesGetOrInsertBlob/package_blob.yml
··· 1 + - 2 + id: 1 3 + size: 10 4 + hash_md5: HASHMD5_1 5 + hash_sha1: HASHSHA1_1 6 + hash_sha256: HASHSHA256_1 7 + hash_sha512: HASHSHA512_1 8 + hash_blake2b: HASHBLAKE2B_1 9 + created_unix: 946687980 10 + - 11 + id: 2 12 + size: 20 13 + hash_md5: HASHMD5_2 14 + hash_sha1: HASHSHA1_2 15 + hash_sha256: HASHSHA256_2 16 + hash_sha512: HASHSHA512_2 17 + created_unix: 946687980
+31
models/packages/main_test.go
··· 1 + // Copyright 2020 The Gitea Authors. All rights reserved. 2 + // SPDX-License-Identifier: MIT 3 + 4 + package packages 5 + 6 + import ( 7 + "path/filepath" 8 + "testing" 9 + 10 + "forgejo.org/models/unittest" 11 + "forgejo.org/modules/setting" 12 + 13 + _ "forgejo.org/models" 14 + _ "forgejo.org/models/actions" 15 + _ "forgejo.org/models/activities" 16 + _ "forgejo.org/models/forgefed" 17 + ) 18 + 19 + func AddFixtures(dirs ...string) func() { 20 + return unittest.OverrideFixtures( 21 + unittest.FixturesOptions{ 22 + Dir: filepath.Join(setting.AppWorkPath, "models/fixtures/"), 23 + Base: setting.AppWorkPath, 24 + Dirs: dirs, 25 + }, 26 + ) 27 + } 28 + 29 + func TestMain(m *testing.M) { 30 + unittest.MainTest(m) 31 + }
+13 -8
models/packages/package_blob.go
··· 44 44 45 45 existing := &PackageBlob{} 46 46 47 - has, err := e.Where(builder.Eq{ 48 - "size": pb.Size, 49 - "hash_md5": pb.HashMD5, 50 - "hash_sha1": pb.HashSHA1, 51 - "hash_sha256": pb.HashSHA256, 52 - "hash_sha512": pb.HashSHA512, 53 - "hash_blake2b": pb.HashBlake2b, 54 - }).Get(existing) 47 + has, err := e.Where(builder.And( 48 + builder.Eq{ 49 + "size": pb.Size, 50 + "hash_md5": pb.HashMD5, 51 + "hash_sha1": pb.HashSHA1, 52 + "hash_sha256": pb.HashSHA256, 53 + "hash_sha512": pb.HashSHA512, 54 + }, 55 + builder.Or( 56 + builder.Eq{"hash_blake2b": pb.HashBlake2b}, 57 + builder.IsNull{"hash_blake2b"}, 58 + ), 59 + )).Get(existing) 55 60 if err != nil { 56 61 return nil, false, err 57 62 }
+65
models/packages/package_blob_test.go
··· 1 + // Copyright 2025 The Forgejo Authors. 2 + // SPDX-License-Identifier: GPL-3.0-or-later 3 + 4 + package packages 5 + 6 + import ( 7 + "testing" 8 + 9 + "forgejo.org/models/unittest" 10 + 11 + "github.com/stretchr/testify/assert" 12 + "github.com/stretchr/testify/require" 13 + ) 14 + 15 + func TestPackagesGetOrInsertBlob(t *testing.T) { 16 + defer AddFixtures("models/fixtures/TestPackagesGetOrInsertBlob/")() 17 + require.NoError(t, unittest.PrepareTestDatabase()) 18 + 19 + blake2bIsSet := unittest.AssertExistsAndLoadBean(t, &PackageBlob{ID: 1}) 20 + blake2bNotSet := unittest.AssertExistsAndLoadBean(t, &PackageBlob{ID: 2}) 21 + 22 + var blake2bSetToRandom PackageBlob 23 + blake2bSetToRandom = *blake2bNotSet 24 + blake2bSetToRandom.HashBlake2b = "SOMETHING RANDOM" 25 + 26 + for _, testCase := range []struct { 27 + name string 28 + exists bool 29 + packageBlob *PackageBlob 30 + }{ 31 + { 32 + name: "exists and blake2b is not null in the database", 33 + exists: true, 34 + packageBlob: blake2bIsSet, 35 + }, 36 + { 37 + name: "exists and blake2b is null in the database", 38 + exists: true, 39 + packageBlob: &blake2bSetToRandom, 40 + }, 41 + { 42 + name: "does not exists", 43 + exists: false, 44 + packageBlob: &PackageBlob{ 45 + Size: 30, 46 + HashMD5: "HASHMD5_3", 47 + HashSHA1: "HASHSHA1_3", 48 + HashSHA256: "HASHSHA256_3", 49 + HashSHA512: "HASHSHA512_3", 50 + HashBlake2b: "HASHBLAKE2B_3", 51 + }, 52 + }, 53 + } { 54 + t.Run(testCase.name, func(t *testing.T) { 55 + found, has, _ := GetOrInsertBlob(t.Context(), testCase.packageBlob) 56 + assert.Equal(t, testCase.exists, has) 57 + require.NotNil(t, found) 58 + if testCase.exists { 59 + assert.Equal(t, found.ID, testCase.packageBlob.ID) 60 + } else { 61 + unittest.BeanExists(t, &PackageBlob{ID: found.ID}) 62 + } 63 + }) 64 + } 65 + }
-9
models/packages/package_test.go
··· 13 13 "forgejo.org/models/unittest" 14 14 user_model "forgejo.org/models/user" 15 15 16 - _ "forgejo.org/models" 17 - _ "forgejo.org/models/actions" 18 - _ "forgejo.org/models/activities" 19 - _ "forgejo.org/models/forgefed" 20 - 21 16 "github.com/stretchr/testify/require" 22 17 ) 23 - 24 - func TestMain(m *testing.M) { 25 - unittest.MainTest(m) 26 - } 27 18 28 19 func prepareExamplePackage(t *testing.T) *packages_model.Package { 29 20 require.NoError(t, unittest.PrepareTestDatabase())