Files
mcr/internal/oci/routes_test.go
Kyle Isom dddc66f31b Phases 5, 6, 8: OCI pull/push paths and admin REST API
Phase 5 (OCI pull): internal/oci/ package with manifest GET/HEAD by
tag/digest, blob GET/HEAD with repo membership check, tag listing with
OCI pagination, catalog listing. Multi-segment repo names via
parseOCIPath() right-split routing. DB query layer in
internal/db/repository.go.

Phase 6 (OCI push): blob uploads (monolithic and chunked) with
uploadManager tracking in-progress BlobWriters, manifest push
implementing full ARCHITECTURE.md §5 flow in a single SQLite
transaction (create repo, upsert manifest, populate manifest_blobs,
atomic tag move). Digest verification on both blob commit and manifest
push-by-digest.

Phase 8 (admin REST): /v1 endpoints for auth (login/logout/health),
repository management (list/detail/delete), policy CRUD with engine
reload, audit log listing with filters, GC trigger/status stubs.
RequireAdmin middleware, platform-standard error format.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-19 18:25:18 -07:00

142 lines
3.5 KiB
Go

package oci
import "testing"
func TestParseOCIPath(t *testing.T) {
tests := []struct {
name string
path string
want ociPathInfo
wantOK bool
}{
{
name: "simple repo manifest by tag",
path: "myrepo/manifests/latest",
want: ociPathInfo{name: "myrepo", kind: "manifests", reference: "latest"},
wantOK: true,
},
{
name: "multi-segment repo manifest by tag",
path: "org/team/app/manifests/v1.0",
want: ociPathInfo{name: "org/team/app", kind: "manifests", reference: "v1.0"},
wantOK: true,
},
{
name: "manifest by digest",
path: "myrepo/manifests/sha256:abc123def456",
want: ociPathInfo{name: "myrepo", kind: "manifests", reference: "sha256:abc123def456"},
wantOK: true,
},
{
name: "simple repo blob",
path: "myrepo/blobs/sha256:abc123",
want: ociPathInfo{name: "myrepo", kind: "blobs", reference: "sha256:abc123"},
wantOK: true,
},
{
name: "multi-segment repo blob",
path: "org/team/app/blobs/sha256:abc123",
want: ociPathInfo{name: "org/team/app", kind: "blobs", reference: "sha256:abc123"},
wantOK: true,
},
{
name: "simple repo tags list",
path: "myrepo/tags/list",
want: ociPathInfo{name: "myrepo", kind: "tags", reference: "list"},
wantOK: true,
},
{
name: "multi-segment repo tags list",
path: "org/app/tags/list",
want: ociPathInfo{name: "org/app", kind: "tags", reference: "list"},
wantOK: true,
},
{
name: "empty path",
path: "",
wantOK: false,
},
{
name: "just repo name",
path: "myrepo",
wantOK: false,
},
{
name: "unknown operation",
path: "myrepo/unknown/ref",
wantOK: false,
},
{
name: "manifests with no ref",
path: "myrepo/manifests/",
wantOK: false,
},
{
name: "blobs with no digest",
path: "myrepo/blobs/",
wantOK: false,
},
{
name: "tags without list suffix",
path: "myrepo/tags/something",
wantOK: false,
},
{
name: "no repo name before manifests",
path: "/manifests/latest",
wantOK: false,
},
{
name: "upload initiate (trailing slash)",
path: "myrepo/blobs/uploads/",
want: ociPathInfo{name: "myrepo", kind: "uploads", reference: ""},
wantOK: true,
},
{
name: "upload initiate (no trailing slash)",
path: "myrepo/blobs/uploads",
want: ociPathInfo{name: "myrepo", kind: "uploads", reference: ""},
wantOK: true,
},
{
name: "upload with uuid",
path: "myrepo/blobs/uploads/abc-123-def",
want: ociPathInfo{name: "myrepo", kind: "uploads", reference: "abc-123-def"},
wantOK: true,
},
{
name: "multi-segment repo upload",
path: "org/team/app/blobs/uploads/uuid-456",
want: ociPathInfo{name: "org/team/app", kind: "uploads", reference: "uuid-456"},
wantOK: true,
},
{
name: "multi-segment repo upload initiate",
path: "org/team/app/blobs/uploads/",
want: ociPathInfo{name: "org/team/app", kind: "uploads", reference: ""},
wantOK: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, ok := parseOCIPath(tt.path)
if ok != tt.wantOK {
t.Fatalf("parseOCIPath(%q) ok = %v, want %v", tt.path, ok, tt.wantOK)
}
if !ok {
return
}
if got.name != tt.want.name {
t.Errorf("name: got %q, want %q", got.name, tt.want.name)
}
if got.kind != tt.want.kind {
t.Errorf("kind: got %q, want %q", got.kind, tt.want.kind)
}
if got.reference != tt.want.reference {
t.Errorf("reference: got %q, want %q", got.reference, tt.want.reference)
}
})
}
}