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>
194 lines
5.4 KiB
Go
194 lines
5.4 KiB
Go
package oci
|
|
|
|
import (
|
|
"encoding/json"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"testing"
|
|
)
|
|
|
|
func TestManifestDeleteByDigest(t *testing.T) {
|
|
fdb := newFakeDB()
|
|
fdb.addRepo("myrepo", 1)
|
|
content := []byte(`{"schemaVersion":2}`)
|
|
fdb.addManifest(1, "latest", "sha256:aaaa", "application/vnd.oci.image.manifest.v1+json", content)
|
|
|
|
h := NewHandler(fdb, newFakeBlobs(), allowAll(), nil, nil)
|
|
router := testRouter(h)
|
|
|
|
req := authedRequest("DELETE", "/v2/myrepo/manifests/sha256:aaaa", nil)
|
|
rr := httptest.NewRecorder()
|
|
router.ServeHTTP(rr, req)
|
|
|
|
if rr.Code != http.StatusAccepted {
|
|
t.Fatalf("status: got %d, want %d", rr.Code, http.StatusAccepted)
|
|
}
|
|
|
|
// Verify manifest was deleted from fakeDB.
|
|
_, err := fdb.GetManifestByDigest(1, "sha256:aaaa")
|
|
if err == nil {
|
|
t.Fatal("manifest should have been deleted")
|
|
}
|
|
}
|
|
|
|
func TestManifestDeleteByTagUnsupported(t *testing.T) {
|
|
fdb := newFakeDB()
|
|
fdb.addRepo("myrepo", 1)
|
|
content := []byte(`{"schemaVersion":2}`)
|
|
fdb.addManifest(1, "latest", "sha256:aaaa", "application/vnd.oci.image.manifest.v1+json", content)
|
|
|
|
h := NewHandler(fdb, newFakeBlobs(), allowAll(), nil, nil)
|
|
router := testRouter(h)
|
|
|
|
req := authedRequest("DELETE", "/v2/myrepo/manifests/latest", nil)
|
|
rr := httptest.NewRecorder()
|
|
router.ServeHTTP(rr, req)
|
|
|
|
if rr.Code != http.StatusMethodNotAllowed {
|
|
t.Fatalf("status: got %d, want %d", rr.Code, http.StatusMethodNotAllowed)
|
|
}
|
|
|
|
var body ociErrorResponse
|
|
if err := json.NewDecoder(rr.Body).Decode(&body); err != nil {
|
|
t.Fatalf("decode error: %v", err)
|
|
}
|
|
if len(body.Errors) != 1 || body.Errors[0].Code != "UNSUPPORTED" {
|
|
t.Fatalf("error code: got %+v, want UNSUPPORTED", body.Errors)
|
|
}
|
|
}
|
|
|
|
func TestManifestDeleteNotFound(t *testing.T) {
|
|
fdb := newFakeDB()
|
|
fdb.addRepo("myrepo", 1)
|
|
// No manifests added.
|
|
|
|
h := NewHandler(fdb, newFakeBlobs(), allowAll(), nil, nil)
|
|
router := testRouter(h)
|
|
|
|
req := authedRequest("DELETE", "/v2/myrepo/manifests/sha256:nonexistent", nil)
|
|
rr := httptest.NewRecorder()
|
|
router.ServeHTTP(rr, req)
|
|
|
|
if rr.Code != http.StatusNotFound {
|
|
t.Fatalf("status: got %d, want %d", rr.Code, http.StatusNotFound)
|
|
}
|
|
|
|
var body ociErrorResponse
|
|
if err := json.NewDecoder(rr.Body).Decode(&body); err != nil {
|
|
t.Fatalf("decode error: %v", err)
|
|
}
|
|
if len(body.Errors) != 1 || body.Errors[0].Code != "MANIFEST_UNKNOWN" {
|
|
t.Fatalf("error code: got %+v, want MANIFEST_UNKNOWN", body.Errors)
|
|
}
|
|
}
|
|
|
|
func TestManifestDeleteRepoNotFound(t *testing.T) {
|
|
fdb := newFakeDB()
|
|
|
|
h := NewHandler(fdb, newFakeBlobs(), allowAll(), nil, nil)
|
|
router := testRouter(h)
|
|
|
|
req := authedRequest("DELETE", "/v2/nosuchrepo/manifests/sha256:aaaa", nil)
|
|
rr := httptest.NewRecorder()
|
|
router.ServeHTTP(rr, req)
|
|
|
|
if rr.Code != http.StatusNotFound {
|
|
t.Fatalf("status: got %d, want %d", rr.Code, http.StatusNotFound)
|
|
}
|
|
|
|
var body ociErrorResponse
|
|
if err := json.NewDecoder(rr.Body).Decode(&body); err != nil {
|
|
t.Fatalf("decode error: %v", err)
|
|
}
|
|
if len(body.Errors) != 1 || body.Errors[0].Code != "NAME_UNKNOWN" {
|
|
t.Fatalf("error code: got %+v, want NAME_UNKNOWN", body.Errors)
|
|
}
|
|
}
|
|
|
|
func TestManifestDeleteCascadesTag(t *testing.T) {
|
|
fdb := newFakeDB()
|
|
fdb.addRepo("myrepo", 1)
|
|
content := []byte(`{"schemaVersion":2}`)
|
|
fdb.addManifest(1, "latest", "sha256:aaaa", "application/vnd.oci.image.manifest.v1+json", content)
|
|
|
|
h := NewHandler(fdb, newFakeBlobs(), allowAll(), nil, nil)
|
|
router := testRouter(h)
|
|
|
|
req := authedRequest("DELETE", "/v2/myrepo/manifests/sha256:aaaa", nil)
|
|
rr := httptest.NewRecorder()
|
|
router.ServeHTTP(rr, req)
|
|
|
|
if rr.Code != http.StatusAccepted {
|
|
t.Fatalf("status: got %d, want %d", rr.Code, http.StatusAccepted)
|
|
}
|
|
|
|
// The tag "latest" should also be gone (cascading delete in fakeDB).
|
|
_, err := fdb.GetManifestByTag(1, "latest")
|
|
if err == nil {
|
|
t.Fatal("tag 'latest' should have been cascaded on manifest delete")
|
|
}
|
|
}
|
|
|
|
func TestBlobDelete(t *testing.T) {
|
|
fdb := newFakeDB()
|
|
fdb.addRepo("myrepo", 1)
|
|
fdb.addBlob(1, "sha256:b1")
|
|
|
|
h := NewHandler(fdb, newFakeBlobs(), allowAll(), nil, nil)
|
|
router := testRouter(h)
|
|
|
|
req := authedRequest("DELETE", "/v2/myrepo/blobs/sha256:b1", nil)
|
|
rr := httptest.NewRecorder()
|
|
router.ServeHTTP(rr, req)
|
|
|
|
if rr.Code != http.StatusAccepted {
|
|
t.Fatalf("status: got %d, want %d", rr.Code, http.StatusAccepted)
|
|
}
|
|
|
|
// Blob should be removed from repo in fakeDB.
|
|
exists, _ := fdb.BlobExistsInRepo(1, "sha256:b1")
|
|
if exists {
|
|
t.Fatal("blob should have been removed from repo")
|
|
}
|
|
}
|
|
|
|
func TestBlobDeleteNotInRepo(t *testing.T) {
|
|
fdb := newFakeDB()
|
|
fdb.addRepo("myrepo", 1)
|
|
// Blob not added to this repo.
|
|
|
|
h := NewHandler(fdb, newFakeBlobs(), allowAll(), nil, nil)
|
|
router := testRouter(h)
|
|
|
|
req := authedRequest("DELETE", "/v2/myrepo/blobs/sha256:nonexistent", nil)
|
|
rr := httptest.NewRecorder()
|
|
router.ServeHTTP(rr, req)
|
|
|
|
if rr.Code != http.StatusNotFound {
|
|
t.Fatalf("status: got %d, want %d", rr.Code, http.StatusNotFound)
|
|
}
|
|
|
|
var body ociErrorResponse
|
|
if err := json.NewDecoder(rr.Body).Decode(&body); err != nil {
|
|
t.Fatalf("decode error: %v", err)
|
|
}
|
|
if len(body.Errors) != 1 || body.Errors[0].Code != "BLOB_UNKNOWN" {
|
|
t.Fatalf("error code: got %+v, want BLOB_UNKNOWN", body.Errors)
|
|
}
|
|
}
|
|
|
|
func TestBlobDeleteRepoNotFound(t *testing.T) {
|
|
fdb := newFakeDB()
|
|
|
|
h := NewHandler(fdb, newFakeBlobs(), allowAll(), nil, nil)
|
|
router := testRouter(h)
|
|
|
|
req := authedRequest("DELETE", "/v2/nosuchrepo/blobs/sha256:b1", nil)
|
|
rr := httptest.NewRecorder()
|
|
router.ServeHTTP(rr, req)
|
|
|
|
if rr.Code != http.StatusNotFound {
|
|
t.Fatalf("status: got %d, want %d", rr.Code, http.StatusNotFound)
|
|
}
|
|
}
|