github.com/buildpacks/pack@v0.33.3-0.20240516162812-884dd1837311/pkg/archive/archive_test.go (about)

     1  package archive_test
     2  
     3  import (
     4  	"archive/tar"
     5  	"net"
     6  	"os"
     7  	"path/filepath"
     8  	"runtime"
     9  	"strings"
    10  	"testing"
    11  
    12  	"github.com/pkg/errors"
    13  
    14  	"github.com/heroku/color"
    15  	"github.com/sclevine/spec"
    16  	"github.com/sclevine/spec/report"
    17  
    18  	"github.com/buildpacks/pack/pkg/archive"
    19  	h "github.com/buildpacks/pack/testhelpers"
    20  )
    21  
    22  func TestArchive(t *testing.T) {
    23  	color.Disable(true)
    24  	defer color.Disable(false)
    25  	spec.Run(t, "Archive", testArchive, spec.Sequential(), spec.Report(report.Terminal{}))
    26  }
    27  
    28  func testArchive(t *testing.T, when spec.G, it spec.S) {
    29  	var (
    30  		tmpDir string
    31  	)
    32  
    33  	it.Before(func() {
    34  		var err error
    35  		tmpDir, err = os.MkdirTemp("", "create-tar-test")
    36  		if err != nil {
    37  			t.Fatalf("failed to create tmp dir %s: %s", tmpDir, err)
    38  		}
    39  	})
    40  
    41  	it.After(func() {
    42  		if err := os.RemoveAll(tmpDir); err != nil {
    43  			if runtime.GOOS != "windows" {
    44  				// skip "The process cannot access the file because it is being used by another process" on windows
    45  				t.Fatalf("failed to clean up tmp dir %s: %s", tmpDir, err)
    46  			}
    47  		}
    48  	})
    49  
    50  	when("#ReadDirAsTar", func() {
    51  		var src string
    52  		it.Before(func() {
    53  			src = filepath.Join("testdata", "dir-to-tar")
    54  		})
    55  
    56  		it("returns a TarReader of the dir", func() {
    57  			rc := archive.ReadDirAsTar(src, "/nested/dir/dir-in-archive", 1234, 2345, 0777, true, false, nil)
    58  
    59  			tr := tar.NewReader(rc)
    60  			verify := h.NewTarVerifier(t, tr, 1234, 2345)
    61  			verify.NextFile("/nested/dir/dir-in-archive/some-file.txt", "some-content", int64(os.ModePerm))
    62  			verify.NextDirectory("/nested/dir/dir-in-archive/sub-dir", int64(os.ModePerm))
    63  			if runtime.GOOS != "windows" {
    64  				verify.NextSymLink("/nested/dir/dir-in-archive/sub-dir/link-file", "../some-file.txt")
    65  				verify.NoMoreFilesExist()
    66  				h.AssertNil(t, rc.Close())
    67  			}
    68  		})
    69  		when("includeRoot", func() {
    70  			it("includes a modified root entry", func() {
    71  				rc := archive.ReadDirAsTar(src, "/nested/dir/dir-in-archive", 1234, 2345, 0777, true, true, nil)
    72  				tr := tar.NewReader(rc)
    73  				verify := h.NewTarVerifier(t, tr, 1234, 2345)
    74  				verify.NextDirectory("/nested/dir/dir-in-archive", int64(os.ModePerm))
    75  			})
    76  		})
    77  	})
    78  
    79  	when("#ReadZipAsTar", func() {
    80  		var src string
    81  		it.Before(func() {
    82  			src = filepath.Join("testdata", "zip-to-tar.zip")
    83  		})
    84  
    85  		it("returns a TarReader of the dir", func() {
    86  			rc := archive.ReadZipAsTar(src, "/nested/dir/dir-in-archive", 1234, 2345, 0777, true, nil)
    87  
    88  			tr := tar.NewReader(rc)
    89  			verify := h.NewTarVerifier(t, tr, 1234, 2345)
    90  			verify.NextFile("/nested/dir/dir-in-archive/some-file.txt", "some-content", int64(os.ModePerm))
    91  			verify.NextDirectory("/nested/dir/dir-in-archive/sub-dir", int64(os.ModePerm))
    92  			verify.NextSymLink("/nested/dir/dir-in-archive/sub-dir/link-file", "../some-file.txt")
    93  
    94  			verify.NoMoreFilesExist()
    95  			h.AssertNil(t, rc.Close())
    96  		})
    97  	})
    98  
    99  	when("#ReadTarEntry", func() {
   100  		var (
   101  			err     error
   102  			tarFile *os.File
   103  		)
   104  		it.Before(func() {
   105  			tarFile, err = os.CreateTemp(tmpDir, "file.tgz")
   106  			h.AssertNil(t, err)
   107  		})
   108  
   109  		it.After(func() {
   110  			_ = tarFile.Close()
   111  		})
   112  
   113  		when("tgz has the path", func() {
   114  			it.Before(func() {
   115  				err = archive.CreateSingleFileTar(tarFile.Name(), "file1", "file-1 content")
   116  				h.AssertNil(t, err)
   117  			})
   118  
   119  			it("returns the file contents", func() {
   120  				_, contents, err := archive.ReadTarEntry(tarFile, "file1")
   121  				h.AssertNil(t, err)
   122  				h.AssertEq(t, string(contents), "file-1 content")
   123  			})
   124  		})
   125  
   126  		when("tgz has ./path", func() {
   127  			it.Before(func() {
   128  				err = archive.CreateSingleFileTar(tarFile.Name(), "./file1", "file-1 content")
   129  				h.AssertNil(t, err)
   130  			})
   131  
   132  			it("returns the file contents", func() {
   133  				_, contents, err := archive.ReadTarEntry(tarFile, "file1")
   134  				h.AssertNil(t, err)
   135  				h.AssertEq(t, string(contents), "file-1 content")
   136  			})
   137  		})
   138  
   139  		when("path doesn't exist", func() {
   140  			it.Before(func() {
   141  				err = archive.CreateSingleFileTar(tarFile.Name(), "file1", "file-1 content")
   142  				h.AssertNil(t, err)
   143  			})
   144  
   145  			it("returns the file contents", func() {
   146  				_, _, err := archive.ReadTarEntry(tarFile, "file2")
   147  				h.AssertError(t, err, "could not find entry path")
   148  				h.AssertTrue(t, archive.IsEntryNotExist(err))
   149  			})
   150  		})
   151  
   152  		when("reader isn't tar", func() {
   153  			it("returns the file contents", func() {
   154  				reader := strings.NewReader("abcde")
   155  				_, _, err := archive.ReadTarEntry(reader, "file1")
   156  				h.AssertError(t, err, "get next tar entry")
   157  			})
   158  		})
   159  	})
   160  
   161  	when("#CreateSingleFileTarReader", func() {
   162  		it("returns the file contents", func() {
   163  			rc := archive.CreateSingleFileTarReader("file1", "file-1 content")
   164  			_, contents, err := archive.ReadTarEntry(rc, "file1")
   165  			h.AssertNil(t, err)
   166  			h.AssertEq(t, string(contents), "file-1 content")
   167  		})
   168  	})
   169  
   170  	when("#IsEntryNotExist", func() {
   171  		it("works", func() {
   172  			h.AssertTrue(t, archive.IsEntryNotExist(errors.Wrap(archive.ErrEntryNotExist, "something")))
   173  			h.AssertFalse(t, archive.IsEntryNotExist(errors.New("something not err not exist")))
   174  		})
   175  	})
   176  
   177  	when("#WriteDirToTar", func() {
   178  		var src string
   179  		it.Before(func() {
   180  			src = filepath.Join("testdata", "dir-to-tar")
   181  		})
   182  
   183  		when("mode is set to 0777", func() {
   184  			it("writes a tar to the dest dir with 0777", func() {
   185  				fh, err := os.Create(filepath.Join(tmpDir, "some.tar"))
   186  				h.AssertNil(t, err)
   187  
   188  				tw := tar.NewWriter(fh)
   189  
   190  				err = archive.WriteDirToTar(tw, src, "/nested/dir/dir-in-archive", 1234, 2345, 0777, true, false, nil)
   191  				h.AssertNil(t, err)
   192  				h.AssertNil(t, tw.Close())
   193  				h.AssertNil(t, fh.Close())
   194  
   195  				file, err := os.Open(filepath.Join(tmpDir, "some.tar"))
   196  				h.AssertNil(t, err)
   197  				defer file.Close()
   198  
   199  				tr := tar.NewReader(file)
   200  
   201  				verify := h.NewTarVerifier(t, tr, 1234, 2345)
   202  				verify.NextFile("/nested/dir/dir-in-archive/some-file.txt", "some-content", int64(os.ModePerm))
   203  				verify.NextDirectory("/nested/dir/dir-in-archive/sub-dir", int64(os.ModePerm))
   204  				if runtime.GOOS != "windows" {
   205  					verify.NextSymLink("/nested/dir/dir-in-archive/sub-dir/link-file", "../some-file.txt")
   206  				}
   207  			})
   208  		})
   209  
   210  		when("mode is set to 0755", func() {
   211  			it("writes a tar to the dest dir with 0755", func() {
   212  				fh, err := os.Create(filepath.Join(tmpDir, "some.tar"))
   213  				h.AssertNil(t, err)
   214  
   215  				tw := tar.NewWriter(fh)
   216  
   217  				err = archive.WriteDirToTar(tw, src, "/nested/dir/dir-in-archive", 1234, 2345, 0755, true, false, nil)
   218  				h.AssertNil(t, err)
   219  				h.AssertNil(t, tw.Close())
   220  				h.AssertNil(t, fh.Close())
   221  
   222  				file, err := os.Open(filepath.Join(tmpDir, "some.tar"))
   223  				h.AssertNil(t, err)
   224  				defer file.Close()
   225  
   226  				tr := tar.NewReader(file)
   227  
   228  				verify := h.NewTarVerifier(t, tr, 1234, 2345)
   229  				verify.NextFile("/nested/dir/dir-in-archive/some-file.txt", "some-content", 0755)
   230  				verify.NextDirectory("/nested/dir/dir-in-archive/sub-dir", 0755)
   231  				if runtime.GOOS != "windows" {
   232  					verify.NextSymLink("/nested/dir/dir-in-archive/sub-dir/link-file", "../some-file.txt")
   233  				}
   234  			})
   235  		})
   236  
   237  		when("includeRoot is true", func() {
   238  			it("writes a tar to the root dir with the provided mode", func() {
   239  				fh, err := os.Create(filepath.Join(tmpDir, "some.tar"))
   240  				h.AssertNil(t, err)
   241  
   242  				tw := tar.NewWriter(fh)
   243  
   244  				err = archive.WriteDirToTar(tw, src, "/nested/dir/dir-in-archive", 1234, 2345, 0777, true, true, nil)
   245  				h.AssertNil(t, err)
   246  				h.AssertNil(t, tw.Close())
   247  				h.AssertNil(t, fh.Close())
   248  
   249  				file, err := os.Open(filepath.Join(tmpDir, "some.tar"))
   250  				h.AssertNil(t, err)
   251  				defer file.Close()
   252  
   253  				tr := tar.NewReader(file)
   254  
   255  				verify := h.NewTarVerifier(t, tr, 1234, 2345)
   256  				verify.NextDirectory("/nested/dir/dir-in-archive", int64(os.ModePerm))
   257  			})
   258  			when("mode is set to -1", func() {
   259  				it("writes a tar to the root dir with default (0777) dir mode", func() {
   260  					fh, err := os.Create(filepath.Join(tmpDir, "some.tar"))
   261  					h.AssertNil(t, err)
   262  
   263  					tw := tar.NewWriter(fh)
   264  
   265  					err = archive.WriteDirToTar(tw, src, "/nested/dir/dir-in-archive", 1234, 2345, -1, true, true, nil)
   266  					h.AssertNil(t, err)
   267  					h.AssertNil(t, tw.Close())
   268  					h.AssertNil(t, fh.Close())
   269  
   270  					file, err := os.Open(filepath.Join(tmpDir, "some.tar"))
   271  					h.AssertNil(t, err)
   272  					defer file.Close()
   273  
   274  					tr := tar.NewReader(file)
   275  
   276  					verify := h.NewTarVerifier(t, tr, 1234, 2345)
   277  					verify.NextDirectory("/nested/dir/dir-in-archive", 0777)
   278  					verify.NextFile("/nested/dir/dir-in-archive/some-file.txt", "some-content", fileMode(t, filepath.Join(src, "some-file.txt")))
   279  					verify.NextDirectory("/nested/dir/dir-in-archive/sub-dir", fileMode(t, filepath.Join(src, "sub-dir")))
   280  					if runtime.GOOS != "windows" {
   281  						verify.NextSymLink("/nested/dir/dir-in-archive/sub-dir/link-file", "../some-file.txt")
   282  					}
   283  				})
   284  			})
   285  		})
   286  
   287  		when("has file filter", func() {
   288  			it("does not add files against the file filter", func() {
   289  				tarFile := filepath.Join(tmpDir, "some.tar")
   290  				fh, err := os.Create(tarFile)
   291  				h.AssertNil(t, err)
   292  
   293  				tw := tar.NewWriter(fh)
   294  
   295  				err = archive.WriteDirToTar(tw, src, "/nested/dir/dir-in-archive", 1234, 2345, 0777, true, false, func(path string) bool {
   296  					return !strings.Contains(path, "some-file.txt")
   297  				})
   298  				h.AssertNil(t, err)
   299  				h.AssertNil(t, tw.Close())
   300  				h.AssertNil(t, fh.Close())
   301  
   302  				file, err := os.Open(filepath.Join(tmpDir, "some.tar"))
   303  				h.AssertNil(t, err)
   304  				defer file.Close()
   305  
   306  				tr := tar.NewReader(file)
   307  
   308  				verify := h.NewTarVerifier(t, tr, 1234, 2345)
   309  				verify.NextDirectory("/nested/dir/dir-in-archive/sub-dir", int64(os.ModePerm))
   310  				if runtime.GOOS != "windows" {
   311  					verify.NextSymLink("/nested/dir/dir-in-archive/sub-dir/link-file", "../some-file.txt")
   312  				}
   313  			})
   314  
   315  			it("filter is only handed relevant section of the filepath", func() {
   316  				tarFile := filepath.Join(tmpDir, "some.tar")
   317  				fh, err := os.Create(tarFile)
   318  				h.AssertNil(t, err)
   319  
   320  				tw := tar.NewWriter(fh)
   321  
   322  				err = archive.WriteDirToTar(tw, src, "/nested/dir/dir-in-archive", 1234, 2345, 0777, true, false, func(path string) bool {
   323  					return !strings.Contains(path, "dir-to-tar")
   324  				})
   325  				h.AssertNil(t, err)
   326  				h.AssertNil(t, tw.Close())
   327  				h.AssertNil(t, fh.Close())
   328  
   329  				file, err := os.Open(filepath.Join(tmpDir, "some.tar"))
   330  				h.AssertNil(t, err)
   331  				defer file.Close()
   332  
   333  				tr := tar.NewReader(file)
   334  
   335  				verify := h.NewTarVerifier(t, tr, 1234, 2345)
   336  				verify.NextFile("/nested/dir/dir-in-archive/some-file.txt", "some-content", int64(os.ModePerm))
   337  				verify.NextDirectory("/nested/dir/dir-in-archive/sub-dir", int64(os.ModePerm))
   338  				if runtime.GOOS != "windows" {
   339  					verify.NextSymLink("/nested/dir/dir-in-archive/sub-dir/link-file", "../some-file.txt")
   340  				}
   341  			})
   342  		})
   343  
   344  		when("normalize mod time is false", func() {
   345  			it("does not normalize mod times", func() {
   346  				tarFile := filepath.Join(tmpDir, "some.tar")
   347  				fh, err := os.Create(tarFile)
   348  				h.AssertNil(t, err)
   349  
   350  				tw := tar.NewWriter(fh)
   351  
   352  				err = archive.WriteDirToTar(tw, src, "/foo", 1234, 2345, 0777, false, false, nil)
   353  				h.AssertNil(t, err)
   354  				h.AssertNil(t, tw.Close())
   355  				h.AssertNil(t, fh.Close())
   356  
   357  				h.AssertOnTarEntry(t, tarFile, "/foo/some-file.txt",
   358  					h.DoesNotHaveModTime(archive.NormalizedDateTime),
   359  				)
   360  			})
   361  		})
   362  
   363  		when("normalize mod time is true", func() {
   364  			it("normalizes mod times", func() {
   365  				tarFile := filepath.Join(tmpDir, "some.tar")
   366  				fh, err := os.Create(tarFile)
   367  				h.AssertNil(t, err)
   368  
   369  				tw := tar.NewWriter(fh)
   370  
   371  				err = archive.WriteDirToTar(tw, src, "/foo", 1234, 2345, 0777, true, false, nil)
   372  				h.AssertNil(t, err)
   373  				h.AssertNil(t, tw.Close())
   374  				h.AssertNil(t, fh.Close())
   375  
   376  				h.AssertOnTarEntry(t, tarFile, "/foo/some-file.txt",
   377  					h.HasModTime(archive.NormalizedDateTime),
   378  				)
   379  			})
   380  		})
   381  
   382  		when("is posix", func() {
   383  			it.Before(func() {
   384  				h.SkipIf(t, runtime.GOOS == "windows", "Skipping on windows")
   385  			})
   386  
   387  			when("socket is present", func() {
   388  				var (
   389  					err        error
   390  					tmpSrcDir  string
   391  					fakeSocket net.Listener
   392  				)
   393  
   394  				it.Before(func() {
   395  					tmpSrcDir, err = os.MkdirTemp("", "socket-test")
   396  					h.AssertNil(t, err)
   397  
   398  					fakeSocket, err = net.Listen(
   399  						"unix",
   400  						filepath.Join(tmpSrcDir, "fake-socket"),
   401  					)
   402  
   403  					err = os.WriteFile(filepath.Join(tmpSrcDir, "fake-file"), []byte("some-content"), 0777)
   404  					h.AssertNil(t, err)
   405  				})
   406  
   407  				it.After(func() {
   408  					os.RemoveAll(tmpSrcDir)
   409  					fakeSocket.Close()
   410  				})
   411  
   412  				it("silently ignore socket", func() {
   413  					fh, err := os.Create(filepath.Join(tmpDir, "some.tar"))
   414  					h.AssertNil(t, err)
   415  
   416  					tw := tar.NewWriter(fh)
   417  
   418  					err = archive.WriteDirToTar(tw, tmpSrcDir, "/nested/dir/dir-in-archive", 1234, 2345, 0777, true, false, nil)
   419  					h.AssertNil(t, err)
   420  					h.AssertNil(t, tw.Close())
   421  					h.AssertNil(t, fh.Close())
   422  
   423  					file, err := os.Open(filepath.Join(tmpDir, "some.tar"))
   424  					h.AssertNil(t, err)
   425  					defer file.Close()
   426  
   427  					tr := tar.NewReader(file)
   428  
   429  					verify := h.NewTarVerifier(t, tr, 1234, 2345)
   430  					verify.NextFile(
   431  						"/nested/dir/dir-in-archive/fake-file",
   432  						"some-content",
   433  						0777,
   434  					)
   435  					verify.NoMoreFilesExist()
   436  				})
   437  			})
   438  		})
   439  
   440  		when("hard link files are present", func() {
   441  			it.Before(func() {
   442  				src = filepath.Join("testdata", "dir-to-tar-with-hardlink")
   443  				// create a hard link
   444  				err := os.Link(filepath.Join(src, "original-file"), filepath.Join(src, "original-file-2"))
   445  				h.AssertNil(t, err)
   446  			})
   447  
   448  			it.After(func() {
   449  				os.RemoveAll(filepath.Join(src, "original-file-2"))
   450  			})
   451  
   452  			it("tar file file doesn't include duplicated data", func() {
   453  				outputFilename := filepath.Join(tmpDir, "file-with-hard-links.tar")
   454  				fh, err := os.Create(outputFilename)
   455  				h.AssertNil(t, err)
   456  
   457  				tw := tar.NewWriter(fh)
   458  				err = archive.WriteDirToTar(tw, src, "/nested/dir", 1234, 2345, 0777, true, false, nil)
   459  
   460  				h.AssertNil(t, err)
   461  				h.AssertNil(t, tw.Close())
   462  				h.AssertNil(t, fh.Close())
   463  				h.AssertOnTarEntries(t, outputFilename,
   464  					"/nested/dir/original-file",
   465  					"/nested/dir/original-file-2",
   466  					h.AreEquivalentHardLinks(),
   467  				)
   468  			})
   469  		})
   470  	})
   471  
   472  	when("#WriteZipToTar", func() {
   473  		var src string
   474  		it.Before(func() {
   475  			src = filepath.Join("testdata", "zip-to-tar.zip")
   476  		})
   477  
   478  		when("mode is set to 0777", func() {
   479  			it("writes a tar to the dest dir with 0777", func() {
   480  				fh, err := os.Create(filepath.Join(tmpDir, "some.tar"))
   481  				h.AssertNil(t, err)
   482  
   483  				tw := tar.NewWriter(fh)
   484  
   485  				err = archive.WriteZipToTar(tw, src, "/nested/dir/dir-in-archive", 1234, 2345, 0777, true, nil)
   486  				h.AssertNil(t, err)
   487  				h.AssertNil(t, tw.Close())
   488  				h.AssertNil(t, fh.Close())
   489  
   490  				file, err := os.Open(filepath.Join(tmpDir, "some.tar"))
   491  				h.AssertNil(t, err)
   492  				defer file.Close()
   493  
   494  				tr := tar.NewReader(file)
   495  
   496  				verify := h.NewTarVerifier(t, tr, 1234, 2345)
   497  				verify.NextFile("/nested/dir/dir-in-archive/some-file.txt", "some-content", 0777)
   498  				verify.NextDirectory("/nested/dir/dir-in-archive/sub-dir", 0777)
   499  				verify.NextSymLink("/nested/dir/dir-in-archive/sub-dir/link-file", "../some-file.txt")
   500  			})
   501  		})
   502  
   503  		when("mode is set to -1", func() {
   504  			it("writes a tar to the dest dir with preexisting file mode", func() {
   505  				fh, err := os.Create(filepath.Join(tmpDir, "some.tar"))
   506  				h.AssertNil(t, err)
   507  
   508  				tw := tar.NewWriter(fh)
   509  
   510  				err = archive.WriteZipToTar(tw, src, "/nested/dir/dir-in-archive", 1234, 2345, -1, true, nil)
   511  				h.AssertNil(t, err)
   512  				h.AssertNil(t, tw.Close())
   513  				h.AssertNil(t, fh.Close())
   514  
   515  				file, err := os.Open(filepath.Join(tmpDir, "some.tar"))
   516  				h.AssertNil(t, err)
   517  				defer file.Close()
   518  
   519  				tr := tar.NewReader(file)
   520  
   521  				verify := h.NewTarVerifier(t, tr, 1234, 2345)
   522  				verify.NextFile("/nested/dir/dir-in-archive/some-file.txt", "some-content", 0644)
   523  				verify.NextDirectory("/nested/dir/dir-in-archive/sub-dir", 0755)
   524  				verify.NextSymLink("/nested/dir/dir-in-archive/sub-dir/link-file", "../some-file.txt")
   525  			})
   526  
   527  			when("files are compressed in fat (MSDOS) format", func() {
   528  				it.Before(func() {
   529  					src = filepath.Join("testdata", "fat-zip-to-tar.zip")
   530  				})
   531  
   532  				it("writes a tar to the dest dir with 0777", func() {
   533  					fh, err := os.Create(filepath.Join(tmpDir, "some.tar"))
   534  					h.AssertNil(t, err)
   535  
   536  					tw := tar.NewWriter(fh)
   537  
   538  					err = archive.WriteZipToTar(tw, src, "/nested/dir/dir-in-archive", 1234, 2345, -1, true, nil)
   539  					h.AssertNil(t, err)
   540  					h.AssertNil(t, tw.Close())
   541  					h.AssertNil(t, fh.Close())
   542  
   543  					file, err := os.Open(filepath.Join(tmpDir, "some.tar"))
   544  					h.AssertNil(t, err)
   545  					defer file.Close()
   546  
   547  					tr := tar.NewReader(file)
   548  
   549  					verify := h.NewTarVerifier(t, tr, 1234, 2345)
   550  					verify.NextFile("/nested/dir/dir-in-archive/some-file.txt", "some-content", 0777)
   551  					verify.NoMoreFilesExist()
   552  				})
   553  			})
   554  		})
   555  
   556  		when("has file filter", func() {
   557  			it("follows it when adding files", func() {
   558  				fh, err := os.Create(filepath.Join(tmpDir, "some.tar"))
   559  				h.AssertNil(t, err)
   560  
   561  				tw := tar.NewWriter(fh)
   562  
   563  				err = archive.WriteZipToTar(tw, src, "/nested/dir/dir-in-archive", 1234, 2345, 0777, true, func(path string) bool {
   564  					return !strings.Contains(path, "some-file.txt")
   565  				})
   566  				h.AssertNil(t, err)
   567  				h.AssertNil(t, tw.Close())
   568  				h.AssertNil(t, fh.Close())
   569  
   570  				file, err := os.Open(filepath.Join(tmpDir, "some.tar"))
   571  				h.AssertNil(t, err)
   572  				defer file.Close()
   573  
   574  				tr := tar.NewReader(file)
   575  
   576  				verify := h.NewTarVerifier(t, tr, 1234, 2345)
   577  				verify.NextDirectory("/nested/dir/dir-in-archive/sub-dir", 0777)
   578  				verify.NextSymLink("/nested/dir/dir-in-archive/sub-dir/link-file", "../some-file.txt")
   579  			})
   580  		})
   581  
   582  		when("normalize mod time is false", func() {
   583  			it("does not normalize mod times", func() {
   584  				tarFile := filepath.Join(tmpDir, "some.tar")
   585  				fh, err := os.Create(tarFile)
   586  				h.AssertNil(t, err)
   587  
   588  				tw := tar.NewWriter(fh)
   589  
   590  				err = archive.WriteZipToTar(tw, src, "/foo", 1234, 2345, 0777, false, nil)
   591  				h.AssertNil(t, err)
   592  				h.AssertNil(t, tw.Close())
   593  				h.AssertNil(t, fh.Close())
   594  
   595  				h.AssertOnTarEntry(t, tarFile, "/foo/some-file.txt",
   596  					h.DoesNotHaveModTime(archive.NormalizedDateTime),
   597  				)
   598  			})
   599  		})
   600  
   601  		when("normalize mod time is true", func() {
   602  			it("normalizes mod times", func() {
   603  				tarFile := filepath.Join(tmpDir, "some.tar")
   604  				fh, err := os.Create(tarFile)
   605  				h.AssertNil(t, err)
   606  
   607  				tw := tar.NewWriter(fh)
   608  
   609  				err = archive.WriteZipToTar(tw, src, "/foo", 1234, 2345, 0777, true, nil)
   610  				h.AssertNil(t, err)
   611  				h.AssertNil(t, tw.Close())
   612  				h.AssertNil(t, fh.Close())
   613  
   614  				h.AssertOnTarEntry(t, tarFile, "/foo/some-file.txt",
   615  					h.HasModTime(archive.NormalizedDateTime),
   616  				)
   617  			})
   618  		})
   619  	})
   620  
   621  	when("#IsZip", func() {
   622  		when("file is a zip file", func() {
   623  			it("returns true", func() {
   624  				path := filepath.Join("testdata", "zip-to-tar.zip")
   625  				isZip, err := archive.IsZip(path)
   626  				h.AssertNil(t, err)
   627  				h.AssertTrue(t, isZip)
   628  			})
   629  		})
   630  
   631  		when("file is a jar file", func() {
   632  			it("returns true", func() {
   633  				path := filepath.Join("testdata", "jar-file.jar")
   634  				isZip, err := archive.IsZip(path)
   635  				h.AssertNil(t, err)
   636  				h.AssertTrue(t, isZip)
   637  			})
   638  		})
   639  
   640  		when("file is not a zip file", func() {
   641  			when("file has some content", func() {
   642  				it("returns false", func() {
   643  					file, err := os.CreateTemp(tmpDir, "file.txt")
   644  					h.AssertNil(t, err)
   645  					defer file.Close()
   646  
   647  					err = os.WriteFile(file.Name(), []byte("content"), os.ModePerm)
   648  					h.AssertNil(t, err)
   649  
   650  					isZip, err := archive.IsZip(file.Name())
   651  					h.AssertNil(t, err)
   652  					h.AssertFalse(t, isZip)
   653  				})
   654  			})
   655  
   656  			when("file doesn't have content", func() {
   657  				it("returns false", func() {
   658  					file, err := os.CreateTemp(tmpDir, "file.txt")
   659  					h.AssertNil(t, err)
   660  					defer file.Close()
   661  
   662  					isZip, err := archive.IsZip(file.Name())
   663  					h.AssertNil(t, err)
   664  					h.AssertFalse(t, isZip)
   665  				})
   666  			})
   667  		})
   668  	})
   669  }
   670  
   671  func fileMode(t *testing.T, path string) int64 {
   672  	t.Helper()
   673  	info, err := os.Stat(path)
   674  	if err != nil {
   675  		t.Fatalf("failed to stat %s", path)
   676  	}
   677  	mode := int64(info.Mode() & os.ModePerm)
   678  	return mode
   679  }