All checks were successful
Test / Create distribution (push) Successful in 44s
Test / Sandbox (push) Successful in 2m30s
Test / ShareFS (push) Successful in 3m40s
Test / Hpkg (push) Successful in 4m24s
Test / Sandbox (race detector) (push) Successful in 4m46s
Test / Hakurei (race detector) (push) Successful in 5m51s
Test / Hakurei (push) Successful in 2m28s
Test / Flake checks (push) Successful in 1m41s
The previous implementation exposes arbitrary user input to the cache as an identifier, which is highly error-prone and can cause the cache to enter an inconsistent state if the user is not careful. This change replaces the implementation to compute identifier late, using url string as params. Signed-off-by: Ophestra <cat@gensokyo.uk>
683 lines
17 KiB
Go
683 lines
17 KiB
Go
package pkg_test
|
|
|
|
import (
|
|
"archive/tar"
|
|
"bytes"
|
|
"crypto/sha512"
|
|
"encoding/binary"
|
|
"errors"
|
|
"fmt"
|
|
"io/fs"
|
|
"net/http"
|
|
"os"
|
|
"path/filepath"
|
|
"reflect"
|
|
"syscall"
|
|
"testing"
|
|
"unsafe"
|
|
|
|
"hakurei.app/container"
|
|
"hakurei.app/container/check"
|
|
"hakurei.app/container/stub"
|
|
"hakurei.app/internal/pkg"
|
|
)
|
|
|
|
// overrideIdent overrides the ID method of [Artifact].
|
|
type overrideIdent struct {
|
|
id pkg.ID
|
|
pkg.Artifact
|
|
}
|
|
|
|
func (a overrideIdent) ID() pkg.ID { return a.id }
|
|
|
|
// overrideIdentFile overrides the ID method of [File].
|
|
type overrideIdentFile struct {
|
|
id pkg.ID
|
|
pkg.File
|
|
}
|
|
|
|
func (a overrideIdentFile) ID() pkg.ID { return a.id }
|
|
|
|
// A knownIdentArtifact implements [pkg.KnownIdent] and [Artifact]
|
|
type knownIdentArtifact interface {
|
|
pkg.KnownIdent
|
|
pkg.Artifact
|
|
}
|
|
|
|
// A knownIdentFile implements [pkg.KnownIdent] and [File]
|
|
type knownIdentFile interface {
|
|
pkg.KnownIdent
|
|
pkg.File
|
|
}
|
|
|
|
// overrideChecksum overrides the Checksum method of [Artifact].
|
|
type overrideChecksum struct {
|
|
checksum pkg.Checksum
|
|
knownIdentArtifact
|
|
}
|
|
|
|
func (a overrideChecksum) Checksum() pkg.Checksum { return a.checksum }
|
|
|
|
// overrideChecksumFile overrides the Checksum method of [File].
|
|
type overrideChecksumFile struct {
|
|
checksum pkg.Checksum
|
|
knownIdentFile
|
|
}
|
|
|
|
func (a overrideChecksumFile) Checksum() pkg.Checksum { return a.checksum }
|
|
|
|
// A stubArtifact implements [Artifact] with hardcoded behaviour.
|
|
type stubArtifact struct {
|
|
kind pkg.Kind
|
|
params []byte
|
|
deps []pkg.Artifact
|
|
|
|
cure func(work *check.Absolute, loadData pkg.CacheDataFunc) error
|
|
}
|
|
|
|
func (a stubArtifact) Kind() pkg.Kind { return a.kind }
|
|
func (a stubArtifact) Params() []byte { return a.params }
|
|
func (a stubArtifact) Dependencies() []pkg.Artifact { return a.deps }
|
|
|
|
func (a stubArtifact) Cure(
|
|
work *check.Absolute,
|
|
loadData pkg.CacheDataFunc,
|
|
) error {
|
|
return a.cure(work, loadData)
|
|
}
|
|
|
|
// A stubFile implements [File] with hardcoded behaviour.
|
|
type stubFile struct {
|
|
data []byte
|
|
err error
|
|
|
|
stubArtifact
|
|
}
|
|
|
|
func (a stubFile) Data() ([]byte, error) { return a.data, a.err }
|
|
|
|
// newStubFile returns an implementation of [pkg.File] with hardcoded behaviour.
|
|
func newStubFile(
|
|
kind pkg.Kind,
|
|
id pkg.ID,
|
|
sum *pkg.Checksum,
|
|
data []byte,
|
|
err error,
|
|
) pkg.File {
|
|
f := overrideIdentFile{id, stubFile{data, err, stubArtifact{
|
|
kind,
|
|
nil,
|
|
nil,
|
|
func(work *check.Absolute, loadData pkg.CacheDataFunc) error {
|
|
panic("unreachable")
|
|
},
|
|
}}}
|
|
if sum == nil {
|
|
return f
|
|
} else {
|
|
return overrideChecksumFile{*sum, f}
|
|
}
|
|
}
|
|
|
|
func TestIdent(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
testCases := []struct {
|
|
name string
|
|
a pkg.Artifact
|
|
want pkg.ID
|
|
}{
|
|
{"tar", stubArtifact{
|
|
pkg.KindTar,
|
|
[]byte{pkg.TarGzip, 0, 0, 0, 0, 0, 0, 0},
|
|
[]pkg.Artifact{
|
|
overrideIdent{pkg.ID{}, stubArtifact{}},
|
|
},
|
|
nil,
|
|
}, pkg.MustDecode(
|
|
"HnySzeLQvSBZuTUcvfmLEX_OmH4yJWWH788NxuLuv7kVn8_uPM6Ks4rqFWM2NZJY",
|
|
)},
|
|
}
|
|
for _, tc := range testCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
if got := pkg.Ident(tc.a); got != tc.want {
|
|
t.Errorf("Ident: %s, want %s",
|
|
pkg.Encode(got),
|
|
pkg.Encode(tc.want),
|
|
)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// cacheTestCase is a test case passed to checkWithCache where a new instance
|
|
// of [pkg.Cache] is prepared for the test case, and is validated and removed
|
|
// on test completion.
|
|
type cacheTestCase struct {
|
|
name string
|
|
early func(t *testing.T, base *check.Absolute)
|
|
f func(t *testing.T, base *check.Absolute, c *pkg.Cache)
|
|
want pkg.Checksum
|
|
}
|
|
|
|
// checkWithCache runs a slice of cacheTestCase.
|
|
func checkWithCache(t *testing.T, testCases []cacheTestCase) {
|
|
t.Helper()
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
t.Helper()
|
|
t.Parallel()
|
|
|
|
base := check.MustAbs(t.TempDir())
|
|
if err := os.Chmod(base.String(), 0700); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
t.Cleanup(func() {
|
|
if err := filepath.WalkDir(base.String(), func(path string, d fs.DirEntry, err error) error {
|
|
if err != nil {
|
|
t.Error(err)
|
|
return nil
|
|
}
|
|
if !d.IsDir() {
|
|
return nil
|
|
}
|
|
return os.Chmod(path, 0700)
|
|
}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
})
|
|
|
|
if c, err := pkg.New(base); err != nil {
|
|
t.Fatalf("New: error = %v", err)
|
|
} else {
|
|
if tc.early != nil {
|
|
tc.early(t, base)
|
|
}
|
|
tc.f(t, base, c)
|
|
}
|
|
|
|
if checksum, err := pkg.HashDir(base); err != nil {
|
|
t.Fatalf("HashDir: error = %v", err)
|
|
} else if checksum != tc.want {
|
|
t.Fatalf("HashDir: %v", &pkg.ChecksumMismatchError{
|
|
Got: checksum,
|
|
Want: tc.want,
|
|
})
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// A cureStep contains an [Artifact] to be cured, and the expected outcome.
|
|
type cureStep struct {
|
|
name string
|
|
|
|
a pkg.Artifact
|
|
|
|
pathname *check.Absolute
|
|
checksum pkg.Checksum
|
|
err error
|
|
}
|
|
|
|
// cureMany cures many artifacts against a [Cache] and checks their outcomes.
|
|
func cureMany(t *testing.T, c *pkg.Cache, steps []cureStep) {
|
|
for _, step := range steps {
|
|
t.Log("cure step:", step.name)
|
|
if pathname, checksum, err := c.Cure(step.a); !reflect.DeepEqual(err, step.err) {
|
|
t.Fatalf("Cure: error = %v, want %v", err, step.err)
|
|
} else if !pathname.Is(step.pathname) {
|
|
t.Fatalf("Cure: pathname = %q, want %q", pathname, step.pathname)
|
|
} else if checksum != step.checksum {
|
|
t.Fatalf("Cure: checksum = %s, want %s", pkg.Encode(checksum), pkg.Encode(step.checksum))
|
|
} else {
|
|
v := any(err)
|
|
if err == nil {
|
|
v = pathname
|
|
}
|
|
t.Log(pkg.Encode(checksum)+":", v)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestCache(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
const testdata = "" +
|
|
"\x00\x00\x00\x00" +
|
|
"\xad\x0b\x00" +
|
|
"\x04" +
|
|
"\xfe\xfe\x00\x00" +
|
|
"\xfe\xca\x00\x00"
|
|
|
|
testdataChecksum := func() pkg.Checksum {
|
|
h := sha512.New384()
|
|
h.Write([]byte(testdata))
|
|
return (pkg.Checksum)(h.Sum(nil))
|
|
}()
|
|
|
|
testCases := []cacheTestCase{
|
|
{"file", nil, func(t *testing.T, base *check.Absolute, c *pkg.Cache) {
|
|
c.SetStrict(true)
|
|
|
|
identifier := (pkg.ID)(bytes.Repeat([]byte{
|
|
0x75, 0xe6, 0x9d, 0x6d, 0xe7, 0x9f,
|
|
}, 8))
|
|
wantPathname := base.Append(
|
|
"identifier",
|
|
"deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef",
|
|
)
|
|
identifier0 := (pkg.ID)(bytes.Repeat([]byte{
|
|
0x71, 0xa7, 0xde, 0x6d, 0xa6, 0xde,
|
|
}, 8))
|
|
wantPathname0 := base.Append(
|
|
"identifier",
|
|
"cafebabecafebabecafebabecafebabecafebabecafebabecafebabecafebabe",
|
|
)
|
|
|
|
cureMany(t, c, []cureStep{
|
|
{"initial file", newStubFile(
|
|
pkg.KindHTTPGet,
|
|
identifier,
|
|
&testdataChecksum,
|
|
[]byte(testdata), nil,
|
|
), wantPathname, testdataChecksum, nil},
|
|
|
|
{"identical content", newStubFile(
|
|
pkg.KindHTTPGet,
|
|
identifier0,
|
|
&testdataChecksum,
|
|
[]byte(testdata), nil,
|
|
), wantPathname0, testdataChecksum, nil},
|
|
|
|
{"existing entry", newStubFile(
|
|
pkg.KindHTTPGet,
|
|
identifier,
|
|
&testdataChecksum,
|
|
[]byte(testdata), nil,
|
|
), wantPathname, testdataChecksum, nil},
|
|
|
|
{"checksum mismatch", newStubFile(
|
|
pkg.KindHTTPGet,
|
|
pkg.ID{0xff, 0},
|
|
new(pkg.Checksum),
|
|
[]byte(testdata), nil,
|
|
), nil, pkg.Checksum{}, &pkg.ChecksumMismatchError{
|
|
Got: testdataChecksum,
|
|
}},
|
|
|
|
{"store without validation", newStubFile(
|
|
pkg.KindHTTPGet,
|
|
pkg.MustDecode("vsAhtPNo4waRNOASwrQwcIPTqb3SBuJOXw2G4T1mNmVZM-wrQTRllmgXqcIIoRcX"),
|
|
nil,
|
|
[]byte{0}, nil,
|
|
), base.Append(
|
|
"identifier",
|
|
"vsAhtPNo4waRNOASwrQwcIPTqb3SBuJOXw2G4T1mNmVZM-wrQTRllmgXqcIIoRcX",
|
|
), pkg.Checksum{
|
|
0xbe, 0xc0, 0x21, 0xb4, 0xf3, 0x68,
|
|
0xe3, 0x06, 0x91, 0x34, 0xe0, 0x12,
|
|
0xc2, 0xb4, 0x30, 0x70, 0x83, 0xd3,
|
|
0xa9, 0xbd, 0xd2, 0x06, 0xe2, 0x4e,
|
|
0x5f, 0x0d, 0x86, 0xe1, 0x3d, 0x66,
|
|
0x36, 0x65, 0x59, 0x33, 0xec, 0x2b,
|
|
0x41, 0x34, 0x65, 0x96, 0x68, 0x17,
|
|
0xa9, 0xc2, 0x08, 0xa1, 0x17, 0x17,
|
|
}, nil},
|
|
|
|
{"error passthrough", newStubFile(
|
|
pkg.KindHTTPGet,
|
|
pkg.ID{0xff, 1},
|
|
nil,
|
|
nil, stub.UniqueError(0xcafe),
|
|
), nil, pkg.Checksum{}, stub.UniqueError(0xcafe)},
|
|
|
|
{"error caching", newStubFile(
|
|
pkg.KindHTTPGet,
|
|
pkg.ID{0xff, 1},
|
|
nil,
|
|
nil, nil,
|
|
), nil, pkg.Checksum{}, stub.UniqueError(0xcafe)},
|
|
|
|
{"cache hit bad type", overrideChecksum{testdataChecksum, overrideIdent{pkg.ID{0xff, 2}, stubArtifact{
|
|
kind: pkg.KindTar,
|
|
}}}, nil, pkg.Checksum{}, pkg.InvalidFileModeError(
|
|
0400,
|
|
)},
|
|
})
|
|
|
|
if c0, err := pkg.New(base); err != nil {
|
|
t.Fatalf("New: error = %v", err)
|
|
} else {
|
|
cureMany(t, c0, []cureStep{
|
|
{"cache hit ident", overrideIdent{
|
|
id: identifier,
|
|
}, wantPathname, testdataChecksum, nil},
|
|
|
|
{"cache miss checksum match", newStubFile(
|
|
pkg.KindHTTPGet,
|
|
testdataChecksum,
|
|
nil,
|
|
[]byte(testdata),
|
|
nil,
|
|
), base.Append(
|
|
"identifier",
|
|
pkg.Encode(testdataChecksum),
|
|
), testdataChecksum, nil},
|
|
})
|
|
}
|
|
}, pkg.MustDecode("St9rlE-mGZ5gXwiv_hzQ_B8bZP-UUvSNmf4nHUZzCMOumb6hKnheZSe0dmnuc4Q2")},
|
|
|
|
{"directory", nil, func(t *testing.T, base *check.Absolute, c *pkg.Cache) {
|
|
id := pkg.KindTar.Ident(
|
|
binary.LittleEndian.AppendUint64(nil, pkg.TarGzip),
|
|
overrideIdent{testdataChecksum, stubArtifact{}},
|
|
)
|
|
makeSample := func(work *check.Absolute, _ pkg.CacheDataFunc) error {
|
|
if err := os.Mkdir(work.String(), 0700); err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := os.WriteFile(
|
|
work.Append("check").String(),
|
|
[]byte{0, 0},
|
|
0400,
|
|
); err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := os.MkdirAll(work.Append(
|
|
"lib",
|
|
"pkgconfig",
|
|
).String(), 0700); err != nil {
|
|
return err
|
|
}
|
|
|
|
return os.Symlink(
|
|
"/proc/nonexistent/libedac.so",
|
|
work.Append(
|
|
"lib",
|
|
"libedac.so",
|
|
).String(),
|
|
)
|
|
}
|
|
wantChecksum := pkg.MustDecode(
|
|
"1TL00Qb8dcqayX7wTO8WNaraHvY6b-KCsctLDTrb64QBCmxj_-byK1HdIUwMaFEP",
|
|
)
|
|
wantPathname := base.Append(
|
|
"identifier",
|
|
pkg.Encode(id),
|
|
)
|
|
|
|
id0 := pkg.KindTar.Ident(
|
|
binary.LittleEndian.AppendUint64(nil, pkg.TarGzip),
|
|
overrideIdent{pkg.ID{}, stubArtifact{}},
|
|
)
|
|
wantPathname0 := base.Append(
|
|
"identifier",
|
|
pkg.Encode(id0),
|
|
)
|
|
|
|
makeGarbage := func(work *check.Absolute, wantErr error) error {
|
|
if err := os.Mkdir(work.String(), 0700); err != nil {
|
|
return err
|
|
}
|
|
|
|
mode := fs.FileMode(0)
|
|
if wantErr == nil {
|
|
mode = 0500
|
|
}
|
|
|
|
if err := os.MkdirAll(work.Append(
|
|
"lib",
|
|
"pkgconfig",
|
|
).String(), 0700); err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := os.WriteFile(work.Append(
|
|
"lib",
|
|
"check",
|
|
).String(), nil, 0400&mode); err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := os.Chmod(work.Append(
|
|
"lib",
|
|
"pkgconfig",
|
|
).String(), 0500&mode); err != nil {
|
|
return err
|
|
}
|
|
if err := os.Chmod(work.Append(
|
|
"lib",
|
|
).String(), 0500&mode); err != nil {
|
|
return err
|
|
}
|
|
|
|
return wantErr
|
|
}
|
|
|
|
cureMany(t, c, []cureStep{
|
|
{"initial directory", overrideChecksum{wantChecksum, overrideIdent{id, stubArtifact{
|
|
kind: pkg.KindTar,
|
|
cure: makeSample,
|
|
}}}, wantPathname, wantChecksum, nil},
|
|
|
|
{"identical identifier", overrideChecksum{wantChecksum, overrideIdent{id, stubArtifact{
|
|
kind: pkg.KindTar,
|
|
}}}, wantPathname, wantChecksum, nil},
|
|
|
|
{"identical checksum", overrideIdent{id0, stubArtifact{
|
|
kind: pkg.KindTar,
|
|
cure: makeSample,
|
|
}}, wantPathname0, wantChecksum, nil},
|
|
|
|
{"cure fault", overrideIdent{pkg.ID{0xff, 0}, stubArtifact{
|
|
kind: pkg.KindTar,
|
|
cure: func(work *check.Absolute, _ pkg.CacheDataFunc) error {
|
|
return makeGarbage(work, stub.UniqueError(0xcafe))
|
|
},
|
|
}}, nil, pkg.Checksum{}, stub.UniqueError(0xcafe)},
|
|
|
|
{"checksum mismatch", overrideChecksum{pkg.Checksum{}, overrideIdent{pkg.ID{0xff, 1}, stubArtifact{
|
|
kind: pkg.KindTar,
|
|
cure: func(work *check.Absolute, _ pkg.CacheDataFunc) error {
|
|
return makeGarbage(work, nil)
|
|
},
|
|
}}}, nil, pkg.Checksum{}, &pkg.ChecksumMismatchError{
|
|
Got: pkg.MustDecode(
|
|
"GbjlYMcHQANdfwL6qNGopBF99IscPTvCy95HSH1_kIF3eKjFDSLP0_iUUT0z8hiw",
|
|
),
|
|
}},
|
|
|
|
{"cache hit bad type", newStubFile(
|
|
pkg.KindHTTPGet,
|
|
pkg.ID{0xff, 2},
|
|
&wantChecksum,
|
|
[]byte(testdata), nil,
|
|
), nil, pkg.Checksum{}, pkg.InvalidFileModeError(
|
|
fs.ModeDir | 0500,
|
|
)},
|
|
|
|
{"loadData directory", overrideIdent{pkg.ID{0xff, 3}, stubArtifact{
|
|
kind: pkg.KindTar,
|
|
cure: func(work *check.Absolute, loadData pkg.CacheDataFunc) error {
|
|
_, err := loadData(overrideChecksumFile{checksum: wantChecksum})
|
|
return err
|
|
},
|
|
}}, nil, pkg.Checksum{}, &os.PathError{
|
|
Op: "read",
|
|
Path: base.Append(
|
|
"checksum",
|
|
pkg.Encode(wantChecksum),
|
|
).String(),
|
|
Err: syscall.EISDIR,
|
|
}},
|
|
|
|
{"no output", overrideIdent{pkg.ID{0xff, 4}, stubArtifact{
|
|
kind: pkg.KindTar,
|
|
cure: func(work *check.Absolute, loadData pkg.CacheDataFunc) error {
|
|
return nil
|
|
},
|
|
}}, nil, pkg.Checksum{}, pkg.NoOutputError{}},
|
|
|
|
{"file output", overrideIdent{pkg.ID{0xff, 5}, stubArtifact{
|
|
kind: pkg.KindTar,
|
|
cure: func(work *check.Absolute, loadData pkg.CacheDataFunc) error {
|
|
return os.WriteFile(work.String(), []byte{0}, 0400)
|
|
},
|
|
}}, nil, pkg.Checksum{}, errors.New("non-file artifact produced regular file")},
|
|
|
|
{"symlink output", overrideIdent{pkg.ID{0xff, 6}, stubArtifact{
|
|
kind: pkg.KindTar,
|
|
cure: func(work *check.Absolute, loadData pkg.CacheDataFunc) error {
|
|
return os.Symlink(work.String(), work.String())
|
|
},
|
|
}}, nil, pkg.Checksum{}, pkg.InvalidFileModeError(
|
|
fs.ModeSymlink | 0777,
|
|
)},
|
|
})
|
|
}, pkg.MustDecode("8OP6YxJAdRrhV2WSBt1BPD7oC_n2Qh7JqUMyVMoGvjDX83bDqq2hgVMNcdiBH_64")},
|
|
|
|
{"pending", nil, func(t *testing.T, base *check.Absolute, c *pkg.Cache) {
|
|
c.SetStrict(true)
|
|
|
|
wantErr := stub.UniqueError(0xcafe)
|
|
n, ready := make(chan struct{}), make(chan struct{})
|
|
go func() {
|
|
if _, _, err := c.Cure(overrideIdent{pkg.ID{0xff}, stubArtifact{
|
|
kind: pkg.KindTar,
|
|
cure: func(work *check.Absolute, loadData pkg.CacheDataFunc) error {
|
|
close(ready)
|
|
<-n
|
|
return wantErr
|
|
},
|
|
}}); !reflect.DeepEqual(err, wantErr) {
|
|
panic(fmt.Sprintf("Cure: error = %v, want %v", err, wantErr))
|
|
}
|
|
}()
|
|
|
|
<-ready
|
|
go func() {
|
|
if _, _, err := c.Cure(overrideIdent{pkg.ID{0xff}, stubArtifact{
|
|
kind: pkg.KindTar,
|
|
}}); !reflect.DeepEqual(err, wantErr) {
|
|
panic(fmt.Sprintf("Cure: error = %v, want %v", err, wantErr))
|
|
}
|
|
}()
|
|
|
|
// check cache activity while a cure is blocking
|
|
cureMany(t, c, []cureStep{
|
|
{"error passthrough", newStubFile(
|
|
pkg.KindHTTPGet,
|
|
pkg.ID{0xff, 1},
|
|
nil,
|
|
nil, stub.UniqueError(0xbad),
|
|
), nil, pkg.Checksum{}, stub.UniqueError(0xbad)},
|
|
|
|
{"file output", overrideIdent{pkg.ID{0xff, 2}, stubArtifact{
|
|
kind: pkg.KindTar,
|
|
cure: func(work *check.Absolute, loadData pkg.CacheDataFunc) error {
|
|
return os.WriteFile(work.String(), []byte{0}, 0400)
|
|
},
|
|
}}, nil, pkg.Checksum{}, errors.New("non-file artifact produced regular file")},
|
|
})
|
|
|
|
identPendingVal := reflect.ValueOf(c).Elem().FieldByName("identPending")
|
|
identPending := reflect.NewAt(
|
|
identPendingVal.Type(),
|
|
unsafe.Pointer(identPendingVal.UnsafeAddr()),
|
|
).Elem().Interface().(map[pkg.ID]<-chan struct{})
|
|
notify := identPending[pkg.ID{0xff}]
|
|
go close(n)
|
|
<-notify
|
|
}, pkg.MustDecode("E4vEZKhCcL2gPZ2Tt59FS3lDng-d_2SKa2i5G_RbDfwGn6EemptFaGLPUDiOa94C")},
|
|
}
|
|
checkWithCache(t, testCases)
|
|
}
|
|
|
|
func TestErrors(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
testCases := []struct {
|
|
name string
|
|
err error
|
|
want string
|
|
}{
|
|
{"ChecksumMismatchError", &pkg.ChecksumMismatchError{
|
|
Want: (pkg.Checksum)(bytes.Repeat([]byte{
|
|
0x75, 0xe6, 0x9d, 0x6d, 0xe7, 0x9f,
|
|
}, 8)),
|
|
}, "got AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" +
|
|
" instead of deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef"},
|
|
|
|
{"ResponseStatusError", pkg.ResponseStatusError(
|
|
http.StatusNotAcceptable,
|
|
), "the requested URL returned non-OK status: Not Acceptable"},
|
|
|
|
{"DisallowedTypeflagError", pkg.DisallowedTypeflagError(
|
|
tar.TypeChar,
|
|
), "disallowed typeflag '3'"},
|
|
|
|
{"InvalidFileModeError", pkg.InvalidFileModeError(
|
|
fs.ModeSymlink | 0777,
|
|
), "artifact did not produce a regular file or directory"},
|
|
|
|
{"NoOutputError", pkg.NoOutputError{
|
|
// empty struct
|
|
}, "artifact cured successfully but did not produce any output"},
|
|
}
|
|
for _, tc := range testCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
if got := tc.err.Error(); got != tc.want {
|
|
t.Errorf("Error: %q, want %q", got, tc.want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestNew(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
t.Run("nonexistent", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
wantErr := &os.PathError{
|
|
Op: "mkdir",
|
|
Path: container.Nonexistent,
|
|
Err: syscall.ENOENT,
|
|
}
|
|
if _, err := pkg.New(check.MustAbs(container.Nonexistent)); !reflect.DeepEqual(err, wantErr) {
|
|
t.Errorf("New: error = %#v, want %#v", err, wantErr)
|
|
}
|
|
})
|
|
|
|
t.Run("permission", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
tempDir := check.MustAbs(t.TempDir())
|
|
if err := os.Chmod(tempDir.String(), 0); err != nil {
|
|
t.Fatal(err)
|
|
} else {
|
|
t.Cleanup(func() {
|
|
if err = os.Chmod(tempDir.String(), 0700); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
})
|
|
}
|
|
|
|
wantErr := &os.PathError{
|
|
Op: "mkdir",
|
|
Path: tempDir.Append("cache").String(),
|
|
Err: syscall.EACCES,
|
|
}
|
|
if _, err := pkg.New(tempDir.Append("cache")); !reflect.DeepEqual(err, wantErr) {
|
|
t.Errorf("New: error = %#v, want %#v", err, wantErr)
|
|
}
|
|
})
|
|
}
|