aboutsummaryrefslogtreecommitdiffhomepage
path: root/internal/pkg/pkg_test.go
diff options
context:
space:
mode:
authorOphestra <cat@gensokyo.uk>2026-01-04 21:57:57 +0900
committerOphestra <cat@gensokyo.uk>2026-01-05 00:01:23 +0900
commit4897b0259edeb256a3d5cf77148758536e366e64 (patch)
tree23c9e0ff7bbc0f49d233381e6c17b4cc357a939c /internal/pkg/pkg_test.go
parentd6e4f85864a1e9b2aadc2e7f47e481242d5e946a (diff)
internal/pkg: improve artifact interface
This moves all cache I/O code to Cache. Artifact now only contains methods for constructing their actual contents. Signed-off-by: Ophestra <cat@gensokyo.uk>
Diffstat (limited to 'internal/pkg/pkg_test.go')
-rw-r--r--internal/pkg/pkg_test.go616
1 files changed, 380 insertions, 236 deletions
diff --git a/internal/pkg/pkg_test.go b/internal/pkg/pkg_test.go
index 915c7203..1479e2e6 100644
--- a/internal/pkg/pkg_test.go
+++ b/internal/pkg/pkg_test.go
@@ -5,6 +5,8 @@ import (
"bytes"
"crypto/sha512"
"encoding/binary"
+ "errors"
+ "fmt"
"io/fs"
"net/http"
"os"
@@ -12,6 +14,7 @@ import (
"reflect"
"syscall"
"testing"
+ "unsafe"
"hakurei.app/container"
"hakurei.app/container/check"
@@ -19,41 +22,131 @@ import (
"hakurei.app/internal/pkg"
)
-// A stubArtifact implements [Artifact] with hardcoded kind and identifier.
+// 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
- id pkg.ID
+ 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)
}
-func (a stubArtifact) Kind() pkg.Kind { return a.kind }
-func (a stubArtifact) ID() pkg.ID { return a.id }
-func (a stubArtifact) Hash() (pkg.Checksum, error) { panic("unreachable") }
-func (a stubArtifact) Pathname() (*check.Absolute, error) { panic("unreachable") }
+// 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
- kind pkg.Kind
- params []byte
- deps []pkg.Artifact
- want pkg.ID
+ name string
+ a pkg.Artifact
+ want pkg.ID
}{
- {"tar", pkg.KindTar, []byte{
- pkg.TarGzip, 0, 0, 0, 0, 0, 0, 0,
- }, []pkg.Artifact{
- stubArtifact{pkg.KindHTTP, pkg.ID{}},
- }, pkg.MustDecode("HnySzeLQvSBZuTUcvfmLEX_OmH4yJWWH788NxuLuv7kVn8_uPM6Ks4rqFWM2NZJY")},
+ {"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 := tc.kind.Ident(tc.params, tc.deps...); got != tc.want {
+ if got := pkg.Ident(tc.a); got != tc.want {
t.Errorf("Ident: %s, want %s",
pkg.Encode(got),
- pkg.Encode(tc.want))
+ pkg.Encode(tc.want),
+ )
}
})
}
@@ -118,6 +211,37 @@ func checkWithCache(t *testing.T, testCases []cacheTestCase) {
}
}
+// 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()
@@ -134,21 +258,9 @@ func TestCache(t *testing.T) {
return (pkg.Checksum)(h.Sum(nil))
}()
- testdataChecksumString := pkg.Encode(testdataChecksum)
-
testCases := []cacheTestCase{
{"file", nil, func(t *testing.T, base *check.Absolute, c *pkg.Cache) {
- wantErrNonexistent := &os.PathError{
- Op: "open",
- Path: base.Append(
- "identifier",
- testdataChecksumString,
- ).String(),
- Err: syscall.ENOENT,
- }
- if _, _, err := c.LoadFile(testdataChecksum); !reflect.DeepEqual(err, wantErrNonexistent) {
- t.Fatalf("LoadFile: error = %#v, want %#v", err, wantErrNonexistent)
- }
+ c.SetStrict(true)
identifier := (pkg.ID)(bytes.Repeat([]byte{
0x75, 0xe6, 0x9d, 0x6d, 0xe7, 0x9f,
@@ -165,154 +277,109 @@ func TestCache(t *testing.T) {
"cafebabecafebabecafebabecafebabecafebabecafebabecafebabecafebabe",
)
- // initial store
- if pathname, err := c.StoreFile(
- identifier,
- []byte(testdata),
- &testdataChecksum,
- true,
- ); err != nil {
- t.Fatalf("StoreFile: error = %v", err)
- } else if !pathname.Is(wantPathname) {
- t.Fatalf("StoreFile: pathname = %q, want %q", pathname, wantPathname)
- }
+ cureMany(t, c, []cureStep{
+ {"initial file", newStubFile(
+ pkg.KindHTTP,
+ identifier,
+ &testdataChecksum,
+ []byte(testdata), nil,
+ ), wantPathname, testdataChecksum, nil},
- // load or store, identical content
- if pathname, data, store, err := c.LoadOrStoreFile(identifier0, func() ([]byte, error) {
- return []byte(testdata), nil
- }, &testdataChecksum, true); err != nil {
- t.Fatalf("LoadOrStoreFile: error = %v", err)
- } else if !pathname.Is(wantPathname0) {
- t.Fatalf("LoadOrStoreFile: pathname = %q, want %q", pathname, wantPathname0)
- } else if string(data) != testdata {
- t.Fatalf("LoadOrStoreFile: data = %x, want %x", data, testdata)
- } else if !store {
- t.Fatal("LoadOrStoreFile did not store nonpresent entry")
- }
+ {"identical content", newStubFile(
+ pkg.KindHTTP,
+ identifier0,
+ &testdataChecksum,
+ []byte(testdata), nil,
+ ), wantPathname0, testdataChecksum, nil},
- // load or store, existing entry
- if pathname, data, store, err := c.LoadOrStoreFile(identifier, func() ([]byte, error) {
- return []byte(testdata), nil
- }, &testdataChecksum, true); err != nil {
- t.Fatalf("LoadOrStoreFile: error = %v", err)
- } else if !pathname.Is(wantPathname) {
- t.Fatalf("LoadOrStoreFile: pathname = %q, want %q", pathname, wantPathname)
- } else if string(data) != testdata {
- t.Fatalf("LoadOrStoreFile: data = %x, want %x", data, testdata)
- } else if store {
- t.Fatal("LoadOrStoreFile stored over present entry")
- }
+ {"existing entry", newStubFile(
+ pkg.KindHTTP,
+ identifier,
+ &testdataChecksum,
+ []byte(testdata), nil,
+ ), wantPathname, testdataChecksum, nil},
- // load, existing entry
- if pathname, data, err := c.LoadFile(identifier0); err != nil {
- t.Fatalf("LoadFile: error = %v", err)
- } else if !pathname.Is(wantPathname0) {
- t.Fatalf("LoadFile: pathname = %q, want %q", pathname, wantPathname0)
- } else if string(data) != testdata {
- t.Fatalf("LoadFile: data = %x, want %x", data, testdata)
- }
+ {"checksum mismatch", newStubFile(
+ pkg.KindHTTP,
+ pkg.ID{0xff, 0},
+ new(pkg.Checksum),
+ []byte(testdata), nil,
+ ), nil, pkg.Checksum{}, &pkg.ChecksumMismatchError{
+ Got: testdataChecksum,
+ }},
- // checksum mismatch
- wantErrChecksum := &pkg.ChecksumMismatchError{
- Got: testdataChecksum,
- }
- if _, err := c.StoreFile(
- testdataChecksum,
- []byte(testdata),
- new(pkg.Checksum),
- true,
- ); !reflect.DeepEqual(err, wantErrChecksum) {
- t.Fatalf("StoreFile: error = %#v, want %#v", err, wantErrChecksum)
- }
+ {"store without validation", newStubFile(
+ pkg.KindHTTP,
+ 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},
- // verify failed store
- if _, _, err := c.LoadFile(testdataChecksum); !reflect.DeepEqual(err, wantErrNonexistent) {
- t.Fatalf("LoadFile: error = %#v, want %#v", err, wantErrNonexistent)
- }
+ {"error passthrough", newStubFile(
+ pkg.KindHTTP,
+ pkg.ID{0xff, 1},
+ nil,
+ nil, stub.UniqueError(0xcafe),
+ ), nil, pkg.Checksum{}, stub.UniqueError(0xcafe)},
- // store, same identifier
- wantPathnameF := base.Append(
- "identifier",
- testdataChecksumString,
- )
- if pathname, err := c.StoreFile(
- testdataChecksum,
- []byte(testdata),
- &testdataChecksum,
- true,
- ); err != nil {
- t.Fatalf("StoreFile: error = %v", err)
- } else if !pathname.Is(wantPathnameF) {
- t.Fatalf("StoreFile: pathname = %q, want %q", pathname, wantPathnameF)
- }
-
- // load, same identifier
- if pathname, data, err := c.LoadFile(testdataChecksum); err != nil {
- t.Fatalf("LoadFile: error = %v", err)
- } else if !pathname.Is(wantPathnameF) {
- t.Fatalf("LoadFile: pathname = %q, want %q", pathname, wantPathnameF)
- } else if string(data) != testdata {
- t.Fatalf("LoadFile: data = %x, want %x", data, testdata)
- }
+ {"error caching", newStubFile(
+ pkg.KindHTTP,
+ pkg.ID{0xff, 1},
+ nil,
+ nil, nil,
+ ), nil, pkg.Checksum{}, stub.UniqueError(0xcafe)},
- // store without validation
- wantChecksum := 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,
- }
- var gotChecksum pkg.Checksum
- wantPathnameG := base.Append(
- "identifier",
- pkg.Encode(wantChecksum),
- )
- if pathname, err := c.StoreFile(
- wantChecksum,
- []byte{0},
- &gotChecksum,
- false,
- ); err != nil {
- t.Fatalf("StoreFile: error = %#v", err)
- } else if !pathname.Is(wantPathnameG) {
- t.Fatalf("StoreFile: pathname = %q, want %q", pathname, wantPathnameG)
- } else if gotChecksum != wantChecksum {
- t.Fatalf("StoreFile: buf = %x, want %x", gotChecksum, wantChecksum)
- }
+ {"cache hit bad type", overrideChecksum{testdataChecksum, overrideIdent{pkg.ID{0xff, 2}, stubArtifact{
+ kind: pkg.KindTar,
+ }}}, nil, pkg.Checksum{}, pkg.InvalidFileModeError(
+ 0400,
+ )},
+ })
- // makeData passthrough
- var zeroIdent pkg.ID
- wantErrPassthrough := stub.UniqueError(0xcafe)
- if _, _, _, err := c.LoadOrStoreFile(zeroIdent, func() ([]byte, error) {
- return nil, wantErrPassthrough
- }, new(pkg.Checksum), true); !reflect.DeepEqual(err, wantErrPassthrough) {
- t.Fatalf("LoadOrStoreFile: error = %#v, want %#v", err, wantErrPassthrough)
- }
+ 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},
- // verify failed store
- wantErrNonexistentZero := &os.PathError{
- Op: "open",
- Path: base.Append(
- "identifier",
- "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
- ).String(),
- Err: syscall.ENOENT,
- }
- if _, _, err := c.LoadFile(zeroIdent); !reflect.DeepEqual(err, wantErrNonexistentZero) {
- t.Fatalf("LoadFile: error = %#v, want %#v", err, wantErrNonexistentZero)
+ {"cache miss checksum match", newStubFile(
+ pkg.KindHTTP,
+ 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),
- stubArtifact{pkg.KindHTTP, testdataChecksum},
+ overrideIdent{testdataChecksum, stubArtifact{}},
)
- makeSample := func(work *check.Absolute) error {
+ 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},
@@ -344,59 +411,22 @@ func TestCache(t *testing.T) {
pkg.Encode(id),
)
- if pathname, store, err := c.Store(
- id,
- makeSample,
- &wantChecksum,
- true,
- ); err != nil {
- t.Fatalf("Store: error = %v", err)
- } else if !store {
- t.Fatal("Store did not store nonpresent entry")
- } else if !pathname.Is(wantPathname) {
- t.Fatalf("Store: pathname = %q, want %q", pathname, wantPathname)
- }
-
- // check lookup
- if pathname, store, err := c.Store(
- id,
- nil,
- &wantChecksum,
- true,
- ); err != nil {
- t.Fatalf("Store: error = %v", err)
- } else if store {
- t.Fatal("Store stored over present entry")
- } else if !pathname.Is(wantPathname) {
- t.Fatalf("Store: pathname = %q, want %q", pathname, wantPathname)
- }
-
- // check exist
id0 := pkg.KindTar.Ident(
binary.LittleEndian.AppendUint64(nil, pkg.TarGzip),
- stubArtifact{pkg.KindHTTP, pkg.ID{}},
+ overrideIdent{pkg.ID{}, stubArtifact{}},
)
wantPathname0 := base.Append(
"identifier",
pkg.Encode(id0),
)
- if pathname, store, err := c.Store(
- id0,
- makeSample,
- &wantChecksum,
- true,
- ); err != nil {
- t.Fatalf("Store: error = %v", err)
- } else if !store {
- t.Fatal("Store did not store nonpresent entry")
- } else if !pathname.Is(wantPathname0) {
- t.Fatalf("Store: pathname = %q, want %q", pathname, wantPathname0)
- }
- var wantErrMakeGarbage error
- makeGarbage := func(work *check.Absolute) error {
+ makeGarbage := func(work *check.Absolute, wantErr error) error {
+ if err := os.Mkdir(work.String(), 0700); err != nil {
+ return err
+ }
+
mode := fs.FileMode(0)
- if wantErrMakeGarbage == nil {
+ if wantErr == nil {
mode = 0500
}
@@ -426,38 +456,144 @@ func TestCache(t *testing.T) {
return err
}
- return wantErrMakeGarbage
+ return wantErr
}
- // check makeArtifact fault
- wantErrMakeGarbage = stub.UniqueError(0xcafe)
- if _, store, err := c.Store(
- pkg.ID{},
- makeGarbage,
- nil,
- false,
- ); !reflect.DeepEqual(err, wantErrMakeGarbage) {
- t.Fatalf("Store: error = %#v, want %#v", err, wantErrMakeGarbage)
- } else if !store {
- t.Fatal("Store did not store nonpresent entry")
- }
+ cureMany(t, c, []cureStep{
+ {"initial directory", overrideChecksum{wantChecksum, overrideIdent{id, stubArtifact{
+ kind: pkg.KindTar,
+ cure: makeSample,
+ }}}, wantPathname, wantChecksum, nil},
- // checksum mismatch
- wantErrMakeGarbage = nil
- wantErrMismatch := &pkg.ChecksumMismatchError{
- Got: pkg.MustDecode("GbjlYMcHQANdfwL6qNGopBF99IscPTvCy95HSH1_kIF3eKjFDSLP0_iUUT0z8hiw"),
- }
- if _, store, err := c.Store(
- pkg.ID{},
- makeGarbage,
- new(pkg.Checksum),
- true,
- ); !reflect.DeepEqual(err, wantErrMismatch) {
- t.Fatalf("Store: error = %v, want %v", err, wantErrMismatch)
- } else if !store {
- t.Fatal("Store did not store nonpresent entry")
- }
+ {"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.KindHTTP,
+ 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.KindHTTP,
+ 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)
}
@@ -484,6 +620,14 @@ func TestErrors(t *testing.T) {
{"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) {