@@ -20,6 +20,7 @@ import (
2020 "io/fs"
2121 "net/http"
2222 "net/http/httptest"
23+ "net/url"
2324 "os"
2425 "path/filepath"
2526 "runtime"
@@ -656,3 +657,276 @@ func TestLoadSystemKeyring(t *testing.T) {
656657 })
657658 }
658659}
660+
661+ // TestFetchPackage_original is the original TestFetchPackage added back to support FetchPackage method
662+ func TestFetchPackage_original (t * testing.T ) {
663+ var (
664+ repo = Repository {URI : fmt .Sprintf ("%s/%s" , testAlpineRepos , testArch )}
665+ packages = []* Package {& testPkg }
666+ repoWithIndex = repo .WithIndex (& APKIndex {
667+ Packages : packages ,
668+ })
669+ testEtag = "testetag"
670+ pkg = NewRepositoryPackage (& testPkg , repoWithIndex )
671+ ctx = context .Background ()
672+ )
673+ prepLayout := func (t * testing.T , tr http.RoundTripper , cache string ) * APK {
674+ src := apkfs .NewMemFS ()
675+ err := src .MkdirAll ("usr/lib/apk/db" , 0o755 )
676+ require .NoError (t , err , "unable to mkdir /usr/lib/apk/db" )
677+
678+ opts := []Option {WithFS (src ), WithIgnoreMknodErrors (ignoreMknodErrors ), WithTransport (tr )}
679+ if cache != "" {
680+ opts = append (opts , WithCache (cache , false , NewCache (false )))
681+ }
682+ a , err := New (ctx , opts ... )
683+ require .NoError (t , err , "unable to create APK" )
684+ err = a .InitDB (ctx )
685+ require .NoError (t , err )
686+
687+ // set a client so we use local testdata instead of heading out to the Internet each time
688+ return a
689+ }
690+ t .Run ("no cache" , func (t * testing.T ) {
691+ a := prepLayout (t , & testLocalTransport {root : testPrimaryPkgDir , basenameOnly : true }, "" )
692+ _ , err := a .FetchPackage (ctx , pkg )
693+ require .NoErrorf (t , err , "unable to install package" )
694+ })
695+ t .Run ("cache miss no network" , func (t * testing.T ) {
696+ // we use a transport that always returns a 404 so we know we're not hitting the network
697+ // it should fail for a cache hit
698+ tmpDir := t .TempDir ()
699+ a := prepLayout (t , & testLocalTransport {fail : true }, tmpDir )
700+ _ , err := a .FetchPackage (ctx , pkg )
701+ require .Error (t , err , "should fail when no cache and no network" )
702+ })
703+ /*
704+ These tests commented out since a.expandPackage is no longer available
705+ t.Run("cache miss network should fill cache", func(t *testing.T) {
706+ tmpDir := t.TempDir()
707+ a := prepLayout(t, &testLocalTransport{root: testPrimaryPkgDir, basenameOnly: true}, tmpDir)
708+ // fill the cache
709+ repoDir := filepath.Join(tmpDir, url.QueryEscape(testAlpineRepos), testArch)
710+ err := os.MkdirAll(repoDir, 0o755)
711+ require.NoError(t, err, "unable to mkdir cache")
712+
713+ cacheApkFile := filepath.Join(repoDir, testPkgFilename)
714+ cacheApkDir := strings.TrimSuffix(cacheApkFile, ".apk")
715+
716+ _, err = a.expandPackage(ctx, pkg)
717+ require.NoErrorf(t, err, "unable to install pkg")
718+ // check that the package file is in place
719+ _, err = os.Stat(cacheApkDir)
720+ require.NoError(t, err, "apk file not found in cache")
721+ // check that the contents are the same
722+ exp, err := a.cachedPackage(ctx, pkg, cacheApkDir)
723+ if err != nil {
724+ t.Logf("did not find cachedPackage(%q) in %s: %v", pkg.Name, cacheApkDir, err)
725+ files, err := os.ReadDir(cacheApkDir)
726+ require.NoError(t, err, "listing "+cacheApkDir)
727+ for _, f := range files {
728+ t.Logf(" found %q", f.Name())
729+ }
730+ }
731+ require.NoError(t, err, "unable to read cache apk file")
732+ f, err := exp.APK()
733+ require.NoError(t, err, "unable to read cached files as apk")
734+ defer f.Close()
735+
736+ apk1, err := io.ReadAll(f)
737+ require.NoError(t, err, "unable to read cached apk bytes")
738+
739+ apk2, err := os.ReadFile(filepath.Join(testPrimaryPkgDir, testPkgFilename))
740+ require.NoError(t, err, "unable to read previous apk file")
741+ require.Equal(t, apk1, apk2, "apk files do not match")
742+ })
743+ t.Run("handle missing cache files when expanding APK", func(t *testing.T) {
744+ tmpDir := t.TempDir()
745+ a := prepLayout(t, http.DefaultTransport, tmpDir)
746+
747+ // Fill the cache
748+ exp, err := a.expandPackage(ctx, pkg)
749+ require.NoError(t, err, "unable to expand package")
750+ _, err = os.Stat(exp.TarFile)
751+ require.NoError(t, err, "unable to stat cached tar file")
752+
753+ // Delete the tar file from the cache
754+ require.NoError(t, os.Remove(exp.TarFile), "unable to delete cached tar file")
755+ _, err = os.Stat(exp.TarFile)
756+ require.ErrorIs(t, err, os.ErrNotExist, "unexpectedly able to stat cached tar file that should have been deleted")
757+
758+ // Expand the package again, this should re-populate the cache.
759+ exp2, err := a.expandPackage(ctx, pkg)
760+ require.NoError(t, err, "unable to expandPackage after deleting cached tar file")
761+ _, err = os.Stat(exp2.TarFile)
762+ require.NoError(t, err, "unable to stat cached tar file")
763+
764+ // Delete and recreate the tar file from the cache (changing its inodes)
765+ bs, err := os.ReadFile(exp2.TarFile)
766+ require.NoError(t, err, "unable to read cached tar file")
767+ require.NoError(t, os.Remove(exp2.TarFile), "unable to delete cached tar file")
768+ require.NoError(t, os.WriteFile(exp2.TarFile, bs, 0o644), "unable to recreate cached tar file")
769+
770+ // Ensure that the underlying reader is different (i.e. we re-read the file)
771+ exp3, err := a.expandPackage(ctx, pkg)
772+ require.NoError(t, err, "unable to expandPackage after deleting and recreating cached tar file")
773+ require.NotEqual(t, exp2.TarFS.UnderlyingReader(), exp3.TarFS.UnderlyingReader())
774+
775+ // We should be able to read the APK contents
776+ rc, err := exp3.APK()
777+ require.NoError(t, err, "unable to get reader for APK()")
778+ _, err = io.ReadAll(rc)
779+ require.NoError(t, err, "unable to read APK contents")
780+ })
781+ */
782+ t .Run ("cache hit no etag" , func (t * testing.T ) {
783+ tmpDir := t .TempDir ()
784+ a := prepLayout (t ,
785+ & testLocalTransport {root : testAlternatePkgDir , basenameOnly : true , headers : map [string ][]string {http .CanonicalHeaderKey ("etag" ): {testEtag }}},
786+ tmpDir )
787+ // fill the cache
788+ repoDir := filepath .Join (tmpDir , url .QueryEscape (testAlpineRepos ), testArch )
789+ err := os .MkdirAll (repoDir , 0o755 )
790+ require .NoError (t , err , "unable to mkdir cache" )
791+
792+ contents , err := os .ReadFile (filepath .Join (testPrimaryPkgDir , testPkgFilename ))
793+ require .NoError (t , err , "unable to read apk file" )
794+ cacheApkFile := filepath .Join (repoDir , testPkgFilename )
795+ err = os .WriteFile (cacheApkFile , contents , 0o644 ) //nolint:gosec // we're writing a test file
796+ require .NoError (t , err , "unable to write cache apk file" )
797+
798+ _ , err = a .FetchPackage (ctx , pkg )
799+ require .NoErrorf (t , err , "unable to install pkg" )
800+ // check that the package file is in place
801+ _ , err = os .Stat (cacheApkFile )
802+ require .NoError (t , err , "apk file not found in cache" )
803+ // check that the contents are the same as the original
804+ apk1 , err := os .ReadFile (cacheApkFile )
805+ require .NoError (t , err , "unable to read cache apk file" )
806+ require .Equal (t , apk1 , contents , "apk files do not match" )
807+ })
808+ t .Run ("cache hit etag match" , func (t * testing.T ) {
809+ tmpDir := t .TempDir ()
810+ a := prepLayout (t ,
811+ & testLocalTransport {root : testAlternatePkgDir , basenameOnly : true , headers : map [string ][]string {http .CanonicalHeaderKey ("etag" ): {testEtag }}},
812+ tmpDir )
813+ // fill the cache
814+ repoDir := filepath .Join (tmpDir , url .QueryEscape (testAlpineRepos ), testArch )
815+ err := os .MkdirAll (repoDir , 0o755 )
816+ require .NoError (t , err , "unable to mkdir cache" )
817+
818+ contents , err := os .ReadFile (filepath .Join (testPrimaryPkgDir , testPkgFilename ))
819+ require .NoError (t , err , "unable to read apk file" )
820+ cacheApkFile := filepath .Join (repoDir , testPkgFilename )
821+ err = os .WriteFile (cacheApkFile , contents , 0o644 ) //nolint:gosec // we're writing a test file
822+ require .NoError (t , err , "unable to write cache apk file" )
823+ err = os .WriteFile (cacheApkFile + ".etag" , []byte (testEtag ), 0o644 ) //nolint:gosec // we're writing a test file
824+ require .NoError (t , err , "unable to write etag" )
825+
826+ _ , err = a .FetchPackage (ctx , pkg )
827+ require .NoErrorf (t , err , "unable to install pkg" )
828+ // check that the package file is in place
829+ _ , err = os .Stat (cacheApkFile )
830+ require .NoError (t , err , "apk file not found in cache" )
831+ // check that the contents are the same as the original
832+ apk1 , err := os .ReadFile (cacheApkFile )
833+ require .NoError (t , err , "unable to read cache apk file" )
834+ require .Equal (t , apk1 , contents , "apk files do not match" )
835+ })
836+ t .Run ("cache hit etag miss" , func (t * testing.T ) {
837+ tmpDir := t .TempDir ()
838+ a := prepLayout (t ,
839+ & testLocalTransport {root : testAlternatePkgDir , basenameOnly : true , headers : map [string ][]string {http .CanonicalHeaderKey ("etag" ): {testEtag + "abcdefg" }}},
840+ tmpDir )
841+ // fill the cache
842+ repoDir := filepath .Join (tmpDir , url .QueryEscape (testAlpineRepos ), testArch )
843+ err := os .MkdirAll (repoDir , 0o755 )
844+ require .NoError (t , err , "unable to mkdir cache" )
845+
846+ contents , err := os .ReadFile (filepath .Join (testPrimaryPkgDir , testPkgFilename ))
847+ require .NoError (t , err , "unable to read apk file" )
848+ cacheApkFile := filepath .Join (repoDir , testPkgFilename )
849+ err = os .WriteFile (cacheApkFile , contents , 0o644 ) //nolint:gosec // we're writing a test file
850+ require .NoError (t , err , "unable to write cache apk file" )
851+ err = os .WriteFile (cacheApkFile + ".etag" , []byte (testEtag ), 0o644 ) //nolint:gosec // we're writing a test file
852+ require .NoError (t , err , "unable to write etag" )
853+
854+ _ , err = a .FetchPackage (ctx , pkg )
855+ require .NoErrorf (t , err , "unable to install pkg" )
856+ // check that the package file is in place
857+ _ , err = os .Stat (cacheApkFile )
858+ require .NoError (t , err , "apk file not found in cache" )
859+ // check that the contents are the same as the original
860+ apk1 , err := os .ReadFile (cacheApkFile )
861+ require .NoError (t , err , "unable to read cache apk file" )
862+ apk2 , err := os .ReadFile (filepath .Join (testAlternatePkgDir , testPkgFilename ))
863+ require .NoError (t , err , "unable to read testdata apk file" )
864+ require .Equal (t , apk1 , apk2 , "apk files do not match" )
865+ })
866+ }
867+
868+ // TestAuth_good_original is the original TestAuth_good added back to support FetchPackage method
869+ func TestAuth_good_original (t * testing.T ) {
870+ called := false
871+ s := httptest .NewServer (http .HandlerFunc (func (w http.ResponseWriter , r * http.Request ) {
872+ called = true
873+ if gotuser , gotpass , ok := r .BasicAuth (); ! ok || gotuser != testUser || gotpass != testPass {
874+ w .WriteHeader (http .StatusForbidden )
875+ return
876+ }
877+ http .FileServer (http .Dir (testPrimaryPkgDir )).ServeHTTP (w , r )
878+ }))
879+ defer s .Close ()
880+ host := strings .TrimPrefix (s .URL , "http://" )
881+
882+ repo := Repository {URI : s .URL }
883+ repoWithIndex := repo .WithIndex (& APKIndex {Packages : []* Package {& testPkg }})
884+ pkg := NewRepositoryPackage (& testPkg , repoWithIndex )
885+ ctx := context .Background ()
886+
887+ src := apkfs .NewMemFS ()
888+ err := src .MkdirAll ("usr/lib/apk/db" , 0o755 )
889+ require .NoError (t , err , "unable to mkdir /usr/lib/apk/db" )
890+
891+ a , err := New (ctx , WithFS (src ), WithAuthenticator (auth .StaticAuth (host , testUser , testPass )))
892+ require .NoError (t , err , "unable to create APK" )
893+ err = a .InitDB (ctx )
894+ require .NoError (t , err )
895+
896+ _ , err = a .FetchPackage (ctx , pkg )
897+ require .NoErrorf (t , err , "unable to install package" )
898+ require .True (t , called , "did not make request" )
899+ }
900+
901+ // TestAuth_bad_original is the original TestAuth_bad added back to support FetchPackage method
902+ func TestAuth_bad_original (t * testing.T ) {
903+ called := false
904+ s := httptest .NewServer (http .HandlerFunc (func (w http.ResponseWriter , r * http.Request ) {
905+ called = true
906+ if gotuser , gotpass , ok := r .BasicAuth (); ! ok || gotuser != testUser || gotpass != testPass {
907+ w .WriteHeader (http .StatusForbidden )
908+ return
909+ }
910+ http .FileServer (http .Dir (testPrimaryPkgDir )).ServeHTTP (w , r )
911+ }))
912+ defer s .Close ()
913+ host := strings .TrimPrefix (s .URL , "http://" )
914+
915+ repo := Repository {URI : s .URL }
916+ repoWithIndex := repo .WithIndex (& APKIndex {Packages : []* Package {& testPkg }})
917+ pkg := NewRepositoryPackage (& testPkg , repoWithIndex )
918+ ctx := context .Background ()
919+
920+ src := apkfs .NewMemFS ()
921+ err := src .MkdirAll ("usr/lib/apk/db" , 0o755 )
922+ require .NoError (t , err , "unable to mkdir /usr/lib/apk/db" )
923+
924+ a , err := New (ctx , WithFS (src ), WithAuthenticator (auth .StaticAuth (host , "baduser" , "badpass" )))
925+ require .NoError (t , err , "unable to create APK" )
926+ err = a .InitDB (ctx )
927+ require .NoError (t , err )
928+
929+ _ , err = a .FetchPackage (ctx , pkg )
930+ require .Error (t , err , "should fail with bad auth" )
931+ require .True (t , called , "did not make request" )
932+ }
0 commit comments