test(docker): cover multi-RepoDigest update comparison

Restore the remaining focused regressions for the all-local-RepoDigests update comparison carried on the earlier core-runtime candidate: multi-digest resolution in getImageRepoDigests, false-update suppression in CheckImageUpdate, and the DigestsDiffer multi-digest cases. Test-only additions; the runtime change and contract sections are in the preceding fix(docker) commit.

Change-source: pulse-maintainer
This commit is contained in:
pulse-triage[bot]
2026-09-19 15:23:52 +01:00
parent 5bc6f919bb
commit ee7ecbd928
3 changed files with 94 additions and 0 deletions
@@ -77,6 +77,42 @@ func TestAgent_getImageRepoDigest_FallbackToFirst(t *testing.T) {
}
}
func TestAgent_getImageRepoDigests_MultipleDigestsForOneImage(t *testing.T) {
agent := &Agent{
docker: &fakeDockerClient{
imageInspectWithRawFn: func(ctx context.Context, imageID string) (image.InspectResponse, []byte, error) {
return image.InspectResponse{
RepoDigests: []string{
"docker.io/library/postgres@sha256:44c4",
"docker.io/library/postgres@sha256:cf78",
"docker.io/other/postgres@sha256:beef",
},
Architecture: "amd64",
Os: "linux",
}, nil, nil
},
},
logger: zerolog.New(io.Discard),
}
digests, arch, os, variant := agent.getImageRepoDigests(context.Background(), "image-id", "postgres:16.15-alpine3.24")
want := []string{"sha256:44c4", "sha256:cf78", "sha256:beef"}
if len(digests) != len(want) {
t.Fatalf("digests = %v, want %v", digests, want)
}
for i := range want {
if digests[i] != want[i] {
t.Fatalf("digests = %v, want %v", digests, want)
}
}
if arch != "amd64" || os != "linux" || variant != "" {
t.Fatalf("platform = %q/%q/%q, want amd64/linux/", arch, os, variant)
}
if got, _, _, _ := agent.getImageRepoDigest(context.Background(), "image-id", "postgres:16.15-alpine3.24"); got != "sha256:44c4" {
t.Fatalf("primary digest = %q, want sha256:44c4", got)
}
}
func TestAgent_getImageRepoDigest_InvalidRepoDigest(t *testing.T) {
agent := &Agent{
docker: &fakeDockerClient{
@@ -123,3 +123,43 @@ func TestRegistryChecker_MultiArchDigestLayersStayConsistentAcrossCache(t *testi
t.Fatalf("expected one HEAD and one GET before the cache hit, got %d requests", requestCount)
}
}
// Issue #2110: a local image can carry several RepoDigests, for example the
// tag's manifest digest and a platform manifest digest. The update check must
// treat any of them as the current image rather than reporting a false update
// just because the first entry differs from the registry digest.
func TestRegistryChecker_MultipleLocalRepoDigestsSuppressFalseUpdate(t *testing.T) {
checker := NewRegistryChecker(zerolog.Nop())
checker.httpClient = &http.Client{
Transport: roundTripFunc(func(req *http.Request) (*http.Response, error) {
if req.Method == http.MethodHead {
return newStringResponse(http.StatusOK, map[string]string{
"Content-Type": "application/vnd.docker.distribution.manifest.v2+json",
"Docker-Content-Digest": "sha256:cf78",
}, ""), nil
}
return newStringResponse(http.StatusOK, nil, "{}"), nil
}),
}
result := checker.CheckImageUpdate(
context.Background(),
"example.test/postgres:16.15-alpine3.24",
"sha256:44c4,sha256:cf78",
"amd64",
"linux",
"",
)
if result == nil {
t.Fatal("expected an update result")
}
if result.UpdateAvailable {
t.Fatalf("a local RepoDigest matching the registry digest must suppress the update: %#v", result)
}
if result.CurrentDigest != "sha256:44c4" {
t.Fatalf("primary current digest = %q, want %q", result.CurrentDigest, "sha256:44c4")
}
if result.LatestDigest != "sha256:cf78" {
t.Fatalf("latest digest = %q, want %q", result.LatestDigest, "sha256:cf78")
}
}
+18
View File
@@ -207,6 +207,24 @@ func TestRegistryChecker_DigestsDiffer(t *testing.T) {
latest: "def456,abc123",
want: true, // No match found
},
{
name: "multiple current digests match second latest",
current: "sha256:44c4,sha256:cf78",
latest: "sha256:cf78",
want: false, // One local RepoDigest is current
},
{
name: "multiple current digests no match",
current: "sha256:44c4,sha256:aaaa",
latest: "sha256:cf78",
want: true, // No local RepoDigest is current
},
{
name: "empty entries in current set are ignored",
current: " ,sha256:cf78",
latest: "sha256:cf78",
want: false,
},
}
for _, tt := range tests {