add tests for original-ref annotation

This commit is contained in:
CamrynCarter
2026-08-13 17:10:52 -07:00
parent e375893381
commit 8224919615
3 changed files with 79 additions and 94 deletions
+60
View File
@@ -251,6 +251,36 @@ func TestApplyDefaultRegistry(t *testing.T) {
}
}
func TestEncodeOriginalChartRef(t *testing.T) {
tests := []struct {
name string
repoURL string
total string
want string
}{
{
name: "repoURL and ref joined with pipe",
repoURL: "https://charts.example.com",
total: "mychart:1.0.0",
want: "https://charts.example.com|mychart:1.0.0",
},
{
name: "empty repoURL still joined with pipe",
repoURL: "",
total: "mychart:1.0.0",
want: "|mychart:1.0.0",
},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
got := encodeOriginalChartRef(tc.repoURL, tc.total)
if got != tc.want {
t.Errorf("got %q, want %q", got, tc.want)
}
})
}
}
func TestRewriteReference(t *testing.T) {
ctx := newTestContext(t)
@@ -499,6 +529,8 @@ func TestStoreFile(t *testing.T) {
t.Fatalf("storeFile: %v", err)
}
assertArtifactInStore(t, s, filepath.Base(tmp.Name()))
// OriginalRefAnnotation must capture the resolved absolute local path.
assertOriginalRefInStore(t, s, filepath.Base(tmp.Name()), tmp.Name())
})
t.Run("HTTP URL stored under basename", func(t *testing.T) {
@@ -508,6 +540,8 @@ func TestStoreFile(t *testing.T) {
t.Fatalf("storeFile: %v", err)
}
assertArtifactInStore(t, s, "script.sh")
// OriginalRefAnnotation must capture the original URL, not the local basename.
assertOriginalRefInStore(t, s, "script.sh", url)
})
t.Run("name override changes stored ref", func(t *testing.T) {
@@ -638,6 +672,9 @@ func TestStoreImage_Rewrite(t *testing.T) {
t.Fatalf("storeImage with rewrite: %v", err)
}
assertArtifactInStore(t, s, "newrepo/img:v2")
// OriginalRefAnnotation must retain the pre-rewrite source reference,
// even though AnnotationRefName/ContainerdImageNameKey were rewritten.
assertOriginalRefInStore(t, s, "newrepo/img:v2", host+"/src/repo:v1")
})
t.Run("rewrite without tag inherits source tag", func(t *testing.T) {
@@ -920,6 +957,29 @@ func TestStoreChart_Rewrite(t *testing.T) {
t.Fatalf("AddChartCmd with rewrite: %v", err)
}
assertArtifactInStore(t, s, "myorg/custom-chart")
// OriginalRefAnnotation must retain the pre-rewrite "repoURL|repo:tag" encoding
// (see encodeOriginalChartRef) regardless of the --rewrite applied above.
wantPrefix := chartTestdataDir + "|"
found := false
if err := s.OCI.Walk(func(_ string, desc ocispec.Descriptor) error {
if strings.Contains(desc.Annotations[ocispec.AnnotationRefName], "myorg/custom-chart") {
original := desc.Annotations[consts.OriginalRefAnnotation]
if !strings.HasPrefix(original, wantPrefix) {
t.Errorf("OriginalRefAnnotation = %q, want prefix %q", original, wantPrefix)
}
if strings.Contains(original, "myorg/custom-chart") {
t.Errorf("OriginalRefAnnotation = %q must not contain the rewritten ref", original)
}
found = true
}
return nil
}); err != nil {
t.Fatalf("walk: %v", err)
}
if !found {
t.Fatal("expected to find rewritten chart artifact in store")
}
}
// seedChartWithImages builds a minimal Helm chart whose helm.sh/images
+19
View File
@@ -532,6 +532,25 @@ func assertAnnotationsInStore(t *testing.T, s *store.Layout, refName, containerd
}
}
// assertOriginalRefInStore walks the store and fails if no descriptor has both
// AnnotationRefName containing refSubstring AND OriginalRefAnnotation == wantOriginalRef.
func assertOriginalRefInStore(t *testing.T, s *store.Layout, refSubstring, wantOriginalRef string) {
t.Helper()
found := false
if err := s.OCI.Walk(func(_ string, desc ocispec.Descriptor) error {
if strings.Contains(desc.Annotations[ocispec.AnnotationRefName], refSubstring) &&
desc.Annotations[consts.OriginalRefAnnotation] == wantOriginalRef {
found = true
}
return nil
}); err != nil {
t.Fatalf("assertOriginalRefInStore walk: %v", err)
}
if !found {
t.Errorf("no artifact with ref containing %q and OriginalRefAnnotation=%q found in store", refSubstring, wantOriginalRef)
}
}
// assertReferrerInStore walks the store and fails if no descriptor has a kind
// annotation with the KindAnnotationReferrers prefix and a ref containing refSubstring.
func assertReferrerInStore(t *testing.T, s *store.Layout, refSubstring string) {
-94
View File
@@ -787,97 +787,3 @@ func TestAddImage_OCI11Referrers(t *testing.T) {
}
t.Logf("captured %d OCI referrer(s) for %s", referrerCount, baseTag.Name())
}
// newTestRegistry starts an in-process registry and returns its host and the
// remote.Option needed to talk to it over plain HTTP.
func newTestRegistry(t *testing.T) (string, []remote.Option) {
t.Helper()
srv := httptest.NewServer(registry.New())
t.Cleanup(srv.Close)
host := strings.TrimPrefix(srv.URL, "http://")
return host, []remote.Option{remote.WithTransport(srv.Client().Transport)}
}
// newTestStore creates a fresh OCI layout store rooted in a temp directory.
func newTestStore(t *testing.T) *store.Layout {
t.Helper()
s, err := store.NewLayout(t.TempDir())
if err != nil {
t.Fatalf("new layout: %v", err)
}
return s
}
// seedImage pushes a random image to host/repo:tag and returns it, so a test
// can later assert on the exact bytes/digest that were pushed.
func seedImage(t *testing.T, host, repo, tag string, opts ...remote.Option) v1.Image {
t.Helper()
img, err := random.Image(1024, 3)
if err != nil {
t.Fatalf("random.Image: %v", err)
}
ref, err := gname.NewTag(host+"/"+repo+":"+tag, gname.Insecure)
if err != nil {
t.Fatalf("new tag: %v", err)
}
if err := remote.Write(ref, img, opts...); err != nil {
t.Fatalf("remote.Write: %v", err)
}
return img
}
// TestAddImagePinnedDigestIgnoresMovedTag proves the TOCTOU fix: once a caller
// pins the digest it verified, a tag that moves to different content between
// verification and the fetch cannot substitute its bytes into the store.
func TestAddImagePinnedDigestIgnoresMovedTag(t *testing.T) {
host, remoteOpts := newTestRegistry(t)
original := seedImage(t, host, "test/pinned", "v1", remoteOpts...)
originalDigest, err := original.Digest()
if err != nil {
t.Fatalf("original digest: %v", err)
}
// Move the tag to different content, exactly as a mutable tag could be
// re-pushed between verification and the pull.
replacement, err := random.Image(1024, 3)
if err != nil {
t.Fatalf("random.Image: %v", err)
}
ref, err := gname.ParseReference(host + "/test/pinned:v1")
if err != nil {
t.Fatalf("parse: %v", err)
}
if err := remote.Write(ref, replacement, remoteOpts...); err != nil {
t.Fatalf("remote.Write replacement: %v", err)
}
s := newTestStore(t)
got, err := s.AddImage(context.Background(), host+"/test/pinned:v1", "", true,
originalDigest.String(), false, "", remoteOpts...)
if err != nil {
t.Fatalf("AddImage: %v", err)
}
if got != originalDigest.String() {
t.Fatalf("stored digest = %s, want the pinned %s (the moved tag won)", got, originalDigest.String())
}
}
// TestAddImageEmptyPinResolvesTag confirms the unpinned path is untouched:
// an empty pinnedDigest still resolves the tag normally.
func TestAddImageEmptyPinResolvesTag(t *testing.T) {
host, remoteOpts := newTestRegistry(t)
img := seedImage(t, host, "test/unpinned", "v1", remoteOpts...)
want, err := img.Digest()
if err != nil {
t.Fatalf("digest: %v", err)
}
s := newTestStore(t)
got, err := s.AddImage(context.Background(), host+"/test/unpinned:v1", "", true, "", false, "", remoteOpts...)
if err != nil {
t.Fatalf("AddImage: %v", err)
}
if got != want.String() {
t.Fatalf("stored digest = %s, want %s", got, want.String())
}
}