jojo/services/packages/container/manifest_test.go
patdyn df79ccf7d8 Move Container API processing logic to service (#11432)
As discussed here: https://codeberg.org/forgejo/discussions/issues/444 the container v2 API logic does need some refactoring for better maintainability.

This is a proposition on how to achieve that. My goal was to be able to write unit tests for functions like processImageManifest() which are currently only tested indirectly by TestPackageContainer() in tests/integration/api_packages_container_test.go.

A first unit test was implemented that targets ProcessManifest(). I think that test also shows what steps are needed to successfully execute the ProcessManifest() function and hopefully helps understanding that code better.

## Checklist

The [contributor guide](https://forgejo.org/docs/next/contributor/) contains information that will be helpful to first time contributors. There also are a few [conditions for merging Pull Requests in Forgejo repositories](https://codeberg.org/forgejo/governance/src/branch/main/PullRequestsAgreement.md). You are also welcome to join the [Forgejo development chatroom](https://matrix.to/#/#forgejo-development:matrix.org).

### Tests for Go changes

(can be removed for JavaScript changes)

- I added test coverage for Go changes...
  - [x ] in their respective `*_test.go` for unit tests.
  - [ ] in the `tests/integration` directory if it involves interactions with a live Forgejo server.
- I ran...
  - [x] `make pr-go` before pushing

### Documentation

- [ ] I created a pull request [to the documentation](https://codeberg.org/forgejo/docs) to explain to Forgejo users how to use this change.
- [x ] I did not document these changes and I do not expect someone else to do it.

### Release notes

- [ ] This change will be noticed by a Forgejo user or admin (feature, bug fix, performance, etc.). I suggest to include a release note for this change.
- [ x] This change is not visible to a Forgejo user or admin (refactor, dependency upgrade, etc.). I think there is no need to add a release note for this change.

*The decision if the pull request will be shown in the release notes is up to the mergers / release team.*

The content of the `release-notes/<pull request number>.md` file will serve as the basis for the release notes. If the file does not exist, the title of the pull request will be used instead.

Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/11432
Reviewed-by: Andreas Ahlenstorf <aahlenst@noreply.codeberg.org>
Reviewed-by: Mathieu Fenniak <mfenniak@noreply.codeberg.org>
Co-authored-by: patdyn <patdyn@noreply.codeberg.org>
Co-committed-by: patdyn <patdyn@noreply.codeberg.org>
2026-03-06 18:56:49 +01:00

95 lines
4.4 KiB
Go

// Copyright 2026 The Forgejo Authors. All rights reserved.
// SPDX-License-Identifier: GPL-3.0-or-later
package container
import (
"encoding/base64"
"io"
"strings"
"testing"
"forgejo.org/models/unittest"
user_model "forgejo.org/models/user"
packages_module "forgejo.org/modules/packages"
packages_service "forgejo.org/services/packages"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func Test_SaveAndGetManifestAndBlob(t *testing.T) {
require.NoError(t, unittest.PrepareTestDatabase())
user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
manifestMediaType := "application/vnd.docker.distribution.manifest.v2+json"
image := "test"
tag := "latest"
mci, err := NewManifestCreationInfo(user2, user2, manifestMediaType, image, tag)
require.NoError(t, err)
blobContent, _ := base64.StdEncoding.DecodeString(`H4sIAAAJbogA/2IYBaNgFIxYAAgAAP//Lq+17wAEAAA=`)
blobReader := &io.LimitedReader{R: strings.NewReader(string(blobContent)), N: int64(MaxManifestSize + 1)}
blobBuf, err := packages_module.CreateHashedBufferFromReaderWithSize(blobReader, MaxManifestSize+1)
blobDigest := DigestFromHashSummer(blobBuf)
require.NoError(t, err)
defer blobBuf.Close()
blobPCI := &packages_service.PackageCreationInfo{
PackageInfo: packages_service.PackageInfo{
Owner: user2,
Name: image,
},
Creator: user2,
}
_, err = SaveAsPackageBlob(t.Context(), blobBuf, blobPCI)
require.NoError(t, err)
configDigest := "sha256:4607e093bec406eaadb6f3a340f63400c9d3a7038680744c406903766b938f0d"
configContent := `{"architecture":"amd64","config":{"Env":["PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"],"Cmd":["/true"],"ArgsEscaped":true,"Image":"sha256:9bd8b88dc68b80cffe126cc820e4b52c6e558eb3b37680bfee8e5f3ed7b8c257"},"container":"b89fe92a887d55c0961f02bdfbfd8ac3ddf66167db374770d2d9e9fab3311510","container_config":{"Hostname":"b89fe92a887d","Env":["PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"],"Cmd":["/bin/sh","-c","#(nop) ","CMD [\"/true\"]"],"ArgsEscaped":true,"Image":"sha256:9bd8b88dc68b80cffe126cc820e4b52c6e558eb3b37680bfee8e5f3ed7b8c257"},"created":"2022-01-01T00:00:00.000000000Z","docker_version":"20.10.12","history":[{"created":"2022-01-01T00:00:00.000000000Z","created_by":"/bin/sh -c #(nop) COPY file:0e7589b0c800daaf6fa460d2677101e4676dd9491980210cb345480e513f3602 in /true "},{"created":"2022-01-01T00:00:00.000000001Z","created_by":"/bin/sh -c #(nop) CMD [\"/true\"]","empty_layer":true}],"os":"linux","rootfs":{"type":"layers","diff_ids":["sha256:0ff3b91bdf21ecdf2f2f3d4372c2098a14dbe06cd678e8f0a85fd4902d00e2e2"]}}`
cfgReader := &io.LimitedReader{R: strings.NewReader(configContent), N: int64(MaxManifestSize + 1)}
cfgBuf, err := packages_module.CreateHashedBufferFromReaderWithSize(cfgReader, MaxManifestSize+1)
require.NoError(t, err)
defer cfgBuf.Close()
confPci := &packages_service.PackageCreationInfo{
PackageInfo: packages_service.PackageInfo{
Owner: user2,
Name: image,
Version: configDigest,
},
Creator: user2,
}
_, err = SaveAsPackageBlob(t.Context(), cfgBuf, confPci)
require.NoError(t, err)
manifestDigest := "sha256:4f10484d1c1bb13e3956b4de1cd42db8e0f14a75be1617b60f2de3cd59c803c6"
manifestContent := `{"schemaVersion":2,"mediaType":"application/vnd.docker.distribution.manifest.v2+json","config":{"mediaType":"application/vnd.docker.container.image.v1+json","digest":"sha256:4607e093bec406eaadb6f3a340f63400c9d3a7038680744c406903766b938f0d","size":1069},"layers":[{"mediaType":"application/vnd.docker.image.rootfs.diff.tar.gzip","digest":"sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4","size":32}]}`
reader := &io.LimitedReader{R: strings.NewReader(manifestContent), N: int64(MaxManifestSize + 1)}
buf, err := packages_module.CreateHashedBufferFromReaderWithSize(reader, MaxManifestSize+1)
require.NoError(t, err)
defer buf.Close()
digest, err := ProcessManifest(t.Context(), *mci, buf)
require.NoError(t, err)
assert.Equal(t, digest, manifestDigest)
pdf, err := GetLocalManifest(t.Context(), user2.ID, image, tag)
require.NoError(t, err)
assert.Equal(t, "sha256:"+pdf.Blob.HashSHA256, digest)
pdf, err = GetLocalBlob(t.Context(), user2.ID, blobDigest, image)
require.NoError(t, err)
assert.Equal(t, "sha256:"+pdf.Blob.HashSHA256, blobDigest)
tl, v, err := GetLocalTagList(t.Context(), user2.LowerName, image, "", 1, user2.ID)
require.NoError(t, err)
assert.Len(t, tl.Tags, 1)
assert.Equal(t, "latest", v.Get("last"))
}