package pkg_test import ( "archive/tar" "bytes" "crypto/sha512" "encoding/binary" "io/fs" "net/http" "os" "path/filepath" "reflect" "syscall" "testing" "hakurei.app/container" "hakurei.app/container/check" "hakurei.app/container/stub" "hakurei.app/internal/pkg" ) // A stubArtifact implements [Artifact] with hardcoded kind and identifier. type stubArtifact struct { kind pkg.Kind id pkg.ID } 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") } func TestIdent(t *testing.T) { t.Parallel() testCases := []struct { name string kind pkg.Kind params []byte deps []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")}, } 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 { 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, }) } }) } } 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)) }() 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) } 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", ) // 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) } // 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") } // 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") } // 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 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) } // verify failed store if _, _, err := c.LoadFile(testdataChecksum); !reflect.DeepEqual(err, wantErrNonexistent) { t.Fatalf("LoadFile: error = %#v, want %#v", err, wantErrNonexistent) } // 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) } // 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) } // 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) } // 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) } }, pkg.MustDecode("G4u4W77C3u46oSAzwPTERKbS9h76iIvcd7Zl8p8Y6hTMb4_QGpH0Glg_DIJg-Usa")}, {"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}, ) makeSample := func(work *check.Absolute) error { 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), ) 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{}}, ) 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 { mode := fs.FileMode(0) if wantErrMakeGarbage == 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 wantErrMakeGarbage } // 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") } // 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") } }, pkg.MustDecode("8OP6YxJAdRrhV2WSBt1BPD7oC_n2Qh7JqUMyVMoGvjDX83bDqq2hgVMNcdiBH_64")}, } 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'"}, } 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) } }) }