Every 500 response in the OCI package silently discarded the actual error, making production debugging impossible. Add slog.Error before each 500 response with the error and relevant context (repo, digest, tag, uuid). Add slog.Info for state-mutating successes (manifest push, blob upload complete, deletions). Logger is injected into the OCI Handler via constructor, falling back to slog.Default() if nil. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
125 lines
3.2 KiB
Go
125 lines
3.2 KiB
Go
package oci
|
|
|
|
import (
|
|
"encoding/json"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"testing"
|
|
)
|
|
|
|
func TestCatalog(t *testing.T) {
|
|
fdb := newFakeDB()
|
|
fdb.addRepo("alpha/lib", 1)
|
|
fdb.addRepo("bravo/svc", 2)
|
|
fdb.addRepo("charlie/app", 3)
|
|
|
|
h := NewHandler(fdb, newFakeBlobs(), allowAll(), nil, nil)
|
|
router := testRouter(h)
|
|
|
|
req := authedRequest("GET", "/v2/_catalog", nil)
|
|
rr := httptest.NewRecorder()
|
|
router.ServeHTTP(rr, req)
|
|
|
|
if rr.Code != http.StatusOK {
|
|
t.Fatalf("status: got %d, want %d", rr.Code, http.StatusOK)
|
|
}
|
|
|
|
var body catalogResponse
|
|
if err := json.NewDecoder(rr.Body).Decode(&body); err != nil {
|
|
t.Fatalf("decode body: %v", err)
|
|
}
|
|
|
|
want := []string{"alpha/lib", "bravo/svc", "charlie/app"}
|
|
if len(body.Repositories) != len(want) {
|
|
t.Fatalf("repos count: got %d, want %d", len(body.Repositories), len(want))
|
|
}
|
|
for i, r := range body.Repositories {
|
|
if r != want[i] {
|
|
t.Fatalf("repos[%d]: got %q, want %q", i, r, want[i])
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestCatalogPagination(t *testing.T) {
|
|
fdb := newFakeDB()
|
|
fdb.addRepo("alpha/lib", 1)
|
|
fdb.addRepo("bravo/svc", 2)
|
|
fdb.addRepo("charlie/app", 3)
|
|
|
|
h := NewHandler(fdb, newFakeBlobs(), allowAll(), nil, nil)
|
|
router := testRouter(h)
|
|
|
|
// First page: n=2.
|
|
req := authedRequest("GET", "/v2/_catalog?n=2", nil)
|
|
rr := httptest.NewRecorder()
|
|
router.ServeHTTP(rr, req)
|
|
|
|
if rr.Code != http.StatusOK {
|
|
t.Fatalf("status: got %d, want %d", rr.Code, http.StatusOK)
|
|
}
|
|
|
|
var body catalogResponse
|
|
if err := json.NewDecoder(rr.Body).Decode(&body); err != nil {
|
|
t.Fatalf("decode body: %v", err)
|
|
}
|
|
if len(body.Repositories) != 2 {
|
|
t.Fatalf("page 1 count: got %d, want 2", len(body.Repositories))
|
|
}
|
|
if body.Repositories[0] != "alpha/lib" || body.Repositories[1] != "bravo/svc" {
|
|
t.Fatalf("page 1: got %v", body.Repositories)
|
|
}
|
|
|
|
// Check Link header.
|
|
link := rr.Header().Get("Link")
|
|
if link == "" {
|
|
t.Fatal("expected Link header for pagination")
|
|
}
|
|
|
|
// Second page: n=2, last=bravo/svc.
|
|
req = authedRequest("GET", "/v2/_catalog?n=2&last=bravo/svc", nil)
|
|
rr = httptest.NewRecorder()
|
|
router.ServeHTTP(rr, req)
|
|
|
|
if rr.Code != http.StatusOK {
|
|
t.Fatalf("page 2 status: got %d, want %d", rr.Code, http.StatusOK)
|
|
}
|
|
|
|
if err := json.NewDecoder(rr.Body).Decode(&body); err != nil {
|
|
t.Fatalf("decode page 2: %v", err)
|
|
}
|
|
if len(body.Repositories) != 1 {
|
|
t.Fatalf("page 2 count: got %d, want 1", len(body.Repositories))
|
|
}
|
|
if body.Repositories[0] != "charlie/app" {
|
|
t.Fatalf("page 2: got %v", body.Repositories)
|
|
}
|
|
|
|
// No Link header on last page.
|
|
if link := rr.Header().Get("Link"); link != "" {
|
|
t.Fatalf("expected no Link header on last page, got %q", link)
|
|
}
|
|
}
|
|
|
|
func TestCatalogEmpty(t *testing.T) {
|
|
fdb := newFakeDB()
|
|
|
|
h := NewHandler(fdb, newFakeBlobs(), allowAll(), nil, nil)
|
|
router := testRouter(h)
|
|
|
|
req := authedRequest("GET", "/v2/_catalog", nil)
|
|
rr := httptest.NewRecorder()
|
|
router.ServeHTTP(rr, req)
|
|
|
|
if rr.Code != http.StatusOK {
|
|
t.Fatalf("status: got %d, want %d", rr.Code, http.StatusOK)
|
|
}
|
|
|
|
var body catalogResponse
|
|
if err := json.NewDecoder(rr.Body).Decode(&body); err != nil {
|
|
t.Fatalf("decode body: %v", err)
|
|
}
|
|
if len(body.Repositories) != 0 {
|
|
t.Fatalf("expected empty repositories, got %v", body.Repositories)
|
|
}
|
|
}
|