diff --git a/pkg/cache/cache.go b/pkg/cache/cache.go index 33968af..beb7b84 100644 --- a/pkg/cache/cache.go +++ b/pkg/cache/cache.go @@ -320,6 +320,22 @@ servePkg: return nil, errors.Wrap(err, errors.ErrCodeStorageFailure, "failed to retrieve just-stored package") } + // Track download count for first-time download (cache miss) + // This ensures download count increments regardless of cache hit/miss + if err := m.metadata.UpdateDownloadCount(ctx, registry, name, version); err != nil { + log.Warn(). + Err(err). + Str("registry", registry). + Str("package", name). + Str("version", version). + Msg("Failed to update download count for newly cached package") + } + + // Track download in analytics if enabled + if m.analytics != nil { + m.trackDownload(registry, name, version, storedPkg.Size) + } + return &CacheEntry{ Package: storedPkg, Data: storedData, diff --git a/pkg/cache/cache_test.go b/pkg/cache/cache_test.go index 7d232a0..44c0073 100644 --- a/pkg/cache/cache_test.go +++ b/pkg/cache/cache_test.go @@ -343,6 +343,7 @@ func TestGet(t *testing.T) { s.On("Put", mock.Anything, "npm/lodash/4.17.21", mock.Anything, mock.Anything).Return(nil) m.On("SavePackage", mock.Anything, mock.Anything).Return(nil) s.On("Get", mock.Anything, "npm/lodash/4.17.21").Return(io.NopCloser(strings.NewReader("upstream data")), nil) + m.On("UpdateDownloadCount", mock.Anything, "npm", "lodash", "4.17.21").Return(nil) }, fetchFunc: func(ctx context.Context) (io.ReadCloser, string, error) { return io.NopCloser(strings.NewReader("upstream data")), "https://registry.npmjs.org/lodash", nil @@ -374,6 +375,7 @@ func TestGet(t *testing.T) { s.On("Put", mock.Anything, "npm/expired-pkg/1.0.0", mock.Anything, mock.Anything).Return(nil) m.On("SavePackage", mock.Anything, mock.Anything).Return(nil) s.On("Get", mock.Anything, "npm/expired-pkg/1.0.0").Return(io.NopCloser(strings.NewReader("refreshed data")), nil) + m.On("UpdateDownloadCount", mock.Anything, "npm", "expired-pkg", "1.0.0").Return(nil) }, fetchFunc: func(ctx context.Context) (io.ReadCloser, string, error) { return io.NopCloser(strings.NewReader("refreshed data")), "https://registry.npmjs.org/expired-pkg", nil @@ -435,6 +437,7 @@ func TestGet(t *testing.T) { m.On("SavePackage", mock.Anything, mock.Anything).Return(nil) // Second Get succeeds (after re-storing) s.On("Get", mock.Anything, "npm/inconsistent/1.0.0").Return(io.NopCloser(strings.NewReader("recovered data")), nil).Once() + m.On("UpdateDownloadCount", mock.Anything, "npm", "inconsistent", "1.0.0").Return(nil) }, fetchFunc: func(ctx context.Context) (io.ReadCloser, string, error) { return io.NopCloser(strings.NewReader("recovered data")), "https://registry.npmjs.org/inconsistent", nil