mirror of
https://github.com/project-zot/zot.git
synced 2024-12-30 22:34:13 -05:00
f618b1d4ef
* ci(deps): upgrade golangci-lint
Signed-off-by: Jan-Otto Kröpke <mail@jkroepke.de>
* build(deps): removed disabled linters
Signed-off-by: Jan-Otto Kröpke <mail@jkroepke.de>
* build(deps): go run github.com/daixiang0/gci@latest write .
Signed-off-by: Jan-Otto Kröpke <joe@cloudeteer.de>
* build(deps): go run golang.org/x/tools/cmd/goimports@latest -l -w .
Signed-off-by: Jan-Otto Kröpke <joe@cloudeteer.de>
* build(deps): go run github.com/bombsimon/wsl/v4/cmd...@latest -strict-append -test=true -fix ./...
Signed-off-by: Jan-Otto Kröpke <joe@cloudeteer.de>
* build(deps): go run github.com/catenacyber/perfsprint@latest -fix ./...
Signed-off-by: Jan-Otto Kröpke <joe@cloudeteer.de>
* build(deps): replace gomnd by mnd
Signed-off-by: Jan-Otto Kröpke <joe@cloudeteer.de>
* build(deps): make gqlgen
Signed-off-by: Jan-Otto Kröpke <joe@cloudeteer.de>
* build: Revert "build(deps): go run github.com/daixiang0/gci@latest write ."
This reverts commit 5bf8c42e1f
.
Signed-off-by: Jan-Otto Kröpke <joe@cloudeteer.de>
* build(deps): go run github.com/daixiang0/gci@latest write -s 'standard' -s default -s 'prefix(zotregistry.dev/zot)' .
Signed-off-by: Jan-Otto Kröpke <joe@cloudeteer.de>
* build(deps): make gqlgen
Signed-off-by: Jan-Otto Kröpke <joe@cloudeteer.de>
* fix: wsl issues
Signed-off-by: Jan-Otto Kröpke <joe@cloudeteer.de>
* fix: wsl issues
Signed-off-by: Jan-Otto Kröpke <joe@cloudeteer.de>
* fix: wsl issues
Signed-off-by: Jan-Otto Kröpke <joe@cloudeteer.de>
* fix: wsl issues
Signed-off-by: Jan-Otto Kröpke <joe@cloudeteer.de>
* fix: wsl issues
Signed-off-by: Jan-Otto Kröpke <joe@cloudeteer.de>
* fix: wsl issues
Signed-off-by: Jan-Otto Kröpke <joe@cloudeteer.de>
* fix: wsl issues
Signed-off-by: Jan-Otto Kröpke <joe@cloudeteer.de>
* fix: wsl issues
Signed-off-by: Jan-Otto Kröpke <joe@cloudeteer.de>
* fix: wsl issues
Signed-off-by: Jan-Otto Kröpke <joe@cloudeteer.de>
* fix: wsl issues
Signed-off-by: Jan-Otto Kröpke <joe@cloudeteer.de>
* fix: wsl issues
Signed-off-by: Jan-Otto Kröpke <joe@cloudeteer.de>
* fix: wsl issues
Signed-off-by: Jan-Otto Kröpke <joe@cloudeteer.de>
* fix: wsl issues
Signed-off-by: Jan-Otto Kröpke <joe@cloudeteer.de>
* fix: wsl issues
Signed-off-by: Jan-Otto Kröpke <joe@cloudeteer.de>
* fix: wsl issues
Signed-off-by: Jan-Otto Kröpke <joe@cloudeteer.de>
* fix: wsl issues
Signed-off-by: Jan-Otto Kröpke <joe@cloudeteer.de>
* fix: wsl issues
Signed-off-by: Jan-Otto Kröpke <joe@cloudeteer.de>
* fix: wsl issues
Signed-off-by: Jan-Otto Kröpke <joe@cloudeteer.de>
* fix: wsl issues
Signed-off-by: Jan-Otto Kröpke <joe@cloudeteer.de>
* fix: wsl issues
Signed-off-by: Jan-Otto Kröpke <joe@cloudeteer.de>
* fix: wsl issues
Signed-off-by: Jan-Otto Kröpke <joe@cloudeteer.de>
* fix: wsl issues
Signed-off-by: Jan-Otto Kröpke <joe@cloudeteer.de>
* fix: wsl issues
Signed-off-by: Jan-Otto Kröpke <joe@cloudeteer.de>
* fix: wsl issues
Signed-off-by: Jan-Otto Kröpke <joe@cloudeteer.de>
* fix: check-log issues
Signed-off-by: Jan-Otto Kröpke <joe@cloudeteer.de>
* fix: gci issues
Signed-off-by: Jan-Otto Kröpke <joe@cloudeteer.de>
* fix: tests
Signed-off-by: Jan-Otto Kröpke <joe@cloudeteer.de>
---------
Signed-off-by: Jan-Otto Kröpke <mail@jkroepke.de>
Signed-off-by: Jan-Otto Kröpke <joe@cloudeteer.de>
478 lines
13 KiB
Go
478 lines
13 KiB
Go
//go:build sync && scrub && metrics && search
|
|
// +build sync,scrub,metrics,search
|
|
|
|
package signature_test
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"os"
|
|
"path"
|
|
"testing"
|
|
|
|
notconfig "github.com/notaryproject/notation-go/config"
|
|
. "github.com/smartystreets/goconvey/convey"
|
|
|
|
"zotregistry.dev/zot/pkg/api"
|
|
"zotregistry.dev/zot/pkg/api/config"
|
|
tcommon "zotregistry.dev/zot/pkg/test/common"
|
|
. "zotregistry.dev/zot/pkg/test/image-utils"
|
|
"zotregistry.dev/zot/pkg/test/signature"
|
|
)
|
|
|
|
func TestLoadNotationSigningkeys(t *testing.T) {
|
|
Convey("notation directory doesn't exist", t, func() {
|
|
_, err := signature.LoadNotationSigningkeys(t.TempDir())
|
|
So(err, ShouldNotBeNil)
|
|
})
|
|
|
|
Convey("wrong content of signingkeys.json", t, func() {
|
|
tempDir := t.TempDir()
|
|
dir := path.Join(tempDir, "notation")
|
|
err := os.Mkdir(dir, 0o777)
|
|
So(err, ShouldBeNil)
|
|
|
|
filePath := path.Join(dir, "signingkeys.json")
|
|
err = os.WriteFile(filePath, []byte("some dummy file content"), 0o666) //nolint: gosec
|
|
So(err, ShouldBeNil)
|
|
|
|
_, err = signature.LoadNotationSigningkeys(tempDir)
|
|
So(err, ShouldNotBeNil)
|
|
})
|
|
|
|
Convey("not enough permissions to access signingkeys.json", t, func() {
|
|
tempDir := t.TempDir()
|
|
dir := path.Join(tempDir, "notation")
|
|
err := os.Mkdir(dir, 0o777)
|
|
So(err, ShouldBeNil)
|
|
|
|
filePath := path.Join(dir, "signingkeys.json")
|
|
err = os.WriteFile(filePath, []byte("some dummy file content"), 0o300) //nolint: gosec
|
|
So(err, ShouldBeNil)
|
|
|
|
_, err = signature.LoadNotationSigningkeys(tempDir)
|
|
So(err, ShouldNotBeNil)
|
|
})
|
|
|
|
Convey("signingkeys.json not exists so it is created successfully", t, func() {
|
|
tempDir := t.TempDir()
|
|
dir := path.Join(tempDir, "notation")
|
|
err := os.Mkdir(dir, 0o777)
|
|
So(err, ShouldBeNil)
|
|
|
|
_, err = signature.LoadNotationSigningkeys(tempDir)
|
|
So(err, ShouldBeNil)
|
|
})
|
|
|
|
Convey("signingkeys.json not exists - error trying to create it", t, func() {
|
|
tempDir := t.TempDir()
|
|
dir := path.Join(tempDir, "notation")
|
|
// create notation directory without write permissions
|
|
err := os.Mkdir(dir, 0o555)
|
|
So(err, ShouldBeNil)
|
|
|
|
_, err = signature.LoadNotationSigningkeys(tempDir)
|
|
So(err, ShouldNotBeNil)
|
|
})
|
|
}
|
|
|
|
func TestLoadNotationConfig(t *testing.T) {
|
|
Convey("directory doesn't exist", t, func() {
|
|
_, err := signature.LoadNotationConfig(t.TempDir())
|
|
So(err, ShouldNotBeNil)
|
|
})
|
|
|
|
Convey("wrong content of signingkeys.json", t, func() {
|
|
tempDir := t.TempDir()
|
|
dir := path.Join(tempDir, "notation")
|
|
err := os.Mkdir(dir, 0o777)
|
|
So(err, ShouldBeNil)
|
|
|
|
filePath := path.Join(dir, "signingkeys.json")
|
|
err = os.WriteFile(filePath, []byte("some dummy file content"), 0o666) //nolint: gosec
|
|
So(err, ShouldBeNil)
|
|
|
|
_, err = signature.LoadNotationConfig(tempDir)
|
|
So(err, ShouldNotBeNil)
|
|
})
|
|
|
|
Convey("check default value of signature format", t, func() {
|
|
tempDir := t.TempDir()
|
|
dir := path.Join(tempDir, "notation")
|
|
err := os.Mkdir(dir, 0o777)
|
|
So(err, ShouldBeNil)
|
|
|
|
filePath := path.Join(dir, "signingkeys.json")
|
|
err = os.WriteFile(filePath, []byte("{\"SignatureFormat\": \"\"}"), 0o666) //nolint: gosec
|
|
So(err, ShouldBeNil)
|
|
|
|
configInfo, err := signature.LoadNotationConfig(tempDir)
|
|
So(err, ShouldBeNil)
|
|
So(configInfo.SignatureFormat, ShouldEqual, "jws")
|
|
})
|
|
}
|
|
|
|
func TestSignWithNotation(t *testing.T) {
|
|
Convey("notation directory doesn't exist", t, func() {
|
|
err := signature.SignWithNotation("key", "reference", t.TempDir(), true)
|
|
So(err, ShouldNotBeNil)
|
|
})
|
|
|
|
Convey("key not found", t, func() {
|
|
tempDir := t.TempDir()
|
|
dir := path.Join(tempDir, "notation")
|
|
err := os.Mkdir(dir, 0o777)
|
|
So(err, ShouldBeNil)
|
|
|
|
filePath := path.Join(dir, "signingkeys.json")
|
|
err = os.WriteFile(filePath, []byte("{}"), 0o666) //nolint: gosec
|
|
So(err, ShouldBeNil)
|
|
|
|
err = signature.SignWithNotation("key", "reference", tempDir, true)
|
|
So(err, ShouldEqual, signature.ErrKeyNotFound)
|
|
})
|
|
|
|
Convey("not enough permissions to access notation/localkeys dir", t, func() {
|
|
cwd, err := os.Getwd()
|
|
So(err, ShouldBeNil)
|
|
|
|
defer func() { _ = os.Chdir(cwd) }()
|
|
|
|
tdir := t.TempDir()
|
|
_ = os.Chdir(tdir)
|
|
|
|
signature.NotationPathLock.Lock()
|
|
defer signature.NotationPathLock.Unlock()
|
|
|
|
signature.LoadNotationPath(tdir)
|
|
|
|
err = signature.GenerateNotationCerts(tdir, "key")
|
|
So(err, ShouldBeNil)
|
|
|
|
err = os.Chmod(path.Join(tdir, "notation", "localkeys"), 0o000)
|
|
So(err, ShouldBeNil)
|
|
|
|
err = signature.SignWithNotation("key", "reference", tdir, true)
|
|
So(err, ShouldNotBeNil)
|
|
|
|
err = os.Chmod(path.Join(tdir, "notation", "localkeys"), 0o755)
|
|
So(err, ShouldBeNil)
|
|
})
|
|
|
|
Convey("error parsing reference", t, func() {
|
|
cwd, err := os.Getwd()
|
|
So(err, ShouldBeNil)
|
|
|
|
defer func() { _ = os.Chdir(cwd) }()
|
|
|
|
tdir := t.TempDir()
|
|
_ = os.Chdir(tdir)
|
|
|
|
signature.NotationPathLock.Lock()
|
|
defer signature.NotationPathLock.Unlock()
|
|
|
|
signature.LoadNotationPath(tdir)
|
|
|
|
err = signature.GenerateNotationCerts(tdir, "key")
|
|
So(err, ShouldBeNil)
|
|
|
|
err = signature.SignWithNotation("key", "invalidReference", tdir, true)
|
|
So(err, ShouldNotBeNil)
|
|
})
|
|
|
|
Convey("error signing", t, func() {
|
|
cwd, err := os.Getwd()
|
|
So(err, ShouldBeNil)
|
|
|
|
defer func() { _ = os.Chdir(cwd) }()
|
|
|
|
tdir := t.TempDir()
|
|
_ = os.Chdir(tdir)
|
|
|
|
signature.NotationPathLock.Lock()
|
|
defer signature.NotationPathLock.Unlock()
|
|
|
|
signature.LoadNotationPath(tdir)
|
|
|
|
err = signature.GenerateNotationCerts(tdir, "key")
|
|
So(err, ShouldBeNil)
|
|
|
|
err = signature.SignWithNotation("key", "localhost:8080/invalidreference:1.0", tdir, true)
|
|
So(err, ShouldNotBeNil)
|
|
})
|
|
}
|
|
|
|
func TestVerifyWithNotation(t *testing.T) {
|
|
Convey("notation directory doesn't exist", t, func() {
|
|
err := signature.VerifyWithNotation("reference", t.TempDir())
|
|
So(err, ShouldNotBeNil)
|
|
})
|
|
|
|
Convey("error parsing reference", t, func() {
|
|
cwd, err := os.Getwd()
|
|
So(err, ShouldBeNil)
|
|
|
|
defer func() { _ = os.Chdir(cwd) }()
|
|
|
|
tdir := t.TempDir()
|
|
_ = os.Chdir(tdir)
|
|
|
|
signature.NotationPathLock.Lock()
|
|
defer signature.NotationPathLock.Unlock()
|
|
|
|
signature.LoadNotationPath(tdir)
|
|
|
|
err = signature.GenerateNotationCerts(tdir, "key")
|
|
So(err, ShouldBeNil)
|
|
|
|
err = signature.VerifyWithNotation("invalidReference", tdir)
|
|
So(err, ShouldNotBeNil)
|
|
})
|
|
|
|
Convey("error trying to get manifest", t, func() {
|
|
cwd, err := os.Getwd()
|
|
So(err, ShouldBeNil)
|
|
|
|
defer func() { _ = os.Chdir(cwd) }()
|
|
|
|
tdir := t.TempDir()
|
|
_ = os.Chdir(tdir)
|
|
|
|
signature.NotationPathLock.Lock()
|
|
defer signature.NotationPathLock.Unlock()
|
|
|
|
signature.LoadNotationPath(tdir)
|
|
|
|
err = signature.GenerateNotationCerts(tdir, "key")
|
|
So(err, ShouldBeNil)
|
|
|
|
err = signature.VerifyWithNotation("localhost:8080/invalidreference:1.0", tdir)
|
|
So(err, ShouldNotBeNil)
|
|
})
|
|
|
|
Convey("invalid content of trustpolicy.json", t, func() {
|
|
// start a new server
|
|
port := tcommon.GetFreePort()
|
|
baseURL := tcommon.GetBaseURL(port)
|
|
dir := t.TempDir()
|
|
|
|
conf := config.New()
|
|
conf.HTTP.Port = port
|
|
conf.Storage.RootDirectory = dir
|
|
|
|
ctlr := api.NewController(conf)
|
|
cm := tcommon.NewControllerManager(ctlr)
|
|
// this blocks
|
|
cm.StartAndWait(port)
|
|
defer cm.StopServer()
|
|
|
|
repoName := "signed-repo"
|
|
tag := "1.0"
|
|
|
|
image := CreateRandomImage()
|
|
|
|
err := UploadImage(image, baseURL, repoName, tag)
|
|
So(err, ShouldBeNil)
|
|
|
|
tempDir := t.TempDir()
|
|
notationDir := path.Join(tempDir, "notation")
|
|
err = os.Mkdir(notationDir, 0o777)
|
|
So(err, ShouldBeNil)
|
|
|
|
filePath := path.Join(notationDir, "trustpolicy.json")
|
|
err = os.WriteFile(filePath, []byte("some dummy file content"), 0o666) //nolint: gosec
|
|
So(err, ShouldBeNil)
|
|
|
|
signature.NotationPathLock.Lock()
|
|
defer signature.NotationPathLock.Unlock()
|
|
|
|
signature.LoadNotationPath(tempDir)
|
|
|
|
err = signature.VerifyWithNotation(fmt.Sprintf("localhost:%s/%s:%s", port, repoName, tag), tempDir)
|
|
So(err, ShouldNotBeNil)
|
|
})
|
|
}
|
|
|
|
func TestListNotarySignatures(t *testing.T) {
|
|
Convey("error parsing reference", t, func() {
|
|
cwd, err := os.Getwd()
|
|
So(err, ShouldBeNil)
|
|
|
|
defer func() { _ = os.Chdir(cwd) }()
|
|
|
|
tdir := t.TempDir()
|
|
_ = os.Chdir(tdir)
|
|
|
|
_, err = signature.ListNotarySignatures("invalidReference", tdir)
|
|
So(err, ShouldNotBeNil)
|
|
})
|
|
|
|
Convey("error trying to get manifest", t, func() {
|
|
cwd, err := os.Getwd()
|
|
So(err, ShouldBeNil)
|
|
|
|
defer func() { _ = os.Chdir(cwd) }()
|
|
|
|
tdir := t.TempDir()
|
|
_ = os.Chdir(tdir)
|
|
|
|
_, err = signature.ListNotarySignatures("localhost:8080/invalidreference:1.0", tdir)
|
|
So(err, ShouldNotBeNil)
|
|
})
|
|
}
|
|
|
|
func TestGenerateNotationCerts(t *testing.T) {
|
|
Convey("write key file with permission", t, func() {
|
|
tempDir := t.TempDir()
|
|
|
|
notationDir := path.Join(tempDir, "notation")
|
|
err := os.Mkdir(notationDir, 0o777)
|
|
So(err, ShouldBeNil)
|
|
|
|
filePath := path.Join(notationDir, "localkeys")
|
|
err = os.WriteFile(filePath, []byte("{}"), 0o666) //nolint: gosec
|
|
So(err, ShouldBeNil)
|
|
|
|
signature.NotationPathLock.Lock()
|
|
defer signature.NotationPathLock.Unlock()
|
|
|
|
signature.LoadNotationPath(tempDir)
|
|
|
|
err = signature.GenerateNotationCerts(t.TempDir(), "cert")
|
|
So(err, ShouldNotBeNil)
|
|
})
|
|
|
|
Convey("write cert file with permission", t, func() {
|
|
tempDir := t.TempDir()
|
|
|
|
notationDir := path.Join(tempDir, "notation", "localkeys")
|
|
err := os.MkdirAll(notationDir, 0o777)
|
|
So(err, ShouldBeNil)
|
|
|
|
filePath := path.Join(notationDir, "cert.crt")
|
|
err = os.WriteFile(filePath, []byte("{}"), 0o666) //nolint: gosec
|
|
So(err, ShouldBeNil)
|
|
|
|
err = os.Chmod(filePath, 0o000)
|
|
So(err, ShouldBeNil)
|
|
|
|
signature.NotationPathLock.Lock()
|
|
defer signature.NotationPathLock.Unlock()
|
|
|
|
signature.LoadNotationPath(tempDir)
|
|
|
|
err = signature.GenerateNotationCerts(t.TempDir(), "cert")
|
|
So(err, ShouldNotBeNil)
|
|
|
|
err = os.Chmod(filePath, 0o755)
|
|
So(err, ShouldBeNil)
|
|
})
|
|
|
|
Convey("signingkeys.json file - not enough permission", t, func() {
|
|
tempDir := t.TempDir()
|
|
|
|
notationDir := path.Join(tempDir, "notation")
|
|
err := os.Mkdir(notationDir, 0o777)
|
|
So(err, ShouldBeNil)
|
|
|
|
filePath := path.Join(notationDir, "signingkeys.json")
|
|
_, err = os.Create(filePath) //nolint: gosec
|
|
So(err, ShouldBeNil)
|
|
err = os.Chmod(filePath, 0o000)
|
|
So(err, ShouldBeNil)
|
|
|
|
signature.NotationPathLock.Lock()
|
|
defer signature.NotationPathLock.Unlock()
|
|
|
|
signature.LoadNotationPath(tempDir)
|
|
|
|
err = signature.GenerateNotationCerts(t.TempDir(), "cert")
|
|
So(err, ShouldNotBeNil)
|
|
|
|
err = os.Remove(filePath)
|
|
So(err, ShouldBeNil)
|
|
err = os.RemoveAll(path.Join(notationDir, "localkeys"))
|
|
So(err, ShouldBeNil)
|
|
signingKeysBuf, err := json.Marshal(notconfig.SigningKeys{})
|
|
So(err, ShouldBeNil)
|
|
err = os.WriteFile(filePath, signingKeysBuf, 0o555) //nolint:gosec // test code
|
|
So(err, ShouldBeNil)
|
|
err = signature.GenerateNotationCerts(t.TempDir(), "cert")
|
|
So(err, ShouldNotBeNil)
|
|
})
|
|
Convey("keysuite already exists in signingkeys.json", t, func() {
|
|
tempDir := t.TempDir()
|
|
|
|
notationDir := path.Join(tempDir, "notation")
|
|
err := os.Mkdir(notationDir, 0o777)
|
|
So(err, ShouldBeNil)
|
|
|
|
certName := "cert-test"
|
|
filePath := path.Join(notationDir, "signingkeys.json")
|
|
keyPath := path.Join(notationDir, "localkeys", certName+".key")
|
|
certPath := path.Join(notationDir, "localkeys", certName+".crt")
|
|
signingKeys := notconfig.SigningKeys{}
|
|
keySuite := notconfig.KeySuite{
|
|
Name: certName,
|
|
X509KeyPair: ¬config.X509KeyPair{
|
|
KeyPath: keyPath,
|
|
CertificatePath: certPath,
|
|
},
|
|
}
|
|
signingKeys.Keys = []notconfig.KeySuite{keySuite}
|
|
signingKeysBuf, err := json.Marshal(signingKeys)
|
|
So(err, ShouldBeNil)
|
|
err = os.WriteFile(filePath, signingKeysBuf, 0o600)
|
|
So(err, ShouldBeNil)
|
|
|
|
signature.NotationPathLock.Lock()
|
|
defer signature.NotationPathLock.Unlock()
|
|
|
|
signature.LoadNotationPath(tempDir)
|
|
|
|
err = signature.GenerateNotationCerts(t.TempDir(), certName)
|
|
So(err, ShouldNotBeNil)
|
|
})
|
|
Convey("truststore files", t, func() {
|
|
tempDir := t.TempDir()
|
|
|
|
notationDir := path.Join(tempDir, "notation")
|
|
err := os.Mkdir(notationDir, 0o777)
|
|
So(err, ShouldBeNil)
|
|
|
|
certName := "cert-test"
|
|
trustStorePath := path.Join(notationDir, "truststore/x509/ca/"+certName)
|
|
err = os.MkdirAll(trustStorePath, 0o755)
|
|
So(err, ShouldBeNil)
|
|
err = os.Chmod(path.Join(notationDir, "truststore/x509"), 0o000)
|
|
So(err, ShouldBeNil)
|
|
|
|
signature.NotationPathLock.Lock()
|
|
defer signature.NotationPathLock.Unlock()
|
|
|
|
signature.LoadNotationPath(tempDir)
|
|
|
|
err = signature.GenerateNotationCerts(tempDir, certName)
|
|
So(err, ShouldNotBeNil)
|
|
|
|
err = os.RemoveAll(path.Join(notationDir, "localkeys"))
|
|
So(err, ShouldBeNil)
|
|
err = os.Chmod(path.Join(notationDir, "truststore/x509"), 0o755)
|
|
So(err, ShouldBeNil)
|
|
_, err = os.Create(path.Join(trustStorePath, "cert-test.crt"))
|
|
So(err, ShouldBeNil)
|
|
|
|
err = signature.GenerateNotationCerts(tempDir, certName)
|
|
So(err, ShouldNotBeNil)
|
|
|
|
err = os.RemoveAll(path.Join(notationDir, "localkeys"))
|
|
So(err, ShouldBeNil)
|
|
err = os.Remove(path.Join(trustStorePath, "cert-test.crt"))
|
|
So(err, ShouldBeNil)
|
|
err = os.Chmod(path.Join(notationDir, "truststore/x509/ca", certName), 0o555)
|
|
So(err, ShouldBeNil)
|
|
|
|
err = signature.GenerateNotationCerts(tempDir, certName)
|
|
So(err, ShouldNotBeNil)
|
|
})
|
|
}
|