1
0
Fork 1
mirror of https://github.com/git-pkgs/proxy.git synced 2026-06-02 16:48:16 -04:00
pkg-proxy/internal/server/gradle_cache_eviction_test.go
Mati Kepa 31a9ca75b2
add Gradle Build Cache support with handler and tests (#87)
* add Gradle Build Cache support with handler and tests

* linting issue

* MR Suggestions: Add Gradle HTTP Build Cache configuration to README

* implement  minor stuff: Refactor Gradle handler to remove unnecessary URL parameter and update related tests

Co-authored-by: Copilot <copilot@github.com>

* Add Gradle build cache configuration and eviction support

- Introduced configuration options for Gradle build cache in config files and documentation.
- Implemented read-only mode and upload size limits for the Gradle build cache.
- Added cache eviction logic based on age and size, with corresponding tests.
- Enhanced storage interfaces to support listing objects by prefix.

* implement minor stuff: Refactor Gradle handler to remove unnecessary URL parameter and update related tests

* last finding fix

* fix tests and implement PR suggestions

Co-authored-by: Copilot <copilot@github.com>

* unify path

---------

Co-authored-by: Mateusz (Mati) Kepa <m.kepa@sportradar.com>
Co-authored-by: Copilot <copilot@github.com>
2026-05-04 11:15:16 +01:00

138 lines
4 KiB
Go

package server
import (
"bytes"
"context"
"io"
"strings"
"testing"
"time"
"github.com/git-pkgs/proxy/internal/storage"
)
type fakeGradleCacheStore struct {
objects map[string]storage.ObjectInfo
}
func newFakeGradleCacheStore(objects []storage.ObjectInfo) *fakeGradleCacheStore {
m := make(map[string]storage.ObjectInfo, len(objects))
for _, obj := range objects {
m[obj.Path] = obj
}
return &fakeGradleCacheStore{objects: m}
}
func (s *fakeGradleCacheStore) Store(_ context.Context, path string, r io.Reader) (int64, string, error) {
data, _ := io.ReadAll(r)
s.objects[path] = storage.ObjectInfo{Path: path, Size: int64(len(data)), ModTime: time.Now()}
return int64(len(data)), "", nil
}
func (s *fakeGradleCacheStore) Open(_ context.Context, path string) (io.ReadCloser, error) {
obj, ok := s.objects[path]
if !ok {
return nil, storage.ErrNotFound
}
return io.NopCloser(bytes.NewReader(make([]byte, obj.Size))), nil
}
func (s *fakeGradleCacheStore) Exists(_ context.Context, path string) (bool, error) {
_, ok := s.objects[path]
return ok, nil
}
func (s *fakeGradleCacheStore) Delete(_ context.Context, path string) error {
delete(s.objects, path)
return nil
}
func (s *fakeGradleCacheStore) Size(_ context.Context, path string) (int64, error) {
obj, ok := s.objects[path]
if !ok {
return 0, storage.ErrNotFound
}
return obj.Size, nil
}
func (s *fakeGradleCacheStore) SignedURL(_ context.Context, _ string, _ time.Duration) (string, error) {
return "", storage.ErrSignedURLUnsupported
}
func (s *fakeGradleCacheStore) UsedSpace(_ context.Context) (int64, error) {
var total int64
for _, obj := range s.objects {
total += obj.Size
}
return total, nil
}
func (s *fakeGradleCacheStore) URL() string { return "mem://" }
func (s *fakeGradleCacheStore) Close() error { return nil }
func (s *fakeGradleCacheStore) ListPrefix(_ context.Context, prefix string) ([]storage.ObjectInfo, error) {
objects := make([]storage.ObjectInfo, 0)
for _, obj := range s.objects {
if strings.HasPrefix(obj.Path, prefix) {
objects = append(objects, obj)
}
}
return objects, nil
}
func TestSweepGradleBuildCache_MaxAge(t *testing.T) {
now := time.Date(2026, 4, 27, 12, 0, 0, 0, time.UTC)
store := newFakeGradleCacheStore([]storage.ObjectInfo{
{Path: "_gradle/http-build-cache/old", Size: 10, ModTime: now.Add(-48 * time.Hour)},
{Path: "_gradle/http-build-cache/new", Size: 10, ModTime: now.Add(-2 * time.Hour)},
})
deleted, freed, err := sweepGradleBuildCache(context.Background(), store, store, 24*time.Hour, 0, now)
if err != nil {
t.Fatalf("sweepGradleBuildCache() error = %v", err)
}
if deleted != 1 {
t.Fatalf("deleted entries = %d, want 1", deleted)
}
if freed != 10 {
t.Fatalf("freed bytes = %d, want 10", freed)
}
if _, ok := store.objects["_gradle/http-build-cache/old"]; ok {
t.Fatal("old entry was not deleted")
}
if _, ok := store.objects["_gradle/http-build-cache/new"]; !ok {
t.Fatal("new entry should remain")
}
}
func TestSweepGradleBuildCache_MaxSizeOldestFirst(t *testing.T) {
now := time.Date(2026, 4, 27, 12, 0, 0, 0, time.UTC)
store := newFakeGradleCacheStore([]storage.ObjectInfo{
{Path: "_gradle/http-build-cache/a", Size: 5, ModTime: now.Add(-3 * time.Hour)},
{Path: "_gradle/http-build-cache/b", Size: 5, ModTime: now.Add(-2 * time.Hour)},
{Path: "_gradle/http-build-cache/c", Size: 5, ModTime: now.Add(-1 * time.Hour)},
})
deleted, freed, err := sweepGradleBuildCache(context.Background(), store, store, 0, 10, now)
if err != nil {
t.Fatalf("sweepGradleBuildCache() error = %v", err)
}
if deleted != 1 {
t.Fatalf("deleted entries = %d, want 1", deleted)
}
if freed != 5 {
t.Fatalf("freed bytes = %d, want 5", freed)
}
if _, ok := store.objects["_gradle/http-build-cache/a"]; ok {
t.Fatal("oldest entry was not deleted")
}
if _, ok := store.objects["_gradle/http-build-cache/b"]; !ok {
t.Fatal("middle entry should remain")
}
if _, ok := store.objects["_gradle/http-build-cache/c"]; !ok {
t.Fatal("newest entry should remain")
}
}