mirror of
https://github.com/git-pkgs/proxy.git
synced 2026-11-09 07:39:19 -05:00
139 lines
4.2 KiB
Go
139 lines
4.2 KiB
Go
package server
|
|
|
|
import (
|
|
"database/sql"
|
|
"encoding/json"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/git-pkgs/proxy/internal/database"
|
|
)
|
|
|
|
func seedCachedArtifact(t *testing.T, db *database.DB, ecosystem, name, version string, size, hits int64) {
|
|
t.Helper()
|
|
|
|
pkgPURL := "pkg:" + ecosystem + "/" + name
|
|
versionPURL := pkgPURL + "@" + version
|
|
|
|
if err := db.UpsertPackage(&database.Package{PURL: pkgPURL, Ecosystem: ecosystem, Name: name}); err != nil {
|
|
t.Fatalf("UpsertPackage: %v", err)
|
|
}
|
|
if err := db.UpsertVersion(&database.Version{PURL: versionPURL, PackagePURL: pkgPURL}); err != nil {
|
|
t.Fatalf("UpsertVersion: %v", err)
|
|
}
|
|
if err := db.UpsertArtifact(&database.Artifact{
|
|
VersionPURL: versionPURL,
|
|
Filename: name + "-" + version + ".tgz",
|
|
UpstreamURL: "https://example.test/" + name,
|
|
StoragePath: sql.NullString{String: "objects/" + name, Valid: true},
|
|
Size: sql.NullInt64{Int64: size, Valid: true},
|
|
FetchedAt: sql.NullTime{Time: time.Now(), Valid: true},
|
|
HitCount: hits,
|
|
}); err != nil {
|
|
t.Fatalf("UpsertArtifact: %v", err)
|
|
}
|
|
}
|
|
|
|
func getStats(t *testing.T, ts *testServer) StatsResponse {
|
|
t.Helper()
|
|
|
|
w := httptest.NewRecorder()
|
|
ts.handler.ServeHTTP(w, httptest.NewRequest("GET", "/stats", nil))
|
|
if w.Code != http.StatusOK {
|
|
t.Fatalf("GET /stats = %d, want 200", w.Code)
|
|
}
|
|
|
|
var stats StatsResponse
|
|
if err := json.NewDecoder(w.Body).Decode(&stats); err != nil {
|
|
t.Fatalf("decode: %v", err)
|
|
}
|
|
return stats
|
|
}
|
|
|
|
func TestStatsReportsEcosystemBreakdown(t *testing.T) {
|
|
ts := newTestServer(t)
|
|
defer ts.close()
|
|
|
|
seedCachedArtifact(t, ts.db, "npm", "lodash", "4.17.21", 1000, 3)
|
|
seedCachedArtifact(t, ts.db, "cargo", "serde", "1.0.0", 500, 2)
|
|
|
|
stats := getStats(t, ts)
|
|
|
|
if stats.DownloadedBytes != 4000 {
|
|
t.Errorf("downloaded_bytes = %d, want 4000", stats.DownloadedBytes)
|
|
}
|
|
if stats.Downloads != 5 {
|
|
t.Errorf("downloads = %d, want 5", stats.Downloads)
|
|
}
|
|
if stats.DownloadedBytesHuman == "" {
|
|
t.Error("downloaded is empty; the human-readable total is part of the shape")
|
|
}
|
|
if stats.StatsUnavailable {
|
|
t.Error("stats_unavailable set on a healthy aggregation")
|
|
}
|
|
|
|
byEcosystem := make(map[string]EcosystemStatsEntry, len(stats.Ecosystems))
|
|
for _, e := range stats.Ecosystems {
|
|
byEcosystem[e.Ecosystem] = e
|
|
}
|
|
npm, ok := byEcosystem["npm"]
|
|
if !ok {
|
|
t.Fatalf("no npm row in %v", byEcosystem)
|
|
}
|
|
if npm.DownloadedBytes != 3000 || npm.Downloads != 3 || npm.CacheSize != 1000 {
|
|
t.Errorf("npm row = %+v, want 3000 bytes over 3 downloads and 1000 cached", npm)
|
|
}
|
|
}
|
|
|
|
// A failed aggregation with nothing to fall back on must not read as an idle
|
|
// proxy: the totals are zero either way, so the flag is the only thing
|
|
// telling a consumer which of the two it is looking at.
|
|
func TestStatsFlagsUnavailableFigures(t *testing.T) {
|
|
ts := newTestServer(t)
|
|
defer ts.close()
|
|
|
|
// GetEcosystemStats counts packages first; the artifact count and cache
|
|
// size the endpoint already reported do not touch that table.
|
|
if _, err := ts.db.Exec(`DROP TABLE packages`); err != nil {
|
|
t.Fatalf("DROP TABLE packages: %v", err)
|
|
}
|
|
|
|
stats := getStats(t, ts)
|
|
|
|
if !stats.StatsUnavailable {
|
|
t.Error("stats_unavailable not set after the aggregation failed with no snapshot")
|
|
}
|
|
if len(stats.Ecosystems) != 0 {
|
|
t.Errorf("ecosystems = %v, want empty", stats.Ecosystems)
|
|
}
|
|
if stats.TotalSizeHuman == "" {
|
|
t.Error("the rest of the response must still be served")
|
|
}
|
|
}
|
|
|
|
// A retained snapshot is served on with no flag: the figures are stale, not
|
|
// absent, and the page is where staleness is surfaced to a human.
|
|
func TestStatsServesRetainedSnapshot(t *testing.T) {
|
|
ts := newTestServer(t)
|
|
defer ts.close()
|
|
|
|
seedCachedArtifact(t, ts.db, "npm", "lodash", "4.17.21", 1000, 3)
|
|
if got := getStats(t, ts).DownloadedBytes; got != 3000 {
|
|
t.Fatalf("downloaded_bytes = %d, want 3000 before the failure", got)
|
|
}
|
|
|
|
if _, err := ts.db.Exec(`DROP TABLE packages`); err != nil {
|
|
t.Fatalf("DROP TABLE packages: %v", err)
|
|
}
|
|
ts.server.ecoStats.lastAt = time.Time{}
|
|
|
|
stats := getStats(t, ts)
|
|
if stats.StatsUnavailable {
|
|
t.Error("stats_unavailable set although a snapshot was retained")
|
|
}
|
|
if stats.DownloadedBytes != 3000 {
|
|
t.Errorf("downloaded_bytes = %d, want the retained 3000", stats.DownloadedBytes)
|
|
}
|
|
}
|